Skip to content

Commit 61e2425

Browse files
christiansandberghardbyte
authored andcommitted
Split filtering for standard and extended IDs
Update socketcan, kvaser, ixxat, nican, and remote filter configuration. Remove SW filtering in kvaser and neovi. Fixes issue hardbyte#147.
1 parent 7a7b3ec commit 61e2425

10 files changed

Lines changed: 53 additions & 133 deletions

File tree

can/bus.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ def __init__(self, channel=None, can_filters=None, **config):
2828
The can interface identifier. Expected type is backend dependent.
2929
3030
:param list can_filters:
31-
A list of dictionaries each containing a "can_id" and a "can_mask".
31+
A list of dictionaries each containing a "can_id", a "can_mask",
32+
and an "extended" key.
3233
33-
>>> [{"can_id": 0x11, "can_mask": 0x21}]
34+
>>> [{"can_id": 0x11, "can_mask": 0x21, "extended": False}]
3435
3536
A filter matches, when ``<received_can_id> & can_mask == can_id & can_mask``
3637

can/interfaces/ixxat/canlib.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ def __init__(self, channel, can_filters=None, **config):
256256
UniqueHardwareId = config.get('UniqueHardwareId', None)
257257
rxFifoSize = config.get('rxFifoSize', 16)
258258
txFifoSize = config.get('txFifoSize', 16)
259-
extended = config.get('extended', False)
260259
# Usually comes as a string from the config file
261260
channel = int(channel)
262261

@@ -319,14 +318,16 @@ def __init__(self, channel, can_filters=None, **config):
319318
if can_filters is not None and len(can_filters):
320319
log.info("The IXXAT VCI backend is filtering messages")
321320
# Disable every message coming in
322-
_canlib.canControlSetAccFilter(self._control_handle,
323-
1 if extended else 0,
324-
constants.CAN_ACC_CODE_NONE,
325-
constants.CAN_ACC_MASK_NONE)
321+
for extended in (0, 1):
322+
_canlib.canControlSetAccFilter(self._control_handle,
323+
extended,
324+
constants.CAN_ACC_CODE_NONE,
325+
constants.CAN_ACC_MASK_NONE)
326326
for can_filter in can_filters:
327327
# Whitelist
328328
code = int(can_filter['can_id'])
329329
mask = int(can_filter['can_mask'])
330+
extended = can_filter.get('extended', False)
330331
_canlib.canControlAddFilterIds(self._control_handle, 1 if extended else 0, code, mask)
331332
rtr = (code & 0x01) and (mask & 0x01)
332333
log.info("Accepting ID:%d MASK:%d RTR:%s", code>>1, mask>>1, "YES" if rtr else "NO")

can/interfaces/kvaser/canlib.py

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,6 @@ def __init__(self, channel, can_filters=None, **config):
358358
self._write_handle = canOpenChannel(channel, canstat.canOPEN_ACCEPT_VIRTUAL)
359359
canBusOn(self._read_handle)
360360

361-
self.sw_filters = []
362361
self.set_filters(can_filters)
363362

