Skip to content

Commit

Permalink
Added configurable wait func (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
pylessard authored Jan 3, 2024
1 parent 4d15123 commit c851cd1
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 1 deletion.
15 changes: 15 additions & 0 deletions doc/source/isotp/implementation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,18 @@ The transport layer ``params`` parameter must be a dictionary with the following

Sets the name of the logger from the ``logging`` module used to log info and debug information

.. _param_wait_func:

.. attribute:: wait_func
:annotation: (callable)

**default: "time.sleep"**

Defines a waiting function used to create the necessary delay between consecutive frames during a transmission, dictated by the receiver STMin.
Expected signature is ``my_wait_func(delay:float) -> None``

Defaults value is the system ``sleep`` function, which can have a coarse granularity, depending on the scheduler policy.

-----

.. _rate_limiter_section:
Expand Down Expand Up @@ -407,6 +419,9 @@ Using the approach described above, a message can be read from the link-layer an
40us latency is far better than the latency caused by calls to ``time.sleep()`` required with v1.x. Considering that a CAN bus running at 500kbps has a message duration of about 230us,
the latency is in the acceptable range.

Finally, the delay between consecutive frames is dictated by a user-definable function passed with the :ref:`wait_func<param_wait_func>` parameter. By default, the wait function is the system ``sleep`` function.
On machines where the ``sleep`` function has a coarse granularity and a high resolution timer is available, it is possible to pass a busy-wait function to this parameter.

--------

.. _backward_compatibility:
Expand Down
12 changes: 11 additions & 1 deletion isotp/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ class Params:
listen_mode: bool
blocking_send: bool
logger_name: str
wait_func: Callable[[float], None]

def __init__(self):
self.stmin = 0
Expand All @@ -342,6 +343,7 @@ def __init__(self):
self.listen_mode = False
self.blocking_send = False
self.logger_name = TransportLayer.LOGGER_NAME
self.wait_func = time.sleep

def set(self, key: str, val: Any, validate: bool = True) -> None:
param_alias: Dict[str, str] = {
Expand Down Expand Up @@ -464,6 +466,14 @@ def validate(self) -> None:
if not isinstance(self.logger_name, str):
raise ValueError('logger_name must be a string')

if not callable(self.wait_func):
raise ValueError('wait_func should be a callable')

try:
self.wait_func(0.001)
except Exception as e:
raise ValueError("Given wait_func raised an exception %s" % e)

class RxState(enum.Enum):
IDLE = 0
WAIT_CF = 1
Expand Down Expand Up @@ -1557,7 +1567,7 @@ def _main_thread_fn(self) -> None:
delay = self.next_cf_delay()
assert delay is not None # Confirmed by is_tx_transmitting_cf()
if delay > 0:
time.sleep(delay) # If we are transmitting CFs, no need to call rxfn, we can stream those CF with short sleep
self.params.wait_func(delay) # If we are transmitting CFs, no need to call rxfn, we can stream those CF with short sleep
if not self.events.stop_requested.is_set():
super().process(do_rx=False, do_tx=True)
else:
Expand Down
14 changes: 14 additions & 0 deletions test/test_transport_layer_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,20 @@ def test_params_bad_values(self):
params['logger_name'] = 'asd'
self.create_layer(params)

for val in ['asd', 1.1, None]:
with self.assertRaises(ValueError):
params['wait_func'] = val
self.create_layer(params)

def wait_func_bad_signature():
pass

with self.assertRaises(ValueError):
params['wait_func'] = val
self.create_layer(params)

params['wait_func'] = time.sleep

# Check the behavior of the transport layer. Sequenece of CAN frames, timings, etc.


Expand Down

0 comments on commit c851cd1

Please sign in to comment.