예제 #1
0
 def __init__(self, connection, publish_snapshot_function):
     super().__init__()
     self._is_valid_connection = True
     # Network
     self.connection = connection
     self.files_handler = _FileHandler()
     # Protocol
     self.supported_fields = DEFAULT_SUPPORTED_FIELDS
     self.protocol = Protocol()
     # Messages
     self.messages = MessageQueueMessages()
     # Publish functions
     self.publish_snapshot_function = publish_snapshot_function
예제 #2
0
 def __init__(self,
              database_type=None,
              database_host=None,
              database_port=None):
     # DataBase
     self._database = _DataBaseCortex(database_type, database_host,
                                      database_port)
     # Messages
     self.messages = MessageQueueMessages()
     # Save methods
     self.SAVE_METHODS           =                       \
         {                                               \
             'pose'          : self.save_parsed_pose,    \
             'user_feelings' : self.save_user_feelings,  \
             'color_image'   : self.save_color_image,    \
             'depth_image'   : self.save_depth_image,    \
         }
예제 #3
0
 def snapshot_publish(message):
     snapshot = MessageQueueMessages().get_message(
         MessageQueueMessagesTyeps.RAW_SNAPSHOT_MESSAGE).deserialize(
             message)
     user_id = snapshot.user_info.user_id
     test_server_snapshot_published_ids.append(user_id)
     # Update shared-memory published snapshots counter
     test_server_snapshot_published_counter.value += 1
예제 #4
0
 def __init__(self,
              parser_type,
              message_queue_type=None,
              message_queue_host=None,
              message_queue_port=None):
     """
     Initializes a new parser of given type, which consumes, parses and published to a messages-queue.
 
     :param parser_type: The parser type.
     :param message_queue_type: Message queue type, if empty - default will be selected.
     :param message_queue_host: Message queue host, if empty - default will be selected.
     :param message_queue_port: Message queue port, if empty - default will be selected.    
     """
     self.parser_type = parser_type
     self.parser_name = ParserService.get_parser_name(parser_type)
     self.parser_handler = ParserHandler(parser_type)
     self.initialized = self.parser_handler.initialized
     # Message Queue
     self.message_queue_type = message_queue_type
     self.message_queue_host = message_queue_host
     self.message_queue_port = message_queue_port
     # Messages
     self.messages = MessageQueueMessages()