364363
can_driver_mode = canstat.canDRIVER_SILENT if driver_mode == DRIVER_MODE_SILENT else canstat.canDRIVER_NORMAL
@@ -393,25 +392,19 @@ def set_filters(self, can_filters=None):
393392
394393
A filter matches, when ``<received_can_id> & can_mask == can_id & can_mask``
395394
"""
396-
can_id = 0
397-
can_mask = 0
398-
399-
if not can_filters:
400-
log.info('Filtering has been disabled')
401-
self.sw_filters = []
402-
elif len(can_filters) == 1:
395+
if can_filters and len(can_filters) == 1:
403396
can_id = can_filters[0]['can_id']
404397
can_mask = can_filters[0]['can_mask']
398+
extended = 1 if can_filters[0].get('extended') else 0
405399
log.info('canlib is filtering on ID 0x%X, mask 0x%X', can_id, can_mask)
406-
self.sw_filters = []
407-
elif len(can_filters) > 1:
408-
log.info('Filtering is handled in Python')
409-
self.sw_filters = can_filters
400+
for handle in (self._read_handle, self._write_handle):
401+
canSetAcceptanceFilter(handle, can_id, can_mask, extended)
402+
else:
403+
log.info('Hardware filtering has been disabled')
404+
for handle in (self._read_handle, self._write_handle):
405+
for extended in (0, 1):
406+
canSetAcceptanceFilter(handle, 0, 0, extended)
410407

411-
# Set same filter for both handles as well as standard and extended IDs
412-
for handle in (self._read_handle, self._write_handle):
413-
for ext in (0, 1):
414-
canSetAcceptanceFilter(handle, can_id, can_mask, ext)
415408

416409
def flush_tx_buffer(self):
417410
""" Wipeout the transmit buffer on the Kvaser.
@@ -445,26 +438,6 @@ def __convert_timestamp(self, value):
445438
self.pc_time_offset += lag
446439
return timestamp
447440

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

496469
if status == canstat.canOK:
497470
#log.debug('read complete -> status OK')
498-
if not self._is_filter_match(arb_id.value):
499-
return None
500471
data_array = data.raw
501472
flags = flags.value
502473
is_extended = bool(flags & canstat.canMSG_EXT)

