Skip to content

Commit a289386

Browse files
authored
Merge pull request #65 from pylessard/59-handle-listen-mode
#59 - Support Listen Mode where no Flow Control is sent.
2 parents 8e473b2 + 184106b commit a289386

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed

Diff for: doc/source/isotp/implementation.rst

+12-1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,17 @@ The transport layer ``params`` parameter must be a dictionary with the following
211211
Refer to :ref:`Rate Limiter Section<rate_limiter_section>` for more details
212212

213213

214+
.. _param_listen_mode:
215+
216+
.. attribute:: listen_mode
217+
:annotation: (bool)
218+
219+
**default: False**
220+
221+
When Listen Mode is enabled, the :class:`TransportLayer<isotp.TransportLayer>` will correctly receive and transmit ISO-TP Frame, but will not send Flow Control
222+
message when receiving a frame. This mode of operation is usefull to listen to a transmission between two third-party devices without interferring.
223+
224+
214225
-----
215226

216227
.. _rate_limiter_section:
@@ -232,7 +243,7 @@ It is important to understand that this product also defines the maximum burst s
232243
rate limiter is intended to fix (See `issue #61 <https://github.com/pylessard/python-can-isotp/issues/61>`_). Consider the case where a big payload of 10000 bytes must be transmitted,
233244
after the transmission of the FirstFrame, the receiving party sends a FlowControl message with BlockSize=0 and STMin=0. In that situation, the whole payload can be sent immediately
234245
but writing 10000 bytes in a single burst might be too much for the CAN driver to handle and may overflow its internal buffer. In
235-
this situation, it is useful to use the rate limiter to reduces the strain on the driver internal buffer.
246+
this situation, it is useful to use the rate limiter to reduces the strain on the driver internal buffer.
236247

237248
In the above scenario, having a bitrate of 80000 bps and a window size of 0.1 sec would make the :class:`isotp.TransportLayer<isotp.TransportLayer>` output a burst of 8000 bits (1000 bytes) every 0.1 seconds.
238249

Diff for: isotp/protocol.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ class Params:
300300
__slots__ = ( 'stmin', 'blocksize', 'squash_stmin_requirement', 'rx_flowcontrol_timeout',
301301
'rx_consecutive_frame_timeout', 'tx_padding', 'wftmax', 'tx_data_length', 'tx_data_min_length',
302302
'max_frame_size', 'can_fd', 'bitrate_switch', 'default_target_address_type',
303-
'rate_limit_max_bitrate', 'rate_limit_window_size', 'rate_limit_enable'
303+
'rate_limit_max_bitrate', 'rate_limit_window_size', 'rate_limit_enable', 'listen_mode'
304304
)
305305

306306
def __init__(self):
@@ -320,6 +320,7 @@ def __init__(self):
320320
self.rate_limit_max_bitrate = 100000000
321321
self.rate_limit_window_size = 0.2
322322
self.rate_limit_enable = False
323+
self.listen_mode = False
323324

324325
def set(self, key, val, validate=True):
325326
param_alias = {
@@ -425,6 +426,8 @@ def validate(self):
425426
if self.rate_limit_max_bitrate * self.rate_limit_window_size < self.tx_data_length * 8:
426427
raise ValueError('Rate limiter is so restrictive that a SingleFrame cannot be sent. Please, allow a higher bitrate or increase the window size. (tx_data_length = %d)' % self.tx_data_length)
427428

429+
if not isinstance(self.listen_mode, bool):
430+
raise ValueError('listen_mode must be a boolean value')
428431

429432
class Timer:
430433
def __init__(self, timeout):
@@ -695,7 +698,9 @@ def process_tx(self):
695698
self.pending_flow_control_tx = False
696699
if self.pending_flowcontrol_status == PDU.FlowStatus.ContinueToSend:
697700
self.start_rx_cf_timer() # We tell the sending party that it can continue to send data, so we start checking the timeout again
698-
return self.make_flow_control(flow_status=self.pending_flowcontrol_status); # No need to wait.
701+
702+
if not self.params.listen_mode: # Inhibit Flow Control in listen mode.
703+
return self.make_flow_control(flow_status=self.pending_flowcontrol_status); # No need to wait.
699704

700705
# Handle flow control reception
701706
flow_control_frame = self.last_flow_control_frame # Reads the last message received and clears it. (Dequeue message)

Diff for: isotp/protocol.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class TransportLayer:
3535
max_frame_size: int
3636
can_fd: bool
3737
bitrate_switch: bool
38+
listen_mode: bool
3839
target_address_type: Optional[TargetAddressType]
3940
def __init__(self) -> None: ...
4041
def set(self,

Diff for: test/test_transport_layer.py

+17
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,23 @@ def test_receive_multiframe_check_flowcontrol(self):
120120
self.assertEqual(data, bytearray(payload))
121121
self.assertIsNone(self.rx_isotp_frame())
122122

123+
def test_receive_multiframe_check_no_flowcontrol_listen_mode(self):
124+
self.stack.params.set('stmin', 0x02)
125+
self.stack.params.set('blocksize', 0x05)
126+
self.stack.params.set('listen_mode', True)
127+
128+
payload_size = 10
129+
payload = self.make_payload(payload_size)
130+
self.simulate_rx(data = [0x10, payload_size] + payload[0:6])
131+
self.stack.process()
132+
self.assertIsNone(self.get_tx_can_msg()) # No Flow Control here. We are in listen mode
133+
self.assertIsNone(self.rx_isotp_frame())
134+
self.simulate_rx(data = [0x21] + payload[6:10])
135+
self.stack.process()
136+
data = self.rx_isotp_frame()
137+
self.assertEqual(data, bytearray(payload))
138+
self.assertIsNone(self.rx_isotp_frame())
139+
123140
def test_receive_overflow_handling(self):
124141
self.stack.params.set('stmin', 0)
125142
self.stack.params.set('blocksize', 0)

0 commit comments

Comments
 (0)