diff --git a/j1939/Dm14Server.py b/j1939/Dm14Server.py index 9928a9a..de57a4a 100644 --- a/j1939/Dm14Server.py +++ b/j1939/Dm14Server.py @@ -43,15 +43,24 @@ def _wait_for_data(self) -> None: Determines whether to send data or wait to receive data based on the command type. If the command is a read command, then the data requested is sent. """ - if self.command is j1939.Command.READ.value: - self._send_dm15( - self.length, - self.direct, - self.status, - self.state, - self.object_count, - self.sa, - ) + self._ca.subscribe(self._parse_dm16) + self._send_dm15( + self.length, + self.direct, + self.status, + self.state, + self.object_count, + self.sa, + j1939.ParameterGroupNumber.PGN.DM15, + self.error, + self.edcp, + ) + + if ( + self.command is j1939.Command.READ.value + and self.state == ResponseState.SEND_PROCEED + ): + self._ca.unsubscribe(self._parse_dm16) self._send_dm16() if (len(self.data)) <= 8: self.proceed = True @@ -64,18 +73,19 @@ def _wait_for_data(self) -> None: self.state, self.object_count, self.sa, + j1939.ParameterGroupNumber.PGN.DM15, + self.error, + self.edcp, ) - else: - self._ca.subscribe(self._parse_dm16) - self._send_dm15( - self.length, - self.direct, - self.status, - self.state, - self.object_count, - self.sa, - ) + elif ( + self.command is j1939.Command.WRITE.value + and self.state == ResponseState.SEND_PROCEED + ): self.state = ResponseState.WAIT_FOR_DM16 + else: + self._ca.unsubscribe(self._parse_dm16) + self.state = ResponseState.IDLE + self.sa = None def parse_dm14( self, priority: int, pgn: int, sa: int, timestamp: int, data: bytearray diff --git a/test/test_memory_access.py b/test/test_memory_access.py index 8438bd4..8c64337 100644 --- a/test/test_memory_access.py +++ b/test/test_memory_access.py @@ -125,6 +125,30 @@ (Feeder.MsgType.CANTX, 0x18D8F9D4, [0x01, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], 0.0), # DM15 proceed response ] +read_with_seed_error = [ + (Feeder.MsgType.CANTX, 0x18D9D4F9, [0x01, 0x13, 0x03, 0x00, 0x00, 0x92, 0x07, 0x00], 0.0), # DM14 read address 0x92000007 + (Feeder.MsgType.CANRX, 0x18D8F9D4, [0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x5A, 0xA5], 0.0), # DM15 seed response + (Feeder.MsgType.CANTX, 0x18D9D4F9, [0x01, 0x13, 0x03, 0x00, 0x00, 0x92, 0xA5, 0x5A], 0.0), # DM14 key response + (Feeder.MsgType.CANRX, 0x18D8F9D4, [0x00, 0x1B, 0x01, 0x00, 0x00, 0x07, 0xFF, 0xFF], 0.0), # DM15 error response +] + +read_no_seed_error = [ + (Feeder.MsgType.CANTX, 0x18D9D4F9, [0x01, 0x13, 0x03, 0x00, 0x00, 0x92, 0x07, 0x00], 0.0), # DM14 read address 0x92000007 + (Feeder.MsgType.CANRX, 0x18D8F9D4, [0x00, 0x1B, 0x01, 0x00, 0x00, 0x07, 0xFF, 0xFF], 0.0), # DM15 error response +] + +write_with_seed_error = [ + (Feeder.MsgType.CANTX, 0x18D9D4F9, [0x01, 0x15, 0x07, 0x00, 0x00, 0x91, 0x07, 0x00], 0.0), # DM14 write address 0x91000007 + (Feeder.MsgType.CANRX, 0x18D8F9D4, [0x00, 0x11, 0xFF, 0xFF, 0xFF, 0xFF, 0x5A, 0xA5], 0.0), # DM15 seed response + (Feeder.MsgType.CANTX, 0x18D9D4F9, [0x01, 0x15, 0x07, 0x00, 0x00, 0x91, 0xA5, 0x5A], 0.0), # DM14 key response + (Feeder.MsgType.CANRX, 0x18D8F9D4, [0x00, 0x1B, 0x01, 0x00, 0x00, 0x07, 0xFF, 0xFF], 0.0), # DM15 error response +] + +write_no_seed_error = [ + (Feeder.MsgType.CANTX, 0x18D9D4F9, [0x01, 0x15, 0x07, 0x00, 0x00, 0x91, 0x07, 0x00], 0.0), # DM14 write address 0x91000007 + (Feeder.MsgType.CANRX, 0x18D8F9D4, [0x00, 0x1B, 0x01, 0x00, 0x00, 0x07, 0xFF, 0xFF], 0.0), # DM15 error response +] + error_codes = [0x10, 0x11, 0x12, 0x100, 0x101, 0x1000, 0x1001, 0x100F, 0x10FE] # fmt: on @@ -654,4 +678,57 @@ def test_dm14_write_error(feeder, error_code): feeder.process_messages() +@pytest.mark.parametrize( + argnames=["expected_messages"], + argvalues=[[read_with_seed_error], [read_no_seed_error]], + ids=["With seed key", "Without seed key"], +) +def test_dm14_read_error_response(feeder, expected_messages): + """ + Tests that the DM14 read query can react to errors correctly + :param feeder: can message feeder + :param expected_messages: list of expected messages + """ + with pytest.raises(RuntimeError) as excinfo: + feeder.can_messages = expected_messages + feeder.pdus_from_messages() + ca = feeder.accept_all_messages( + device_address_preferred=0xF9, bypass_address_claim=True + ) + dm14 = j1939.MemoryAccess(ca) + dm14.set_seed_key_algorithm(key_from_seed) + dm14.read(0xD4, 1, 0x92000003, 1) + + assert j1939.ErrorInfo[0x1] in str(excinfo.value) + + feeder.process_messages() + + +@pytest.mark.parametrize( + argnames=["expected_messages"], + argvalues=[[write_with_seed_error], [write_no_seed_error]], + ids=["With seed key", "Without seed key"], +) +def test_dm14_write_error_response(feeder, expected_messages): + """ + Tests that the DM14 read query can react to errors correctly + :param feeder: can message feeder + :param expected_messages: list of expected messages + """ + with pytest.raises(RuntimeError) as excinfo: + feeder.can_messages = expected_messages + feeder.pdus_from_messages() + ca = feeder.accept_all_messages( + device_address_preferred=0xF9, bypass_address_claim=True + ) + dm14 = j1939.MemoryAccess(ca) + dm14.set_seed_key_algorithm(key_from_seed) + values = [0x11223344] + dm14.write(0xD4, 1, 0x91000007, values, object_byte_size=4) + + assert j1939.ErrorInfo[0x1] in str(excinfo.value) + + feeder.process_messages() + + # TODO: moar test