From 3dfa261322213e2ab418a981dc99d3a7a1891537 Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 09:43:09 +0100 Subject: [PATCH 1/9] Add _detect_available_configs to ixxat bus --- can/interfaces/ixxat/canlib.py | 8 +++-- can/interfaces/ixxat/canlib_vcinpl.py | 44 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index f18c86acd..0e9a42717 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -156,9 +156,7 @@ def _send_periodic_internal( duration: Optional[float] = None, modifier_callback: Optional[Callable[[Message], None]] = None, ) -> CyclicSendTaskABC: - return self.bus._send_periodic_internal( - msgs, period, duration, modifier_callback - ) + return self.bus._send_periodic_internal(msgs, period, duration, modifier_callback) def shutdown(self) -> None: super().shutdown() @@ -170,3 +168,7 @@ def state(self) -> BusState: Return the current state of the hardware """ return self.bus.state + + @staticmethod + def _detect_available_configs(): + return vcinpl._detect_available_configs() diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 922c683b8..74ba28a2c 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -943,3 +943,47 @@ def get_ixxat_hwids(): _canlib.vciEnumDeviceClose(device_handle) return hwids + + +def _detect_available_configs(): + + config_list = [] # list in wich to store the resulting bus kwargs + + # used to detect HWID + device_handle = HANDLE() + device_info = structures.VCIDEVICEINFO() + + # used to attempt to open channels + channel_handle = HANDLE() + device_handle2 = HANDLE() + + _canlib.vciEnumDeviceOpen(ctypes.byref(device_handle)) + while True: + try: + _canlib.vciEnumDeviceNext(device_handle, ctypes.byref(device_info)) + except StopIteration: + break + else: + hwid = device_info.UniqueHardwareId.AsChar.decode("ascii") + _canlib.vciDeviceOpen( + ctypes.byref(device_info.VciObjectId), + ctypes.byref(device_handle2), + ) + for channel in range(4): + try: + _canlib.canChannelOpen( + device_handle2, + channel, + constants.FALSE, + ctypes.byref(channel_handle), + ) + except Exception: + # Array outside of bounds error == accessing a channel not in the hardware + break + else: + _canlib.canChannelClose(channel_handle) + config_list.append({"interface": "ixxat", "channel": channel, "unique_hardware_id": hwid}) + _canlib.vciDeviceClose(device_handle2) + _canlib.vciEnumDeviceClose(device_handle) + + return config_list From 2c60427d77a097fa9506017c8ca3c53d2466d446 Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 14:21:55 +0100 Subject: [PATCH 2/9] Add typing and cover CI test failure --- can/interfaces/ixxat/canlib.py | 5 ++- can/interfaces/ixxat/canlib_vcinpl.py | 64 ++++++++++++++------------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 0e9a42717..d2e3bdf00 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, Sequence, Union +from typing import Callable, Optional, Sequence, Union, List import can.interfaces.ixxat.canlib_vcinpl as vcinpl import can.interfaces.ixxat.canlib_vcinpl2 as vcinpl2 @@ -8,6 +8,7 @@ CyclicSendTaskABC, Message, ) +from can.typechecking import AutoDetectedConfig class IXXATBus(BusABC): @@ -170,5 +171,5 @@ def state(self) -> BusState: return self.bus.state @staticmethod - def _detect_available_configs(): + def _detect_available_configs() -> List[AutoDetectedConfig]: return vcinpl._detect_available_configs() diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 74ba28a2c..a9b4a10d2 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -14,7 +14,7 @@ import logging import sys import warnings -from typing import Callable, Optional, Sequence, Tuple, Union +from typing import Callable, Optional, Sequence, Tuple, Union, List from can import ( BusABC, @@ -29,6 +29,7 @@ from can.ctypesutil import HRESULT as ctypes_HRESULT from can.exceptions import CanInitializationError, CanInterfaceNotImplementedError from can.util import deprecated_args_alias +from can.typechecking import AutoDetectedConfig from . import constants, structures from .exceptions import * @@ -945,7 +946,7 @@ def get_ixxat_hwids(): return hwids -def _detect_available_configs(): +def _detect_available_configs() -> List[AutoDetectedConfig]: config_list = [] # list in wich to store the resulting bus kwargs @@ -957,33 +958,36 @@ def _detect_available_configs(): channel_handle = HANDLE() device_handle2 = HANDLE() - _canlib.vciEnumDeviceOpen(ctypes.byref(device_handle)) - while True: - try: - _canlib.vciEnumDeviceNext(device_handle, ctypes.byref(device_info)) - except StopIteration: - break - else: - hwid = device_info.UniqueHardwareId.AsChar.decode("ascii") - _canlib.vciDeviceOpen( - ctypes.byref(device_info.VciObjectId), - ctypes.byref(device_handle2), - ) - for channel in range(4): - try: - _canlib.canChannelOpen( - device_handle2, - channel, - constants.FALSE, - ctypes.byref(channel_handle), - ) - except Exception: - # Array outside of bounds error == accessing a channel not in the hardware - break - else: - _canlib.canChannelClose(channel_handle) - config_list.append({"interface": "ixxat", "channel": channel, "unique_hardware_id": hwid}) - _canlib.vciDeviceClose(device_handle2) - _canlib.vciEnumDeviceClose(device_handle) + try: + _canlib.vciEnumDeviceOpen(ctypes.byref(device_handle)) + while True: + try: + _canlib.vciEnumDeviceNext(device_handle, ctypes.byref(device_info)) + except StopIteration: + break + else: + hwid = device_info.UniqueHardwareId.AsChar.decode("ascii") + _canlib.vciDeviceOpen( + ctypes.byref(device_info.VciObjectId), + ctypes.byref(device_handle2), + ) + for channel in range(4): + try: + _canlib.canChannelOpen( + device_handle2, + channel, + constants.FALSE, + ctypes.byref(channel_handle), + ) + except Exception: + # Array outside of bounds error == accessing a channel not in the hardware + break + else: + _canlib.canChannelClose(channel_handle) + config_list.append({"interface": "ixxat", "channel": channel, "unique_hardware_id": hwid}) + _canlib.vciDeviceClose(device_handle2) + _canlib.vciEnumDeviceClose(device_handle) + except AttributeError: + pass # _canlib is None in the CI tests -> return a blank list return config_list From 5e1aac36c86803ac67f0119d8d266fd5bb6467be Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 12:15:52 +0000 Subject: [PATCH 3/9] Format code with black --- can/interfaces/ixxat/canlib.py | 4 +++- can/interfaces/ixxat/canlib_vcinpl.py | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index d2e3bdf00..e5e926ce1 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -157,7 +157,9 @@ def _send_periodic_internal( duration: Optional[float] = None, modifier_callback: Optional[Callable[[Message], None]] = None, ) -> CyclicSendTaskABC: - return self.bus._send_periodic_internal(msgs, period, duration, modifier_callback) + return self.bus._send_periodic_internal( + msgs, period, duration, modifier_callback + ) def shutdown(self) -> None: super().shutdown() diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index a9b4a10d2..743598f7a 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -947,7 +947,6 @@ def get_ixxat_hwids(): def _detect_available_configs() -> List[AutoDetectedConfig]: - config_list = [] # list in wich to store the resulting bus kwargs # used to detect HWID @@ -984,7 +983,13 @@ def _detect_available_configs() -> List[AutoDetectedConfig]: break else: _canlib.canChannelClose(channel_handle) - config_list.append({"interface": "ixxat", "channel": channel, "unique_hardware_id": hwid}) + config_list.append( + { + "interface": "ixxat", + "channel": channel, + "unique_hardware_id": hwid, + } + ) _canlib.vciDeviceClose(device_handle2) _canlib.vciEnumDeviceClose(device_handle) except AttributeError: From 01ca3f40c194cf9b4d3e75ed2318e6b5801ec98b Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 13:25:41 +0000 Subject: [PATCH 4/9] Format code with black --- can/interfaces/ixxat/canlib_vcinpl.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 743598f7a..f2bc207ba 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -984,12 +984,12 @@ def _detect_available_configs() -> List[AutoDetectedConfig]: else: _canlib.canChannelClose(channel_handle) config_list.append( - { - "interface": "ixxat", - "channel": channel, - "unique_hardware_id": hwid, - } - ) + { + "interface": "ixxat", + "channel": channel, + "unique_hardware_id": hwid, + } + ) _canlib.vciDeviceClose(device_handle2) _canlib.vciEnumDeviceClose(device_handle) except AttributeError: From 0af75431d3bdbd56172db61ab0128bc5b7af3195 Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 14:33:51 +0100 Subject: [PATCH 5/9] re-order imports for ruff --- can/interfaces/ixxat/canlib.py | 2 +- can/interfaces/ixxat/canlib_vcinpl.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index e5e926ce1..330ccdcd9 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, Sequence, Union, List +from typing import Callable, List, Optional, Sequence, Union import can.interfaces.ixxat.canlib_vcinpl as vcinpl import can.interfaces.ixxat.canlib_vcinpl2 as vcinpl2 diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index f2bc207ba..334adee11 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -14,7 +14,7 @@ import logging import sys import warnings -from typing import Callable, Optional, Sequence, Tuple, Union, List +from typing import Callable, List, Optional, Sequence, Tuple, Union from can import ( BusABC, @@ -28,8 +28,8 @@ from can.ctypesutil import HANDLE, PHANDLE, CLibrary from can.ctypesutil import HRESULT as ctypes_HRESULT from can.exceptions import CanInitializationError, CanInterfaceNotImplementedError -from can.util import deprecated_args_alias from can.typechecking import AutoDetectedConfig +from can.util import deprecated_args_alias from . import constants, structures from .exceptions import * From c96a8c81b74966081cbd2bad5c530469ce38345d Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 17:31:25 +0100 Subject: [PATCH 6/9] Update ixxat docs --- doc/interfaces/ixxat.rst | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index f73a01036..f441c115a 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -56,17 +56,35 @@ VCI documentation, section "Message filters" for more info. List available devices ---------------------- -In case you have connected multiple IXXAT devices, you have to select them by using their unique hardware id. -To get a list of all connected IXXAT you can use the function ``get_ixxat_hwids()`` as demonstrated below: + +In case you have connected multiple IXXAT devices, you have to select them by using their unique hardware id. +The function :meth:`~can.detect_available_configs` can be used to generate a list of :class:`~can.BusABC` constructors +(including the channel number and unique hardware ID number for the connected devices). .. testsetup:: ixxat + from unittest.mock import Mock + can.detect_available_configs = Mock(side_effect=lambda: [{'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW441489'}, {'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW107422'}, {'interface': 'ixxat', 'channel': 1, 'unique_hardware_id': 'HW107422'}]) + + .. doctest:: ixxat + + >>> import can + >>> print(can.detect_available_configs("ixxat")) + [{'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW509182'}, + {'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW107422'}, + {'interface': 'ixxat', 'channel': 1, 'unique_hardware_id': 'HW107422'}] + + +You may also get a list of all connected IXXAT devices using the function ``get_ixxat_hwids()`` as demonstrated below: + + .. testsetup:: ixxat2 + from unittest.mock import Mock import can.interfaces.ixxat assert hasattr(can.interfaces.ixxat, "get_ixxat_hwids") can.interfaces.ixxat.get_ixxat_hwids = Mock(side_effect=lambda: ['HW441489', 'HW107422']) - .. doctest:: ixxat + .. doctest:: ixxat2 >>> from can.interfaces.ixxat import get_ixxat_hwids >>> for hwid in get_ixxat_hwids(): From 0fbd8678b47e7ca98536c0d2b2ed0108d5eea6b8 Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 18:07:22 +0100 Subject: [PATCH 7/9] fix doctest --- doc/interfaces/ixxat.rst | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index f441c115a..1337bf738 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -64,15 +64,22 @@ The function :meth:`~can.detect_available_configs` can be used to generate a lis .. testsetup:: ixxat from unittest.mock import Mock - can.detect_available_configs = Mock(side_effect=lambda: [{'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW441489'}, {'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW107422'}, {'interface': 'ixxat', 'channel': 1, 'unique_hardware_id': 'HW107422'}]) + import can + assert hasattr(can, "detect_available_configs") + can.detect_available_configs = Mock( + "interface", + return_value=[{'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW441489'}, {'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW107422'}, {'interface': 'ixxat', 'channel': 1, 'unique_hardware_id': 'HW107422'}], + ) .. doctest:: ixxat >>> import can - >>> print(can.detect_available_configs("ixxat")) - [{'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW509182'}, - {'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW107422'}, - {'interface': 'ixxat', 'channel': 1, 'unique_hardware_id': 'HW107422'}] + >>> configs = can.detect_available_configs("ixxat") + >>> for config in configs: + ... print(config) + {'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW441489'} + {'interface': 'ixxat', 'channel': 0, 'unique_hardware_id': 'HW107422'} + {'interface': 'ixxat', 'channel': 1, 'unique_hardware_id': 'HW107422'} You may also get a list of all connected IXXAT devices using the function ``get_ixxat_hwids()`` as demonstrated below: From 65bde81d2567519b445dae64349b3d982f08d361 Mon Sep 17 00:00:00 2001 From: MattWoodhead Date: Sun, 28 May 2023 18:40:54 +0100 Subject: [PATCH 8/9] Update test_interface_ixxat.py --- test/test_interface_ixxat.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/test_interface_ixxat.py b/test/test_interface_ixxat.py index 2ff016d97..90b5f7adc 100644 --- a/test/test_interface_ixxat.py +++ b/test/test_interface_ixxat.py @@ -51,6 +51,18 @@ def setUp(self): raise unittest.SkipTest("not available on this platform") def test_bus_creation(self): + try: + configs = can.detect_available_configs("ixxat") + if configs: + for interface_kwargs in configs: + bus = can.Bus(**interface_kwargs) + bus.shutdown() + else: + raise unittest.SkipTest("No adapters were detected") + except can.CanInterfaceNotImplementedError: + raise unittest.SkipTest("not available on this platform") + + def test_bus_creation_incorrect_channel(self): # non-existent channel -> use arbitrary high value with self.assertRaises(can.CanInitializationError): can.Bus(interface="ixxat", channel=0xFFFF) From 65d9fe0702d6a8c370ccc252c73670ebfa9dda40 Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Mon, 16 Oct 2023 22:40:01 +0200 Subject: [PATCH 9/9] make ruff happy --- can/interfaces/ixxat/canlib_vcinpl2.py | 10 ++++------ can/interfaces/pcan/pcan.py | 4 +--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 2c306c880..aaefa1bf9 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -509,17 +509,15 @@ def __init__( tseg1_abr is None or tseg2_abr is None or sjw_abr is None ): raise ValueError( - "To use bitrate {} (that has not predefined preset) is mandatory to use also parameters tseg1_abr, tseg2_abr and swj_abr".format( - bitrate - ) + f"To use bitrate {bitrate} (that has not predefined preset) is mandatory " + f"to use also parameters tseg1_abr, tseg2_abr and swj_abr" ) if data_bitrate not in constants.CAN_DATABITRATE_PRESETS and ( tseg1_dbr is None or tseg2_dbr is None or sjw_dbr is None ): raise ValueError( - "To use data_bitrate {} (that has not predefined preset) is mandatory to use also parameters tseg1_dbr, tseg2_dbr and swj_dbr".format( - data_bitrate - ) + f"To use data_bitrate {data_bitrate} (that has not predefined preset) is mandatory " + f"to use also parameters tseg1_dbr, tseg2_dbr and swj_dbr" ) if rx_fifo_size <= 0: diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 01a4b1dc3..884c1680b 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -396,9 +396,7 @@ def bits(n): for b in bits(error): stsReturn = self.m_objPCANBasic.GetErrorText(b, 0x9) if stsReturn[0] != PCAN_ERROR_OK: - text = "An error occurred. Error-code's text ({:X}h) couldn't be retrieved".format( - error - ) + text = f"An error occurred. Error-code's text ({error:X}h) couldn't be retrieved" else: text = stsReturn[1].decode("utf-8", errors="replace")