Skip to content

Commit 832eee6

Browse files
authored
Merge pull request #426 from hardbyte/fix-425
Separate interface and implementation for periodic sending
2 parents 56a4cb2 + daa4cf4 commit 832eee6

6 files changed

Lines changed: 50 additions & 22 deletions

File tree

can/broadcastmanager.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ class CyclicTask(object):
2424
@abc.abstractmethod
2525
def stop(self):
2626
"""Cancel this periodic task.
27+
28+
:raises can.CanError:
29+
If stop is called on an already stopped task.
2730
"""
2831

2932

can/bus.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,7 @@ def send_periodic(self, msg, period, duration=None, store_task=True):
198198
api with ``store_task==True`` may not be appropriate as the stopped tasks are
199199
still taking up memory as they are associated with the Bus instance.
200200
"""
201-
if not hasattr(self, "_lock_send_periodic"):
202-
# Create a send lock for this bus
203-
self._lock_send_periodic = threading.Lock()
204-
task = ThreadBasedCyclicSendTask(self, self._lock_send_periodic, msg, period, duration)
201+
task = self._send_periodic_internal(msg, period, duration)
205202
# we wrap the task's stop method to also remove it from the Bus's list of tasks
206203
original_stop_method = task.stop
207204

@@ -213,8 +210,33 @@ def wrapped_stop_method(remove_task=True):
213210
pass
214211
original_stop_method()
215212
task.stop = wrapped_stop_method
213+
216214
if store_task:
217215
self._periodic_tasks.append(task)
216+
217+
return task
218+
219+
def _send_periodic_internal(self, msg, period, duration=None):
220+
"""Default implementation of periodic message sending using threading.
221+
222+
Override this method to enable a more efficient backend specific approach.
223+
224+
:param can.Message msg:
225+
Message to transmit
226+
:param float period:
227+
Period in seconds between each message
228+
:param float duration:
229+
The duration to keep sending this message at given rate. If
230+
no duration is provided, the task will continue indefinitely.
231+
:return:
232+
A started task instance. Note the task can be stopped (and depending on
233+
the backend modified) by calling the :meth:`stop` method.
234+
:rtype: can.broadcastmanager.CyclicSendTaskABC
235+
"""
236+
if not hasattr(self, "_lock_send_periodic"):
237+
# Create a send lock for this bus
238+
self._lock_send_periodic = threading.Lock()
239+
task = ThreadBasedCyclicSendTask(self, self._lock_send_periodic, msg, period, duration)
218240
return task
219241

220242
def stop_all_periodic_tasks(self, remove_tasks=True):

can/interfaces/ixxat/canlib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ def send(self, msg, timeout=None):
499499
else:
500500
_canlib.canChannelPostMessage(self._channel_handle, message)
501501

502-
def send_periodic(self, msg, period, duration=None):
502+
def _send_periodic_internal(self, msg, period, duration=None):
503503
"""Send a message using built-in cyclic transmit list functionality."""
504504
if self._scheduler is None:
505505
self._scheduler = HANDLE()

can/interfaces/socketcan/socketcan.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ def _send_once(self, data, channel=None):
570570
raise can.CanError("Failed to transmit: %s" % exc)
571571
return sent
572572

573-
def send_periodic(self, msg, period, duration=None):
573+
def _send_periodic_internal(self, msg, period, duration=None):
574574
"""Start sending a message at a given period on this bus.
575575
576576
The kernel's broadcast manager will be used.
@@ -598,7 +598,6 @@ def send_periodic(self, msg, period, duration=None):
598598
"""
599599
bcm_socket = self._get_bcm_socket(msg.channel or self.channel)
600600
task = CyclicSendTask(bcm_socket, msg, period, duration)
601-
self._periodic_tasks.append(task)
602601
return task
603602

604603
def _get_bcm_socket(self, channel):

doc/development.rst

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ Creating a new interface/backend
3232

3333
These steps are a guideline on how to add a new backend to python-can.
3434

35-
- Create a module (either a ``*.py`` or an entire subdirctory depending
35+
- Create a module (either a ``*.py`` or an entire subdirectory depending
3636
on the complexity) inside ``can.interfaces``
3737
- Implement the central part of the backend: the bus class that extends
3838
:class:`can.BusABC`. See below for more info on this one!
3939
- Register your backend bus class in ``can.interface.BACKENDS`` and
40-
``can.interfaces.VALID_INTERFACES``.
41-
- Add docs where appropiate, like in ``doc/interfaces.rst`` and add
42-
an entry in ``doc/interface/*``.
43-
Update ``doc/scripts.rst`` accordingly.
44-
- Add tests in ``test/*`` where appropiate.
40+
``can.interfaces.VALID_INTERFACES`` in ``can.interfaces.__init__.py``.
41+
- Add docs where appropriate. At a minimum add to ``doc/interfaces.rst`` and add
42+
a new interface specific document in ``doc/interface/*``.
43+
- Update ``doc/scripts.rst`` accordingly.
44+
- Add tests in ``test/*`` where appropriate.
4545

4646

4747
About the ``BusABC`` class
@@ -59,15 +59,15 @@ They *might* implement the following:
5959
messages yet to be sent
6060
* :meth:`~can.BusABC.shutdown` to override how the bus should
6161
shut down
62-
* :meth:`~can.BusABC.send_periodic` to override the software based
63-
periodic sending and push it down to the kernel or hardware
62+
* :meth:`~can.BusABC._send_periodic_internal` to override the software based
63+
periodic sending and push it down to the kernel or hardware.
6464
* :meth:`~can.BusABC._apply_filters` to apply efficient filters
65-
to lower level systems like the OS kernel or hardware
65+
to lower level systems like the OS kernel or hardware.
6666
* :meth:`~can.BusABC._detect_available_configs` to allow the interface
6767
to report which configurations are currently available for new
68-
connections
68+
connections.
6969
* :meth:`~can.BusABC.state` property to allow reading and/or changing
70-
the bus state
70+
the bus state.
7171

7272
.. note::
7373

examples/cyclic.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,18 @@ def simple_periodic_send(bus):
3737
def limited_periodic_send(bus):
3838
print("Starting to send a message every 200ms for 1s")
3939
msg = can.Message(arbitration_id=0x12345678, data=[0, 0, 0, 0, 0, 0], extended_id=True)
40-
task = bus.send_periodic(msg, 0.20, 1)
40+
task = bus.send_periodic(msg, 0.20, 1, store_task=False)
4141
if not isinstance(task, can.LimitedDurationCyclicSendTaskABC):
4242
print("This interface doesn't seem to support a ")
4343
task.stop()
4444
return
4545

46-
time.sleep(1.5)
47-
print("stopped cyclic send")
46+
time.sleep(2)
47+
print("Cyclic send should have stopped as duration expired")
48+
# Note the (finished) task will still be tracked by the Bus
49+
# unless we pass `store_task=False` to bus.send_periodic
50+
# alternatively calling stop removes the task from the bus
51+
#task.stop()
4852

4953

5054
def test_periodic_send_with_modifying_data(bus):
@@ -106,7 +110,7 @@ def test_periodic_send_with_modifying_data(bus):
106110
reset_msg = can.Message(arbitration_id=0x00, data=[0, 0, 0, 0, 0, 0], extended_id=False)
107111

108112
for interface, channel in [
109-
('socketcan', 'can0'),
113+
('socketcan', 'vcan0'),
110114
#('ixxat', 0)
111115
]:
112116
print("Carrying out cyclic tests with {} interface".format(interface))

0 commit comments

Comments
 (0)