Skip to content

Commit 2f770c4

Browse files
authored
Added reportWWHOBDDTCWithPermanentStatus + improvement to reportWWHOBDDTCByMaskRecord (#266)
- Report missing fields in reportWWHOBDDTCWithPermanentStatus - Added reportWWHOBDDTCWithPermanentStatus - Added a check for FunctionalGroupIdentifier match in request/response (reportWWHOBDDTCWithPermanentStatus & reportWWHOBDDTCByMaskRecord) - Added DtcClass class - fixed type hint in client dtc function that was missing the possibility to pass Severity/Status as objects - Moved the dtc class bit logic into the service layer - Improved unit testing
1 parent 5b0acb9 commit 2f770c4

File tree

6 files changed

+377
-66
lines changed

6 files changed

+377
-66
lines changed

doc/source/udsoncan/client.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ Methods by services
438438
.. automethod:: udsoncan.client.Client.get_mirrormemory_dtc_by_status_mask
439439
.. automethod:: udsoncan.client.Client.get_dtc_by_status_severity_mask
440440
.. automethod:: udsoncan.client.Client.get_wwh_obd_dtc_by_status_mask
441+
.. automethod:: udsoncan.client.Client.get_wwh_obd_dtc_with_permanent_status
441442
.. automethod:: udsoncan.client.Client.get_number_of_dtc_by_status_mask
442443
.. automethod:: udsoncan.client.Client.get_mirrormemory_number_of_dtc_by_status_mask
443444
.. automethod:: udsoncan.client.Client.get_number_of_emission_dtc_by_status_mask

doc/source/udsoncan/helper_classes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ DTC.Status
8585

8686
-----
8787

88+
.. _DTC_DtcClass:
89+
90+
DTC.DtcClass
91+
------------
92+
93+
.. autoclass:: udsoncan::Dtc.DtcClass
94+
95+
-----
96+
8897
.. _DTC_Severity:
8998

9099
DTC.Severity

test/client/test_read_dtc_information.py

Lines changed: 173 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2584,103 +2584,245 @@ def __init__(self, *args, **kwargs):
25842584

25852585
class TestreportDTCWWHOBDDTCByMaskRecord(ClientServerTest): # Subfn = 0x16
25862586
sb = struct.pack('B', 0x42)
2587-
badsb = struct.pack('B', 0x42+1)
2588-
functional_group_id = 0x1
2589-
status_mask = 0x2
2590-
severity_mask = 0x20
2591-
dtc_class = 0x4
2592-
expected_request_bytes = b'\x19' + sb + bytes([functional_group_id, status_mask, severity_mask | dtc_class])
25932587

25942588
def assert_no_data_response(self, response):
25952589
self.assertEqual(len(response.service_data.dtcs), 0)
25962590
self.assertEqual(response.service_data.dtc_count, 0)
25972591

25982592
def test_no_data(self):
25992593
request = self.conn.touserqueue.get(timeout=0.2)
2600-
self.assertEqual(request, self.expected_request_bytes)
2601-
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0x1, 0x2, 0x3, 0x4]))
2594+
self.assertEqual(request, b'\x19' + self.sb + bytes([1, 2, 0xA5]))
2595+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0x1, 0x2, 0xA0, 0x4]))
26022596

26032597
def _test_no_data(self):
2604-
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(self.functional_group_id, self.status_mask, self.severity_mask, self.dtc_class)
2598+
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(functional_group_id=1, status_mask=2, severity_mask=0xA0, dtc_class=5)
26052599
self.assert_no_data_response(response)
26062600

26072601
def test_functional_group_verification(self):
26082602
pass
26092603

26102604
def _test_functional_group_verification(self):
26112605
with self.assertRaises(ValueError):
2612-
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(None, self.status_mask, self.severity_mask, self.dtc_class)
2606+
self.udsclient.get_wwh_obd_dtc_by_status_mask(None, status_mask=2, severity_mask=0xA0, dtc_class=4)
26132607

26142608
with self.assertRaises(ValueError):
2615-
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(0xff, self.status_mask, self.severity_mask, self.dtc_class)
2609+
self.udsclient.get_wwh_obd_dtc_by_status_mask(0xff, status_mask=2, severity_mask=0xA0, dtc_class=4)
26162610

26172611
def assert_no_data_response_with_severity_class(self, response):
26182612
self.assertEqual(len(response.service_data.dtcs), 0)
26192613
self.assertEqual(response.service_data.dtc_count, 0)
26202614

26212615
def test_no_data_with_severity_class(self):
26222616
request = self.conn.touserqueue.get(timeout=0.2)
2623-
self.assertEqual(request, self.expected_request_bytes)
2624-
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0x1, 0x2, 0x3, 0x4]))
2617+
self.assertEqual(request, b'\x19' + self.sb + bytes([0x01, 0x02, 0xA7]))
2618+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0x1, 0x2, 0xA0, 0x4]))
26252619

