Skip to content

Commit 6bd21fa

Browse files
Implement set_filters on Kvaser.
Do filtering in recv() if multiple filters are used since canlib only supports one filter.
1 parent 4699845 commit 6bd21fa

File tree

2 files changed

+112
-9
lines changed

2 files changed

+112
-9
lines changed

can/interfaces/kvaser/canlib.py

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,15 +349,8 @@ def __init__(self, channel, can_filters=None, **config):
349349
4)
350350
canSetBusParams(self._read_handle, bitrate, tseg1, tseg2, sjw, no_samp, 0)
351351

352-
if can_filters is not None and len(can_filters):
353-
log.info("The kvaser canlib backend is filtering messages")
354-
code, mask = 0, 0
355-
for can_filter in can_filters:
356-
code |= can_filter['can_id']
357-
mask |= can_filter['can_mask']
358-
log.info("Filtering on: {} {}".format(code, mask))
359-
canSetAcceptanceFilter(self._read_handle, code, mask, 0)
360-
canSetAcceptanceFilter(self._read_handle, code, mask, 1)
352+
self.sw_filters = []
353+
self.set_filters(can_filters)
361354

362355
if self.single_handle:
363356
log.debug("We don't require separate handles to the bus")
@@ -384,6 +377,44 @@ def __init__(self, channel, can_filters=None, **config):
384377

385378
super(KvaserBus, self).__init__()
386379

380+
def set_filters(self, can_filters=None):
381+
"""Apply filtering to all messages received by this Bus.
382+
383+
Calling without passing any filters will reset the applied filters.
384+
385+
Since Kvaser only supports setting one filter per handle, the filtering
386+
will be done in the :meth:`recv` if more than one filter is requested.
387+
388+
:param list can_filters:
389+
A list of dictionaries each containing a "can_id" and a "can_mask".
390+
391+
>>> [{"can_id": 0x11, "can_mask": 0x21}]
392+
393+
A filter matches, when ``<received_can_id> & can_mask == can_id & can_mask``
394+
"""
395+
if not can_filters:
396+
# Disable all filters
397+
self.sw_filters = []
398+
canSetAcceptanceFilter(self._read_handle, 0, 0, 0)
399+
canSetAcceptanceFilter(self._read_handle, 0, 0, 1)
400+
elif len(can_filters) == 1:
401+
# Standard messages
402+
canSetAcceptanceFilter(self._read_handle,
403+
can_filters[0]['can_id'],
404+
can_filters[0]['can_mask'],
405+
0)
406+
# Extended messages
407+
canSetAcceptanceFilter(self._read_handle,
408+
can_filters[0]['can_id'],
409+
can_filters[0]['can_mask'],
410+
1)
411+
self.sw_filters = []
412+
elif len(can_filters) > 1:
413+
self.sw_filters = can_filters
414+
# Disable HW filtering
415+
canSetAcceptanceFilter(self._read_handle, 0, 0, 0)
416+
canSetAcceptanceFilter(self._read_handle, 0, 0, 1)
417+
387418
def flush_tx_buffer(self):
388419
"""
389420
Flushes the transmit buffer on the Kvaser
@@ -417,6 +448,26 @@ def __convert_timestamp(self, value):
417448
self.pc_time_offset += lag
418449
return timestamp
419450

451+
def _is_filter_match(self, arb_id):
452+
"""
453+
If SW filtering is used, checks if the `arb_id` matches any of
454+
the filters setup.
455+
456+
:param int arb_id:
457+
CAN ID to check against.
458+
459+
:return:
460+
True if `arb_id` matches any filters
461+
(or if SW filtering is not used).
462+
"""
463+
if not self.sw_filters:
464+
# Filtering done on HW or driver level
465+
return True
466+
for can_filter in self.sw_filters:
467+
if not (arb_id ^ can_filter['can_id']) & can_filter['can_mask']:
468+
return True
469+
return False
470+
420471
def recv(self, timeout=None):
421472
"""
422473
Read a message from kvaser device.
@@ -441,6 +492,8 @@ def recv(self, timeout=None):
441492

442493
if status == canstat.canOK:
443494
log.debug('read complete -> status OK')
495+
if not self._is_filter_match(arb_id.value):
496+
return None
444497
data_array = data.raw
445498
flags = flags.value
446499
is_extended = bool(flags & canstat.canMSG_EXT)

test/test_kvaser.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def setUp(self):
2626
canlib.canClose = Mock()
2727
canlib.canSetBusOutputControl = Mock()
2828
canlib.canGetChannelData = Mock()
29+
canlib.canSetAcceptanceFilter = Mock()
2930
canlib.canWriteWait = self.canWriteWait
3031
canlib.canReadWait = self.canReadWait
3132

@@ -49,6 +50,55 @@ def test_bus_shutdown(self):
4950
self.assertTrue(canlib.canBusOff.called)
5051
self.assertTrue(canlib.canClose.called)
5152

53+
def test_filter_setup(self):
54+
# No filter in constructor
55+
expected_args = [
56+
((0, 0, 0, 0),), # Disable filtering STD
57+
((0, 0, 0, 1),), # Disable filtering EXT
58+
]
59+
self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list,
60+
expected_args)
61+
62+
# One filter, will be handled by canlib
63+
canlib.canSetAcceptanceFilter.reset_mock()
64+
self.bus.set_filters([
65+
{'can_id': 0x8, 'can_mask': 0xff}
66+
])
67+
expected_args = [
68+
((0, 0x8, 0xff, 0),), # Enable filtering STD
69+
((0, 0x8, 0xff, 1),), # Enable filtering EXT
70+
]
71+
self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list,
72+
expected_args)
73+
74+
# Multiple filters, will be handled in Python
75+
canlib.canSetAcceptanceFilter.reset_mock()
76+
multiple_filters = [
77+
{'can_id': 0x8, 'can_mask': 0xff},
78+
{'can_id': 0x9, 'can_mask': 0xff}
79+
]
80+
self.bus.set_filters(multiple_filters)
81+
expected_args = [
82+
((0, 0, 0, 0),), # Disable filtering STD
83+
((0, 0, 0, 1),), # Disable filtering EXT
84+
]
85+
self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list,
86+
expected_args)
87+
self.assertEqual(self.bus.sw_filters, multiple_filters)
88+
89+
def test_sw_filtering(self):
90+
self.bus.set_filters([
91+
{'can_id': 0x8, 'can_mask': 0xff},
92+
{'can_id': 0x9, 'can_mask': 0xffff}
93+
])
94+
self.assertTrue(self.bus._is_filter_match(0x8))
95+
self.assertTrue(self.bus._is_filter_match(0x9))
96+
self.assertTrue(self.bus._is_filter_match(0x108))
97+
self.assertTrue(self.bus._is_filter_match(0x10009))
98+
self.assertFalse(self.bus._is_filter_match(0x10))
99+
self.assertFalse(self.bus._is_filter_match(0x0))
100+
self.assertFalse(self.bus._is_filter_match(0x109))
101+
52102
def test_send_extended(self):
53103
msg = can.Message(
54104
arbitration_id=0xc0ffee,

0 commit comments

Comments
 (0)