Skip to content

Commit fc6ff22

Browse files
authored
add silent mode support for vector (#1764)
* add silent mode support for vector * fix: address comments and test works as expected * formatting
1 parent 034b400 commit fc6ff22

File tree

2 files changed

+75
-4
lines changed

2 files changed

+75
-4
lines changed

can/interfaces/vector/canlib.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def __init__(
104104
sjw_dbr: int = 2,
105105
tseg1_dbr: int = 6,
106106
tseg2_dbr: int = 3,
107+
listen_only: Optional[bool] = False,
107108
**kwargs: Any,
108109
) -> None:
109110
"""
@@ -157,6 +158,8 @@ def __init__(
157158
Bus timing value tseg1 (data)
158159
:param tseg2_dbr:
159160
Bus timing value tseg2 (data)
161+
:param listen_only:
162+
if the bus should be set to listen only mode.
160163
161164
:raise ~can.exceptions.CanInterfaceNotImplementedError:
162165
If the current operating system is not supported or the driver could not be loaded.
@@ -205,6 +208,8 @@ def __init__(
205208
self.index_to_channel: Dict[int, int] = {}
206209
self._can_protocol = CanProtocol.CAN_FD if is_fd else CanProtocol.CAN_20
207210

211+
self._listen_only = listen_only
212+
208213
for channel in self.channels:
209214
if (
210215
len(self.channels) == 1
@@ -232,7 +237,7 @@ def __init__(
232237

233238
permission_mask = xlclass.XLaccess()
234239
# Set mask to request channel init permission if needed
235-
if bitrate or fd or timing:
240+
if bitrate or fd or timing or self._listen_only:
236241
permission_mask.value = self.mask
237242

238243
interface_version = (
@@ -311,6 +316,9 @@ def __init__(
311316
if assert_timing:
312317
self._check_can_settings(channel_mask=self.mask, bitrate=bitrate)
313318

319+
if self._listen_only:
320+
self._set_output_mode(channel_mask=self.mask, listen_only=True)
321+
314322
# Enable/disable TX receipts
315323
tx_receipts = 1 if receive_own_messages else 0
316324
self.xldriver.xlCanSetChannelMode(self.port_handle, self.mask, tx_receipts, 0)
@@ -445,6 +453,28 @@ def _read_bus_params(
445453
f"Channel configuration for channel {channel} not found."
446454
)
447455

456+
def _set_output_mode(self, channel_mask: int, listen_only: bool) -> None:
457+
# set parameters for channels with init access
458+
channel_mask = channel_mask & self.permission_mask
459+
460+
if channel_mask:
461+
if listen_only:
462+
self.xldriver.xlCanSetChannelOutput(
463+
self.port_handle,
464+
channel_mask,
465+
xldefine.XL_OutputMode.XL_OUTPUT_MODE_SILENT,
466+
)
467+
else:
468+
self.xldriver.xlCanSetChannelOutput(
469+
self.port_handle,
470+
channel_mask,
471+
xldefine.XL_OutputMode.XL_OUTPUT_MODE_NORMAL,
472+
)
473+
474+
LOG.info("xlCanSetChannelOutput: listen_only=%u", listen_only)
475+
else:
476+
LOG.warning("No channels with init access to set listen only mode")
477+
448478
def _set_bitrate(self, channel_mask: int, bitrate: int) -> None:
449479
# set parameters for channels with init access
450480
channel_mask = channel_mask & self.permission_mask
@@ -643,9 +673,11 @@ def _apply_filters(self, filters: Optional[CanFilters]) -> None:
643673
self.mask,
644674
can_filter["can_id"],
645675
can_filter["can_mask"],
646-
xldefine.XL_AcceptanceFilter.XL_CAN_EXT
647-
if can_filter.get("extended")
648-
else xldefine.XL_AcceptanceFilter.XL_CAN_STD,
676+
(
677+
xldefine.XL_AcceptanceFilter.XL_CAN_EXT
678+
if can_filter.get("extended")
679+
else xldefine.XL_AcceptanceFilter.XL_CAN_STD
680+
),
649681
)
650682
except VectorOperationError as exception:
651683
LOG.warning("Could not set filters: %s", exception)

test/test_vector.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def mock_xldriver() -> None:
4848
xldriver_mock.xlCanSetChannelAcceptance = Mock(return_value=0)
4949
xldriver_mock.xlCanSetChannelBitrate = Mock(return_value=0)
5050
xldriver_mock.xlSetNotification = Mock(side_effect=xlSetNotification)
51+
xldriver_mock.xlCanSetChannelOutput = Mock(return_value=0)
5152

5253
# bus deactivation functions
5354
xldriver_mock.xlDeactivateChannel = Mock(return_value=0)
@@ -78,6 +79,44 @@ def mock_xldriver() -> None:
7879
canlib.HAS_EVENTS = real_has_events
7980

8081

82+
def test_listen_only_mocked(mock_xldriver) -> None:
83+
bus = can.Bus(channel=0, interface="vector", listen_only=True, _testing=True)
84+
assert isinstance(bus, canlib.VectorBus)
85+
assert bus.protocol == can.CanProtocol.CAN_20
86+
87+
can.interfaces.vector.canlib.xldriver.xlCanSetChannelOutput.assert_called()
88+
xlCanSetChannelOutput_args = (
89+
can.interfaces.vector.canlib.xldriver.xlCanSetChannelOutput.call_args[0]
90+
)
91+
assert xlCanSetChannelOutput_args[2] == xldefine.XL_OutputMode.XL_OUTPUT_MODE_SILENT
92+
93+
94+
@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable")
95+
def test_listen_only() -> None:
96+
bus = can.Bus(
97+
channel=0,
98+
serial=_find_virtual_can_serial(),
99+
interface="vector",
100+
receive_own_messages=True,
101+
listen_only=True,
102+
)
103+
assert isinstance(bus, canlib.VectorBus)
104+
assert bus.protocol == can.CanProtocol.CAN_20
105+
106+
msg = can.Message(
107+
arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True
108+
)
109+
110+
bus.send(msg)
111+
112+
received_msg = bus.recv()
113+
114+
assert received_msg.arbitration_id == msg.arbitration_id
115+
assert received_msg.data == msg.data
116+
117+
bus.shutdown()
118+
119+
81120
def test_bus_creation_mocked(mock_xldriver) -> None:
82121
bus = can.Bus(channel=0, interface="vector", _testing=True)
83122
assert isinstance(bus, canlib.VectorBus)

0 commit comments

Comments
 (0)