Exemple #1
0
    def __init__(self, conn):
        """ Initialize the protocol.

            @param conn:        Connection instance which provides callback
                                functions.
            @type  conn:        rce.comm.client.RCE
        """
        self._connection = conn
        self._assembler = MessageAssembler(self, 60)
        self._registered = False
Exemple #2
0
    def __init__(self, realm):
        """ Initialize the Protocol.

            @param realm:       Robot realm implementing necessary callback
                                methods.
            @type  realm:       rce.comm.interfaces.IRobotRealm
        """
        verifyObject(IRobotRealm, realm)

        self._realm = realm
        self._assembler = MessageAssembler(self, self.MSG_QUEUE_TIMEOUT)
        self._avatar = None
Exemple #3
0
class RobotWebSocketProtocol(WebSocketServerProtocol):
    """ Protocol which is used for the connections from the robots to the
        robot manager.
    """
    implements(IServersideProtocol)

    # CONFIG
    MSG_QUEUE_TIMEOUT = 60

    def __init__(self, realm):
        """ Initialize the Protocol.

            @param realm:       Robot realm implementing necessary callback
                                methods.
            @type  realm:       rce.comm.interfaces.IRobotRealm
        """
        verifyObject(IRobotRealm, realm)

        self._realm = realm
        self._assembler = MessageAssembler(self, self.MSG_QUEUE_TIMEOUT)
        self._avatar = None

    def onConnect(self, req):
        """ Method is called by the Autobahn engine when a request to establish
            a connection has been received.

            @param req:     Connection Request object.
            @type  req:     autobahn.websocket.ConnectionRequest

            @return:        Deferred which fires callback with None or errback
                            with autobahn.websocket.HttpException
        """
        params = req.params

        try:
            userID = params['userID']
            robotID = params['robotID']
            password = params['password']
        except KeyError as e:
            raise HttpException(httpstatus.HTTP_STATUS_CODE_BAD_REQUEST[0],
                                'Request is missing parameter: {0}'.format(e))

        for name, param in [('userID', userID), ('robotID', robotID),
                            ('password', password)]:
            if len(param) != 1:
                raise HttpException(httpstatus.HTTP_STATUS_CODE_BAD_REQUEST[0],
                                    "Parameter '{0}' has to be unique in "
                                    'request.'.format(name))

        d = self._realm.login(userID[0], robotID[0], password[0])
        d.addCallback(self._authenticate_success)
        d.addErrback(self._authenticate_failed)
        return d

    def _authenticate_success(self, avatar):
        """ Method is called by deferred when the connection has been
            successfully authenticated while being in 'onConnect'.
        """
        verifyObject(IRobot, avatar)
        verifyObject(IMessageReceiver, avatar)

        self._realm.registerWebsocketProtocol(avatar, self)
        self._avatar = avatar
        self._assembler.start()

    def _authenticate_failed(self, e):
        """ Method is called by deferred when the connection could not been
            authenticated while being in 'onConnect'.
        """
        if e.check(InvalidRequest):
            code = httpstatus.HTTP_STATUS_CODE_BAD_REQUEST[0]
            msg = e.getErrorMessage()
        elif e.check(UnauthorizedLogin):
            code = httpstatus.HTTP_STATUS_CODE_UNAUTHORIZED[0]
            msg = httpstatus.HTTP_STATUS_CODE_UNAUTHORIZED[1]
        else:
            e.printTraceback()
            code = httpstatus.HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR[0]
            msg = httpstatus.HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR[1]

        return Failure(HttpException(code, msg))

    def processCompleteMessage(self, msg):
        """ Process complete messages by calling the appropriate handler for
            the manager. (Called by rce.comm.assembler.MessageAssembler)
        """
        try:
            msgType = msg['type']
            data = msg['data']
        except KeyError as e:
            raise InvalidRequest('Message is missing key: {0}'.format(e))

        if msgType == types.DATA_MESSAGE:
            self._process_DataMessage(data)
        elif msgType == types.CONFIGURE_COMPONENT:
            self._process_configureComponent(data)
        elif msgType == types.CONFIGURE_CONNECTION:
            self._process_configureConnection(data)
        elif msgType == types.CREATE_CONTAINER:
            self._process_createContainer(data)
        elif msgType == types.DESTROY_CONTAINER:
            self._process_destroyContainer(data)
        else:
            raise InvalidRequest('This message type is not supported.')

    def _process_createContainer(self, data):
        """ Internally used method to process a request to create a container.
        """
        try:
            self._avatar.createContainer(data['containerTag'],
                                         data.get('containerData', {}))
        except KeyError as e:
            raise InvalidRequest("Can not process 'CreateContainer' request. "
                                 'Missing key: {0}'.format(e))

    def _process_destroyContainer(self, data):
        """ Internally used method to process a request to destroy a container.
        """
        try:
            self._avatar.destroyContainer(data['containerTag'])
        except KeyError as e:
            raise InvalidRequest("Can not process 'DestroyContainer' request. "
                                 'Missing key: {0}'.format(e))

    def _process_configureComponent(self, data):
        """ Internally used method to process a request to configure
            components.
        """
        for node in data.pop('addNodes', []):
            try:
                self._avatar.addNode(node['containerTag'],
                                     node['nodeTag'],
                                     node['pkg'],
                                     node['exe'],
                                     node.get('args', ''),
                                     node.get('name', ''),
                                     node.get('namespace', ''))
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'addNodes' is missing key: "
                                     '{0}'.format(e))

        for node in data.pop('removeNodes', []):
            try:
                self._avatar.removeNode(node['containerTag'],
                                        node['nodeTag'])
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'removeNodes' is missing key: "
                                     '{0}'.format(e))

        for conf in data.pop('addInterfaces', []):
            try:
                self._avatar.addInterface(conf['endpointTag'],
                                          conf['interfaceTag'],
                                          conf['interfaceType'],
                                          conf['className'],
                                          conf.get('addr', ''))
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'addInterfaces' is missing "
                                     'key: {0}'.format(e))

        for conf in data.pop('removeInterfaces', []):
            try:
                self._avatar.removeInterface(conf['endpointTag'],
                                             conf['interfaceTag'])
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'removeInterfaces' is missing "
                                     'key: {0}'.format(e))

        for param in data.pop('setParam', []):
            try:
                self._avatar.addParameter(param['containerTag'],
                                          param['name'],
                                          param['value'])
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'setParam' is missing key: "
                                     '{0}'.format(e))

        for param in data.pop('deleteParam', []):
            try:
                self._avatar.removeParameter(param['containerTag'],
                                             param['name'])
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'deleteParam' is missing key: "
                                     '{0}'.format(e))

    def _process_configureConnection(self, data):
        """ Internally used method to process a request to configure
            connections.
        """
        for conf in data.pop('connect', []):
            try:
                self._avatar.addConnection(conf['tagA'], conf['tagB'])
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'connect' is missing key: "
                                     '{0}'.format(e))

        for conf in data.pop('disconnect', []):
            try:
                self._avatar.removeConnection(conf['tagA'], conf['tagB'])
            except KeyError as e:
                raise InvalidRequest("Can not process 'ConfigureComponent' "
                                     "request. 'disconnect' is missing key: "
                                     '{0}'.format(e))

    def _process_DataMessage(self, data):
        """ Internally used method to process a data message.
        """
        try:
            iTag = str(data['iTag'])
            mType = str(data['type'])
            msgID = str(data['msgID'])
            msg = data['msg']
        except KeyError as e:
            raise InvalidRequest("Can not process 'DataMessage' request. "
                                 'Missing key: {0}'.format(e))

        if len(msgID) > 255:
            raise InvalidRequest("Can not process 'DataMessage' request. "
                                 'Message ID can not be longer than 255.')

        self._avatar.processReceivedMessage(iTag, mType, msgID, msg)

    def onMessage(self, msg, binary):
        """ Method is called by the Autobahn engine when a message has been
            received from the client.

            @param msg:         Message which was received as a string.
            @type  msg:         str

            @param binary:      Flag which is True if the message has binary
                                format and False otherwise.
            @type  binary:      bool
        """