예제 #5
0
class ParserService:
    """    
    Server Parser class, runs the micro-service logic

    :ivar ParserService.SERVICE_TYPE: name of the service
    """
    SERVICE_TYPE = 'parser'

    @staticmethod
    def get_parser_name(parser_type):
        """
        Returns parser name.
    
        :param parser_type: The parser type.    
        """
        return f'{parser_type}.parser'

    def __init__(self,
                 parser_type,
                 message_queue_type=None,
                 message_queue_host=None,
                 message_queue_port=None):
        """
        Initializes a new parser of given type, which consumes, parses and published to a messages-queue.
    
        :param parser_type: The parser type.
        :param message_queue_type: Message queue type, if empty - default will be selected.
        :param message_queue_host: Message queue host, if empty - default will be selected.
        :param message_queue_port: Message queue port, if empty - default will be selected.    
        """
        self.parser_type = parser_type
        self.parser_name = ParserService.get_parser_name(parser_type)
        self.parser_handler = ParserHandler(parser_type)
        self.initialized = self.parser_handler.initialized
        # Message Queue
        self.message_queue_type = message_queue_type
        self.message_queue_host = message_queue_host
        self.message_queue_port = message_queue_port
        # Messages
        self.messages = MessageQueueMessages()

    # Snapshot Methods Section
    def desrialize_raw_snapshot_message(self, incoming_snapshot_message):
        """
        Desrializes a raw snapshot message, as received from the message-queue.
    
        :param parser_type: The parser type.
        :param message_queue_type: Message queue type, if empty - default will be selected.
        :param message_queue_host: Message queue host, if empty - default will be selected.
        :param message_queue_port: Message queue port, if empty - default will be selected.    
        """
        raw_snapshot_message =                                      \
            self.messages.get_message(                              \
                MessageQueueMessagesTyeps.RAW_SNAPSHOT_MESSAGE).deserialize(incoming_snapshot_message)
        return raw_snapshot_message

    def _get_context(self, raw_snapshot_message):
        """
        Gets `ParserContext` from a raw snapshot message.
    
        :param raw_snapshot_message: The raw snapshot message.    
        """
        return ParserContext(raw_snapshot_message.user_info,
                             raw_snapshot_message.snapshot_uuid,
                             self.parser_type)

    # Parse Message Section
    def parse_message(self, incoming_snapshot_message):
        """
        Parses an incoming snapshot message.
    
        :param incoming_snapshot_message: The incoming snapshot message to parse.            
        :return: If error occurs during parsing, None would be returned, If parsed successfully, a parsed snapshot message would be returned.
        """
        try:
            raw_snapshot_message = self.desrialize_raw_snapshot_message(
                incoming_snapshot_message)
        except Exception as e:
            logger.error(f'error deserializing raw snapshot message : {e}')
            return None
        context = self._get_context(raw_snapshot_message)
        export_result = self.parser_handler.export_parse(
            context, raw_snapshot_message)
        if not export_result:
            logger.info(f'{self.parser_name} Failed to Parse message')
            return None
        is_uri, result, snapshot, metadata = export_result
        parsed_snapshot_message             =                       \
            self.messages.get_message(                              \
                MessageQueueMessagesTyeps.PARSED_SNAPSHOT_MESSAGE)( \
                    context.user_info,                              \
                    context.snapshot_uuid,                          \
                    snapshot.timestamp,                             \
                    self.parser_type,                               \
                    result,                                         \
                    is_uri,                                         \
                    metadata)
        logger.info(f'{self.parser_name} Parsed message')
        return parsed_snapshot_message

    # Generates parse callback with custom arguments - by this currying function
    def publish_parsed_callback(self):
        """
        Decorator for callback used to parse and publish incoming snapshot message.        
        """
        def parse_and_publish(incoming_snapshot_message):
            logger.info(f'{self.parser_name} Received message')
            parsed_snapshot_message = self.parse_message(
                incoming_snapshot_message)
            if parsed_snapshot_message:
                self.publish_function(parsed_snapshot_message.serialize(),
                                      publisher_name=self.parser_name)
                logger.info(f'{self.parser_name} Sent message')

        return parse_and_publish

    # Core Logic Method Section
    def run(self):
        """
        Runs the parser micro-service logic, consuming messages from the message queue, parsing them and publishing the results to the message queue.        
        """
        if not self.initialized:
            logger.error(ERROR_DURING_INITIALIZATION_CANT_RUN_ERROR_MESSAGE)
            return
        mq_context_factory = MessageQueueContextFactory(
            self.message_queue_type)
        self.register_publish_parsed(mq_context_factory)
        self.consume_messages(mq_context_factory)

    # Message Queue Methods Section
    # Consume snapshot messages
    def consume_messages(self, mq_context_factory):
        """
        Consumes messages from the message-queue, and calling the parse callbakc to parse them.
    
        :param mq_context_factory: The context factory used to create context for the consumer.
        """
        message_queue_context   =                   \
            mq_context_factory.get_mq_context(ParserService.SERVICE_TYPE, 'consumers', 'snapshots')
        message_queue_consumer  =                   \
            MessageQueueConsumer(                   \
                self.publish_parsed_callback(),     \
                message_queue_context,              \
                self.message_queue_type,            \
                self.message_queue_host,            \
                self.message_queue_port,            \
                )
        message_queue_consumer.run()

    # Publish parsed
    def register_publish_parsed(self, mq_context_factory):
        """
        Registers publisher to the message-queue, to be called when publishing to the message-queue is needed.
    
        :param mq_context_factory: The context factory used to create context for the publisher.        
        """
        message_queue_context           = \
            mq_context_factory.get_mq_context(ParserService.SERVICE_TYPE, 'publishers', 'parsed_snapshot', name=self.parser_name)
        self.message_queue_publisher = MessageQueuePublisherThread(
            message_queue_context)
        self.publish_function = self.message_queue_publisher.get_publish_function(
        )
        self.message_queue_publisher.run()