can/interfaces/neovi_api/neovi_api.py

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ def __init__(self, channel=None, can_filters=None, **config):
5757
5858
:param int channel:
5959
The Channel id to create this bus with.
60-
:param list can_filters:
61-
A list of dictionaries each containing a "can_id" and a "can_mask".
62-
63-
>>> [{"can_id": 0x11, "can_mask": 0x21}]
64-
6560
"""
6661
type_filter = config.get('type_filter', neovi.NEODEVICE_ALL)
6762
neodevice.init_api()
@@ -73,8 +68,6 @@ def __init__(self, channel=None, can_filters=None, **config):
7368
channel
7469
)
7570

76-
self.sw_filters = None
77-
self.set_filters(can_filters)
7871
self.rx_buffer = queue.Queue()
7972

8073
self.network = int(channel) if channel is not None else None
@@ -91,28 +84,6 @@ def shutdown(self):
9184
def _rx_buffer(self, msg, user_data):
9285
self.rx_buffer.put_nowait(msg)
9386

94-
def _is_filter_match(self, arb_id):
95-
"""
96-
If SW filtering is used, checks if the `arb_id` matches any of
97-
the filters setup.
98-
99-
:param int arb_id:
100-
CAN ID to check against.
101-
102-
:return:
103-
True if `arb_id` matches any filters
104-
(or if SW filtering is not used).
105-
"""
106-
if not self.sw_filters:
107-
# Filtering done on HW or driver level or no filtering
108-
return True
109-
for can_filter in self.sw_filters:
110-
if not (arb_id ^ can_filter['can_id']) & can_filter['can_mask']:
111-
return True
112-
113-
logging.info("%s not matching" % arb_id)
114-
return False
115-
11687
def _ics_msg_to_message(self, ics_msg):
11788
return Message(
11889
timestamp=neovi.GetTimeStampForMsg(self.device.handle, ics_msg)[1],
@@ -131,8 +102,7 @@ def recv(self, timeout=None):
131102
except queue.Empty:
132103
pass
133104
else:
134-
if ics_msg.NetworkID == self.network and \
135-
self._is_filter_match(ics_msg.ArbIDOrHeader):
105+
if ics_msg.NetworkID == self.network:
136106
return self._ics_msg_to_message(ics_msg)
137107

138108
def send(self, msg, timeout=None):
@@ -150,27 +120,3 @@ def send(self, msg, timeout=None):
150120
ics_msg.DescriptionID = self.device.tx_id
151121
self.device.tx_id += 1
152122
self.device.tx_raw_message(ics_msg, self.network)
153-
154-
def set_filters(self, can_filters=None):
155-
"""Apply filtering to all messages received by this Bus.
156-
157-
Calling without passing any filters will reset the applied filters.
158-
159-
:param list can_filters:
160-
A list of dictionaries each containing a "can_id" and a "can_mask".
161-
162-
>>> [{"can_id": 0x11, "can_mask": 0x21}]
163-
164-
A filter matches, when
165-
``<received_can_id> & can_mask == can_id & can_mask``
166-
167-
"""
168-
self.sw_filters = can_filters
169-
170-
if self.sw_filters is None:
171-
logger.info("Filtering has been disabled")
172-
else:
173-
for can_filter in can_filters:
174-
can_id = can_filter["can_id"]
175-
can_mask = can_filter["can_mask"]
176-
logger.info("Filtering on ID 0x%X, mask 0x%X", can_id, can_mask)

can/interfaces/nican.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,16 @@ def __init__(self, channel, can_filters=None, bitrate=None, log_errors=True,
159159
can_id = can_filter["can_id"]
160160
can_mask = can_filter["can_mask"]
161161
logger.info("Filtering on ID 0x%X, mask 0x%X", can_id, can_mask)
162-
config.extend([
163-
(NC_ATTR_CAN_COMP_STD, can_id),
164-
(NC_ATTR_CAN_MASK_STD, can_mask),
165-
(NC_ATTR_CAN_COMP_XTD, can_id | NC_FL_CAN_ARBID_XTD),
166-
(NC_ATTR_CAN_MASK_XTD, can_mask)
167-
])
162+
if can_filter.get("extended"):
163+
config.extend([
164+
(NC_ATTR_CAN_COMP_XTD, can_id | NC_FL_CAN_ARBID_XTD),
165+
(NC_ATTR_CAN_MASK_XTD, can_mask)
166+
])
167+
else:
168+
config.extend([
169+
(NC_ATTR_CAN_COMP_STD, can_id),
170+
(NC_ATTR_CAN_MASK_STD, can_mask),
171+
])
168172

169173
if bitrate:
170174
config.append((NC_ATTR_BAUD_RATE, bitrate))

can/interfaces/remote/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33

44

55
# If the protocol changes, increase this number
6-
PROTOCOL_VERSION = 4
6+
PROTOCOL_VERSION = 5
77

88
DEFAULT_PORT = 54701

can/interfaces/remote/events.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ class FilterConfig(BaseEvent):
397397
+========+=======+========================================================+
398398
| 0 | U8 | Number of filters |
399399
+--------+-------+--------------------------------------------------------+
400-
| 1 - 4 | U32 | CAN ID for filter 1 |
400+
| 1 - 4 | U32 | CAN ID for filter 1 (bit 31 set if extended) |
401401
+--------+-------+--------------------------------------------------------+
402402
| 5 - 8 | U32 | CAN mask for filter 1 |
403403
+--------+-------+--------------------------------------------------------+
@@ -425,8 +425,11 @@ def __init__(self, can_filters=None):
425425
def encode(self):
426426
data = [struct.pack('B', len(self.can_filters))]
427427
for can_filter in self.can_filters:
428+
can_id = can_filter['can_id']
429+
if can_filter.get('extended'):
430+
can_id |= 0x80000000
428431
filter_data = self._STRUCT.pack(
429-
can_filter['can_id'], can_filter['can_mask'])
432+
can_id, can_filter['can_mask'])
430433
data.append(filter_data)
431434
return b''.join(data)
432435

@@ -438,7 +441,11 @@ def from_buffer(cls, buf):
438441
for i in range(nof_filters):
439442
offset = 1 + i * cls._STRUCT.size
440443
can_id, can_mask = cls._STRUCT.unpack_from(buf, offset)
441-
can_filters.append({'can_id': can_id, 'can_mask': can_mask})
444+
extended = bool(can_id & 0x80000000)
445+
can_filters.append({
446+
'can_id': can_id & 0x1FFFFFFF,
447+
'can_mask': can_mask,
448+
'extended': extended})
442449
except struct.error:
443450
raise NeedMoreDataError()
444451

can/interfaces/socketcan/socketcan_common.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@
44
"""
55
import struct
66