#        print('WebSocket: Received new message from client. '
#              '(binary={0})'.format(binary))

        try:
            self._assembler.processMessage(msg, binary)
        except InvalidRequest as e:
            self.sendErrorMessage('Invalid Request: {0}'.format(e))
        except DeadConnection:
            self.sendErrorMessage('Dead Connection')
            self.dropConnection()
        except:
            import traceback
            traceback.print_exc()
            self.sendErrorMessage('Fatal Error')

    def sendMessage(self, msg):
        """ Internally used method to send a message to the robot.

            Should not be used from outside the Protocol; instead use the
            methods 'sendDataMessage' or 'sendErrorMessage'.

            (Overwrites method from autobahn.websocket.WebSocketServerProtocol)

            @param msg:     Message which should be sent.
        """
        uriBinary, msgURI = recursiveBinarySearch(msg)

        WebSocketServerProtocol.sendMessage(self, json.dumps(msgURI))

        for binData in uriBinary:
            WebSocketServerProtocol.sendMessage(self,
                binData[0] + binData[1].getvalue(), binary=True)

    def sendDataMessage(self, iTag, clsName, msgID, msg):
        """ Callback for Connection object to send a data message to the robot
            using this websocket connection.

            @param iTag:        Tag which is used to identify the interface
                                from the message is sent.
            @type  iTag:        str

            @param clsName:     Message type/Service type consisting of the
                                package and the name of the message/service,
                                i.e. 'std_msgs/Int32'.
            @type  clsName:     str

            @param msgID:       Message ID which can be used to get a
                                correspondence between request and response
                                message for a service call.
            @type  msgID:       str

            @param msg:         Message which should be sent. It has to be a
                                JSON compatible dictionary where part or the
                                complete message can be replaced by a StringIO
                                instance which is interpreted as binary data.
            @type  msg:         {str : {} / base_types / StringIO} / StringIO
        """
        self.sendMessage({'type' : types.DATA_MESSAGE,
                          'data' : {'iTag' : iTag, 'type' : clsName,
                                    'msgID' : msgID, 'msg' : msg}})

    def sendErrorMessage(self, msg):
        """ Callback for Connection object to send an error message to the robot
            using this websocket connection.

            @param msg:         Message which should be sent to the robot.
            @type  msg:         str
        """
        self.sendMessage({'data' : msg, 'type' : types.ERROR})

    def onClose(self, wasClean, code, reason):
        """ Method is called by the Autobahn engine when the connection has
            been lost.
        """
        if self._avatar:
            self._realm.unregisterWebsocketProtocol(self._avatar, self)

        self._assembler.stop()

        self._avatar = None
        self._assembler = None
