Skip to content

Commit 0d46c4b

Browse files
authored
Support wait NRC when suppress_positive_response is set (#174)
Fixes #174
1 parent ef470fc commit 0d46c4b

File tree

3 files changed

+97
-10
lines changed

3 files changed

+97
-10
lines changed

doc/source/udsoncan/client.rst

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,23 @@ The ``Client`` object lets you use that feature by using ``suppress_positive_res
256256

257257
.. code-block:: python
258258
259-
with client.suppress_positive_response:
260-
client.tester_present()
261-
262-
When ``suppress_positive_response`` is asking for a service using a subfunction byte, the client will set suppressPosRspMsgIndicationBit before sending the request. The client will not wait for any response and will disregard negative responses if they happen. The response returned by the client function will always be ``None`` in that case.
263-
264-
If ``suppress_positive_response`` is asking for a service with no subfunction byte, the directive will be ignored and a warning message will be logged.
259+
with client.suppress_positive_response(wait_nrc=False):
260+
client.tester_present() # Will not wait for a response and always return None
261+
262+
with client.suppress_positive_response(wait_nrc=True):
263+
client.tester_present() # Will wait in case an NRC is returned.
264+
265+
When ``suppress_positive_response`` is askied for a service using a subfunction byte, the client will set suppressPosRspMsgIndicationBit before sending the request.
266+
If `wait_nrc` is `False` (default value), the client will not wait for any response and will disregard positive and negative responses if they happen.
267+
The response returned by the client function will always be ``None`` in that case.
268+
269+
If `wait_nrc` is `True`, the client will wait to see if a negative response is returned. In that scenario
270+
- No timeout will be raised if no response is received
271+
- If a positive response is received, it will not be validated and `None` will be returned
272+
- If a negative response is received, the normal processing will happen. Meaning either the response will be returned or a `NegativeResponseException`
273+
will be raised, depending on :ref:`exception_on_negative_response<config_exception_on_negative_response>` parameter.
274+
275+
If ``suppress_positive_response`` is askied for a service with no subfunction byte, the directive will be ignored and a warning message will be logged.
265276

266277
-----
267278

test/client/test_client.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,58 @@ def _test_request_in_response(self):
226226
req = Request(service=services.TesterPresent, subfunction=0)
227227
response = self.udsclient.send_request(req)
228228
self.assertIs(response.original_request, req)
229+
230+
def test_suppress_positive_response(self):
231+
self.conn.touserqueue.get(timeout=0.2)
232+
self.conn.fromuserqueue.put(b"\x7E\x00")
233+
234+
def _test_suppress_positive_response(self):
235+
req = Request(service=services.TesterPresent, subfunction=0)
236+
with self.udsclient.suppress_positive_response:
237+
response = self.udsclient.send_request(req)
238+
self.assertIsNone(response)
239+
self.conn.fromuserqueue.get() # Make sure to not exit too quick
240+
241+
def test_suppress_positive_response_wait_nrc_case1(self):
242+
self.conn.touserqueue.get(timeout=0.2)
243+
self.conn.fromuserqueue.put(b"\x7E\x00")
244+
245+
def _test_suppress_positive_response_wait_nrc_case1(self):
246+
# Case 1 - Positive
247+
req = Request(service=services.TesterPresent, subfunction=0)
248+
with self.udsclient.suppress_positive_response(wait_nrc=False):
249+
response = self.udsclient.send_request(req)
250+
self.assertIsNone(response)
251+
self.conn.fromuserqueue.get() # Make sure to not exit too quick
252+
253+
def test_suppress_positive_response_wait_nrc_case2(self):
254+
self.conn.touserqueue.get(timeout=0.2)
255+
self.conn.fromuserqueue.put(b"\x7E\x00")
256+
257+
def _test_suppress_positive_response_wait_nrc_case2(self):
258+
# Case 2 - Negative no wait
259+
req = Request(service=services.TesterPresent, subfunction=0)
260+
with self.udsclient.suppress_positive_response(wait_nrc=False):
261+
response = self.udsclient.send_request(req)
262+
self.assertIsNone(response)
263+
self.conn.fromuserqueue.get() # Make sure to not exit too quick
264+
265+
def test_suppress_positive_response_wait_nrc_case3(self):
266+
self.conn.touserqueue.get(timeout=0.2)
267+
self.conn.fromuserqueue.put(b"\x7F\x3E\x31")
268+
269+
def _test_suppress_positive_response_wait_nrc_case3(self):
270+
# Case 3 - Negative and wait
271+
req = Request(service=services.TesterPresent, subfunction=0)
272+
with self.assertRaises(NegativeResponseException):
273+
with self.udsclient.suppress_positive_response(wait_nrc=True):
274+
self.udsclient.send_request(req)
275+
276+
def test_suppress_positive_response_wait_nrc_case4(self):
277+
pass
278+
279+
def _test_suppress_positive_response_wait_nrc_case4(self):
280+
# Case 4 - No response is OK
281+
req = Request(service=services.TesterPresent, subfunction=0)
282+
with self.udsclient.suppress_positive_response(wait_nrc=True):
283+
self.udsclient.send_request(req)

udsoncan/client.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,23 @@ class Client:
5454

5555
class SuppressPositiveResponse:
5656
enabled: bool
57+
wait_nrc: bool
5758

5859
def __init__(self):
5960
self.enabled = False
61+
self.wait_nrc = False
62+
63+
def __call__(self, wait_nrc: bool = False):
64+
self.wait_nrc = wait_nrc
65+
return self
6066

6167
def __enter__(self):
6268
self.enabled = True
6369
return self
6470

6571
def __exit__(self, type, value, traceback):
6672
self.enabled = False
73+
self.wait_nrc = None
6774

6875
class PayloadOverrider:
6976
modifier: Optional[Union[Callable, bytes]]
@@ -2132,13 +2139,18 @@ def send_request(self, request: Request, timeout: int = -1) -> Optional[Response
21322139

21332140
self.conn.send(payload)
21342141

2135-
if request.suppress_positive_response or override_suppress_positive_response:
2142+
spr_used = request.suppress_positive_response or override_suppress_positive_response
2143+
wait_nrc = self.suppress_positive_response.enabled and self.suppress_positive_response.wait_nrc
2144+
2145+
if spr_used and not wait_nrc:
21362146
return None
21372147

21382148
done_receiving = False
21392149
if respect_overall_timeout:
21402150
overall_timeout_time = time.time() + overall_timeout
2141-
while not done_receiving:
2151+
2152+
timed_out = False
2153+
while not done_receiving and not timed_out:
21422154
done_receiving = True
21432155
self.logger.debug("Waiting for server response")
21442156

@@ -2158,11 +2170,17 @@ def send_request(self, request: Request, timeout: int = -1) -> Optional[Response
21582170
timeout_name_to_report = 'Global request timeout'
21592171
else: # Shouldn't go here.
21602172
timeout_name_to_report = 'Timeout'
2161-
raise TimeoutException('Did not receive response in time. %s time has expired (timeout=%.3f sec)' %
2162-
(timeout_name_to_report, timeout_value))
2173+
timed_out = True
2174+
21632175
except Exception as e:
21642176
raise e
21652177

2178+
if timed_out:
2179+
if spr_used:
2180+
return None
2181+
raise TimeoutException('Did not receive response in time. %s time has expired (timeout=%.3f sec)' %
2182+
(timeout_name_to_report, timeout_value))
2183+
21662184
response = Response.from_payload(payload)
21672185
self.last_response = response
21682186
self.logger.debug("Received response from server")
@@ -2210,4 +2228,7 @@ def send_request(self, request: Request, timeout: int = -1) -> Optional[Response
22102228

22112229
response.original_request = request
22122230

2231+
if spr_used:
2232+
return None
2233+
22132234
return response

0 commit comments

Comments
 (0)