def test_parse_invalid_header(self):
     parser = MessageParser()
     crc_calc = Crc8Calculator(0x97)
     msg = bytearray(10)
     msg[0] = 0xFE
     msg[1] = 0x03
     msg[2] = 0x00
     msg[3:9] = 'Hello '.encode()
     msg[9] = crc_calc.compute_crc(msg[0:9])
     with self.assertRaises(AssertionError) as cm:
         parser.parse_incoming_message(msg)
     self.assertEqual('Invalid message header', str(cm.exception))
 def test_parse_string_message(self):
     parser = MessageParser()
     crc_calc = Crc8Calculator(0x97)
     msg = bytearray(10)
     msg[0] = 0xFF
     msg[1] = 0x03
     msg[2] = 0x00
     msg[3:9] = 'Hello '.encode()
     msg[9] = crc_calc.compute_crc(msg[0:9])
     expected_parsed_message = dict()
     expected_parsed_message['type'] = 'string'
     expected_parsed_message['id'] = 0x00
     expected_parsed_message['data'] = 'Hello '
     self.assertEqual(expected_parsed_message, parser.parse_incoming_message(msg))
 def test_pack_command(self):
     parser = MessageParser()
     crc_calc = Crc8Calculator(0x97)
     ADDRESS = 0x01
     SUB_ADDRESS = 0x02
     DATA = 10
     expected_msg = bytearray(10)
     expected_msg[0] = 0xFF
     expected_msg[1] = 0x00
     expected_msg[2] = 0x00
     expected_msg[3] = ADDRESS
     expected_msg[4] = SUB_ADDRESS
     expected_msg[5:9] = DATA.to_bytes(4, byteorder='little')
     expected_msg[9] = crc_calc.compute_crc(expected_msg[0:9])
     self.assertEqual(expected_msg, parser.pack_command(ADDRESS, SUB_ADDRESS, DATA))
    def __init__(self):
        """

        """
        CRC_POLYNOMIAL = 0x97
        self.app_logger = getLogger('application.usart_communication')
        self.app_logger.info('Creating serial communicator')
        self.__parser = MessageParser()
        self.__arduino_logger = ArduinoLogger()
        self.__serial_port = Serial()
        self.__crc_calculator = Crc8Calculator(CRC_POLYNOMIAL)
        self.__receiver_thread = Thread(target=self.__read_incoming_message)
        self.__is_reading = False
        self.__request_queue = Queue()
        self.__command_queue = Queue()
        self.__request_counter = 0
        self.__command_counter = 0
        self.__input_message_queue = Queue()
class ArduinoSerialCommunicator(IArduinoCommunicator):
    """

    """
    def __init__(self):
        """

        """
        CRC_POLYNOMIAL = 0x97
        self.app_logger = getLogger('application.usart_communication')
        self.app_logger.info('Creating serial communicator')
        self.__parser = MessageParser()
        self.__arduino_logger = ArduinoLogger()
        self.__serial_port = Serial()
        self.__crc_calculator = Crc8Calculator(CRC_POLYNOMIAL)
        self.__receiver_thread = Thread(target=self.__read_incoming_message)
        self.__is_reading = False
        self.__request_queue = Queue()
        self.__command_queue = Queue()
        self.__request_counter = 0
        self.__command_counter = 0
        self.__input_message_queue = Queue()

    def request_object_data(self, address, sub_address):
        content = self.__parser.pack_request(self.__command_counter, address, sub_address)
        self.app_logger.debug('Writing request to arduino: %s', hexlify(content))
        self.__serial_port.write(content)
        msg = self.__get_incoming_message()
        if msg['id'] == self.__command_counter:
            self.app_logger.debug('Request replied successfully')
            print('Arduino replied with: %s' % msg['data'])
        self.__request_counter += 1
        self.__request_counter %= 256

    def open(self, port_name):
        self.__serial_port.port = port_name
        self.__serial_port.baudrate = 57600
        self.__serial_port.open()
        self.__start_reading()

    def close(self):
        self.__stop_reading()
        self.__serial_port.close()

    def write_to_object(self, address, sub_address, data):
        content = self.__parser.pack_command(self.__command_counter, address, sub_address, data)
        self.app_logger.debug('Writing command to arduino')
        self.app_logger.debug(content)
        self.__serial_port.write(content)
        msg = self.__get_incoming_message()
        if msg['id'] == self.__command_counter:
            if msg['data'] == 1:
                self.app_logger.debug('Command applied successfully')

        self.__command_counter += 1
        self.__command_counter %= 256

    def __start_reading(self):
        self.__is_reading = True
        self.__receiver_thread.setDaemon(True)
        self.__receiver_thread.start()

    def __stop_reading(self):
        self.__is_reading = False
        self.__receiver_thread.join()

    def __read_incoming_message(self):
        incoming_message = bytearray()
        msg_size = 0
        try:
            while self.__is_reading:
                data = ord(self.__serial_port.read(1))

                # First byte condition
                if data == 0xFF and len(incoming_message) == 0:
                    incoming_message.append(data)
                # Complete header (0xFF + msgType) Stop to determine size
                elif len(incoming_message) == 1:
                    incoming_message.append(data)
                    msg_size = self.__parser.get_message_size(incoming_message)
                elif 1 < len(incoming_message) < msg_size:
                    incoming_message.append(data)
                    if len(incoming_message) == msg_size:
                        self.__process_message(incoming_message)
                        incoming_message = bytearray()
                        msg_size = 0

        except SerialException as e:
            self.app_logger.fatal('Error reading serial port:')
            self.app_logger.fatal(e.strerror)

    def __process_message(self, message):
        self.app_logger.debug('Processing incoming message.')
        assert(type(message) is bytearray)
        parsed_msg = dict(type='unknown')

        try:
            parsed_msg = self.__parser.parse_incoming_message(message)
        except AssertionError as ae:
            self.app_logger.error('Invalid incoming message with error ' + ae.args[0])
            self.app_logger.error('Data: %s' % hexlify(message))
            self.__serial_port.flush()

        if parsed_msg['type'] == 'string':
            self.__arduino_logger.process_log_message(parsed_msg)
        elif parsed_msg['type'] == 'error':
            self.app_logger.warning('Arduino replied with error message')
            self.app_logger.warning('Data: %s' % hexlify(message))
        else:
            self.app_logger.debug('Message put in queue')
            self.app_logger.debug('Type: %s' % parsed_msg['type'])
            self.__input_message_queue.put(parsed_msg)

    def __get_incoming_message(self):
        msg = self.__input_message_queue.get()
        self.app_logger.debug('Message read from queue')
        self.app_logger.debug('Type: %s' % msg['type'])
        self.__input_message_queue.task_done()
        return msg