26262620
def _test_no_data_with_severity_class(self):
2627-
severity_class = Dtc.Severity()
2628-
severity_class.set_byte(self.severity_mask)
2629-
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(self.functional_group_id, self.status_mask, severity_class, self.dtc_class)
2621+
severity_obj = Dtc.Severity.from_byte(0xA0)
2622+
dtc_class_obj = Dtc.DtcClass.from_byte(0x7)
2623+
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(functional_group_id=1, status_mask=2, severity_mask=severity_obj, dtc_class=dtc_class_obj)
26302624
self.assert_no_data_response(response)
26312625

26322626
def assert_single_data_response(self, response):
2627+
assert isinstance(response, Response)
2628+
assert isinstance(response.service_data, ReadDTCInformation.ResponseData)
26332629
self.assertEqual(len(response.service_data.dtcs), 1)
26342630
self.assertEqual(response.service_data.dtc_count, 1)
26352631

2632+
self.assertIsNotNone(response.service_data.functional_group_id)
2633+
self.assertIsNotNone(response.service_data.status_availability)
2634+
self.assertIsNotNone(response.service_data.severity_availability)
2635+
self.assertIsNotNone(response.service_data.dtc_format)
2636+
2637+
self.assertEqual(response.service_data.functional_group_id, 0xAB)
2638+
self.assertEqual(response.service_data.status_availability.get_byte_as_int(), 0x7E)
2639+
self.assertEqual(response.service_data.severity_availability.get_byte_as_int(), 0xA0)
2640+
self.assertEqual(response.service_data.dtc_format, Dtc.Format.SAE_J2012_DA_DTCFormat_04) # 0x04
2641+
26362642
dtc = response.service_data.dtcs[0]
26372643

26382644
self.assertEqual(dtc.id, 0x123456)
2639-
self.assertEqual(dtc.status.get_byte_as_int(), 0x20)
2640-
self.assertEqual(dtc.severity.get_byte_as_int(), 32)
2645+
self.assertEqual(dtc.status.get_byte_as_int(), 0x25)
2646+
self.assertEqual(dtc.severity.get_byte_as_int(), 0x20)
26412647

26422648
self.assertEqual(len(dtc.extended_data), 0)
26432649

26442650
def test_single_data(self):
26452651
request = self.conn.touserqueue.get(timeout=0.2)
2646-
self.assertEqual(request, self.expected_request_bytes)
2647-
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0x1, 0x2, 0x3, 0x4, 0x33, 0x12, 0x34, 0x56, 0x20]))
2652+
self.assertEqual(request, b'\x19' + self.sb + bytes([0xAB, 0x02, 0xA7]))
2653+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0xAB, 0x7E, 0xA0, 0x4, 0x20, 0x12, 0x34, 0x56, 0x25]))
26482654

26492655
def _test_single_data(self):
2650-
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(self.functional_group_id, self.status_mask, self.severity_mask, self.dtc_class)
2656+
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(functional_group_id=0xAB, status_mask=2, severity_mask=0xA0, dtc_class=0x7)
26512657
self.assert_single_data_response(response)
26522658

26532659
def assert_multiple_data_response(self, response):
2660+
assert isinstance(response, Response)
2661+
assert isinstance(response.service_data, ReadDTCInformation.ResponseData)
2662+
26542663
self.assertEqual(len(response.service_data.dtcs), 2)
26552664
self.assertEqual(response.service_data.dtc_count, 2)
26562665

2666+
self.assertIsNotNone(response.service_data.functional_group_id)
2667+
self.assertIsNotNone(response.service_data.status_availability)
2668+
self.assertIsNotNone(response.service_data.severity_availability)
2669+
self.assertIsNotNone(response.service_data.dtc_format)
2670+
2671+
self.assertEqual(response.service_data.functional_group_id, 0xAB)
2672+
self.assertEqual(response.service_data.status_availability.get_byte_as_int(), 0x7E)
2673+
self.assertEqual(response.service_data.severity_availability.get_byte_as_int(), 0xA0)
2674+
self.assertEqual(response.service_data.dtc_format, Dtc.Format.SAE_J2012_DA_DTCFormat_04) # 0x04
2675+
26572676
dtc = response.service_data.dtcs[0]
26582677

26592678
self.assertEqual(dtc.id, 0x123456)
2660-
self.assertEqual(dtc.status.get_byte_as_int(), 0x20)
2661-
self.assertEqual(dtc.severity.get_byte_as_int(), 32)
2679+
self.assertEqual(dtc.status.get_byte_as_int(), 0x78)
2680+
self.assertEqual(dtc.severity.get_byte_as_int(), 0xA0)
26622681
self.assertEqual(len(dtc.extended_data), 0)
26632682

