Skip to content

Commit c22d437

Browse files
committed
Added reportDTCExtDataRecordByRecordNumber subfunction support
1 parent fcbae0b commit c22d437

File tree

4 files changed

+452
-21
lines changed

4 files changed

+452
-21
lines changed

doc/source/udsoncan/client.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ Methods by services
400400
.. automethod:: udsoncan.client.Client.get_user_defined_dtc_snapshot_by_dtc_number
401401
.. automethod:: udsoncan.client.Client.get_dtc_snapshot_by_record_number
402402
.. automethod:: udsoncan.client.Client.get_dtc_extended_data_by_dtc_number
403+
.. automethod:: udsoncan.client.Client.get_dtc_extended_data_by_record_number
403404
.. automethod:: udsoncan.client.Client.get_user_defined_dtc_extended_data_by_dtc_number
404405
.. automethod:: udsoncan.client.Client.get_mirrormemory_dtc_extended_data_by_dtc_number
405406

test/client/test_read_dtc_information.py

Lines changed: 313 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2584,6 +2584,319 @@ def __init__(self, *args, **kwargs):
25842584

25852585

25862586

2587+
class TestreportDTCExtDataRecordByRecordNumber(ClientServerTest): # Subfn = 0x16
2588+
sb = struct.pack('B', 0x16)
2589+
badsb = struct.pack('B', 0x16+1)
2590+
2591+
def assert_single_data_response(self, response):
2592+
self.assertEqual(len(response.service_data.dtcs), 1)
2593+
self.assertEqual(response.service_data.dtc_count, 1)
2594+
2595+
dtc = response.service_data.dtcs[0]
2596+
2597+
self.assertEqual(dtc.id, 0x123456)
2598+
self.assertEqual(dtc.status.get_byte_as_int(), 0x20)
2599+
2600+
self.assertEqual(len(dtc.extended_data), 1)
2601+
extended_data = dtc.extended_data[0]
2602+
2603+
self.assertTrue(isinstance(extended_data, Dtc.ExtendedData))
2604+
self.assertEqual(extended_data.record_number, 0x33)
2605+
2606+
self.assertEqual(extended_data.raw_data, b'\x01\x02\x03\x04\x05')
2607+
2608+
def test_single_data(self):
2609+
for i in range(3):
2610+
request = self.conn.touserqueue.get(timeout=0.2)
2611+
self.assertEqual(request, b'\x19' + self.sb + b'\x33')
2612+
self.conn.fromuserqueue.put(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05')
2613+
2614+
def _test_single_data(self):
2615+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = 5)
2616+
self.assert_single_data_response(response)
2617+
2618+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5})
2619+
self.assert_single_data_response(response)
2620+
2621+
self.udsclient.config['extended_data_size'] = {0x123456 : 5}
2622+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33)
2623+
self.assert_single_data_response(response)
2624+
2625+
def test_single_data_missing_size(self):
2626+
for i in range(2):
2627+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05')
2628+
2629+
def _test_single_data_missing_size(self):
2630+
with self.assertRaises(ConfigError):
2631+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x111111 : 5}) # 123456 is not there!
2632+
2633+
with self.assertRaises(ConfigError):
2634+
self.udsclient.config['extended_data_size'] = {0x111111 : 5} # 123456 is not there!
2635+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33)
2636+
2637+
def test_single_data_zeropadding_ok(self):
2638+
data = b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05'
2639+
for i in range(8):
2640+
self.wait_request_and_respond(data + b'\x00' * (i+1))
2641+
2642+
def _test_single_data_zeropadding_ok(self):
2643+
self.udsclient.config['tolerate_zero_padding'] = True
2644+
for i in range(8):
2645+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5})
2646+
self.assert_single_data_response(response)
2647+
2648+
def test_single_data_zeropadding_notok_exception(self):
2649+
data = b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05'
2650+
for i in range(8):
2651+
self.wait_request_and_respond(data + b'\x00' * (i+1))
2652+
2653+
def _test_single_data_zeropadding_notok_exception(self):
2654+
self.udsclient.config['tolerate_zero_padding'] = False
2655+
for i in range(8):
2656+
with self.assertRaises(InvalidResponseException):
2657+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5})
2658+
2659+
2660+
def test_single_data_zeropadding_notok_no_exception(self):
2661+
data = b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05'
2662+
for i in range(8):
2663+
self.wait_request_and_respond(data + b'\x00' * (i+1))
2664+
2665+
def _test_single_data_zeropadding_notok_no_exception(self):
2666+
self.udsclient.config['tolerate_zero_padding'] = False
2667+
self.udsclient.config['exception_on_invalid_response'] = False
2668+
for i in range(8):
2669+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5})
2670+
self.assertFalse(response.valid)
2671+
2672+
2673+
def test_double_data(self):
2674+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05\x78\x9a\xbc\x30\xaa\xbb\xcc')
2675+
2676+
def _test_double_data(self):
2677+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2678+
2679+
self.assertEqual(len(response.service_data.dtcs), 2)
2680+
self.assertEqual(response.service_data.dtc_count, 2)
2681+
2682+
dtc = response.service_data.dtcs[0]
2683+
2684+
self.assertEqual(dtc.id, 0x123456)
2685+
self.assertEqual(dtc.status.get_byte_as_int(), 0x20)
2686+
2687+
self.assertEqual(len(dtc.extended_data), 1)
2688+
2689+
self.assertTrue(isinstance(dtc.extended_data[0], Dtc.ExtendedData))
2690+
self.assertEqual(dtc.extended_data[0].record_number, 0x33)
2691+
self.assertEqual(dtc.extended_data[0].raw_data, b'\x01\x02\x03\x04\x05')
2692+
2693+
2694+
dtc = response.service_data.dtcs[1]
2695+
2696+
self.assertEqual(dtc.id, 0x789abc)
2697+
self.assertEqual(dtc.status.get_byte_as_int(), 0x30)
2698+
2699+
self.assertEqual(len(dtc.extended_data), 1)
2700+
2701+
self.assertTrue(isinstance(dtc.extended_data[0], Dtc.ExtendedData))
2702+
self.assertEqual(dtc.extended_data[0].record_number, 0x33)
2703+
self.assertEqual(dtc.extended_data[0].raw_data, b'\xaa\xbb\xcc')
2704+
2705+
2706+
def test_no_data(self):
2707+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20')
2708+
2709+
def _test_no_data(self):
2710+
with self.assertRaises(InvalidResponseException):
2711+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5})
2712+
2713+
def test_zero_sized_data(self):
2714+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20')
2715+
2716+
def _test_zero_sized_data(self):
2717+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 0})
2718+
2719+
self.assertEqual(len(response.service_data.dtcs), 1)
2720+
self.assertEqual(response.service_data.dtc_count, 1)
2721+
dtc = response.service_data.dtcs[0]
2722+
2723+
self.assertEqual(dtc.id, 0x123456)
2724+
self.assertEqual(dtc.status.get_byte_as_int(), 0x20)
2725+
self.assertEqual(len(dtc.extended_data), 1)
2726+
2727+
self.assertEqual(len(dtc.extended_data[0].raw_data), 0)
2728+
2729+
def test_zero_sized_data_zeropadding_ok(self):
2730+
data = b'\x59' + self.sb + b'\x33\x12\x34\x56\x20'
2731+
for i in range(8):
2732+
self.wait_request_and_respond(data + b'\x00' * (i+1) )
2733+
2734+
def _test_zero_sized_data_zeropadding_ok(self):
2735+
self.udsclient.config['tolerate_zero_padding'] = True
2736+
for i in range(8):
2737+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 0})
2738+
2739+
self.assertEqual(len(response.service_data.dtcs), 1)
2740+
self.assertEqual(response.service_data.dtc_count, 1)
2741+
dtc = response.service_data.dtcs[0]
2742+
2743+
self.assertEqual(dtc.id, 0x123456)
2744+
self.assertEqual(dtc.status.get_byte_as_int(), 0x20)
2745+
self.assertEqual(len(dtc.extended_data), 1)
2746+
2747+
self.assertEqual(len(dtc.extended_data[0].raw_data), 0)
2748+
2749+
def test_zero_sized_data_zeropadding_not_ok_exception(self):
2750+
data = b'\x59' + self.sb + b'\x33\x12\x34\x56\x20'
2751+
for i in range(8):
2752+
self.wait_request_and_respond(data + b'\x00' * (i+1) )
2753+
2754+
def _test_zero_sized_data_zeropadding_not_ok_exception(self):
2755+
self.udsclient.config['tolerate_zero_padding'] = False
2756+
for i in range(8):
2757+
with self.assertRaises(InvalidResponseException):
2758+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 0})
2759+
2760+
def invalid_length_no_response_server_task(self):
2761+
self.wait_request_and_respond(b'')
2762+
self.wait_request_and_respond(b'\x59')
2763+
self.wait_request_and_respond(b'\x59'+ self.sb)
2764+
2765+
def test_invalid_length_no_response_exception(self):
2766+
self.invalid_length_no_response_server_task()
2767+
2768+
def _test_invalid_length_no_response_exception(self):
2769+
for i in range(3):
2770+
with self.assertRaises(InvalidResponseException):
2771+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2772+
2773+
def test_invalid_length_no_response_no_exception(self):
2774+
self.invalid_length_no_response_server_task()
2775+
2776+
def _test_invalid_length_no_response_no_exception(self):
2777+
self.udsclient.config['exception_on_invalid_response'] = False
2778+
for i in range(3):
2779+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2780+
self.assertFalse(response.valid)
2781+
2782+
2783+
def invalid_length_incomplete_dtc_server_task(self):
2784+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12')
2785+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34')
2786+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56')
2787+
2788+
def test_invalid_length_incomplete_dtc_exception(self):
2789+
self.invalid_length_incomplete_dtc_server_task()
2790+
2791+
def _test_invalid_length_incomplete_dtc_exception(self):
2792+
for i in range(3):
2793+
with self.assertRaises(InvalidResponseException):
2794+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2795+
2796+
def test_invalid_length_incomplete_dtc_no_exception(self):
2797+
self.invalid_length_incomplete_dtc_server_task()
2798+
2799+
def _test_invalid_length_incomplete_dtc_no_exception(self):
2800+
self.udsclient.config['exception_on_invalid_response'] = False
2801+
for i in range(3):
2802+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2803+
self.assertFalse(response.valid)
2804+
2805+
def invalid_length_missing_data_server_task(self):
2806+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20')
2807+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01')
2808+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02')
2809+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03')
2810+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04')
2811+
2812+
def test_invalid_length_missing_data_exception(self):
2813+
self.invalid_length_missing_data_server_task()
2814+
2815+
def _test_invalid_length_missing_data_exception(self):
2816+
for i in range(5):
2817+
with self.assertRaises(InvalidResponseException):
2818+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2819+
2820+
def test_invalid_length_missing_data_no_exception(self):
2821+
self.invalid_length_missing_data_server_task()
2822+
2823+
def _test_invalid_length_missing_data_no_exception(self):
2824+
self.udsclient.config['exception_on_invalid_response'] = False
2825+
for i in range(5):
2826+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2827+
self.assertFalse(response.valid)
2828+
2829+
def test_wrong_subfn_response_exception(self):
2830+
self.wait_request_and_respond(b'\x59' + self.badsb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05')
2831+
2832+
def _test_wrong_subfn_response_exception(self):
2833+
with self.assertRaises(UnexpectedResponseException):
2834+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33)
2835+
2836+
def test_wrong_subfn_response_no_exception(self):
2837+
self.wait_request_and_respond(b'\x59' + self.badsb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05')
2838+
2839+
def _test_wrong_subfn_response_no_exception(self):
2840+
self.udsclient.config['exception_on_unexpected_response'] = False
2841+
response = self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2842+
self.assertTrue(response.valid)
2843+
self.assertTrue(response.unexpected)
2844+
2845+
def test_wrong_record_number_response_exception(self):
2846+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x34\x12\x34\x56\x20\x01\x02\x03\x04\x05')
2847+
2848+
def _test_wrong_record_number_response_exception(self):
2849+
with self.assertRaises(UnexpectedResponseException):
2850+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2851+
2852+
def test_duplicate_dtc(self):
2853+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05\x12\x34\x56\x20\x01\x02\x03\x04\x05')
2854+
2855+
def _test_duplicate_dtc(self):
2856+
with self.assertRaises(InvalidResponseException):
2857+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2858+
2859+
def test_wrong_service_response_exception(self):
2860+
self.wait_request_and_respond(b'\x6F' + self.sb + b'\x33\x12\x34\x56\x20\x01\x02\x03\x04\x05')
2861+
2862+
def _test_wrong_service_response_exception(self):
2863+
with self.assertRaises(UnexpectedResponseException):
2864+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : 5, 0x789abc : 3})
2865+
2866+
def test_oob_values(self):
2867+
pass
2868+
2869+
def _test_oob_values(self):
2870+
with self.assertRaises(ValueError):
2871+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = -1, data_size=5)
2872+
2873+
with self.assertRaises(ValueError):
2874+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0xF0, data_size=5) # Limited to 0xEF
2875+
2876+
with self.assertRaises(ValueError):
2877+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 'asd', data_size=5)
2878+
2879+
with self.assertRaises(NotImplementedError):
2880+
self.udsclient.set_config('standard_version', 2013)
2881+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size=5)
2882+
self.udsclient.set_config('standard_version', latest_standard)
2883+
2884+
def test_oob_values_data_size(self): # validation is made at interpret_response
2885+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x12\x34\x56\x20\x99\x01\x02\x03')
2886+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x12\x34\x56\x20\x99\x01\x02\x03')
2887+
self.wait_request_and_respond(b'\x59' + self.sb + b'\x12\x34\x56\x20\x99\x01\x02\x03')
2888+
2889+
def _test_oob_values_data_size(self):
2890+
with self.assertRaises(ValueError):
2891+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = -1)
2892+
2893+
with self.assertRaises(ValueError):
2894+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33, data_size = {0x123456 : -1})
2895+
2896+
with self.assertRaises(ValueError):
2897+
self.udsclient.config['extended_data_size'] = {0x123456 : -1}
2898+
self.udsclient.get_dtc_extended_data_by_record_number(record_number = 0x33)
2899+
25872900

25882901
class TestReportUserDefMemoryDTCByStatusMask(ClientServerTest): # Subfn = 0x17
25892902
# Almost identical copy of duplicate of GenericTestStatusMaskRequest_DtcAndStatusMaskResponse
@@ -2860,7 +3173,6 @@ def _test_oob_value(self):
28603173
self.udsclient.set_config('standard_version', latest_standard)
28613174

28623175

2863-
28643176
class TestReportUserDefMemoryDTCSnapshotRecordByDTCNumber(ClientServerTest): # Subfn = 0x18
28653177

28663178
class Codec4711(DidCodec):
@@ -3301,8 +3613,6 @@ def _test_oob_values(self):
33013613
self.udsclient.set_config('standard_version', latest_standard)
33023614

33033615

3304-
3305-
33063616
class TestTeportUserDefMemoryDTCExtDataRecordByDTCNumber(ClientServerTest): # Subfn = 0x19
33073617
sb = struct.pack('B', 0x19)
33083618
badsb = struct.pack('B', 0x19+1)

0 commit comments

Comments
 (0)