def get_slave_bus_mode(): # type: () -> CoreCommandSpec """ Receives the slave bus mode """ return CoreCommandSpec( instruction='ST', request_fields=[LiteralBytesField(0)], response_fields=[ByteField('info_type'), ByteField('mode')])
def test_transparent_mode(self): received_commands = [] def do_command(command, fields, timeout=None): received_commands.append(fields) return {'mode': fields['mode']} core_communicator = CoreCommunicator(controller_serial=Mock()) core_communicator.do_command = do_command slave_communicator = SlaveCommunicator(master_communicator=core_communicator, verbose=True) address = '000.000.000.000' command_spec = SlaveCommandSpec(instruction=Instruction(instruction='AB'), request_fields=[ByteField('foo')], response_fields=[ByteField('bar')]) with self.assertRaises(RuntimeError): # Transparent mode inactive slave_communicator.do_command(address, command_spec, {'foo': 0}, timeout=None) self.assertFalse(slave_communicator._transparent_mode) with slave_communicator: # SlaveCommunicator as ContextManager activates transparent mode self.assertEqual(1, len(received_commands)) self.assertEqual({'mode': CoreAPI.SlaveBusMode.TRANSPARENT}, received_commands[0]) self.assertTrue(slave_communicator._transparent_mode) self.assertEqual(2, len(received_commands)) self.assertEqual({'mode': CoreAPI.SlaveBusMode.LIVE}, received_commands[1]) self.assertFalse(slave_communicator._transparent_mode)
def test_unresponsiveness(self): core_communicator = CoreCommunicator(controller_serial=Mock()) core_communicator.do_command = Mock() slave_communicator = SlaveCommunicator( master_communicator=core_communicator, verbose=True) slave_communicator._transparent_mode = True address = '000.000.000.000' command_spec = SlaveCommandSpec( instruction=Instruction(instruction='AB'), request_fields=[ByteField('foo')], response_fields=[ByteField('bar')]) with self.assertRaises(CommunicationTimedOutException): core_communicator.do_command.side_effect = lambda command, fields, timeout=None: time.sleep( 2) slave_communicator.do_command(address, command_spec, {'foo': 0}, timeout=1) # No slave response with self.assertRaises(CommunicationTimedOutException): core_communicator.do_command.side_effect = CommunicationTimedOutException( 'Master unresponsive') slave_communicator.do_command(address, command_spec, {'foo': 0}, timeout=1)
def event_information(): # type: () -> CoreCommandSpec """ Event information """ return CoreCommandSpec(instruction='EV', response_fields=[ ByteField('type'), ByteField('action'), WordField('device_nr'), RawByteArrayField('data', 4) ])
def error_information(): # type: () -> CoreCommandSpec """ Error information """ return CoreCommandSpec(instruction='ER', response_fields=[ ByteField('type'), ByteField('parameter_a'), WordField('parameter_b'), WordField('parameter_c') ])
def ucan_rx_transport_message(): # type: () -> CoreCommandSpec """ uCAN transport layer packages """ return CoreCommandSpec(instruction='FM', response_fields=[ AddressField('cc_address'), ByteField('nr_can_bytes'), ByteField('sid'), RawByteArrayField('payload', 8) ])
def get_master_modes(): # type: () -> CoreCommandSpec """ Receives various master modes (rs485 bus mode, BA debug mode) """ return CoreCommandSpec(instruction='ST', request_fields=[LiteralBytesField(0)], response_fields=[ ByteField('info_type'), ByteField('rs485_mode'), ByteField('ba_debug_mode') ])
def get_firmware_version(): # type: () -> SlaveCommandSpec """ Gets a slave firmware version """ return SlaveCommandSpec(instruction=Instruction(instruction='FV', padding=9), response_fields=[ ByteField('return_code'), ByteField('hardware_version'), VersionField('version'), ByteField('status') ])
def _sensor_values(instruction, field): # type: (int, Field) -> CoreCommandSpec """ Receive sensor byte values """ return CoreCommandSpec( instruction='SI', request_fields=[ ByteField('module_nr'), LiteralBytesField(instruction) ], response_fields=[ByteField('module_nr'), PaddingField(1), field])
def set_bootloader_safety_flag(): # type: () -> UCANCommandSpec """ Sets the bootloader's safety flag """ return UCANCommandSpec( sid=SID.BOOTLOADER_COMMAND, identifier=AddressField('ucan_address', 3), instructions=[Instruction(instruction=[0, 125])], request_fields=[[ByteField('safety_flag')]], response_instructions=[ Instruction(instruction=[125, 125], checksum_byte=6) ], response_fields=[ByteField('safety_flag')])
def ping(sid=SID.NORMAL_COMMAND): # type: (int) -> UCANCommandSpec """ Basic action spec """ return UCANCommandSpec(sid=sid, identifier=AddressField('ucan_address', 3), instructions=[Instruction(instruction=[0, 96])], request_fields=[[ByteField('data')]], response_instructions=[ Instruction(instruction=[1, 96], checksum_byte=6) ], response_fields=[ByteField('data')])
def test_pallet_reconstructing(self): received_commands = [] def send_command(_cid, _command, _fields): received_commands.append(_fields) core_communicator = CoreCommunicator(controller_serial=Mock()) core_communicator._send_command = send_command ucan_communicator = UCANCommunicator(master_communicator=core_communicator) cc_address = '000.000.000.000' ucan_address = '000.000.000' pallet_type = PalletType.MCU_ID_REQUEST # Not important for this test for length in [1, 3]: # Build command command = UCANPalletCommandSpec(identifier=AddressField('ucan_address', 3), pallet_type=pallet_type, request_fields=[ByteField('foo'), ByteField('bar')], response_fields=[ByteArrayField('other', length)]) # Send command to mocked Core communicator received_commands = [] ucan_communicator.do_command(cc_address, command, ucan_address, {'foo': 1, 'bar': 2}, timeout=None) # Validate whether the correct data was send to the Core self.assertEqual(len(received_commands), 2) self.assertDictEqual(received_commands[0], {'cc_address': cc_address, 'nr_can_bytes': 8, 'payload': bytearray([129, 0, 0, 0, 0, 0, 0, pallet_type]), # +--------------+ = source and destination uCAN address 'sid': SID.BOOTLOADER_PALLET}) self.assertDictEqual(received_commands[1], {'cc_address': cc_address, 'nr_can_bytes': 7, 'payload': bytearray([0, 1, 2, 219, 155, 250, 178, 0]), # | | +----------------+ = checksum # | + = bar # + = foo 'sid': SID.BOOTLOADER_PALLET}) # Build fake reply from Core consumer = ucan_communicator._consumers[cc_address][0] fixed_payload = bytearray([0, 0, 0, 0, 0, 0, pallet_type]) variable_payload = bytearray(list(range(7, 7 + length))) # [7] or [7, 8, 9] crc_payload = self._uint32_helper.encode(UCANPalletCommandSpec.calculate_crc(fixed_payload + variable_payload)) ucan_communicator._process_transport_message({'cc_address': cc_address, 'nr_can_bytes': 8, 'sid': 1, 'payload': bytearray([129]) + fixed_payload}) ucan_communicator._process_transport_message({'cc_address': cc_address, 'nr_can_bytes': length + 5, 'sid': 1, 'payload': bytearray([0]) + variable_payload + crc_payload}) self.assertDictEqual(consumer.get(1), {'other': list(variable_payload)})
def set_bootloader_timeout( sid=SID.NORMAL_COMMAND): # type: (int) -> UCANCommandSpec """ Sets the bootloader timeout """ return UCANCommandSpec( sid=sid, identifier=AddressField('ucan_address', 3), instructions=[Instruction(instruction=[0, 123])], request_fields=[[ByteField('timeout')]], response_instructions=[ Instruction(instruction=[123, 123], checksum_byte=6) ], response_fields=[ByteField('timeout')])
def ucan_module_information(): # type: () -> CoreCommandSpec """ Receives information from a uCAN module """ return CoreCommandSpec(instruction='CD', response_fields=[ AddressField('ucan_address', 3), WordArrayField('input_links', 6), ByteArrayField('sensor_links', 2), ByteField('sensor_type'), VersionField('version'), ByteField('bootloader'), CharField('new_indicator'), ByteField('min_led_brightness'), ByteField('max_led_brightness') ])
def basic_action(): # type: () -> CoreCommandSpec """ Basic action spec """ return CoreCommandSpec(instruction='BA', request_fields=[ ByteField('type'), ByteField('action'), WordField('device_nr'), WordField('extra_parameter') ], response_fields=[ ByteField('type'), ByteField('action'), WordField('device_nr'), WordField('extra_parameter') ])
def get_firmware_version(): # type: () -> CoreCommandSpec """ Receives the Core firmware version """ return CoreCommandSpec( instruction='ST', request_fields=[LiteralBytesField(1)], response_fields=[ByteField('info_type'), VersionField('version')])
def request_slave_firmware_versions(): # type: () -> CoreCommandSpec """ Requests the slave firmware versions, which will be send to the GW in separate FW calls """ return CoreCommandSpec( instruction='ST', request_fields=[LiteralBytesField(2)], response_fields=[ByteField('info_type'), PaddingField(2)])
def memory_read(): # type: () -> CoreCommandSpec """ Reads memory """ return CoreCommandSpec(instruction='MR', request_fields=[ CharField('type'), WordField('page'), ByteField('start'), ByteField('length') ], response_fields=[ CharField('type'), WordField('page'), ByteField('start'), RawByteArrayField('data', lambda length: length - 4) ])
def memory_write(length): # type: (int) -> CoreCommandSpec """ Writes memory """ return CoreCommandSpec(instruction='MW', request_fields=[ CharField('type'), WordField('page'), ByteField('start'), RawByteArrayField('data', length) ], response_fields=[ CharField('type'), WordField('page'), ByteField('start'), ByteField('length'), CharField('result') ])
def set_min_led_brightness(): # type: () -> UCANCommandSpec """ Sets the minimum brightness for a uCAN led """ return UCANCommandSpec( sid=SID.NORMAL_COMMAND, identifier=AddressField('ucan_address', 3), instructions=[Instruction(instruction=[0, 246])], request_fields=[[ByteField('brightness')]])
def __init__(self, action_type, action, device_nr=None, extra_parameter=None ): # type: (int, int, Optional[int], Optional[int]) -> None self._word_helper = WordField('') self._byte_helper = ByteField('') self._action_type = self._byte_helper.encode( action_type) # type: bytearray self._action = self._byte_helper.encode(action) # type: bytearray self._device_nr = self._word_helper.encode( device_nr if device_nr is not None else 0) # type: bytearray self._extra_parameter = self._word_helper.encode( extra_parameter if extra_parameter is not None else 0) # type: bytearray
def execute_basic_action_series(length): # type: (int) -> CoreCommandSpec """ Executes a Basic action on multiple devices """ if not 2 <= length <= 40: raise ValueError('Amount of device numbers should be 2 <= n <= 40') return CoreCommandSpec(instruction='ES', request_fields=[ ByteField('type'), ByteField('action'), WordField('extra_parameter'), WordArrayField('device_nrs', length) ], response_fields=[ ByteField('type'), ByteField('action'), WordField('extra_parameter') ])
def write_firmware_block(): # type: () -> SlaveCommandSpec """ Writes a single 64-byte firmware block to a given address """ return SlaveCommandSpec(instruction=Instruction(instruction='FD'), request_fields=[ WordField('address'), ByteArrayField('payload', length=64) ], response_fields=[ByteField('return_code')])
def erase_flash(): # type: () -> UCANCommandSpec """ Erases uCAN flash Note: uCAN needs to be in bootloader """ return UCANPalletCommandSpec( identifier=AddressField('ucan_address', 3), pallet_type=PalletType.FLASH_ERASE_REQUEST, response_fields=[ByteField('success')])
def device_information_list_inputs(): # type: () -> CoreCommandSpec """ Device information list for inputs """ return CoreCommandSpec(instruction='DL', request_fields=[LiteralBytesField(1)], response_fields=[ ByteField('type'), ByteArrayField('information', lambda length: length - 1) ])
def reset(sid=SID.NORMAL_COMMAND): # type: (int) -> UCANCommandSpec """ Resets the uCAN """ return UCANCommandSpec(sid=sid, identifier=AddressField('ucan_address', 3), instructions=[Instruction(instruction=[0, 94])], response_instructions=[ Instruction(instruction=[94, 94], checksum_byte=6) ], response_fields=[ByteField('application_mode')])
def general_configuration_number_of_modules( ): # type: () -> CoreCommandSpec """ Receives general configuration regarding number of modules """ return CoreCommandSpec(instruction='GC', request_fields=[LiteralBytesField(0)], response_fields=[ ByteField('type'), ByteField('output'), ByteField('input'), ByteField('sensor'), ByteField('ucan'), ByteField('ucan_input'), ByteField('ucan_sensor'), ByteField('power_rs485'), ByteField('power_can') ])
def test_rxtx(self): received_commands = [] def do_command(command, fields, timeout=None): received_commands.append(fields) core_communicator = CoreCommunicator(controller_serial=Mock()) core_communicator.do_command = do_command slave_communicator = SlaveCommunicator(master_communicator=core_communicator, verbose=True) slave_communicator._transparent_mode = True address = '000.000.000.000' command_spec = SlaveCommandSpec(instruction=Instruction(instruction='AB'), request_fields=[ByteField('foo')], response_fields=[ByteField('bar')]) slave_communicator.do_command(address, command_spec, {'foo': 0}, timeout=None) self.assertEqual(1, len(received_commands)) self.assertTrue('payload' in received_commands[0]) payload = received_commands[0]['payload'] self.assertEqual(SlaveCommunicatorTest._build_request_message(b'\x00\x00\x00\x00AB\x00'), payload) consumer = slave_communicator._consumers[0] response_payload = SlaveCommunicatorTest._build_response_message(b'\x00\x00\x00\x00AC\x04') slave_communicator._process_transport_message({'payload': bytearray(b'FOO') + response_payload[:5]}) slave_communicator._process_transport_message({'payload': response_payload[5:]}) slave_communicator._process_transport_message({'payload': SlaveCommunicatorTest._build_response_message(b'\x00\x00\x00\x01AB\x03')}) slave_communicator._process_transport_message({'payload': SlaveCommunicatorTest._build_response_message(b'\x00\x00\x00\x00AB\x02', bad_crc=True)}) with self.assertRaises(CommunicationTimedOutException): self.assertEqual({'bar': 1}, consumer.get(1)) # Invalid CRC slave_communicator.do_command(address, command_spec, {'foo': 0}, timeout=None) consumer = slave_communicator._consumers[0] slave_communicator._process_transport_message({'payload': SlaveCommunicatorTest._build_response_message(b'\x00\x00\x00\x00AB\x01')}) self.assertEqual({'bar': 1}, consumer.get(1)) command_spec = SlaveCommandSpec(instruction=Instruction(instruction='AB'), request_fields=[ByteField('foo')]) slave_communicator.do_command(address, command_spec, {'foo': 0}, timeout=None) consumer = slave_communicator._consumers[0] with self.assertRaises(CommunicationTimedOutException): consumer.get(0)
def get_date_time(): # type: () -> CoreCommandSpec """ Reads the date/time from the Core """ return CoreCommandSpec(instruction='TR', request_fields=[LiteralBytesField(0)], response_fields=[ ByteField('info_type'), ByteField('hours'), ByteField('minutes'), ByteField('seconds'), ByteField('weekday'), ByteField('day'), ByteField('month'), ByteField('year') ])
def read_flash(data_length): # type: (int) -> UCANCommandSpec """ Reads uCAN flash Note: uCAN needs to be in bootloader """ return UCANPalletCommandSpec( identifier=AddressField('ucan_address', 3), pallet_type=PalletType.FLASH_READ_REQUEST, request_fields=[ UInt32Field('start_address'), ByteField('data_length') ], response_fields=[ByteArrayField('data', data_length)])