Skip to content

Commit b23db8f

Browse files
authored
Do not report error in listen mode when listening to the transmitter (#127)
1 parent fb3cad3 commit b23db8f

File tree

2 files changed

+75
-9
lines changed

2 files changed

+75
-9
lines changed

isotp/protocol.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,12 +709,15 @@ def send(self,
709709
:type send_timeout: float or None
710710
711711
:raises ValueError: Given data is not a bytearray, a tuple (generator,size) or the size is too big
712-
:raises RuntimeError: Transmit queue is full
712+
:raises RuntimeError: Transmit queue is full or tried to transmit while the stack is configured in :ref:`listen mode<param_listen_mode>`
713713
:raises BlockingSendTimeout: When :ref:`blocking_send<param_blocking_send>` is set to ``True`` and the send operation does not complete in the given timeout.
714714
:raises BlockingSendFailure: When :ref:`blocking_send<param_blocking_send>` is set to ``True`` and the transmission failed for any reason (e.g. unexpected frame or bad timings), including a timeout. Note that
715715
:class:`BlockingSendTimeout<BlockingSendTimeout>` inherits :class:`BlockingSendFailure<BlockingSendFailure>`.
716716
"""
717717

718+
if self.params.listen_mode:
719+
raise RuntimeError("Cannot transmit when listen_mode=True")
720+
718721
if target_address_type is None:
719722
target_address_type = self.params.default_target_address_type
720723
else:
@@ -1007,13 +1010,13 @@ def _process_tx(self) -> ProcessTxReport:
10071010
return self.ProcessTxReport(msg=None, immediate_rx_required=False)
10081011

10091012
if self.tx_state == self.TxState.IDLE:
1010-
self._trigger_error(isotp.errors.UnexpectedFlowControlError('Received a FlowControl message while transmission was Idle. Ignoring'))
1013+
self._trigger_error(isotp.errors.UnexpectedFlowControlError('Received a FlowControl message while transmission was Idle. Ignoring'), inhibit_in_listen_mode=True)
10111014
else:
10121015
if flow_control_frame.flow_status == PDU.FlowStatus.Wait:
1013-
if self.params.wftmax == 0:
1016+
if self.params.wftmax == 0 and not self.params.listen_mode:
10141017
self._trigger_error(isotp.errors.UnsupportedWaitFrameError(
10151018
'Received a FlowControl requesting to wait, but wftmax is set to 0'))
1016-
elif self.wft_counter >= self.params.wftmax:
1019+
elif self.wft_counter >= self.params.wftmax and not self.params.listen_mode:
10171020
self._trigger_error(isotp.errors.MaximumWaitFrameReachedError(
10181021
'Received %d wait frame which is the maximum set in params.wftmax' % (self.wft_counter)))
10191022
self._stop_sending(success=False)
@@ -1388,10 +1391,11 @@ def _start_reception_after_first_frame_if_valid(self, pdu: PDU) -> bool:
13881391

13891392
return started
13901393

1391-
def _trigger_error(self, error: isotp.errors.IsoTpError) -> None:
1394+
def _trigger_error(self, error: isotp.errors.IsoTpError, inhibit_in_listen_mode:bool=False) -> None:
13921395
if self.error_handler is not None:
13931396
if hasattr(self.error_handler, '__call__') and isinstance(error, isotp.errors.IsoTpError):
1394-
self.error_handler(error)
1397+
if not (inhibit_in_listen_mode and self.params.listen_mode):
1398+
self.error_handler(error)
13951399
else:
13961400
self.logger.warning('Given error handler is not a callable object.')
13971401

test/test_transport_layer.py

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def error_handler(self, error):
104104
self.error_triggered[error.__class__].append(error)
105105

106106
def assert_no_error_reported(self):
107-
self.assertEqual(len(self.error_triggered), 0, "At least 1 error was reported")
107+
self.assertEqual(len(self.error_triggered), 0, "Errors were reported and shouldn't have")
108108

109109
def read_queue_blocking(self, q: queue.Queue, timeout: float):
110110
try:
@@ -171,7 +171,11 @@ def test_blocking_send(self):
171171
self.layer1.send(bytes([1] * 100), send_timeout=5)
172172
self.assert_no_error_reported()
173173

174-
def test_listen_mode(self):
174+
def test_listen_mode_receiver(self):
175+
# listen mode enabled. Address is the receiver address
176+
self.layer2.params.blocksize=5
177+
self.layer2.params.stmin=10
178+
self.layer2.load_params()
175179
layer3_rx_queue = queue.Queue()
176180
layer3_tx_queue = queue.Queue()
177181

@@ -206,6 +210,64 @@ def test_listen_mode(self):
206210
finally:
207211
layer3.stop()
208212

213+
def test_listen_mode_transmitter(self):
214+
# listen mode enabled. Address is the transmitter address
215+
# Expect no errors
216+
self.layer2.params.blocksize=5
217+
self.layer2.params.stmin=10
218+
self.layer2.load_params()
219+
layer3_rx_queue = queue.Queue()
220+
layer3_tx_queue = queue.Queue()
221+
222+
self.queue1to2.add_tx_splice(layer3_rx_queue)
223+
self.queue2to1.add_tx_splice(layer3_rx_queue)
224+
225+
params3 = self.STACK_PARAMS.copy()
226+
params3.update(dict(logger_name='layer3', listen_mode=True))
227+
228+
# Layer 3 should receive the same thing as layer 2 even though it receives all messages
229+
layer3 = isotp.TransportLayer(
230+
txfn=partial(self.send_queue, layer3_tx_queue),
231+
rxfn=partial(self.read_queue_blocking, layer3_rx_queue),
232+
address=self.address1,
233+
error_handler=self.error_handler,
234+
params=params3
235+
)
236+
237+
unittest_logging.configure_transport_layer(layer3)
238+
layer3.start()
239+
try:
240+
payload = bytes([x % 255 for x in range(100)])
241+
self.layer1.send(payload)
242+
payload2 = self.layer2.recv(block=True, timeout=5)
243+
self.assertEqual(payload, payload2)
244+
245+
self.assertFalse(layer3.available()) # Address does not match receiver address
246+
247+
self.assert_no_error_reported()
248+
self.assertTrue(layer3_tx_queue.empty()) # layer3 cannot send
249+
finally:
250+
layer3.stop()
251+
252+
def test_listen_mode_cannot_transmit(self):
253+
params3 = self.STACK_PARAMS.copy()
254+
params3.update(dict(logger_name='layer3', listen_mode=True))
255+
256+
layer3_tx_queue = queue.Queue()
257+
layer3_rx_queue = queue.Queue()
258+
# Layer 3 should receive the same thing as layer 2 even though it receives all messages
259+
layer3 = isotp.TransportLayer(
260+
txfn=partial(self.send_queue, layer3_tx_queue),
261+
rxfn=partial(self.read_queue_blocking, layer3_rx_queue),
262+
address=self.address1,
263+
error_handler=self.error_handler,
264+
params=params3
265+
)
266+
267+
with self.assertRaises(Exception):
268+
layer3.send(bytes([1,2,3,4,5]))
269+
270+
209271
def test_no_call_to_process_after_start(self):
210272
# Make sure we maintain backward compatibility without introducing weird race conditions into old application
211273
with self.assertRaises(RuntimeError):
@@ -287,7 +349,7 @@ def error_handler(self, error):
287349
self.error_triggered[error.__class__].append(error)
288350

289351
def assert_no_error_reported(self):
290-
self.assertEqual(len(self.error_triggered), 0, "At least 1 error was reported")
352+
self.assertEqual(len(self.error_triggered), 0, "Errors were reported and shouldn't have")
291353

292354
def read_queue_blocking(self, q: queue.Queue, timeout: float):
293355
try:

0 commit comments

Comments
 (0)