예제 #6
0
class SaverMessagesHandler:
    DEFAULT_ENCODING = 'utf-8'

    def __init__(self,
                 database_type=None,
                 database_host=None,
                 database_port=None):
        # DataBase
        self._database = _DataBaseCortex(database_type, database_host,
                                         database_port)
        # Messages
        self.messages = MessageQueueMessages()
        # Save methods
        self.SAVE_METHODS           =                       \
            {                                               \
                'pose'          : self.save_parsed_pose,    \
                'user_feelings' : self.save_user_feelings,  \
                'color_image'   : self.save_color_image,    \
                'depth_image'   : self.save_depth_image,    \
            }

    # Message Handling Methods
    def handle(self, message):
        message = message if isinstance(message, str) else message.decode(
            SaverMessagesHandler.DEFAULT_ENCODING)
        parsed_snapshot_message = self.messages.get_message(                              \
                    MessageQueueMessagesTyeps.PARSED_SNAPSHOT_MESSAGE).deserialize(message)
        # Fetch user id and path
        user_info = parsed_snapshot_message.user_info
        self.save_user(user_info)
        self.save_snapshot(parsed_snapshot_message.snapshot_uuid,
                           user_info.user_id,
                           parsed_snapshot_message.snapshot_timestamp)
        # Fetch parsed field
        field = parsed_snapshot_message.field
        snapshot_uuid = parsed_snapshot_message.snapshot_uuid
        result = parsed_snapshot_message.result
        is_uri = parsed_snapshot_message.is_uri
        metadata = parsed_snapshot_message.metadata
        self.save_parsed(field, snapshot_uuid, result, is_uri, metadata)
        return snapshot_uuid

    # Save Methods
    def save_parsed(self, field, snapshot_uuid, result, is_uri, metadata):
        if field not in self.SAVE_METHODS.keys():
            logger.error(f'saving field {field} is unknown')
            return
        self.SAVE_METHODS[field](snapshot_uuid, result, is_uri, metadata)

    # User Saving Method
    def save_user(self, user_info):
        if self._database.has_user(user_id=user_info.user_id):
            return
        user_id = user_info.user_id
        username = user_info.username
        birth_date = TimeUtils.timestamp_to_dateime(user_info.birth_date)
        gender = user_info.gender
        creation_status =                                   \
            self._database.create_user(                     \
                user_id    = user_id,                       \
                username   = username,                      \
                birth_date = birth_date,                    \
                gender     = gender,                        \
                )
        if creation_status:
            logger.info(f'user {user_id} added successfully!')
        else:
            logger.error(f'failed to add user {user_id}')

    # Snapshot Saving Method
    def save_snapshot(self, snapshot_uuid, user_id, snapshot_timestamp):
        if self._database.has_snapshot(snapshot_uuid=snapshot_uuid):
            return
        creation_status =                                   \
            self._database.create_snapshot(                 \
                snapshot_uuid = snapshot_uuid,              \
                user_id       = user_id,                    \
                timestamp     = snapshot_timestamp          \
                )
        if creation_status:
            logger.info(f'snapshot {snapshot_uuid} added successfully!')
        else:
            logger.error(f'failed to add snapshot {snapshot_uuid}')

    # Fields Saving Methods
    def save_parsed_pose(self, snapshot_uuid, result, is_uri, metadata):
        snapshot_uuid = snapshot_uuid
        pose = json_to_object(result)
        creation_status =                                       \
            self._database.create_pose(                         \
                snapshot_uuid  = snapshot_uuid,                 \
                translation_x  = pose.translation.x,            \
                translation_y  = pose.translation.y,            \
                translation_z  = pose.translation.z,            \
                rotation_x     = pose.rotation.x,               \
                rotation_y     = pose.rotation.y,               \
                rotation_z     = pose.rotation.z,               \
                rotation_w     = pose.rotation.w,               \
                )
        if creation_status:
            logger.info(
                f'pose of snapshot {snapshot_uuid} added successfully!')
        else:
            logger.error(f'failed to add pose of snapshot {snapshot_uuid}')

    def save_user_feelings(self, snapshot_uuid, result, is_uri, metadata):
        snapshot_uuid = snapshot_uuid
        user_feelings = json_to_object(result)
        creation_status =                                       \
            self._database.create_user_feelings(                \
                snapshot_uuid  = snapshot_uuid,                 \
                hunger         = user_feelings.hunger,          \
                thirst         = user_feelings.thirst,          \
                exhaustion     = user_feelings.exhaustion,      \
                happiness      = user_feelings.happiness,       \
                )
        if creation_status:
            logger.info(
                f'user_feelings of snapshot {snapshot_uuid} added successfully!'
            )
        else:
            logger.error(
                f'failed to add user_feelings of snapshot {snapshot_uuid}')

    def save_color_image(self, snapshot_uuid, result, is_uri, metadata):
        snapshot_uuid = snapshot_uuid
        uri = expand_file_path_relative_to_project_root(result)
        width, height = metadata
        creation_status =                                       \
            self._database.create_color_image(                  \
                snapshot_uuid  = snapshot_uuid,                 \
                uri            = uri,                           \
                width          = width,                         \
                height         = height,                        \
                )
        if creation_status:
            logger.info(
                f'color_image of snapshot {snapshot_uuid} added successfully!')
        else:
            logger.error(
                f'failed to add color_image of snapshot {snapshot_uuid}')

    def save_depth_image(self, snapshot_uuid, result, is_uri, metadata):
        snapshot_uuid = snapshot_uuid
        uri = expand_file_path_relative_to_project_root(result)
        width, height = metadata
        creation_status =                                       \
            self._database.create_depth_image(                  \
                snapshot_uuid  = snapshot_uuid,                 \
                uri            = uri,                           \
                width          = width,                         \
                height         = height,                        \
                )
        if creation_status:
            logger.info(
                f'depth_image of snapshot {snapshot_uuid} added successfully!')
        else:
            logger.error(
                f'failed to add depth_image of snapshot {snapshot_uuid}')