26642683
dtc = response.service_data.dtcs[1]
26652684

2666-
self.assertEqual(dtc.id, 0x123457)
2667-
self.assertEqual(dtc.status.get_byte_as_int(), 0x20)
2668-
self.assertEqual(dtc.severity.get_byte_as_int(), 32)
2685+
self.assertEqual(dtc.id, 0x112233)
2686+
self.assertEqual(dtc.status.get_byte_as_int(), 0x44)
2687+
self.assertEqual(dtc.severity.get_byte_as_int(), 0xE0)
26692688
self.assertEqual(len(dtc.extended_data), 0)
26702689

26712690

26722691
def test_multiple_data(self):
26732692
request = self.conn.touserqueue.get(timeout=0.2)
2674-
self.assertEqual(request, self.expected_request_bytes)
2675-
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0x1, 0x2, 0x3, 0x4])
2676-
+ bytes([0x33, 0x12, 0x34, 0x56, 0x20])
2677-
+ bytes([0x33, 0x12, 0x34, 0x57, 0x20]))
2693+
self.assertEqual(request, b'\x19' + self.sb + bytes([0xAB, 0x02, 0xA7]))
2694+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0xAB, 0x7E, 0xA0, 0x4])
2695+
+ bytes([0xA0, 0x12, 0x34, 0x56, 0x78])
2696+
+ bytes([0xE0, 0x11, 0x22, 0x33, 0x44]))
26782697

26792698
def _test_multiple_data(self):
2680-
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(self.functional_group_id, self.status_mask, self.severity_mask, self.dtc_class)
2699+
response = self.udsclient.get_wwh_obd_dtc_by_status_mask(functional_group_id=0xAB, status_mask=2, severity_mask=0xA0, dtc_class=0x7)
26812700
self.assert_multiple_data_response(response)
26822701

26832702