7+
from can.interfaces.socketcan.socketcan_constants import CAN_EFF_FLAG
8+
79

810
def pack_filters(can_filters=None):
911
if can_filters is None:
1012
# Pass all messages
1113
can_filters = [{
1214
'can_id': 0,
13-
'can_mask': 0
15+
'can_mask': 0,
16+
'extended': False
1417
}]
1518

1619
can_filter_fmt = "={}I".format(2 * len(can_filters))
1720
filter_data = []
1821
for can_filter in can_filters:
19-
filter_data.append(can_filter['can_id'])
22+
can_id = can_filter['can_id']
23+
if can_filter.get('extended'):
24+
can_id |= CAN_EFF_FLAG
25+
filter_data.append(can_id)
2026
filter_data.append(can_filter['can_mask'])
2127
return struct.pack(can_filter_fmt, *filter_data)

test/remote_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ def test_bus_request(self):
4949

5050
def test_filter_config(self):
5151
event1 = events.FilterConfig([
52-
{'can_id': 0x123, 'can_mask': 0xFFF},
53-
{'can_id': 0x001, 'can_mask': 0x00F}
52+
{'can_id': 0x1FFFFFFF, 'can_mask': 0x1FFFFFFF, 'extended': True},
53+
{'can_id': 0x001, 'can_mask': 0x00F, 'extended': False}
5454
])
5555
buf = event1.encode()
5656
event2 = events.FilterConfig.from_buffer(buf)
@@ -187,8 +187,8 @@ def test_initialization(self):
187187

188188
# Test to create a new bus with filters
189189
can_filters = [
190-
{'can_id': 0x12, 'can_mask': 0xFF},
191-
{'can_id': 0x13, 'can_mask': 0xFF}
190+
{'can_id': 0x12, 'can_mask': 0xFF, 'extended': False},
191+
{'can_id': 0x13, 'can_mask': 0xFF, 'extended': True}
192192
]
193193
bus = can.interface.Bus('127.0.0.1:54700',
194194
bustype='remote',

test/test_kvaser.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,10 @@ def test_filter_setup(self):
6565
# One filter, will be handled by canlib
6666
canlib.canSetAcceptanceFilter.reset_mock()
6767
self.bus.set_filters([
68-
{'can_id': 0x8, 'can_mask': 0xff}
68+
{'can_id': 0x8, 'can_mask': 0xff, 'extended': True}
6969
])
7070
expected_args = [
71-
((0, 0x8, 0xff, 0),), # Enable filtering STD on read handle
7271
((0, 0x8, 0xff, 1),), # Enable filtering EXT on read handle
73-
((0, 0x8, 0xff, 0),), # Enable filtering STD on write handle
7472
((0, 0x8, 0xff, 1),), # Enable filtering EXT on write handle
7573
]
7674
self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list,
@@ -91,20 +89,6 @@ def test_filter_setup(self):
9189
]
9290
self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list,
9391
expected_args)
94-
self.assertEqual(self.bus.sw_filters, multiple_filters)
95-
96-
def test_sw_filtering(self):
97-
self.bus.set_filters([
98-
{'can_id': 0x8, 'can_mask': 0xff},
99-
{'can_id': 0x9, 'can_mask': 0xffff}
100-
])
101-
self.assertTrue(self.bus._is_filter_match(0x8))
102-
self.assertTrue(self.bus._is_filter_match(0x9))
103-
self.assertTrue(self.bus._is_filter_match(0x108))
104-
self.assertTrue(self.bus._is_filter_match(0x10009))
105-
self.assertFalse(self.bus._is_filter_match(0x10))
106-
self.assertFalse(self.bus._is_filter_match(0x0))
107-
self.assertFalse(self.bus._is_filter_match(0x109))
10892

10993
def test_send_extended(self):
11094
msg = can.Message(

0 commit comments

Comments
 (0)