diff --git a/CHANGELOG.md b/CHANGELOG.md index 651b222fb..0220b11a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Bug Fixes * Fix BLFReader error for incomplete or truncated stream (#1662) * PCAN: remove Windows registry check to fix 32bit compatibility (#1672) * Vector: Skip the `can_op_mode check` if the device reports `can_op_mode=0` (#1678) +* Vector: using the config from `detect_available_configs` might raise XL_ERR_INVALID_CHANNEL_MASK error (#1681) Features -------- diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 024f6a4c9..576fa26cf 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -202,12 +202,20 @@ def __init__( self._can_protocol = CanProtocol.CAN_FD if is_fd else CanProtocol.CAN_20 for channel in self.channels: - channel_index = self._find_global_channel_idx( - channel=channel, - serial=serial, - app_name=app_name, - channel_configs=channel_configs, - ) + if (_channel_index := kwargs.get("channel_index", None)) is not None: + # VectorBus._detect_available_configs() might return multiple + # devices with the same serial number, e.g. if a VN8900 is connected via both USB and Ethernet + # at the same time. If the VectorBus is instantiated with a config, that was returned from + # VectorBus._detect_available_configs(), then use the contained global channel_index + # to avoid any ambiguities. + channel_index = cast(int, _channel_index) + else: + channel_index = self._find_global_channel_idx( + channel=channel, + serial=serial, + app_name=app_name, + channel_configs=channel_configs, + ) LOG.debug("Channel index %d found", channel) channel_mask = 1 << channel_index @@ -950,6 +958,7 @@ def _detect_available_configs() -> List[AutoDetectedConfig]: "interface": "vector", "channel": channel_config.hw_channel, "serial": channel_config.serial_number, + "channel_index": channel_config.channel_index, # data for use in VectorBus.set_application_config(): "hw_type": channel_config.hw_type, "hw_index": channel_config.hw_index, diff --git a/test/test_vector.py b/test/test_vector.py index 3db43fbbb..3e53bdaff 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -118,6 +118,22 @@ def test_bus_creation() -> None: bus.shutdown() +@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") +def test_bus_creation_channel_index() -> None: + channel_index = 3 + bus = can.Bus( + channel=0, + serial=_find_virtual_can_serial(), + channel_index=channel_index, + interface="vector", + ) + assert isinstance(bus, canlib.VectorBus) + assert bus.protocol == can.CanProtocol.CAN_20 + assert bus.channel_masks[0] == 1 << channel_index + + bus.shutdown() + + def test_bus_creation_bitrate_mocked(mock_xldriver) -> None: bus = can.Bus(channel=0, interface="vector", bitrate=200_000, _testing=True) assert isinstance(bus, canlib.VectorBus) @@ -833,6 +849,31 @@ def test_get_channel_configs() -> None: canlib._get_xl_driver_config = _original_func +@pytest.mark.skipif( + sys.byteorder != "little", reason="Test relies on little endian data." +) +def test_detect_available_configs() -> None: + _original_func = canlib._get_xl_driver_config + canlib._get_xl_driver_config = _get_predefined_xl_driver_config + + available_configs = canlib.VectorBus._detect_available_configs() + + assert len(available_configs) == 5 + + assert available_configs[0]["interface"] == "vector" + assert available_configs[0]["channel"] == 2 + assert available_configs[0]["serial"] == 1001 + assert available_configs[0]["channel_index"] == 2 + assert available_configs[0]["hw_type"] == xldefine.XL_HardwareType.XL_HWTYPE_VN8900 + assert available_configs[0]["hw_index"] == 0 + assert available_configs[0]["supports_fd"] is True + assert isinstance( + available_configs[0]["vector_channel_config"], VectorChannelConfig + ) + + canlib._get_xl_driver_config = _original_func + + @pytest.mark.skipif(not IS_WINDOWS, reason="Windows specific test") def test_winapi_availability() -> None: assert canlib.WaitForSingleObject is not None