2703+
def test_unexpected_functional_group_id(self):
2704+
request = self.conn.touserqueue.get(timeout=0.2)
2705+
self.assertEqual(request, b'\x19' + self.sb + bytes([0xAB, 0x02, 0xA7])) # AB -> AB+1
2706+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0xAB+1, 0x7E, 0xA0, 0x4]))
2707+
2708+
def _test_unexpected_functional_group_id(self):
2709+
with self.assertRaises(UnexpectedResponseException):
2710+
self.udsclient.get_wwh_obd_dtc_by_status_mask(functional_group_id=0xAB, status_mask=2, severity_mask=0xA0, dtc_class=0x7)
2711+
2712+
class TestreportWWHOBDDTCWithPermanentStatus(ClientServerTest): # Subfn = 0x16
2713+
sb = struct.pack('B', 0x55)
2714+
2715+
def assert_no_data_response(self, response):
2716+
self.assertEqual(len(response.service_data.dtcs), 0)
2717+
self.assertEqual(response.service_data.dtc_count, 0)
2718+
2719+
def test_no_data(self):
2720+
request = self.conn.touserqueue.get(timeout=0.2)
2721+
self.assertEqual(request, b'\x19' + self.sb + bytes([1]))
2722+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0x1, 0x2, 0x4]))
2723+
2724+
def _test_no_data(self):
2725+
response = self.udsclient.get_wwh_obd_dtc_with_permanent_status(functional_group_id=1)
2726+
self.assert_no_data_response(response)
2727+
2728+
def test_functional_group_verification(self):
2729+
pass
2730+
2731+
def _test_functional_group_verification(self):
2732+
with self.assertRaises(ValueError):
2733+
self.udsclient.get_wwh_obd_dtc_with_permanent_status(None)
2734+
2735+
with self.assertRaises(ValueError):
2736+
self.udsclient.get_wwh_obd_dtc_with_permanent_status(0xff)
2737+
2738+
def assert_no_data_response_with_severity_class(self, response):
2739+
self.assertEqual(len(response.service_data.dtcs), 0)
2740+
self.assertEqual(response.service_data.dtc_count, 0)
2741+
2742+
2743+
def assert_single_data_response(self, response):
2744+
assert isinstance(response, Response)
2745+
assert isinstance(response.service_data, ReadDTCInformation.ResponseData)
2746+
self.assertEqual(len(response.service_data.dtcs), 1)
2747+
self.assertEqual(response.service_data.dtc_count, 1)
2748+
2749+
self.assertIsNotNone(response.service_data.functional_group_id)
2750+
self.assertIsNotNone(response.service_data.status_availability)
2751+
self.assertIsNone(response.service_data.severity_availability)
2752+
self.assertIsNotNone(response.service_data.dtc_format)
2753+
2754+
self.assertEqual(response.service_data.functional_group_id, 0xAB)
2755+
self.assertEqual(response.service_data.status_availability.get_byte_as_int(), 0x7E)
2756+
self.assertEqual(response.service_data.dtc_format, Dtc.Format.SAE_J2012_DA_DTCFormat_04) # 0x04
2757+
2758+
dtc = response.service_data.dtcs[0]
2759+
2760+
self.assertEqual(dtc.id, 0x123456)
2761+
self.assertEqual(dtc.status.get_byte_as_int(), 0x33)
2762+
self.assertEqual(dtc.severity.get_byte_as_int(), 0x20)
2763+
2764+
self.assertEqual(len(dtc.extended_data), 0)
2765+
2766+
def test_single_data(self):
2767+
request = self.conn.touserqueue.get(timeout=0.2)
2768+
self.assertEqual(request, b'\x19' + self.sb + bytes([0xAB]))
2769+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0xAB, 0x7E, 0x4, 0x20, 0x12, 0x34, 0x56, 0x33]))
2770+
2771+
def _test_single_data(self):
2772+
response = self.udsclient.get_wwh_obd_dtc_with_permanent_status(functional_group_id=0xAB)
2773+
self.assert_single_data_response(response)
2774+
2775+
def assert_multiple_data_response(self, response):
2776+
assert isinstance(response, Response)
2777+
assert isinstance(response.service_data, ReadDTCInformation.ResponseData)
2778+
2779+
self.assertEqual(len(response.service_data.dtcs), 2)
2780+
self.assertEqual(response.service_data.dtc_count, 2)
2781+
2782+
self.assertIsNotNone(response.service_data.functional_group_id)
2783+
self.assertIsNotNone(response.service_data.status_availability)
2784+
self.assertIsNone(response.service_data.severity_availability)
2785+
self.assertIsNotNone(response.service_data.dtc_format)
2786+
2787+
self.assertEqual(response.service_data.functional_group_id, 0xAB)
2788+
self.assertEqual(response.service_data.status_availability.get_byte_as_int(), 0x7E)
2789+
self.assertEqual(response.service_data.dtc_format, Dtc.Format.SAE_J2012_DA_DTCFormat_04) # 0x04
2790+
2791+
dtc = response.service_data.dtcs[0]
2792+
2793+
self.assertEqual(dtc.id, 0x123456)
2794+
self.assertEqual(dtc.status.get_byte_as_int(), 0x78)
2795+
self.assertEqual(dtc.severity.get_byte_as_int(), 0xA0)
2796+
self.assertEqual(len(dtc.extended_data), 0)
2797+
2798+
dtc = response.service_data.dtcs[1]
2799+
2800+
self.assertEqual(dtc.id, 0x112233)
2801+
self.assertEqual(dtc.status.get_byte_as_int(), 0x44)
2802+
self.assertEqual(dtc.severity.get_byte_as_int(), 0xE0)
2803+
self.assertEqual(len(dtc.extended_data), 0)
2804+
2805+
2806+
def test_multiple_data(self):
2807+
request = self.conn.touserqueue.get(timeout=0.2)
2808+
self.assertEqual(request, b'\x19' + self.sb + bytes([0xAB]))
2809+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0xAB, 0x7E, 0x4])
2810+
+ bytes([0xA0, 0x12, 0x34, 0x56, 0x78])
2811+
+ bytes([0xE0, 0x11, 0x22, 0x33, 0x44]))
2812+
2813+
def _test_multiple_data(self):
2814+
response = self.udsclient.get_wwh_obd_dtc_with_permanent_status(functional_group_id=0xAB)
2815+
self.assert_multiple_data_response(response)
2816+
2817+
def test_unexpected_functional_group_id(self):
2818+
request = self.conn.touserqueue.get(timeout=0.2)
2819+
self.assertEqual(request, b'\x19' + self.sb + bytes([0xAB])) # AB -> AB+1
2820+
self.conn.fromuserqueue.put(b'\x59' + self.sb + bytes([0xAB+1, 0x7E, 0x4]))
2821+
2822+
def _test_unexpected_functional_group_id(self):
2823+
with self.assertRaises(UnexpectedResponseException):
2824+
self.udsclient.get_wwh_obd_dtc_with_permanent_status(functional_group_id=0xAB)
2825+
26842826
class TestreportDTCExtDataRecordByRecordNumber(ClientServerTest): # Subfn = 0x16
26852827
sb = struct.pack('B', 0x16)
26862828
badsb = struct.pack('B', 0x16+1)

0 commit comments

Comments
 (0)