예제 #7
0
class ServerHandler(threading.Thread):
    # Constructor Section
    def __init__(self, connection, publish_snapshot_function):
        super().__init__()
        self._is_valid_connection = True
        # Network
        self.connection = connection
        self.files_handler = _FileHandler()
        # Protocol
        self.supported_fields = DEFAULT_SUPPORTED_FIELDS
        self.protocol = Protocol()
        # Messages
        self.messages = MessageQueueMessages()
        # Publish functions
        self.publish_snapshot_function = publish_snapshot_function

    # Methods Section
    # Protocol Methods Section
    # Receives hello message from client
    def receive_hello_message(self):
        try:
            hello_message_bytes = self.connection.receive_message()
        except Exception as e:
            logger.error(f'error receiving hello_message : {e}')
            self._is_valid_connection = False
            return None
        try:
            hello_message = self.protocol.get_message(
                ProtocolMessagesTyeps.HELLO_MESSAGE).read(hello_message_bytes)
        except Exception as e:
            logger.error(f'error parsing hello_message : {e}')
            self._is_valid_connection = False
            return None
        return hello_message

    # Sends configuration message to client
    def send_config_message(self):
        config_message = self.protocol.get_message(
            ProtocolMessagesTyeps.CONFIG_MESSAGE)(self.supported_fields)
        try:
            self.connection.send_message(config_message.serialize())
        except Exception as e:
            logger.error(
                f'error sending config_message to user {self.context.user_info.user_id}: {e}'
            )
            self._is_valid_connection = False
            return

    # Receives snapshot message from client
    def receive_snapshot_message_bytes(self):
        try:
            snapshot_message_bytes = self.connection.receive_message()
        except EOFError as e:
            raise e
        except Exception as e:
            logger.error(
                f'error receiving snapshot_message of user {self.context.user_info.user_id}: {e}'
            )
            self._is_valid_connection = False
            raise EOFError
        return snapshot_message_bytes

    # UUID Methods Section
    def _get_uuid(self):
        return generate_uuid()

    def _get_hash(self, data):
        return get_data_hash(data)

    # File Handling Methods Section
    # Generate path to save file
    @staticmethod
    def _get_save_path(*pathsegments, fname=None, extension=None):
        return _FileHandler.to_safe_file_path(*pathsegments,
                                              fname=fname,
                                              extension=extension)

    # Save file
    def _save_file(self, path, data):
        return self.files_handler.save(path, data)

    # Messages Handling Methods Section
    # Sets context
    def _set_context(self, hello_message):
        try:
            self.context = UserContext(hello_message.user_info)
        except Exception as e:
            logger.error(f'error parsing hello_message : {e}')
            self._is_valid_connection = False

    @staticmethod
    def get_user_snapshots_path(user_id):
        return ServerHandler._get_save_path(
            ConstantPathes.get_snapshots_path(), user_id)

    # Saves snapshot
    def _get_snapshot_save_path(self, snapshot_uuid):
        snapshot_path   =                                                               \
            ServerHandler._get_save_path(                                               \
                ServerHandler.get_user_snapshots_path(self.context.user_info.user_id),  \
                snapshot_uuid,                                                          \
                SNAPSHOT_FILE_NAME)
        return snapshot_path

    def _save_snapshot(self, snapshot_uuid, snapshot_message_bytes):
        snapshot_path = self._get_snapshot_save_path(snapshot_uuid)
        # Deduping snapshots (to avoid multiple saving and handling of existing snapshots)
        if self.files_handler.is_file_exists(snapshot_path):
            return None
        # Save snapshot
        is_saved = self._save_file(snapshot_path, snapshot_message_bytes)
        if not is_saved:
            logger.error(
                f'error saving snapshot of user {self.context.user_info.user_id}'
            )
            return None
        return snapshot_path

    # Publish snapshot message
    def publish_snapshot_message(self, snapshot_uuid, raw_snapshot_path):
        # Creating raw snapshot message
        raw_snapshot_message =                                      \
            self.messages.get_message(                              \
                MessageQueueMessagesTyeps.RAW_SNAPSHOT_MESSAGE)(    \
                    self.context.user_info,                         \
                    snapshot_uuid,                                  \
                    raw_snapshot_path)
        # Publish raw snapshot message
        self.publish_snapshot_function(raw_snapshot_message.serialize())

    # Handle snapshot message
    def handle_snapshot_message(self, snapshot_message_bytes):
        # Gets snapshot uuid
        snapshot_uuid = self._get_hash(snapshot_message_bytes)
        # Saving snapshot
        raw_snapshot_path = self._save_snapshot(snapshot_uuid,
                                                snapshot_message_bytes)
        # If error occurred while saving snapshot
        if not raw_snapshot_path:
            return
        # Publishing snapshot message
        self.publish_snapshot_message(snapshot_uuid, raw_snapshot_path)

    # Core Logic Methods Section
    # Runs client handling loop
    def run(self):
        # Receives hello_message from client
        hello_message = self.receive_hello_message()
        if not self._is_valid_connection:
            return
        # Sets client context based on hello message
        self._set_context(hello_message)
        if not self._is_valid_connection:
            return
        # Sends configuration message to client
        self.send_config_message()
        if not self._is_valid_connection:
            return
        logger.info(
            f'start receiving snapshots from user {self.context.user_info.user_id}'
        )
        # As long as connection is valid
        while self._is_valid_connection:
            try:
                # Receives snapshot_message from client
                snapshot_message_bytes = self.receive_snapshot_message_bytes()
                # Handle snapshot message
                self.handle_snapshot_message(snapshot_message_bytes)
            except EOFError:
                logger.info(
                    f'finished snapshots from user {self.context.user_info.user_id}'
                )
                break