Exemple #4
0
class RCERobotProtocol(WebSocketClientProtocol):
    """ WebSocket client protocol which is used to communicate with the Robot
        Manager.
    """
    def __init__(self, conn):
        """ Initialize the protocol.

            @param conn:        Connection instance which provides callback
                                functions.
            @type  conn:        rce.comm.client.RCE
        """
        self._connection = conn
        self._assembler = MessageAssembler(self, 60)
        self._registered = False

    def onOpen(self):
        """ This method is called by twisted as soon as the WebSocket
            connection has been successfully established.
        """
        self._assembler.start()
        self._connection.registerConnection(self)
        self._registered = True

    def onMessage(self, msg, binary):
        """ This method is called by twisted when a new message has been
            received.
        """
        self._assembler.processMessage(msg, binary)

    def processCompleteMessage(self, msg):
        """ Callback for MessageAssembler which will be called as soon as a
            message has been completed and is ready for processing.
        """
        self._connection.receivedMessage(msg)

    def sendMessage(self, msg):
        """ Internally used method to send messages via WebSocket connection.
            Thread-safe implementation.

            @param msg:         Message which should be sent.
        """
        binaries, msg = recursiveBinarySearch(msg)
        msg = json.dumps(msg)

        if isInIOThread():
            self._send(msg, binaries)
        else:
            self._connection.reactor.callFromThread(self._send, msg, binaries)

    def _send(self, msg, binaries):
        """ Internally used method to send messages via WebSocket connection.
            Handles the actual sending of the message. (Not thread-safe; use
            sendMessage instead.)
        """
        WebSocketClientProtocol.sendMessage(self, msg)

        for data in binaries:
            binMsg = data[0] + data[1].getvalue()
            WebSocketClientProtocol.sendMessage(self, binMsg, binary=True)

    def onClose(self, *args):
        """ This method is called by twisted when the connection has been
            closed.
        """
        if self._registered:
            self._connection.unregisterConnection(self)
            self._assembler.stop()
            self._registered = False

    def failHandshake(self, reason):
        """ This method is called by twisted when the connection could not be
            initialized.
        """
        print(reason)
        WebSocketClientProtocol.failHandshake(self, reason)