From 07e05109734db8d118ec6c372be8e119d3fb2cdb Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Sat, 27 Aug 2022 15:03:34 -0500 Subject: [PATCH 01/38] Raise error when blf GzipFile is requested --- can/io/blf.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/can/io/blf.py b/can/io/blf.py index 93fa54ca2..062526014 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -11,7 +11,7 @@ of uncompressed data each. This data contains the actual CAN messages and other objects types. """ - +import gzip import struct import zlib import datetime @@ -395,6 +395,11 @@ def __init__( speed and compression (currently equivalent to level 6). """ mode = "rb+" if append else "wb" + if type(file) == gzip.GzipFile: + raise ValueError( + f"The BLFWriter is currently incompatible with " + f"{gzip.GzipFile.__name__}s. Try using a different zip format." + ) try: super().__init__(file, mode=mode) except FileNotFoundError: From 4eaa7df8bf0b32589ca4b3d9b4e8b00afb6cefe9 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Mon, 31 Oct 2022 12:07:06 -0500 Subject: [PATCH 02/38] Revert "Raise error when blf GzipFile is requested" This reverts commit 07e05109734db8d118ec6c372be8e119d3fb2cdb. --- can/io/blf.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/can/io/blf.py b/can/io/blf.py index 062526014..93fa54ca2 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -11,7 +11,7 @@ of uncompressed data each. This data contains the actual CAN messages and other objects types. """ -import gzip + import struct import zlib import datetime @@ -395,11 +395,6 @@ def __init__( speed and compression (currently equivalent to level 6). """ mode = "rb+" if append else "wb" - if type(file) == gzip.GzipFile: - raise ValueError( - f"The BLFWriter is currently incompatible with " - f"{gzip.GzipFile.__name__}s. Try using a different zip format." - ) try: super().__init__(file, mode=mode) except FileNotFoundError: From df2b3a5ff167748d166378ee06447756ff6d38cb Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Mon, 31 Oct 2022 12:20:58 -0500 Subject: [PATCH 03/38] Check the real suffix in base io logger --- can/io/logger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index b50825d3f..b6dc2f3c1 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -84,17 +84,17 @@ def __new__( # type: ignore ) Logger.fetched_plugins = True - suffix = pathlib.PurePath(filename).suffix.lower() + real_suffix = "".join(pathlib.Path(filename).suffixes[-2:]).lower() file_or_filename: AcceptedIOType = filename - if suffix == ".gz": + if ".gz" in real_suffix: suffix, file_or_filename = Logger.compress(filename) try: - return Logger.message_writers[suffix](file_or_filename, *args, **kwargs) + return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) except KeyError: raise ValueError( - f'No write support for this unknown log format "{suffix}"' + f'No write support for this unknown log format "{real_suffix}"' ) from None @staticmethod From 33bb24ad3039ea8e7da51c0e4071c964db9be5ef Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Mon, 31 Oct 2022 12:31:59 -0500 Subject: [PATCH 04/38] Add error handling for gzip files --- can/io/logger.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index b6dc2f3c1..7dec01c0d 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -54,12 +54,12 @@ class Logger(MessageWriter): # pylint: disable=abstract-method fetched_plugins = False message_writers: Dict[str, Type[MessageWriter]] = { - ".asc": ASCWriter, - ".blf": BLFWriter, - ".csv": CSVWriter, - ".db": SqliteWriter, - ".log": CanutilsLogWriter, - ".txt": Printer, + (".asc",): ASCWriter, + (".blf",): BLFWriter, + (".csv",): CSVWriter, + (".db",): SqliteWriter, + (".log",): CanutilsLogWriter, + (".txt",): Printer, } @staticmethod @@ -89,10 +89,16 @@ def __new__( # type: ignore file_or_filename: AcceptedIOType = filename if ".gz" in real_suffix: suffix, file_or_filename = Logger.compress(filename) - - try: - return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) - except KeyError: + else: + suffix = real_suffix + + for log_writer_key in Logger.message_writers: + if real_suffix in log_writer_key: + return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) + elif suffix in log_writer_key: + raise ValueError(f"The {Logger.message_writers[log_writer_key].__name__} does not " + f"currently support {real_suffix} files.") + else: raise ValueError( f'No write support for this unknown log format "{real_suffix}"' ) from None From 7bf36ae2a74b764e4f2fd35d19f531e63c41ca89 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Mon, 31 Oct 2022 12:32:42 -0500 Subject: [PATCH 05/38] Format with black --- can/io/logger.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 7dec01c0d..e2fcac84f 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -94,10 +94,14 @@ def __new__( # type: ignore for log_writer_key in Logger.message_writers: if real_suffix in log_writer_key: - return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) + return Logger.message_writers[real_suffix]( + file_or_filename, *args, **kwargs + ) elif suffix in log_writer_key: - raise ValueError(f"The {Logger.message_writers[log_writer_key].__name__} does not " - f"currently support {real_suffix} files.") + raise ValueError( + f"The {Logger.message_writers[log_writer_key].__name__} does not " + f"currently support {real_suffix} files." + ) else: raise ValueError( f'No write support for this unknown log format "{real_suffix}"' From f5b1e0be1587c68ef8384020a90ff2a181dbdecd Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Mon, 31 Oct 2022 12:33:20 -0500 Subject: [PATCH 06/38] Fix message writer key reference --- can/io/logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/logger.py b/can/io/logger.py index e2fcac84f..e88bd9d54 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -94,7 +94,7 @@ def __new__( # type: ignore for log_writer_key in Logger.message_writers: if real_suffix in log_writer_key: - return Logger.message_writers[real_suffix]( + return Logger.message_writers[log_writer_key]( file_or_filename, *args, **kwargs ) elif suffix in log_writer_key: From dfefc2fb95e27c5d4640bd595c29304d63fd2b0b Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 4 Nov 2022 23:23:45 -0500 Subject: [PATCH 07/38] Add in gzip option for canutils and txt --- can/io/logger.py | 4 ++-- test/logformats_test.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 737e2085e..25dbf22a6 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -57,8 +57,8 @@ class Logger(MessageWriter): # pylint: disable=abstract-method (".blf",): BLFWriter, (".csv",): CSVWriter, (".db",): SqliteWriter, - (".log",): CanutilsLogWriter, - (".txt",): Printer, + (".log", ".log.gz"): CanutilsLogWriter, + (".txt", ".log.gz"): Printer, } @staticmethod diff --git a/test/logformats_test.py b/test/logformats_test.py index 6a0eafac1..9a1417ac7 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -46,10 +46,10 @@ class ReaderWriterExtensionTest(unittest.TestCase): def test_extension_matching(self): for suffix, (writer, reader) in self.message_writers_and_readers.items(): suffix_variants = [ - suffix.upper(), - suffix.lower(), - f"can.msg.ext{suffix}", - "".join([c.upper() if i % 2 else c for i, c in enumerate(suffix)]), + suffix[0].upper(), + suffix[0].lower(), + f"can.msg.ext{suffix[0]}", + "".join([c.upper() if i % 2 else c for i, c in enumerate(suffix[0])]), ] for suffix_variant in suffix_variants: tmp_file = tempfile.NamedTemporaryFile( From 6806594d041344f55aec7de8a00378c840a75336 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Sat, 5 Nov 2022 11:05:07 -0500 Subject: [PATCH 08/38] Fix test errors --- can/io/logger.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 25dbf22a6..db8410dda 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -58,7 +58,7 @@ class Logger(MessageWriter): # pylint: disable=abstract-method (".csv",): CSVWriter, (".db",): SqliteWriter, (".log", ".log.gz"): CanutilsLogWriter, - (".txt", ".log.gz"): Printer, + (".txt", ".txt.gz"): Printer, } @staticmethod @@ -83,13 +83,15 @@ def __new__( # type: ignore ) Logger.fetched_plugins = True - real_suffix = "".join(pathlib.Path(filename).suffixes[-2:]).lower() + path = pathlib.Path(filename) + real_suffix = "".join(path.suffixes[-2:]).lower() + suffix = path.suffix.lower() file_or_filename: AcceptedIOType = filename - if ".gz" in real_suffix: + if "gz" in real_suffix.split(".")[-1]: suffix, file_or_filename = Logger.compress(filename, *args, **kwargs) - else: - suffix = real_suffix + if real_suffix.index(suffix) != 0: + real_suffix = suffix for log_writer_key in Logger.message_writers: if real_suffix in log_writer_key: From 5e260ad66207f260a4be6428088ab6473dade00a Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:33:57 -0600 Subject: [PATCH 09/38] Revert "Fix test errors" This reverts commit 6806594d041344f55aec7de8a00378c840a75336. --- can/io/logger.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index db8410dda..25dbf22a6 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -58,7 +58,7 @@ class Logger(MessageWriter): # pylint: disable=abstract-method (".csv",): CSVWriter, (".db",): SqliteWriter, (".log", ".log.gz"): CanutilsLogWriter, - (".txt", ".txt.gz"): Printer, + (".txt", ".log.gz"): Printer, } @staticmethod @@ -83,15 +83,13 @@ def __new__( # type: ignore ) Logger.fetched_plugins = True - path = pathlib.Path(filename) - real_suffix = "".join(path.suffixes[-2:]).lower() - suffix = path.suffix.lower() + real_suffix = "".join(pathlib.Path(filename).suffixes[-2:]).lower() file_or_filename: AcceptedIOType = filename - if "gz" in real_suffix.split(".")[-1]: + if ".gz" in real_suffix: suffix, file_or_filename = Logger.compress(filename, *args, **kwargs) - if real_suffix.index(suffix) != 0: - real_suffix = suffix + else: + suffix = real_suffix for log_writer_key in Logger.message_writers: if real_suffix in log_writer_key: From 5d418ec6310052148425243f61068da7501184a7 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:34:02 -0600 Subject: [PATCH 10/38] Revert "Add in gzip option for canutils and txt" This reverts commit dfefc2fb95e27c5d4640bd595c29304d63fd2b0b. --- can/io/logger.py | 4 ++-- test/logformats_test.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 25dbf22a6..737e2085e 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -57,8 +57,8 @@ class Logger(MessageWriter): # pylint: disable=abstract-method (".blf",): BLFWriter, (".csv",): CSVWriter, (".db",): SqliteWriter, - (".log", ".log.gz"): CanutilsLogWriter, - (".txt", ".log.gz"): Printer, + (".log",): CanutilsLogWriter, + (".txt",): Printer, } @staticmethod diff --git a/test/logformats_test.py b/test/logformats_test.py index 9a1417ac7..6a0eafac1 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -46,10 +46,10 @@ class ReaderWriterExtensionTest(unittest.TestCase): def test_extension_matching(self): for suffix, (writer, reader) in self.message_writers_and_readers.items(): suffix_variants = [ - suffix[0].upper(), - suffix[0].lower(), - f"can.msg.ext{suffix[0]}", - "".join([c.upper() if i % 2 else c for i, c in enumerate(suffix[0])]), + suffix.upper(), + suffix.lower(), + f"can.msg.ext{suffix}", + "".join([c.upper() if i % 2 else c for i, c in enumerate(suffix)]), ] for suffix_variant in suffix_variants: tmp_file = tempfile.NamedTemporaryFile( From 73a36db6249b55f35b1f03713a7586cc7b75d5d9 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:35:01 -0600 Subject: [PATCH 11/38] Revert "Fix message writer key reference" This reverts commit f5b1e0be1587c68ef8384020a90ff2a181dbdecd. --- can/io/logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/logger.py b/can/io/logger.py index 737e2085e..be524c71e 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -93,7 +93,7 @@ def __new__( # type: ignore for log_writer_key in Logger.message_writers: if real_suffix in log_writer_key: - return Logger.message_writers[log_writer_key]( + return Logger.message_writers[real_suffix]( file_or_filename, *args, **kwargs ) elif suffix in log_writer_key: From 1a70733d1db4b340434a92765879af053bc1aea5 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:35:03 -0600 Subject: [PATCH 12/38] Revert "Format with black" This reverts commit 7bf36ae2a74b764e4f2fd35d19f531e63c41ca89. --- can/io/logger.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index be524c71e..aa2959bf3 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -93,14 +93,10 @@ def __new__( # type: ignore for log_writer_key in Logger.message_writers: if real_suffix in log_writer_key: - return Logger.message_writers[real_suffix]( - file_or_filename, *args, **kwargs - ) + return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) elif suffix in log_writer_key: - raise ValueError( - f"The {Logger.message_writers[log_writer_key].__name__} does not " - f"currently support {real_suffix} files." - ) + raise ValueError(f"The {Logger.message_writers[log_writer_key].__name__} does not " + f"currently support {real_suffix} files.") else: raise ValueError( f'No write support for this unknown log format "{real_suffix}"' From 2badc183db4a37434573a905f84bec0d7d186d1a Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:16 -0600 Subject: [PATCH 13/38] Revert "Add error handling for gzip files" This reverts commit 33bb24ad3039ea8e7da51c0e4071c964db9be5ef. --- can/io/logger.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index aa2959bf3..5870f86e0 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -53,12 +53,12 @@ class Logger(MessageWriter): # pylint: disable=abstract-method fetched_plugins = False message_writers: Dict[str, Type[MessageWriter]] = { - (".asc",): ASCWriter, - (".blf",): BLFWriter, - (".csv",): CSVWriter, - (".db",): SqliteWriter, - (".log",): CanutilsLogWriter, - (".txt",): Printer, + ".asc": ASCWriter, + ".blf": BLFWriter, + ".csv": CSVWriter, + ".db": SqliteWriter, + ".log": CanutilsLogWriter, + ".txt": Printer, } @staticmethod @@ -87,17 +87,11 @@ def __new__( # type: ignore file_or_filename: AcceptedIOType = filename if ".gz" in real_suffix: - suffix, file_or_filename = Logger.compress(filename, *args, **kwargs) - else: - suffix = real_suffix - - for log_writer_key in Logger.message_writers: - if real_suffix in log_writer_key: - return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) - elif suffix in log_writer_key: - raise ValueError(f"The {Logger.message_writers[log_writer_key].__name__} does not " - f"currently support {real_suffix} files.") - else: + suffix, file_or_filename = Logger.compress(filename) + + try: + return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) + except KeyError: raise ValueError( f'No write support for this unknown log format "{real_suffix}"' ) from None From 6ec71514bf788eb2549e87ecc06ec122b8b8d90d Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:17 -0600 Subject: [PATCH 14/38] Revert "Check the real suffix in base io logger" This reverts commit df2b3a5ff167748d166378ee06447756ff6d38cb. --- can/io/logger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 5870f86e0..26b0c3262 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -83,17 +83,17 @@ def __new__( # type: ignore ) Logger.fetched_plugins = True - real_suffix = "".join(pathlib.Path(filename).suffixes[-2:]).lower() + suffix = pathlib.PurePath(filename).suffix.lower() file_or_filename: AcceptedIOType = filename - if ".gz" in real_suffix: + if suffix == ".gz": suffix, file_or_filename = Logger.compress(filename) try: - return Logger.message_writers[real_suffix](file_or_filename, *args, **kwargs) + return Logger.message_writers[suffix](file_or_filename, *args, **kwargs) except KeyError: raise ValueError( - f'No write support for this unknown log format "{real_suffix}"' + f'No write support for this unknown log format "{suffix}"' ) from None @staticmethod From 04c8a4d43c0d3b40b20e29062d5f3ced20e0c5e6 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:18 -0600 Subject: [PATCH 15/38] Revert "Revert "Raise error when blf GzipFile is requested"" This reverts commit 4eaa7df8bf0b32589ca4b3d9b4e8b00afb6cefe9. --- can/io/blf.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/can/io/blf.py b/can/io/blf.py index 93fa54ca2..062526014 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -11,7 +11,7 @@ of uncompressed data each. This data contains the actual CAN messages and other objects types. """ - +import gzip import struct import zlib import datetime @@ -395,6 +395,11 @@ def __init__( speed and compression (currently equivalent to level 6). """ mode = "rb+" if append else "wb" + if type(file) == gzip.GzipFile: + raise ValueError( + f"The BLFWriter is currently incompatible with " + f"{gzip.GzipFile.__name__}s. Try using a different zip format." + ) try: super().__init__(file, mode=mode) except FileNotFoundError: From 1d8aa14634d032b8cade9dfee453a73b908aae44 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:18 -0600 Subject: [PATCH 16/38] Revert "check whether CAN settings were correctly applied (#1426)" This reverts commit cd51ec4c07041c7b3a15b999aa4be5ec3829b05e. --- can/interfaces/vector/canlib.py | 66 +-------------------------------- test/test_vector.py | 7 +--- 2 files changed, 4 insertions(+), 69 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 8f699f3f7..d015f2407 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -140,7 +140,7 @@ def __init__( :raise ~can.exceptions.CanInterfaceNotImplementedError: If the current operating system is not supported or the driver could not be loaded. - :raise ~can.exceptions.CanInitializationError: + :raise can.exceptions.CanInitializationError: If the bus could not be set up. This may or may not be a :class:`~can.interfaces.vector.VectorInitializationError`. """ @@ -218,7 +218,6 @@ def __init__( interface_version, xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, ) - self.permission_mask = permission_mask.value LOG.debug( "Open Port: PortHandle: %d, PermissionMask: 0x%X", @@ -226,9 +225,8 @@ def __init__( permission_mask.value, ) - # set CAN settings for channel in self.channels: - if self._has_init_access(channel): + if permission_mask.value & self.channel_masks[channel]: if fd: self._set_bitrate_canfd( channel=channel, @@ -244,51 +242,6 @@ def __init__( elif bitrate: self._set_bitrate_can(channel=channel, bitrate=bitrate) - # Check CAN settings - for channel in self.channels: - if kwargs.get("_testing", False): - # avoid check if xldriver is mocked for testing - break - - bus_params = self._read_bus_params(channel) - if fd: - _canfd = bus_params.canfd - if not all( - [ - bus_params.bus_type is xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, - _canfd.can_op_mode - & xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CANFD, - _canfd.bitrate == bitrate if bitrate else True, - _canfd.sjw_abr == sjw_abr if bitrate else True, - _canfd.tseg1_abr == tseg1_abr if bitrate else True, - _canfd.tseg2_abr == tseg2_abr if bitrate else True, - _canfd.data_bitrate == data_bitrate if data_bitrate else True, - _canfd.sjw_dbr == sjw_dbr if data_bitrate else True, - _canfd.tseg1_dbr == tseg1_dbr if data_bitrate else True, - _canfd.tseg2_dbr == tseg2_dbr if data_bitrate else True, - ] - ): - raise CanInitializationError( - f"The requested CAN FD settings could not be set for channel {channel}. " - f"Another application might have set incompatible settings. " - f"These are the currently active settings: {_canfd._asdict()}" - ) - else: - _can = bus_params.can - if not all( - [ - bus_params.bus_type is xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, - _can.can_op_mode - & xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CAN20, - _can.bitrate == bitrate if bitrate else True, - ] - ): - raise CanInitializationError( - f"The requested CAN settings could not be set for channel {channel}. " - f"Another application might have set incompatible settings. " - f"These are the currently active settings: {_can._asdict()}" - ) - # Enable/disable TX receipts tx_receipts = 1 if receive_own_messages else 0 self.xldriver.xlCanSetChannelMode(self.port_handle, self.mask, tx_receipts, 0) @@ -387,21 +340,6 @@ def _find_global_channel_idx( error_code=xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT, ) - def _has_init_access(self, channel: int) -> bool: - return bool(self.permission_mask & self.channel_masks[channel]) - - def _read_bus_params(self, channel: int) -> "VectorBusParams": - channel_mask = self.channel_masks[channel] - - vcc_list = get_channel_configs() - for vcc in vcc_list: - if vcc.channel_mask == channel_mask: - return vcc.bus_params - - raise CanInitializationError( - f"Channel configuration for channel {channel} not found." - ) - def _set_bitrate_can( self, channel: int, diff --git a/test/test_vector.py b/test/test_vector.py index b0f305821..619277443 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -62,7 +62,6 @@ def mock_xldriver() -> None: # backup unmodified values real_xldriver = canlib.xldriver real_waitforsingleobject = canlib.WaitForSingleObject - real_has_events = canlib.HAS_EVENTS # set mock canlib.xldriver = xldriver_mock @@ -73,7 +72,6 @@ def mock_xldriver() -> None: # cleanup canlib.xldriver = real_xldriver canlib.WaitForSingleObject = real_waitforsingleobject - canlib.HAS_EVENTS = real_has_events def test_bus_creation_mocked(mock_xldriver) -> None: @@ -872,14 +870,13 @@ def xlGetChannelIndex( def xlOpenPort( port_handle_p: ctypes.POINTER(xlclass.XLportHandle), app_name_p: ctypes.c_char_p, - access_mask: int, - permission_mask: xlclass.XLaccess, + access_mask: xlclass.XLaccess, + permission_mask_p: ctypes.POINTER(xlclass.XLaccess), rx_queue_size: ctypes.c_uint, xl_interface_version: ctypes.c_uint, bus_type: ctypes.c_uint, ) -> int: port_handle_p.value = 0 - permission_mask.value = access_mask return 0 From 6fdf0b71c2c04a346f04dd9d1728e0bcac68b534 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:19 -0600 Subject: [PATCH 17/38] Revert "Fix #1424 (#1425)" This reverts commit 8a7b1d0e549d42332e5fb5c640faafa5c360b2b9. --- test/test_vector.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/test_vector.py b/test/test_vector.py index 619277443..9aea76281 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -7,7 +7,6 @@ import ctypes import functools import pickle -import sys import time from unittest.mock import Mock @@ -588,9 +587,6 @@ def test_vector_subtype_error_from_generic() -> None: raise specific -@pytest.mark.skipif( - sys.byteorder != "little", reason="Test relies on little endian data." -) def test_get_channel_configs() -> None: _original_func = canlib._get_xl_driver_config canlib._get_xl_driver_config = _get_predefined_xl_driver_config From 9e30cd85b460b068abe8a16f7e95171ccc4e48ba Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:20 -0600 Subject: [PATCH 18/38] Revert "Fix #1376 (#1412)" This reverts commit a344a1305d248a9a065aada119ef90ef4951c491. --- test/test_socketcan.py | 61 +++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/test/test_socketcan.py b/test/test_socketcan.py index 1c38e1583..a2c4faed3 100644 --- a/test/test_socketcan.py +++ b/test/test_socketcan.py @@ -3,10 +3,13 @@ """ Test functions in `can.interfaces.socketcan.socketcan`. """ -import ctypes -import struct import unittest + +from unittest.mock import Mock from unittest.mock import patch +from unittest.mock import call + +import ctypes from can.interfaces.socketcan.socketcan import ( bcm_header_factory, @@ -237,25 +240,51 @@ def side_effect_ctypes_alignment(value): ] self.assertEqual(expected_fields, BcmMsgHead._fields_) - def test_build_bcm_header(self): - def _find_u32_fmt_char() -> str: - for _fmt in ("H", "I", "L", "Q"): - if struct.calcsize(_fmt) == 4: - return _fmt + @unittest.skipIf( + not ( + ctypes.sizeof(ctypes.c_long) == 4 and ctypes.alignment(ctypes.c_long) == 4 + ), + "Should only run on platforms where sizeof(long) == 4 and alignof(long) == 4", + ) + def test_build_bcm_header_sizeof_long_4_alignof_long_4(self): + expected_result = ( + b"\x02\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x01\x04\x00\x00" + b"\x01\x00\x00\x00\x00\x00\x00\x00" + ) - def _standard_size_little_endian_to_native(data: bytes) -> bytes: - std_le_fmt = " Date: Fri, 11 Nov 2022 15:36:20 -0600 Subject: [PATCH 19/38] Revert "fix typo" This reverts commit c9d6d4e287975154d776f555aa509edbda1a3e66. --- doc/interfaces.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/interfaces.rst b/doc/interfaces.rst index 54d70ca86..c25ea8bea 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -45,7 +45,7 @@ The *Interface Names* are listed in :doc:`configuration`. Plugin Interface ^^^^^^^^^^^^^^^^ -External packages can register new interfaces by using the ``can.interface`` entry point +External packages can register a new interfaces by using the ``can.interface`` entry point in its project configuration. The format of the entry point depends on your project configuration format (*pyproject.toml*, *setup.cfg* or *setup.py*). From f9c761240842f38b2693b989543e4be77bc053fe Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:21 -0600 Subject: [PATCH 20/38] Revert "Add python 3.11 to supported versions (#1423)" This reverts commit 9d2620bea467352def01e3898e5da9e63a2a82e4. --- .github/workflows/build.yml | 5 +---- setup.py | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39eae343f..93094017f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: "3.8", "3.9", "3.10", - "3.11", + "3.11.0-alpha - 3.11.0", "pypy-3.7", "pypy-3.8", "pypy-3.9", @@ -67,9 +67,6 @@ jobs: - name: mypy 3.10 run: | mypy --python-version 3.10 . - - name: mypy 3.11 - run: | - mypy --python-version 3.11 . - name: pylint run: | pylint --rcfile=.pylintrc \ diff --git a/setup.py b/setup.py index a32471844..841425482 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,6 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", "Natural Language :: English", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", From 6cfed6bbc78f41c9d4d26dbcc255169e058b4a1d Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:22 -0600 Subject: [PATCH 21/38] Revert "add busParams and use snake case for VectorChannelConfig (#1422)" This reverts commit fb892d6f3b24ad57ad37e97ed7031950009ad423. --- can/interfaces/vector/__init__.py | 9 +- can/interfaces/vector/canlib.py | 140 ++++++++---------------------- doc/interfaces/vector.rst | 24 ----- test/test_vector.py | 56 +++--------- 4 files changed, 50 insertions(+), 179 deletions(-) diff --git a/can/interfaces/vector/__init__.py b/can/interfaces/vector/__init__.py index c5eae7140..f543a6109 100644 --- a/can/interfaces/vector/__init__.py +++ b/can/interfaces/vector/__init__.py @@ -1,12 +1,5 @@ """ """ -from .canlib import ( - VectorBus, - get_channel_configs, - VectorChannelConfig, - VectorBusParams, - VectorCanParams, - VectorCanFdParams, -) +from .canlib import VectorBus, VectorChannelConfig, get_channel_configs from .exceptions import VectorError, VectorOperationError, VectorInitializationError diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index d015f2407..8f5c557d6 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -295,12 +295,12 @@ def _find_global_channel_idx( if serial is not None: hw_type: Optional[xldefine.XL_HardwareType] = None for channel_config in channel_configs: - if channel_config.serial_number != serial: + if channel_config.serialNumber != serial: continue - hw_type = xldefine.XL_HardwareType(channel_config.hw_type) - if channel_config.hw_channel == channel: - return channel_config.channel_index + hw_type = xldefine.XL_HardwareType(channel_config.hwType) + if channel_config.hwChannel == channel: + return channel_config.channelIndex if hw_type is None: err_msg = f"No interface with serial {serial} found." @@ -331,7 +331,7 @@ def _find_global_channel_idx( # check if channel is a valid global channel index for channel_config in channel_configs: - if channel == channel_config.channel_index: + if channel == channel_config.channelIndex: return channel raise CanInitializationError( @@ -727,28 +727,26 @@ def _detect_available_configs() -> List[AutoDetectedConfig]: LOG.info("Found %d channels", len(channel_configs)) for channel_config in channel_configs: if ( - not channel_config.channel_bus_capabilities + not channel_config.channelBusCapabilities & xldefine.XL_BusCapabilities.XL_BUS_ACTIVE_CAP_CAN ): continue LOG.info( - "Channel index %d: %s", - channel_config.channel_index, - channel_config.name, + "Channel index %d: %s", channel_config.channelIndex, channel_config.name ) configs.append( { # data for use in VectorBus.__init__(): "interface": "vector", - "channel": channel_config.hw_channel, - "serial": channel_config.serial_number, + "channel": channel_config.hwChannel, + "serial": channel_config.serialNumber, # data for use in VectorBus.set_application_config(): - "hw_type": channel_config.hw_type, - "hw_index": channel_config.hw_index, - "hw_channel": channel_config.hw_channel, + "hw_type": channel_config.hwType, + "hw_index": channel_config.hwIndex, + "hw_channel": channel_config.hwChannel, # additional information: "supports_fd": bool( - channel_config.channel_capabilities + channel_config.channelCapabilities & xldefine.XL_ChannelCapabilities.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT ), "vector_channel_config": channel_config, @@ -877,53 +875,22 @@ def set_timer_rate(self, timer_rate_ms: int) -> None: self.xldriver.xlSetTimerRate(self.port_handle, timer_rate_10us) -class VectorCanParams(NamedTuple): - bitrate: int - sjw: int - tseg1: int - tseg2: int - sam: int - output_mode: xldefine.XL_OutputMode - can_op_mode: xldefine.XL_CANFD_BusParams_CanOpMode - - -class VectorCanFdParams(NamedTuple): - bitrate: int - data_bitrate: int - sjw_abr: int - tseg1_abr: int - tseg2_abr: int - sam_abr: int - sjw_dbr: int - tseg1_dbr: int - tseg2_dbr: int - output_mode: xldefine.XL_OutputMode - can_op_mode: xldefine.XL_CANFD_BusParams_CanOpMode - - -class VectorBusParams(NamedTuple): - bus_type: xldefine.XL_BusTypes - can: VectorCanParams - canfd: VectorCanFdParams - - class VectorChannelConfig(NamedTuple): """NamedTuple which contains the channel properties from Vector XL API.""" name: str - hw_type: xldefine.XL_HardwareType - hw_index: int - hw_channel: int - channel_index: int - channel_mask: int - channel_capabilities: xldefine.XL_ChannelCapabilities - channel_bus_capabilities: xldefine.XL_BusCapabilities - is_on_bus: bool - connected_bus_type: xldefine.XL_BusTypes - bus_params: VectorBusParams - serial_number: int - article_number: int - transceiver_name: str + hwType: xldefine.XL_HardwareType + hwIndex: int + hwChannel: int + channelIndex: int + channelMask: int + channelCapabilities: xldefine.XL_ChannelCapabilities + channelBusCapabilities: xldefine.XL_BusCapabilities + isOnBus: bool + connectedBusType: xldefine.XL_BusTypes + serialNumber: int + articleNumber: int + transceiverName: str def _get_xl_driver_config() -> xlclass.XLdriverConfig: @@ -940,38 +907,6 @@ def _get_xl_driver_config() -> xlclass.XLdriverConfig: return driver_config -def _read_bus_params_from_c_struct(bus_params: xlclass.XLbusParams) -> VectorBusParams: - return VectorBusParams( - bus_type=xldefine.XL_BusTypes(bus_params.busType), - can=VectorCanParams( - bitrate=bus_params.data.can.bitRate, - sjw=bus_params.data.can.sjw, - tseg1=bus_params.data.can.tseg1, - tseg2=bus_params.data.can.tseg2, - sam=bus_params.data.can.sam, - output_mode=xldefine.XL_OutputMode(bus_params.data.can.outputMode), - can_op_mode=xldefine.XL_CANFD_BusParams_CanOpMode( - bus_params.data.can.canOpMode - ), - ), - canfd=VectorCanFdParams( - bitrate=bus_params.data.canFD.arbitrationBitRate, - data_bitrate=bus_params.data.canFD.dataBitRate, - sjw_abr=bus_params.data.canFD.sjwAbr, - tseg1_abr=bus_params.data.canFD.tseg1Abr, - tseg2_abr=bus_params.data.canFD.tseg2Abr, - sam_abr=bus_params.data.canFD.samAbr, - sjw_dbr=bus_params.data.canFD.sjwDbr, - tseg1_dbr=bus_params.data.canFD.tseg1Dbr, - tseg2_dbr=bus_params.data.canFD.tseg2Dbr, - output_mode=xldefine.XL_OutputMode(bus_params.data.canFD.outputMode), - can_op_mode=xldefine.XL_CANFD_BusParams_CanOpMode( - bus_params.data.canFD.canOpMode - ), - ), - ) - - def get_channel_configs() -> List[VectorChannelConfig]: """Read channel properties from Vector XL API.""" try: @@ -984,23 +919,22 @@ def get_channel_configs() -> List[VectorChannelConfig]: xlcc: xlclass.XLchannelConfig = driver_config.channel[i] vcc = VectorChannelConfig( name=xlcc.name.decode(), - hw_type=xldefine.XL_HardwareType(xlcc.hwType), - hw_index=xlcc.hwIndex, - hw_channel=xlcc.hwChannel, - channel_index=xlcc.channelIndex, - channel_mask=xlcc.channelMask, - channel_capabilities=xldefine.XL_ChannelCapabilities( + hwType=xldefine.XL_HardwareType(xlcc.hwType), + hwIndex=xlcc.hwIndex, + hwChannel=xlcc.hwChannel, + channelIndex=xlcc.channelIndex, + channelMask=xlcc.channelMask, + channelCapabilities=xldefine.XL_ChannelCapabilities( xlcc.channelCapabilities ), - channel_bus_capabilities=xldefine.XL_BusCapabilities( + channelBusCapabilities=xldefine.XL_BusCapabilities( xlcc.channelBusCapabilities ), - is_on_bus=bool(xlcc.isOnBus), - bus_params=_read_bus_params_from_c_struct(xlcc.busParams), - connected_bus_type=xldefine.XL_BusTypes(xlcc.connectedBusType), - serial_number=xlcc.serialNumber, - article_number=xlcc.articleNumber, - transceiver_name=xlcc.transceiverName.decode(), + isOnBus=bool(xlcc.isOnBus), + connectedBusType=xldefine.XL_BusTypes(xlcc.connectedBusType), + serialNumber=xlcc.serialNumber, + articleNumber=xlcc.articleNumber, + transceiverName=xlcc.transceiverName.decode(), ) channel_list.append(vcc) return channel_list diff --git a/doc/interfaces/vector.rst b/doc/interfaces/vector.rst index 7f9aa1f3f..a4708c28f 100644 --- a/doc/interfaces/vector.rst +++ b/doc/interfaces/vector.rst @@ -59,18 +59,6 @@ Miscellaneous :show-inheritance: :class-doc-from: class -.. autoclass:: can.interfaces.vector.canlib.VectorBusParams - :show-inheritance: - :class-doc-from: class - -.. autoclass:: can.interfaces.vector.canlib.VectorCanParams - :show-inheritance: - :class-doc-from: class - -.. autoclass:: can.interfaces.vector.canlib.VectorCanFdParams - :show-inheritance: - :class-doc-from: class - .. autoclass:: can.interfaces.vector.xldefine.XL_HardwareType :show-inheritance: :member-order: bysource @@ -95,18 +83,6 @@ Miscellaneous :members: :undoc-members: -.. autoclass:: can.interfaces.vector.xldefine.XL_OutputMode - :show-inheritance: - :member-order: bysource - :members: - :undoc-members: - -.. autoclass:: can.interfaces.vector.xldefine.XL_CANFD_BusParams_CanOpMode - :show-inheritance: - :member-order: bysource - :members: - :undoc-members: - .. autoclass:: can.interfaces.vector.xldefine.XL_Status :show-inheritance: :member-order: bysource diff --git a/test/test_vector.py b/test/test_vector.py index 9aea76281..c4ae21f4e 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -22,7 +22,6 @@ VectorOperationError, VectorChannelConfig, ) -from can.interfaces.vector import VectorBusParams, VectorCanParams, VectorCanFdParams from test.config import IS_WINDOWS XLDRIVER_FOUND = canlib.xldriver is not None @@ -605,49 +604,18 @@ def test_winapi_availability() -> None: def test_vector_channel_config_attributes(): assert hasattr(VectorChannelConfig, "name") - assert hasattr(VectorChannelConfig, "hw_type") - assert hasattr(VectorChannelConfig, "hw_index") - assert hasattr(VectorChannelConfig, "hw_channel") - assert hasattr(VectorChannelConfig, "channel_index") - assert hasattr(VectorChannelConfig, "channel_mask") - assert hasattr(VectorChannelConfig, "channel_capabilities") - assert hasattr(VectorChannelConfig, "channel_bus_capabilities") - assert hasattr(VectorChannelConfig, "is_on_bus") - assert hasattr(VectorChannelConfig, "bus_params") - assert hasattr(VectorChannelConfig, "connected_bus_type") - assert hasattr(VectorChannelConfig, "serial_number") - assert hasattr(VectorChannelConfig, "article_number") - assert hasattr(VectorChannelConfig, "transceiver_name") - - -def test_vector_bus_params_attributes(): - assert hasattr(VectorBusParams, "bus_type") - assert hasattr(VectorBusParams, "can") - assert hasattr(VectorBusParams, "canfd") - - -def test_vector_can_params_attributes(): - assert hasattr(VectorCanParams, "bitrate") - assert hasattr(VectorCanParams, "sjw") - assert hasattr(VectorCanParams, "tseg1") - assert hasattr(VectorCanParams, "tseg2") - assert hasattr(VectorCanParams, "sam") - assert hasattr(VectorCanParams, "output_mode") - assert hasattr(VectorCanParams, "can_op_mode") - - -def test_vector_canfd_params_attributes(): - assert hasattr(VectorCanFdParams, "bitrate") - assert hasattr(VectorCanFdParams, "data_bitrate") - assert hasattr(VectorCanFdParams, "sjw_abr") - assert hasattr(VectorCanFdParams, "tseg1_abr") - assert hasattr(VectorCanFdParams, "tseg2_abr") - assert hasattr(VectorCanFdParams, "sam_abr") - assert hasattr(VectorCanFdParams, "sjw_dbr") - assert hasattr(VectorCanFdParams, "tseg1_dbr") - assert hasattr(VectorCanFdParams, "tseg2_dbr") - assert hasattr(VectorCanFdParams, "output_mode") - assert hasattr(VectorCanFdParams, "can_op_mode") + assert hasattr(VectorChannelConfig, "hwType") + assert hasattr(VectorChannelConfig, "hwIndex") + assert hasattr(VectorChannelConfig, "hwChannel") + assert hasattr(VectorChannelConfig, "channelIndex") + assert hasattr(VectorChannelConfig, "channelMask") + assert hasattr(VectorChannelConfig, "channelCapabilities") + assert hasattr(VectorChannelConfig, "channelBusCapabilities") + assert hasattr(VectorChannelConfig, "isOnBus") + assert hasattr(VectorChannelConfig, "connectedBusType") + assert hasattr(VectorChannelConfig, "serialNumber") + assert hasattr(VectorChannelConfig, "articleNumber") + assert hasattr(VectorChannelConfig, "transceiverName") # ***************************************************************************** From a8d0fb1be4c1f50de2e81d562e5b796156467b4c Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:24 -0600 Subject: [PATCH 22/38] Revert "update docs (#1421)" This reverts commit d3103d83813e4f077d1b0c010e3b063bc5ad5f58. --- can/interface.py | 51 ++++++++++++---------------- can/interfaces/cantact.py | 2 +- doc/api.rst | 2 +- doc/bus.rst | 21 +++++++++--- doc/conf.py | 1 - doc/configuration.rst | 45 ++++++------------------- doc/development.rst | 12 +++---- doc/doc-requirements.txt | 1 - doc/interfaces.rst | 65 ++++++------------------------------ doc/interfaces/cantact.rst | 8 ----- doc/interfaces/neousys.rst | 13 -------- doc/internal-api.rst | 4 +-- examples/receive_all.py | 10 +++--- examples/send_one.py | 10 +++--- examples/serial_com.py | 4 +-- examples/vcan_filtered.py | 2 +- examples/virtual_can_demo.py | 4 +-- 17 files changed, 85 insertions(+), 170 deletions(-) delete mode 100644 doc/interfaces/cantact.rst delete mode 100644 doc/interfaces/neousys.rst diff --git a/can/interface.py b/can/interface.py index 527f84d20..f1a0087e3 100644 --- a/can/interface.py +++ b/can/interface.py @@ -60,44 +60,37 @@ class Bus(BusABC): # pylint: disable=abstract-method Instantiates a CAN Bus of the given ``interface``, falls back to reading a configuration file from default locations. + """ - :param channel: - Channel identification. Expected type is backend dependent. - Set to ``None`` to let it be resolved automatically from the default - :ref:`configuration`. + @staticmethod + def __new__( # type: ignore # pylint: disable=keyword-arg-before-vararg + cls: Any, channel: Optional[Channel] = None, *args: Any, **kwargs: Any + ) -> BusABC: + """ + Takes the same arguments as :class:`can.BusABC.__init__`. + Some might have a special meaning, see below. - :param interface: - See :ref:`interface names` for a list of supported interfaces. - Set to ``None`` to let it be resolved automatically from the default - :ref:`configuration`. + :param channel: + Set to ``None`` to let it be resolved automatically from the default + configuration. That might fail, see below. - :param args: - ``interface`` specific positional arguments. + Expected type is backend dependent. - :param kwargs: - ``interface`` specific keyword arguments. + :param dict kwargs: + Should contain an ``interface`` key with a valid interface name. If not, + it is completed using :meth:`can.util.load_config`. - :raises ~can.exceptions.CanInterfaceNotImplementedError: - if the ``interface`` isn't recognized or cannot be loaded + :raises: can.CanInterfaceNotImplementedError + if the ``interface`` isn't recognized or cannot be loaded - :raises ~can.exceptions.CanInitializationError: - if the bus cannot be instantiated + :raises: can.CanInitializationError + if the bus cannot be instantiated - :raises ValueError: - if the ``channel`` could not be determined - """ + :raises: ValueError + if the ``channel`` could not be determined + """ - @staticmethod - def __new__( # type: ignore # pylint: disable=keyword-arg-before-vararg - cls: Any, - channel: Optional[Channel] = None, - interface: Optional[str] = None, - *args: Any, - **kwargs: Any, - ) -> BusABC: # figure out the rest of the configuration; this might raise an error - if interface is not None: - kwargs["interface"] = interface if channel is not None: kwargs["channel"] = channel if "context" in kwargs: diff --git a/can/interfaces/cantact.py b/can/interfaces/cantact.py index 9ad7fbef8..056a64a6b 100644 --- a/can/interfaces/cantact.py +++ b/can/interfaces/cantact.py @@ -59,7 +59,7 @@ def __init__( Bitrate in bits/s :param bool monitor: If true, operate in listen-only monitoring mode - :param BitTiming bit_timing: + :param BitTiming bit_timing Optional BitTiming to use for custom bit timing setting. Overrides bitrate if not None. """ diff --git a/doc/api.rst b/doc/api.rst index 23342f992..011553b1b 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -1,7 +1,7 @@ Library API =========== -The main objects are the :class:`~can.Bus` and the :class:`~can.Message`. +The main objects are the :class:`~can.BusABC` and the :class:`~can.Message`. A form of CAN interface is also required. .. hint:: diff --git a/doc/bus.rst b/doc/bus.rst index 4db49ee29..9f7077cc1 100644 --- a/doc/bus.rst +++ b/doc/bus.rst @@ -15,11 +15,24 @@ and implements the :class:`~can.BusABC` API. A thread safe bus wrapper is also available, see `Thread safe bus`_. +Autoconfig Bus +'''''''''''''' + .. autoclass:: can.Bus - :class-doc-from: class - :show-inheritance: :members: - :inherited-members: + + +API +''' + +.. autoclass:: can.BusABC + :members: + + .. automethod:: __iter__ + .. automethod:: _recv_internal + .. automethod:: _apply_filters + .. automethod:: _detect_available_configs + .. automethod:: _send_periodic_internal .. autoclass:: can.bus.BusState :members: @@ -68,7 +81,7 @@ Example defining two filters, one to pass 11-bit ID ``0x451``, the other to pass See :meth:`~can.BusABC.set_filters` for the implementation. Thread safe bus -''''''''''''''' +--------------- This thread safe version of the :class:`~can.BusABC` class can be used by multiple threads at once. Sending and receiving is locked separately to avoid unnecessary delays. diff --git a/doc/conf.py b/doc/conf.py index c1409b8c2..495416078 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,7 +48,6 @@ "sphinx.ext.viewcode", "sphinx.ext.graphviz", "sphinxcontrib.programoutput", - "sphinx_inline_tabs", "sphinx_rtd_theme", ] diff --git a/doc/configuration.rst b/doc/configuration.rst index d92d6164f..9bda3030f 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -1,5 +1,3 @@ -.. _configuration: - Configuration ============= @@ -102,7 +100,6 @@ For example: ``CAN_INTERFACE=socketcan CAN_CONFIG={"receive_own_messages": true, "fd": true}`` -.. _interface names: Interface Names --------------- @@ -112,51 +109,31 @@ Lookup table of interface names: +---------------------+-------------------------------------+ | Name | Documentation | +=====================+=====================================+ -| ``"canalystii"`` | :doc:`interfaces/canalystii` | -+---------------------+-------------------------------------+ -| ``"cantact"`` | :doc:`interfaces/cantact` | +| ``"socketcan"`` | :doc:`interfaces/socketcan` | +---------------------+-------------------------------------+ -| ``"etas"`` | :doc:`interfaces/etas` | +| ``"kvaser"`` | :doc:`interfaces/kvaser` | +---------------------+-------------------------------------+ -| ``"gs_usb"`` | :doc:`interfaces/gs_usb` | +| ``"serial"`` | :doc:`interfaces/serial` | +---------------------+-------------------------------------+ -| ``"iscan"`` | :doc:`interfaces/iscan` | +| ``"slcan"`` | :doc:`interfaces/slcan` | +---------------------+-------------------------------------+ | ``"ixxat"`` | :doc:`interfaces/ixxat` | +---------------------+-------------------------------------+ -| ``"kvaser"`` | :doc:`interfaces/kvaser` | -+---------------------+-------------------------------------+ -| ``"neousys"`` | :doc:`interfaces/neousys` | +| ``"pcan"`` | :doc:`interfaces/pcan` | +---------------------+-------------------------------------+ -| ``"neovi"`` | :doc:`interfaces/neovi` | +| ``"usb2can"`` | :doc:`interfaces/usb2can` | +---------------------+-------------------------------------+ | ``"nican"`` | :doc:`interfaces/nican` | +---------------------+-------------------------------------+ -| ``"nixnet"`` | :doc:`interfaces/nixnet` | -+---------------------+-------------------------------------+ -| ``"pcan"`` | :doc:`interfaces/pcan` | -+---------------------+-------------------------------------+ -| ``"robotell"`` | :doc:`interfaces/robotell` | -+---------------------+-------------------------------------+ -| ``"seeedstudio"`` | :doc:`interfaces/seeedstudio` | +| ``"iscan"`` | :doc:`interfaces/iscan` | +---------------------+-------------------------------------+ -| ``"serial"`` | :doc:`interfaces/serial` | +| ``"neovi"`` | :doc:`interfaces/neovi` | +---------------------+-------------------------------------+ -| ``"slcan"`` | :doc:`interfaces/slcan` | +| ``"vector"`` | :doc:`interfaces/vector` | +---------------------+-------------------------------------+ -| ``"socketcan"`` | :doc:`interfaces/socketcan` | +| ``"virtual"`` | :doc:`interfaces/virtual` | +---------------------+-------------------------------------+ -| ``"socketcand"`` | :doc:`interfaces/socketcand` | +| ``"canalystii"`` | :doc:`interfaces/canalystii` | +---------------------+-------------------------------------+ | ``"systec"`` | :doc:`interfaces/systec` | +---------------------+-------------------------------------+ -| ``"udp_multicast"`` | :doc:`interfaces/udp_multicast` | -+---------------------+-------------------------------------+ -| ``"usb2can"`` | :doc:`interfaces/usb2can` | -+---------------------+-------------------------------------+ -| ``"vector"`` | :doc:`interfaces/vector` | -+---------------------+-------------------------------------+ -| ``"virtual"`` | :doc:`interfaces/virtual` | -+---------------------+-------------------------------------+ - -Additional interface types can be added via the :ref:`plugin interface`. \ No newline at end of file diff --git a/doc/development.rst b/doc/development.rst index 055401bdc..03bf9a374 100644 --- a/doc/development.rst +++ b/doc/development.rst @@ -35,8 +35,8 @@ The following assumes that the commands are executed from the root of the reposi The project can be built with:: - pipx run build - pipx run twine check dist/* + pip install wheel + python setup.py sdist bdist_wheel The project can be installed in editable mode with:: @@ -44,7 +44,8 @@ The project can be installed in editable mode with:: The unit tests can be run with:: - pipx run tox -e py + pip install tox + tox -e py The documentation can be built with:: @@ -78,11 +79,6 @@ These steps are a guideline on how to add a new backend to python-can. To get started, have a look at ``back2back_test.py``: Simply add a test case like ``BasicTestSocketCan`` and some basic tests will be executed for the new interface. -.. attention:: - We strongly recommend using the :ref:`plugin interface` to extend python-can. - Publish a python package that contains your :class:`can.BusABC` subclass and use - it within the python-can API. We will mention your package inside this documentation - and add it as an optional dependency. Code Structure -------------- diff --git a/doc/doc-requirements.txt b/doc/doc-requirements.txt index b1c2da632..9732ea4ef 100644 --- a/doc/doc-requirements.txt +++ b/doc/doc-requirements.txt @@ -1,4 +1,3 @@ sphinx>=5.2.3 sphinxcontrib-programoutput sphinx_rtd_theme -sphinx-inline-tabs diff --git a/doc/interfaces.rst b/doc/interfaces.rst index c25ea8bea..dbe4ad426 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -1,5 +1,3 @@ -.. _can interface modules: - CAN Interface Modules --------------------- @@ -14,13 +12,11 @@ The available interfaces are: :maxdepth: 1 interfaces/canalystii - interfaces/cantact interfaces/etas interfaces/gs_usb interfaces/iscan interfaces/ixxat interfaces/kvaser - interfaces/neousys interfaces/neovi interfaces/nican interfaces/nixnet @@ -37,58 +33,19 @@ The available interfaces are: interfaces/vector interfaces/virtual -The *Interface Names* are listed in :doc:`configuration`. - - -.. _plugin interface: - -Plugin Interface -^^^^^^^^^^^^^^^^ - -External packages can register a new interfaces by using the ``can.interface`` entry point -in its project configuration. The format of the entry point depends on your project -configuration format (*pyproject.toml*, *setup.cfg* or *setup.py*). - -In the following example ``module`` defines the location of your bus class inside your -package e.g. ``my_package.subpackage.bus_module`` and ``classname`` is the name of -your :class:`can.BusABC` subclass. - -.. tab:: pyproject.toml (PEP 621) - - .. code-block:: toml +Additional interfaces can be added via a plugin interface. An external package +can register a new interface by using the ``can.interface`` entry point in its setup.py. - # Note the quotes around can.interface in order to escape the dot . - [project.entry-points."can.interface"] - interface_name = "module:classname" +The format of the entry point is ``interface_name=module:classname`` where +``classname`` is a concrete :class:`can.BusABC` implementation. -.. tab:: setup.cfg +:: - .. code-block:: ini + entry_points={ + 'can.interface': [ + "interface_name=module:classname", + ] + }, - [options.entry_points] - can.interface = - interface_name = module:classname -.. tab:: setup.py - - .. code-block:: python - - from setuptools import setup - - setup( - # ..., - entry_points = { - 'can.interface': [ - 'interface_name = module:classname' - ] - } - ) - -The ``interface_name`` can be used to -create an instance of the bus in the **python-can** API: - -.. code-block:: python - - import can - - bus = can.Bus(interface="interface_name", channel=0) +The *Interface Names* are listed in :doc:`configuration`. diff --git a/doc/interfaces/cantact.rst b/doc/interfaces/cantact.rst deleted file mode 100644 index dc9667218..000000000 --- a/doc/interfaces/cantact.rst +++ /dev/null @@ -1,8 +0,0 @@ -CANtact CAN Interface -===================== - -Interface for CANtact devices from Linklayer Labs - -.. autoclass:: can.interfaces.cantact.CantactBus - :show-inheritance: - :members: diff --git a/doc/interfaces/neousys.rst b/doc/interfaces/neousys.rst deleted file mode 100644 index 97a37868c..000000000 --- a/doc/interfaces/neousys.rst +++ /dev/null @@ -1,13 +0,0 @@ -Neousys CAN Interface -===================== - -This kind of interface can be found for example on Neousys POC-551VTC -One needs to have correct drivers and DLL (Share object for Linux) from -`Neousys `_. - -Beware this is only tested on Linux kernel higher than v5.3. This should be drop in -with Windows but you have to replace with correct named DLL - -.. autoclass:: can.interfaces.neousys.NeousysBus - :show-inheritance: - :members: diff --git a/doc/internal-api.rst b/doc/internal-api.rst index 3ef599598..1367dca50 100644 --- a/doc/internal-api.rst +++ b/doc/internal-api.rst @@ -55,15 +55,15 @@ configuration into account. Bus Internals ~~~~~~~~~~~~~ -Several methods are not documented in the main :class:`can.Bus` +Several methods are not documented in the main :class:`can.BusABC` as they are primarily useful for library developers as opposed to library users. This is the entire ABC bus class with all internal methods: .. autoclass:: can.BusABC - :members: :private-members: :special-members: + :noindex: diff --git a/examples/receive_all.py b/examples/receive_all.py index 7b94d526f..7ff532079 100755 --- a/examples/receive_all.py +++ b/examples/receive_all.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Shows how to receive messages via polling. +Shows how the receive messages via polling. """ import can @@ -11,9 +11,11 @@ def receive_all(): """Receives all messages and prints them to the console until Ctrl+C is pressed.""" - with can.Bus(interface="pcan", channel="PCAN_USBBUS1", bitrate=250000) as bus: - # bus = can.Bus(interface='ixxat', channel=0, bitrate=250000) - # bus = can.Bus(interface='vector', app_name='CANalyzer', channel=0, bitrate=250000) + with can.interface.Bus( + bustype="pcan", channel="PCAN_USBBUS1", bitrate=250000 + ) as bus: + # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) # set to read-only, only supported on some interfaces bus.state = BusState.PASSIVE diff --git a/examples/send_one.py b/examples/send_one.py index 7e3fb8a4c..49a4f1ee1 100755 --- a/examples/send_one.py +++ b/examples/send_one.py @@ -12,13 +12,13 @@ def send_one(): # this uses the default configuration (for example from the config file) # see https://python-can.readthedocs.io/en/stable/configuration.html - with can.Bus() as bus: + with can.interface.Bus() as bus: # Using specific buses works similar: - # bus = can.Bus(interface='socketcan', channel='vcan0', bitrate=250000) - # bus = can.Bus(interface='pcan', channel='PCAN_USBBUS1', bitrate=250000) - # bus = can.Bus(interface='ixxat', channel=0, bitrate=250000) - # bus = can.Bus(interface='vector', app_name='CANalyzer', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000) + # bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) + # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) # ... msg = can.Message( diff --git a/examples/serial_com.py b/examples/serial_com.py index 76b95c3e7..c57207a77 100755 --- a/examples/serial_com.py +++ b/examples/serial_com.py @@ -48,8 +48,8 @@ def receive(bus, stop_event): def main(): """Controls the sender and receiver.""" - with can.Bus(interface="serial", channel="/dev/ttyS10") as server: - with can.Bus(interface="serial", channel="/dev/ttyS11") as client: + with can.interface.Bus(interface="serial", channel="/dev/ttyS10") as server: + with can.interface.Bus(interface="serial", channel="/dev/ttyS11") as client: tx_msg = can.Message( arbitration_id=0x01, diff --git a/examples/vcan_filtered.py b/examples/vcan_filtered.py index a43fbe821..fa6c71547 100755 --- a/examples/vcan_filtered.py +++ b/examples/vcan_filtered.py @@ -11,7 +11,7 @@ def main(): """Send some messages to itself and apply filtering.""" - with can.Bus(interface="virtual", receive_own_messages=True) as bus: + with can.Bus(bustype="virtual", receive_own_messages=True) as bus: can_filters = [{"can_id": 1, "can_mask": 0xF, "extended": True}] bus.set_filters(can_filters) diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index af50a87a7..d0d6a4a6a 100755 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -14,9 +14,9 @@ def producer(thread_id: int, message_count: int = 16) -> None: """Spam the bus with messages including the data id. :param thread_id: the id of the thread/process - :param message_count: the number of messages that shall be sent + :param message_count: the number of messages that shall be send """ - with can.Bus(interface="socketcan", channel="vcan0") as bus: # type: ignore + with can.Bus(bustype="socketcan", channel="vcan0") as bus: # type: ignore for i in range(message_count): msg = can.Message( arbitration_id=0x0CF02200 + thread_id, From 661a39c893a0ee45d75f9d8fc4e270c1bbe45c3f Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:24 -0600 Subject: [PATCH 23/38] Revert "improve vector documentation (#1420)" This reverts commit 4b43f337878091bce1a737b0acf98a4fa2bbd968. --- can/interfaces/vector/__init__.py | 2 +- can/interfaces/vector/canlib.py | 3 -- doc/interfaces/vector.rst | 65 ++----------------------------- 3 files changed, 4 insertions(+), 66 deletions(-) diff --git a/can/interfaces/vector/__init__.py b/can/interfaces/vector/__init__.py index f543a6109..cdeb1d3cb 100644 --- a/can/interfaces/vector/__init__.py +++ b/can/interfaces/vector/__init__.py @@ -1,5 +1,5 @@ """ """ -from .canlib import VectorBus, VectorChannelConfig, get_channel_configs +from .canlib import VectorBus, VectorChannelConfig from .exceptions import VectorError, VectorOperationError, VectorInitializationError diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 8f5c557d6..a36f67e9e 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -876,8 +876,6 @@ def set_timer_rate(self, timer_rate_ms: int) -> None: class VectorChannelConfig(NamedTuple): - """NamedTuple which contains the channel properties from Vector XL API.""" - name: str hwType: xldefine.XL_HardwareType hwIndex: int @@ -908,7 +906,6 @@ def _get_xl_driver_config() -> xlclass.XLdriverConfig: def get_channel_configs() -> List[VectorChannelConfig]: - """Read channel properties from Vector XL API.""" try: driver_config = _get_xl_driver_config() except VectorError: diff --git a/doc/interfaces/vector.rst b/doc/interfaces/vector.rst index a4708c28f..7b5ede616 100644 --- a/doc/interfaces/vector.rst +++ b/doc/interfaces/vector.rst @@ -4,7 +4,7 @@ Vector This interface adds support for CAN controllers by `Vector`_. Only Windows is supported. By default this library uses the channel configuration for CANalyzer. -To use a different application, open **Vector Hardware Configuration** program and create +To use a different application, open Vector Hardware Config program and create a new application and assign the channels you may want to use. Specify the application name as ``app_name='Your app name'`` when constructing the bus or in a config file. @@ -21,72 +21,13 @@ application named "python-can":: -VectorBus ---------- +Bus +--- .. autoclass:: can.interfaces.vector.VectorBus - :show-inheritance: - :member-order: bysource - :members: - set_filters, - recv, - send, - send_periodic, - stop_all_periodic_tasks, - flush_tx_buffer, - reset, - shutdown, - popup_vector_hw_configuration, - get_application_config, - set_application_config - -Exceptions ----------- .. autoexception:: can.interfaces.vector.VectorError - :show-inheritance: .. autoexception:: can.interfaces.vector.VectorInitializationError - :show-inheritance: .. autoexception:: can.interfaces.vector.VectorOperationError - :show-inheritance: - -Miscellaneous -------------- - -.. autofunction:: can.interfaces.vector.get_channel_configs - -.. autoclass:: can.interfaces.vector.VectorChannelConfig - :show-inheritance: - :class-doc-from: class - -.. autoclass:: can.interfaces.vector.xldefine.XL_HardwareType - :show-inheritance: - :member-order: bysource - :members: - :undoc-members: - -.. autoclass:: can.interfaces.vector.xldefine.XL_ChannelCapabilities - :show-inheritance: - :member-order: bysource - :members: - :undoc-members: - -.. autoclass:: can.interfaces.vector.xldefine.XL_BusCapabilities - :show-inheritance: - :member-order: bysource - :members: - :undoc-members: - -.. autoclass:: can.interfaces.vector.xldefine.XL_BusTypes - :show-inheritance: - :member-order: bysource - :members: - :undoc-members: - -.. autoclass:: can.interfaces.vector.xldefine.XL_Status - :show-inheritance: - :member-order: bysource - :members: - :undoc-members: .. _Vector: https://vector.com/ From 0095c9ca15cd191294120ba219b767fc54858a85 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:25 -0600 Subject: [PATCH 24/38] Revert "update github actions (#1409)" This reverts commit 0c82d2c442811291c5748306f6767c9a093723d6. --- .github/workflows/build.yml | 47 ++++++++++--------------------- .github/workflows/format-code.yml | 6 ++-- setup.py | 1 - 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93094017f..aad9274fe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,21 +13,17 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] experimental: [false] - python-version: [ - "3.7", - "3.8", - "3.9", - "3.10", - "3.11.0-alpha - 3.11.0", - "pypy-3.7", - "pypy-3.8", - "pypy-3.9", - ] + python-version: ["3.7", "3.8", "3.9", "3.10", "pypy-3.7", "pypy-3.8"] + include: + # Only test on a single configuration while there are just pre-releases + - os: ubuntu-latest + experimental: true + python-version: "3.11.0-alpha - 3.11.0" fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -38,16 +34,16 @@ jobs: run: | tox -e gh - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v2 with: fail_ci_if_error: true static-code-analysis: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v3 with: python-version: "3.10" - name: Install dependencies @@ -79,9 +75,9 @@ jobs: format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v3 with: python-version: "3.10" - name: Install dependencies @@ -95,9 +91,9 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v3 with: python-version: "3.10" - name: Install dependencies @@ -113,16 +109,3 @@ jobs: name: sphinx-out path: ./build/ retention-days: 5 - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - name: Build wheel and sdist - run: pipx run build - - name: Check build artifacts - run: pipx run twine check --strict dist/* diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index 68c6f56d8..b86789662 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -9,9 +9,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v2 with: python-version: "3.10" - name: Install dependencies @@ -22,7 +22,7 @@ jobs: run: | black --verbose . - name: Commit Formated Code - uses: EndBug/add-and-commit@v9 + uses: EndBug/add-and-commit@v7 with: message: "Format code with black" # Ref https://git-scm.com/docs/git-add#_examples diff --git a/setup.py b/setup.py index 841425482..adbd61f91 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,6 @@ url="https://github.com/hardbyte/python-can", description="Controller Area Network interface module for Python", long_description=long_description, - long_description_content_type="text/x-rst", classifiers=[ # a list of all available ones: https://pypi.org/classifiers/ "Programming Language :: Python", From 705da47d58911f2b20581f2fecbe29ce043cf987 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:26 -0600 Subject: [PATCH 25/38] Revert "setup.cfg: Use license_files instead of license_file (#1408)" This reverts commit b587878554fda1c9476788c27ffa1f9881870442. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 2f3ee032f..043fff73a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -license_files = LICENSE.txt +license_file = LICENSE.txt [mypy] warn_return_any = True From 89f9f1f90c3a8166290679cac1fd7f9f124826e8 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:26 -0600 Subject: [PATCH 26/38] Revert "explicitly set supported file formats (#1406)" This reverts commit 99fea55f1aea868a64b9d6ba34ec3b713c26170b. --- can/io/logger.py | 34 ++++++++++++++-------------------- can/io/printer.py | 13 +++---------- test/test_rotating_loggers.py | 2 -- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 26b0c3262..256358228 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -7,13 +7,14 @@ from abc import ABC, abstractmethod from datetime import datetime import gzip -from typing import Any, Optional, Callable, Type, Tuple, cast, Dict, Set +from typing import Any, Optional, Callable, Type, Tuple, cast, Dict from types import TracebackType from typing_extensions import Literal from pkg_resources import iter_entry_points +import can.io from ..message import Message from ..listener import Listener from .generic import BaseIOHandler, FileIOMessageWriter, MessageWriter @@ -130,9 +131,8 @@ class BaseRotatingLogger(Listener, BaseIOHandler, ABC): :class:`~logging.handlers.BaseRotatingHandler`. Subclasses must set the `_writer` attribute upon initialization. - """ - _supported_formats: Set[str] = set() + """ #: If this attribute is set to a callable, the :meth:`~BaseRotatingLogger.rotation_filename` #: method delegates to this callable. The parameters passed to the callable are @@ -224,21 +224,17 @@ def _get_new_writer(self, filename: StringPathLike) -> FileIOMessageWriter: :return: An instance of a writer class. """ - suffix = "".join(pathlib.Path(filename).suffixes[-2:]).lower() - - if suffix in self._supported_formats: - logger = Logger(filename, *self.writer_args, **self.writer_kwargs) - if isinstance(logger, FileIOMessageWriter): - return logger - elif isinstance(logger, Printer) and logger.file is not None: - return cast(FileIOMessageWriter, logger) - - raise Exception( - f'The log format "{suffix}" ' - f"is not supported by {self.__class__.__name__}. " - f"{self.__class__.__name__} supports the following formats: " - f"{', '.join(self._supported_formats)}" - ) + + logger = Logger(filename, *self.writer_args, **self.writer_kwargs) + if isinstance(logger, FileIOMessageWriter): + return logger + elif isinstance(logger, Printer) and logger.file is not None: + return cast(FileIOMessageWriter, logger) + else: + raise Exception( + f"The log format \"{''.join(pathlib.Path(filename).suffixes[-2:])}" + f'" is not supported by {self.__class__.__name__}' + ) def stop(self) -> None: """Stop handling new messages. @@ -310,8 +306,6 @@ class SizedRotatingLogger(BaseRotatingLogger): :meth:`~can.Listener.stop` is called. """ - _supported_formats = {".asc", ".blf", ".csv", ".log", ".txt"} - def __init__( self, base_filename: StringPathLike, diff --git a/can/io/printer.py b/can/io/printer.py index 6a43c63b9..61871e8ad 100644 --- a/can/io/printer.py +++ b/can/io/printer.py @@ -4,7 +4,7 @@ import logging -from typing import Optional, TextIO, Union, Any, cast +from typing import Optional, TextIO, Union, Any from ..message import Message from .generic import MessageWriter @@ -40,18 +40,11 @@ def __init__( :param append: If set to `True` messages, are appended to the file, else the file is truncated """ - self.write_to_file = file is not None mode = "a" if append else "w" super().__init__(file, mode=mode) def on_message_received(self, msg: Message) -> None: - if self.write_to_file: - cast(TextIO, self.file).write(str(msg) + "\n") + if self.file is not None: + self.file.write(str(msg) + "\n") else: print(msg) - - def file_size(self) -> int: - """Return an estimate of the current file size in bytes.""" - if self.file is not None: - return self.file.tell() - return 0 diff --git a/test/test_rotating_loggers.py b/test/test_rotating_loggers.py index ad4388bf7..d900f4f23 100644 --- a/test/test_rotating_loggers.py +++ b/test/test_rotating_loggers.py @@ -18,8 +18,6 @@ def _get_instance(path, *args, **kwargs) -> can.io.BaseRotatingLogger: class SubClass(can.io.BaseRotatingLogger): """Subclass that implements abstract methods for testing.""" - _supported_formats = {".asc", ".blf", ".csv", ".log", ".txt"} - def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self._writer = can.Printer(file=path / "__unused.txt") From 9d119c015c1a82ad2f0413ae4c5be51aa88672d8 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:27 -0600 Subject: [PATCH 27/38] Revert "Fix Sphinx warnings (#1405)" This reverts commit 451ce48eacd529453c14ec59fe6bc5d142f2e65a. --- .github/workflows/build.yml | 22 ----------- .readthedocs.yml | 31 --------------- can/broadcastmanager.py | 2 +- can/bus.py | 31 ++++++--------- can/exceptions.py | 2 +- can/interfaces/ixxat/canlib_vcinpl.py | 6 ++- can/interfaces/ixxat/canlib_vcinpl2.py | 5 ++- can/interfaces/kvaser/canlib.py | 3 +- can/interfaces/kvaser/structures.py | 7 +++- can/interfaces/nican.py | 10 +++-- can/interfaces/pcan/pcan.py | 4 +- can/interfaces/robotell.py | 8 ++-- can/interfaces/serial/serial_can.py | 10 ++--- can/interfaces/slcan.py | 4 +- can/interfaces/socketcan/socketcan.py | 13 +++---- can/interfaces/systec/ucanbus.py | 8 ++-- can/interfaces/usb2can/usb2canInterface.py | 21 +++++----- .../usb2can/usb2canabstractionlayer.py | 22 ++++------- can/interfaces/vector/canlib.py | 6 +-- can/interfaces/virtual.py | 4 +- can/io/logger.py | 39 +++++++++---------- can/io/sqlite.py | 2 +- can/message.py | 2 +- can/typechecking.py | 4 +- can/util.py | 9 ++--- doc/bcm.rst | 4 -- doc/bus.rst | 11 +----- doc/conf.py | 29 ++------------ doc/doc-requirements.txt | 4 +- doc/interfaces.rst | 1 - doc/interfaces/etas.rst | 4 +- doc/interfaces/ixxat.rst | 19 +-------- doc/interfaces/kvaser.rst | 2 - doc/interfaces/neovi.rst | 5 +-- doc/interfaces/nican.rst | 1 - doc/interfaces/pcan.rst | 1 - doc/interfaces/seeedstudio.rst | 5 ++- doc/interfaces/serial.rst | 2 - doc/interfaces/socketcan.rst | 6 +-- doc/interfaces/socketcand.rst | 13 +++---- doc/interfaces/usb2can.rst | 2 - doc/interfaces/vector.rst | 6 ++- doc/interfaces/virtual.rst | 6 +-- doc/internal-api.rst | 2 +- doc/message.rst | 2 - doc/scripts.rst | 2 +- setup.cfg | 1 - 47 files changed, 136 insertions(+), 267 deletions(-) delete mode 100644 .readthedocs.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aad9274fe..5365620cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,25 +87,3 @@ jobs: - name: Code Format Check with Black run: | black --check --verbose . - - docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e .[canalystii,gs_usb] - pip install -r doc/doc-requirements.txt - - name: Build documentation - run: | - python -m sphinx -an doc build - - uses: actions/upload-artifact@v3 - with: - name: sphinx-out - path: ./build/ - retention-days: 5 diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 74cb9dbdd..000000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,31 +0,0 @@ -# .readthedocs.yaml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the version of Python and other tools you might need -build: - os: ubuntu-22.04 - tools: - python: "3.10" - -# Build documentation in the docs/ directory with Sphinx -sphinx: - configuration: doc/conf.py - -# If using Sphinx, optionally build your docs in additional formats such as PDF -formats: - - pdf - - epub - -# Optionally declare the Python requirements required to build your docs -python: - install: - - requirements: doc/doc-requirements.txt - - method: pip - path: . - extra_requirements: - - canalystii - - gs_usb diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 239d1d7d5..c15186e2c 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -39,7 +39,7 @@ class CyclicTask(abc.ABC): def stop(self) -> None: """Cancel this periodic task. - :raises ~can.exceptions.CanError: + :raises can.CanError: If stop is called on an already stopped task. """ diff --git a/can/bus.py b/can/bus.py index c0793c5f7..b9ccfcfad 100644 --- a/can/bus.py +++ b/can/bus.py @@ -66,10 +66,8 @@ def __init__( Any backend dependent configurations are passed in this dictionary :raises ValueError: If parameters are out of range - :raises ~can.exceptions.CanInterfaceNotImplementedError: - If the driver cannot be accessed - :raises ~can.exceptions.CanInitializationError: - If the bus cannot be initialized + :raises can.CanInterfaceNotImplementedError: If the driver cannot be accessed + :raises can.CanInitializationError: If the bus cannot be initialized """ self._periodic_tasks: List[_SelfRemovingCyclicTask] = [] self.set_filters(can_filters) @@ -83,11 +81,9 @@ def recv(self, timeout: Optional[float] = None) -> Optional[Message]: :param timeout: seconds to wait for a message or None to wait indefinitely - :return: - :obj:`None` on timeout or a :class:`~can.Message` object. + :return: ``None`` on timeout or a :class:`Message` object. - :raises ~can.exceptions.CanOperationError: - If an error occurred while reading + :raises can.CanOperationError: If an error occurred while reading """ start = time() time_left = timeout @@ -152,8 +148,7 @@ def _recv_internal( 2. a bool that is True if message filtering has already been done and else False - :raises ~can.exceptions.CanOperationError: - If an error occurred while reading + :raises can.CanOperationError: If an error occurred while reading :raises NotImplementedError: if the bus provides it's own :meth:`~can.BusABC.recv` implementation (legacy implementation) @@ -176,8 +171,7 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None: Might not be supported by all interfaces. None blocks indefinitely. - :raises ~can.exceptions.CanOperationError: - If an error occurred while sending + :raises can.CanOperationError: If an error occurred while sending """ raise NotImplementedError("Trying to write to a readonly bus?") @@ -195,8 +189,8 @@ def send_periodic( - the (optional) duration expires - the Bus instance goes out of scope - the Bus instance is shutdown - - :meth:`stop_all_periodic_tasks` is called - - the task's :meth:`~can.broadcastmanager.CyclicTask.stop` method is called. + - :meth:`BusABC.stop_all_periodic_tasks()` is called + - the task's :meth:`CyclicTask.stop()` method is called. :param msgs: Message(s) to transmit @@ -210,8 +204,7 @@ def send_periodic( Disable to instead manage tasks manually. :return: A started task instance. Note the task can be stopped (and depending on - the backend modified) by calling the task's - :meth:`~can.broadcastmanager.CyclicTask.stop` method. + the backend modified) by calling the task's :meth:`stop` method. .. note:: @@ -281,8 +274,8 @@ def _send_periodic_internal( no duration is provided, the task will continue indefinitely. :return: A started task instance. Note the task can be stopped (and - depending on the backend modified) by calling the - :meth:`~can.broadcastmanager.CyclicTask.stop` method. + depending on the backend modified) by calling the :meth:`stop` + method. """ if not hasattr(self, "_lock_send_periodic"): # Create a send lock for this bus, but not for buses which override this method @@ -295,7 +288,7 @@ def _send_periodic_internal( return task def stop_all_periodic_tasks(self, remove_tasks: bool = True) -> None: - """Stop sending any messages that were started using :meth:`send_periodic`. + """Stop sending any messages that were started using **bus.send_periodic**. .. note:: The result is undefined if a single task throws an exception while being stopped. diff --git a/can/exceptions.py b/can/exceptions.py index dc08be3b8..5a7aa0b7c 100644 --- a/can/exceptions.py +++ b/can/exceptions.py @@ -74,7 +74,7 @@ class CanInitializationError(CanError): """Indicates an error the occurred while initializing a :class:`can.BusABC`. If initialization fails due to a driver or platform missing/being unsupported, - a :exc:`~can.exceptions.CanInterfaceNotImplementedError` is raised instead. + a :class:`can.CanInterfaceNotImplementedError` is raised instead. If initialization fails due to a value being out of range, a :class:`ValueError` is raised. diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index bdb05cda5..cb0447b49 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -372,8 +372,10 @@ class IXXATBus(BusABC): .. warning:: This interface does implement efficient filtering of messages, but - the filters have to be set in ``__init__`` using the ``can_filters`` parameter. - Using :meth:`~can.BusABC.set_filters` does not work. + the filters have to be set in :meth:`~can.interfaces.ixxat.IXXATBus.__init__` + using the ``can_filters`` parameter. Using :meth:`~can.interfaces.ixxat.IXXATBus.set_filters` + does not work. + """ CHANNEL_BITRATES = { diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 802168630..37085d74a 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -411,8 +411,9 @@ class IXXATBus(BusABC): .. warning:: This interface does implement efficient filtering of messages, but - the filters have to be set in ``__init__`` using the ``can_filters`` parameter. - Using :meth:`~can.BusABC.set_filters` does not work. + the filters have to be set in :meth:`~can.interfaces.ixxat.IXXATBus.__init__` + using the ``can_filters`` parameter. Using :meth:`~can.interfaces.ixxat.IXXATBus.set_filters` + does not work. """ diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index f60a43bc5..a951e39de 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -657,7 +657,7 @@ def shutdown(self): canBusOff(self._write_handle) canClose(self._write_handle) - def get_stats(self) -> structures.BusStatistics: + def get_stats(self): """Retrieves the bus statistics. Use like so: @@ -667,6 +667,7 @@ def get_stats(self) -> structures.BusStatistics: std_data: 0, std_remote: 0, ext_data: 0, ext_remote: 0, err_frame: 0, bus_load: 0.0%, overruns: 0 :returns: bus statistics. + :rtype: can.interfaces.kvaser.structures.BusStatistics """ canRequestBusStatistics(self._write_handle) stats = structures.BusStatistics() diff --git a/can/interfaces/kvaser/structures.py b/can/interfaces/kvaser/structures.py index 996f16c37..c7d363dd4 100644 --- a/can/interfaces/kvaser/structures.py +++ b/can/interfaces/kvaser/structures.py @@ -7,8 +7,11 @@ class BusStatistics(ctypes.Structure): - """This structure is used with the method - :meth:`~can.interfaces.kvaser.canlib.KvaserBus.get_stats`. + """ + This structure is used with the method :meth:`KvaserBus.get_stats`. + + .. seealso:: :meth:`KvaserBus.get_stats` + """ _fields_ = [ diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index ea13e28e8..0beeee429 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -179,8 +179,10 @@ class NicanBus(BusABC): .. warning:: This interface does implement efficient filtering of messages, but - the filters have to be set in ``__init__`` using the ``can_filters`` parameter. - Using :meth:`~can.BusABC.set_filters` does not work. + the filters have to be set in :meth:`~can.interfaces.nican.NicanBus.__init__` + using the ``can_filters`` parameter. Using :meth:`~can.interfaces.nican.NicanBus.set_filters` + does not work. + """ def __init__( @@ -206,9 +208,9 @@ def __init__( ``is_error_frame`` set to True and ``arbitration_id`` will identify the error (default True) - :raise ~can.exceptions.CanInterfaceNotImplementedError: + :raise can.CanInterfaceNotImplementedError: If the current operating system is not supported or the driver could not be loaded. - :raise ~can.interfaces.nican.NicanInitializationError: + :raise can.interfaces.nican.NicanInitializationError: If the bus could not be set up. """ if nican is None: diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index c60b9e6c9..e5b877762 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -113,8 +113,8 @@ def __init__( """A PCAN USB interface to CAN. On top of the usual :class:`~can.Bus` methods provided, - the PCAN interface includes the :meth:`flash` - and :meth:`status` methods. + the PCAN interface includes the :meth:`~can.interface.pcan.PcanBus.flash` + and :meth:`~can.interface.pcan.PcanBus.status` methods. :param str channel: The can interface name. An example would be 'PCAN_USBBUS1'. diff --git a/can/interfaces/robotell.py b/can/interfaces/robotell.py index 0d3ad1b77..709fad78d 100644 --- a/can/interfaces/robotell.py +++ b/can/interfaces/robotell.py @@ -5,10 +5,9 @@ import io import time import logging -from typing import Optional from can import BusABC, Message -from ..exceptions import CanInterfaceNotImplementedError, CanOperationError +from ..exceptions import CanInterfaceNotImplementedError logger = logging.getLogger(__name__) @@ -378,11 +377,12 @@ def fileno(self): except Exception as exception: raise CanOperationError("Cannot fetch fileno") from exception - def get_serial_number(self, timeout: Optional[int]) -> Optional[str]: + def get_serial_number(self, timeout): """Get serial number of the slcan interface. - + :type timeout: int or None :param timeout: seconds to wait for serial number or None to wait indefinitely + :rtype str or None :return: None on timeout or a str object. """ diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index c1507b4fa..ec4bb8671 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -74,10 +74,8 @@ def __init__( :param rtscts: turn hardware handshake (RTS/CTS) on and off - :raises ~can.exceptions.CanInitializationError: - If the given parameters are invalid. - :raises ~can.exceptions.CanInterfaceNotImplementedError: - If the serial module is not installed. + :raises can.CanInitializationError: If the given parameters are invalid. + :raises can.CanInterfaceNotImplementedError: If the serial module is not installed. """ if not serial: @@ -165,10 +163,10 @@ def _recv_internal( This parameter will be ignored. The timeout value of the channel is used. :returns: - Received message and :obj:`False` (because no filtering as taken place). + Received message and `False` (because no filtering as taken place). .. warning:: - Flags like ``is_extended_id``, ``is_remote_frame`` and ``is_error_frame`` + Flags like is_extended_id, is_remote_frame and is_error_frame will not be set over this function, the flags in the return message are the default values. """ diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 212c4c85c..63ea4ca42 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -323,10 +323,10 @@ def get_serial_number(self, timeout: Optional[float]) -> Optional[str]: """Get serial number of the slcan interface. :param timeout: - seconds to wait for serial number or :obj:`None` to wait indefinitely + seconds to wait for serial number or ``None`` to wait indefinitely :return: - :obj:`None` on timeout or a :class:`str` object. + ``None`` on timeout or a :class:`~builtin.str` object. """ cmd = "N" self._write(cmd) diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index 549998dc8..c7c038520 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -402,7 +402,7 @@ def stop(self) -> None: """Stop a task by sending TX_DELETE message to Linux kernel. This will delete the entry for the transmission of the CAN-message - with the specified ``task_id`` identifier. The message length + with the specified :attr:`~task_id` identifier. The message length for the command TX_DELETE is {[bcm_msg_head]} (only the header). """ log.debug("Stopping periodic task") @@ -444,7 +444,7 @@ def start(self) -> None: message to Linux kernel prior to scheduling. :raises ValueError: - If the task referenced by ``task_id`` is already running. + If the task referenced by :attr:`~task_id` is already running. """ self._tx_setup(self.messages) @@ -617,10 +617,9 @@ def __init__( If setting some socket options fails, an error will be printed but no exception will be thrown. This includes enabling: - - - that own messages should be received, - - CAN-FD frames and - - error frames. + - that own messages should be received, + - CAN-FD frames and + - error frames. :param channel: The can interface name with which to create this bus. @@ -740,7 +739,7 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None: Wait up to this many seconds for the transmit queue to be ready. If not given, the call may fail immediately. - :raises ~can.exceptions.CanError: + :raises can.CanError: if the message could not be written. """ log.debug("We've been asked to write a message to the bus") diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index fee110b08..88224b856 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -88,10 +88,10 @@ def __init__(self, channel, can_filters=None, **kwargs): :raises ValueError: If invalid input parameter were passed. - :raises ~can.exceptions.CanInterfaceNotImplementedError: + :raises can.CanInterfaceNotImplementedError: If the platform is not supported. - :raises ~can.exceptions.CanInitializationError: + :raises can.CanInitializationError: If hardware or CAN interface initialization failed. """ try: @@ -181,7 +181,7 @@ def send(self, msg, timeout=None): :param float timeout: Transmit timeout in seconds (value 0 switches off the "auto delete") - :raises ~can.exceptions.CanOperationError: + :raises can.CanOperationError: If the message could not be sent. """ try: @@ -243,7 +243,7 @@ def flush_tx_buffer(self): """ Flushes the transmit buffer. - :raises ~can.exceptions.CanError: + :raises can.CanError: If flushing of the transmit buffer failed. """ log.info("Flushing transmit buffer") diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 504b61c7b..e51d485cd 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -67,22 +67,22 @@ class Usb2canBus(BusABC): This interface only works on Windows. Please use socketcan on Linux. - :param channel: + :param str channel (optional): The device's serial number. If not provided, Windows Management Instrumentation will be used to identify the first such device. - :param bitrate: + :param int bitrate (optional): Bitrate of channel in bit/s. Values will be limited to a maximum of 1000 Kb/s. Default is 500 Kbs - :param flags: + :param int flags (optional): Flags to directly pass to open function of the usb2can abstraction layer. - :param dll: + :param str dll (optional): Path to the DLL with the CANAL API to load Defaults to 'usb2can.dll' - :param serial: + :param str serial (optional): Alias for `channel` that is provided for legacy reasons. If both `serial` and `channel` are set, `serial` will be used and channel will be ignored. @@ -91,19 +91,18 @@ class Usb2canBus(BusABC): def __init__( self, - channel: Optional[str] = None, - dll: str = "usb2can.dll", - flags: int = 0x00000008, + channel=None, + dll="usb2can.dll", + flags=0x00000008, *_, - bitrate: int = 500000, - serial: Optional[str] = None, + bitrate=500000, **kwargs, ): self.can = Usb2CanAbstractionLayer(dll) # get the serial number of the device - device_id = serial or channel + device_id = kwargs.get("serial", channel) # search for a serial number if the device_id is None or empty if not device_id: diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index a6708cb42..8a3ae34ca 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -9,7 +9,6 @@ import can from ...exceptions import error_check -from ...typechecking import StringPathLike log = logging.getLogger("can.usb2can") @@ -109,13 +108,12 @@ class Usb2CanAbstractionLayer: Documentation: http://www.8devices.com/media/products/usb2can/downloads/CANAL_API.pdf """ - def __init__(self, dll: StringPathLike = "usb2can.dll") -> None: + def __init__(self, dll="usb2can.dll"): """ - :param dll: - the path to the usb2can DLL to load + :type dll: str or path-like + :param dll (optional): the path to the usb2can DLL to load - :raises ~can.exceptions.CanInterfaceNotImplementedError: - if the DLL could not be loaded + :raises can.CanInterfaceNotImplementedError: if the DLL could not be loaded """ try: self.__m_dllBasic = windll.LoadLibrary(dll) @@ -130,15 +128,11 @@ def open(self, configuration: str, flags: int): """ Opens a CAN connection using `CanalOpen()`. - :param configuration: - the configuration: "device_id; baudrate" - :param flags: - the flags to be set - :returns: - Valid handle for CANAL API functions on success + :param configuration: the configuration: "device_id; baudrate" + :param flags: the flags to be set - :raises ~can.exceptions.CanInterfaceNotImplementedError: - if any error occurred + :raises can.CanInitializationError: if any error occurred + :returns: Valid handle for CANAL API functions on success """ try: # we need to convert this into bytes, since the underlying DLL cannot diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index a36f67e9e..7737a99d3 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -138,11 +138,11 @@ def __init__( :param tseg2_dbr: Bus timing value tseg2 (data) - :raise ~can.exceptions.CanInterfaceNotImplementedError: + :raise can.CanInterfaceNotImplementedError: If the current operating system is not supported or the driver could not be loaded. - :raise can.exceptions.CanInitializationError: + :raise can.CanInitializationError: If the bus could not be set up. - This may or may not be a :class:`~can.interfaces.vector.VectorInitializationError`. + This may or may not be a :class:`can.interfaces.vector.VectorInitializationError`. """ if os.name != "nt" and not kwargs.get("_testing", False): raise CanInterfaceNotImplementedError( diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index cc71469b5..ffd5b0241 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -40,7 +40,7 @@ class VirtualBus(BusABC): an identifier for connected buses. Implements :meth:`can.BusABC._detect_available_configs`; see - :meth:`_detect_available_configs` for how it + :meth:`can.VirtualBus._detect_available_configs` for how it behaves here. .. note:: @@ -84,7 +84,7 @@ def __init__( self.channel.append(self.queue) def _check_if_open(self) -> None: - """Raises :exc:`~can.exceptions.CanOperationError` if the bus is not open. + """Raises :class:`~can.CanOperationError` if the bus is not open. Has to be called in every method that accesses the bus. """ diff --git a/can/io/logger.py b/can/io/logger.py index 256358228..30216bb24 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -120,31 +120,32 @@ def on_message_received(self, msg: Message) -> None: class BaseRotatingLogger(Listener, BaseIOHandler, ABC): """ Base class for rotating CAN loggers. This class is not meant to be - instantiated directly. Subclasses must implement the :meth:`should_rollover` - and :meth:`do_rollover` methods according to their rotation strategy. + instantiated directly. Subclasses must implement the :attr:`should_rollover` + and `do_rollover` methods according to their rotation strategy. The rotation behavior can be further customized by the user by setting the :attr:`namer` and :attr:`rotator` attributes after instantiating the subclass. - These attributes as well as the methods :meth:`rotation_filename` and :meth:`rotate` + These attributes as well as the methods `rotation_filename` and `rotate` and the corresponding docstrings are carried over from the python builtin - :class:`~logging.handlers.BaseRotatingHandler`. + `BaseRotatingHandler`. Subclasses must set the `_writer` attribute upon initialization. + :attr namer: + If this attribute is set to a callable, the :meth:`rotation_filename` method + delegates to this callable. The parameters passed to the callable are + those passed to :meth:`rotation_filename`. + :attr rotator: + If this attribute is set to a callable, the :meth:`rotate` method delegates + to this callable. The parameters passed to the callable are those + passed to :meth:`rotate`. + :attr rollover_count: + An integer counter to track the number of rollovers. """ - #: If this attribute is set to a callable, the :meth:`~BaseRotatingLogger.rotation_filename` - #: method delegates to this callable. The parameters passed to the callable are - #: those passed to :meth:`~BaseRotatingLogger.rotation_filename`. namer: Optional[Callable[[StringPathLike], StringPathLike]] = None - - #: If this attribute is set to a callable, the :meth:`~BaseRotatingLogger.rotate` method - #: delegates to this callable. The parameters passed to the callable are those - #: passed to :meth:`~BaseRotatingLogger.rotate`. rotator: Optional[Callable[[StringPathLike, StringPathLike], None]] = None - - #: An integer counter to track the number of rollovers. rollover_count: int = 0 def __init__(self, *args: Any, **kwargs: Any) -> None: @@ -168,7 +169,7 @@ def rotation_filename(self, default_name: StringPathLike) -> StringPathLike: This is provided so that a custom filename can be provided. The default implementation calls the :attr:`namer` attribute of the handler, if it's callable, passing the default name to - it. If the attribute isn't callable (the default is :obj:`None`), the name + it. If the attribute isn't callable (the default is `None`), the name is returned unchanged. :param default_name: @@ -183,8 +184,8 @@ def rotate(self, source: StringPathLike, dest: StringPathLike) -> None: """When rotating, rotate the current log. The default implementation calls the :attr:`rotator` attribute of the - handler, if it's callable, passing the `source` and `dest` arguments to - it. If the attribute isn't callable (the default is :obj:`None`), the source + handler, if it's callable, passing the source and dest arguments to + it. If the attribute isn't callable (the default is `None`), the source is simply renamed to the destination. :param source: @@ -272,10 +273,8 @@ class SizedRotatingLogger(BaseRotatingLogger): by adding a timestamp and the rollover count. A new log file is then created and written to. - This behavior can be customized by setting the - :attr:`~can.io.BaseRotatingLogger.namer` and - :attr:`~can.io.BaseRotatingLogger.rotator` - attribute. + This behavior can be customized by setting the :attr:`namer` and + :attr:`rotator` attribute. Example:: diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 0a4de85f2..b9cbf9f93 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -25,7 +25,7 @@ class SqliteReader(MessageReader): This class can be iterated over or used to fetch all messages in the database with :meth:`~SqliteReader.read_all`. - Calling :func:`len` on this object might not run in constant time. + Calling :func:`~builtin.len` on this object might not run in constant time. :attr str table_name: the name of the database table used for storing the messages diff --git a/can/message.py b/can/message.py index 8e0c4deee..87cb6a199 100644 --- a/can/message.py +++ b/can/message.py @@ -29,7 +29,7 @@ class Message: # pylint: disable=too-many-instance-attributes; OK for a datacla :func:`~copy.copy`/:func:`~copy.deepcopy` is supported as well. Messages do not support "dynamic" attributes, meaning any others than the - documented ones, since it uses :obj:`~object.__slots__`. + documented ones, since it uses :attr:`~object.__slots__`. """ __slots__ = ( diff --git a/can/typechecking.py b/can/typechecking.py index b3a513a3a..ed76b6c85 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -8,9 +8,7 @@ import typing_extensions -CanFilter: typing_extensions = typing_extensions.TypedDict( - "CanFilter", {"can_id": int, "can_mask": int} -) +CanFilter = typing_extensions.TypedDict("CanFilter", {"can_id": int, "can_mask": int}) CanFilterExtended = typing_extensions.TypedDict( "CanFilterExtended", {"can_id": int, "can_mask": int, "extended": bool} ) diff --git a/can/util.py b/can/util.py index e64eb13b5..d1ff643de 100644 --- a/can/util.py +++ b/can/util.py @@ -258,10 +258,8 @@ def _create_bus_config(config: Dict[str, Any]) -> typechecking.BusConfig: def set_logging_level(level_name: str) -> None: """Set the logging level for the `"can"` logger. - :param level_name: - One of: `'critical'`, `'error'`, `'warning'`, `'info'`, - `'debug'`, `'subdebug'`, or the value :obj:`None` (=default). - Defaults to `'debug'`. + :param level_name: One of: `'critical'`, `'error'`, `'warning'`, `'info'`, + `'debug'`, `'subdebug'`, or the value `None` (=default). Defaults to `'debug'`. """ can_logger = logging.getLogger("can") @@ -318,7 +316,7 @@ def deprecated_args_alias(**aliases): """Allows to rename/deprecate a function kwarg(s) and optionally have the deprecated kwarg(s) set as alias(es) - Example:: + Example: @deprecated_args_alias(oldArg="new_arg", anotherOldArg="another_new_arg") def library_function(new_arg, another_new_arg): @@ -327,7 +325,6 @@ def library_function(new_arg, another_new_arg): @deprecated_args_alias(oldArg="new_arg", obsoleteOldArg=None) def library_function(new_arg): pass - """ def deco(f): diff --git a/doc/bcm.rst b/doc/bcm.rst index 94cde0e60..549b06edd 100644 --- a/doc/bcm.rst +++ b/doc/bcm.rst @@ -42,7 +42,3 @@ which inherits from :class:`~can.broadcastmanager.CyclicTask`. .. autoclass:: can.RestartableCyclicTaskABC :members: - -.. autoclass:: can.broadcastmanager.ThreadBasedCyclicSendTask - :members: - diff --git a/doc/bus.rst b/doc/bus.rst index 9f7077cc1..bbe52cbd6 100644 --- a/doc/bus.rst +++ b/doc/bus.rst @@ -20,6 +20,7 @@ Autoconfig Bus .. autoclass:: can.Bus :members: + :undoc-members: API @@ -27,17 +28,9 @@ API .. autoclass:: can.BusABC :members: - - .. automethod:: __iter__ - .. automethod:: _recv_internal - .. automethod:: _apply_filters - .. automethod:: _detect_available_configs - .. automethod:: _send_periodic_internal - -.. autoclass:: can.bus.BusState - :members: :undoc-members: + .. automethod:: __iter__ Transmitting '''''''''''' diff --git a/doc/conf.py b/doc/conf.py index 495416078..14390d5ad 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -8,8 +8,6 @@ import sys import os -import ctypes -from unittest.mock import MagicMock # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -17,7 +15,6 @@ sys.path.insert(0, os.path.abspath("..")) import can # pylint: disable=wrong-import-position -from can import ctypesutil # -- General configuration ----------------------------------------------------- @@ -48,11 +45,11 @@ "sphinx.ext.viewcode", "sphinx.ext.graphviz", "sphinxcontrib.programoutput", - "sphinx_rtd_theme", + "sphinx_autodoc_typehints", ] # Now, you can use the alias name as a new role, e.g. :issue:`123`. -extlinks = {"issue": ("https://github.com/hardbyte/python-can/issues/%s/", "issue #%s")} +extlinks = {"issue": ("https://github.com/hardbyte/python-can/issues/%s/", "issue ")} intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} @@ -114,31 +111,11 @@ # Keep cached intersphinx inventories indefinitely intersphinx_cache_limit = -1 -# location of typehints -autodoc_typehints = "description" - -# disable specific warnings -nitpick_ignore = [ - # Ignore warnings for type aliases. Remove once Sphinx supports PEP613 - ("py:class", "can.typechecking.BusConfig"), - ("py:class", "can.typechecking.CanFilter"), - ("py:class", "can.typechecking.CanFilterExtended"), - ("py:class", "can.typechecking.AutoDetectedConfig"), - # intersphinx fails to reference some builtins - ("py:class", "asyncio.events.AbstractEventLoop"), - ("py:class", "_thread.allocate_lock"), -] - -# mock windows specific attributes -autodoc_mock_imports = ["win32com"] -ctypes.windll = MagicMock() -ctypesutil.HRESULT = ctypes.c_long - # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = "sphinx_rtd_theme" +html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/doc/doc-requirements.txt b/doc/doc-requirements.txt index 9732ea4ef..dead5e2e5 100644 --- a/doc/doc-requirements.txt +++ b/doc/doc-requirements.txt @@ -1,3 +1,3 @@ -sphinx>=5.2.3 +sphinx>=1.8.1 sphinxcontrib-programoutput -sphinx_rtd_theme +sphinx-autodoc-typehints diff --git a/doc/interfaces.rst b/doc/interfaces.rst index dbe4ad426..757cf67b4 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -26,7 +26,6 @@ The available interfaces are: interfaces/serial interfaces/slcan interfaces/socketcan - interfaces/socketcand interfaces/systec interfaces/udp_multicast interfaces/usb2can diff --git a/doc/interfaces/etas.rst b/doc/interfaces/etas.rst index 2b59a4eee..cc3cbdea4 100644 --- a/doc/interfaces/etas.rst +++ b/doc/interfaces/etas.rst @@ -6,7 +6,7 @@ The ETAS BOA_ (Basic Open API) is used. Install the "ETAS ECU and Bus Interfaces – Distribution Package". Only Windows is supported by this interface. The Linux kernel v5.13 (and greater) natively supports ETAS ES581.4, ES582.1 and ES584.1 USB modules. -To use these under Linux, please refer to :ref:`SocketCAN`. +To use these under Linux, please refer to :ref:`socketcan`. Bus --- @@ -25,7 +25,7 @@ The simplest configuration file would be:: Channels are the URIs used by the underlying API. -To find available URIs, use :meth:`~can.detect_available_configs`:: +To find available URIs, use :meth:`~can.interface.detect_available_configs`:: configs = can.interface.detect_available_configs(interfaces="etas") for c in configs: diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index 02e707c1c..28fb6f314 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -8,7 +8,7 @@ Interface to `IXXAT `__ Virtual CAN Interface V3 SDK. Wor The Linux ECI SDK is currently unsupported, however on Linux some devices are supported with :doc:`socketcan`. -The :meth:`~can.BusABC.send_periodic` method is supported +The :meth:`~can.interfaces.ixxat.canlib.IXXATBus.send_periodic` method is supported natively through the on-board cyclic transmit list. Modifying cyclic messages is not possible. You will need to stop it, and then start a new periodic message. @@ -20,22 +20,7 @@ Bus .. autoclass:: can.interfaces.ixxat.IXXATBus :members: -Implementation based on vcinpl.dll -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: can.interfaces.ixxat.canlib_vcinpl.IXXATBus - :members: - -.. autoclass:: can.interfaces.ixxat.canlib_vcinpl.CyclicSendTask - :members: - -Implementation based on vcinpl2.dll -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. autoclass:: can.interfaces.ixxat.canlib_vcinpl2.IXXATBus - :members: - -.. autoclass:: can.interfaces.ixxat.canlib_vcinpl2.CyclicSendTask +.. autoclass:: can.interfaces.ixxat.canlib.CyclicSendTask :members: diff --git a/doc/interfaces/kvaser.rst b/doc/interfaces/kvaser.rst index 4e0062cfa..a4a51ad09 100644 --- a/doc/interfaces/kvaser.rst +++ b/doc/interfaces/kvaser.rst @@ -46,5 +46,3 @@ This section contains Kvaser driver specific methods. .. automethod:: can.interfaces.kvaser.canlib.KvaserBus.get_stats -.. autoclass:: can.interfaces.kvaser.structures.BusStatistics - :members: diff --git a/doc/interfaces/neovi.rst b/doc/interfaces/neovi.rst index 588c5e914..05423ac8e 100644 --- a/doc/interfaces/neovi.rst +++ b/doc/interfaces/neovi.rst @@ -42,6 +42,5 @@ Bus --- .. autoclass:: can.interfaces.ics_neovi.NeoViBus -.. autoexception:: can.interfaces.ics_neovi.ICSApiError -.. autoexception:: can.interfaces.ics_neovi.ICSInitializationError -.. autoexception:: can.interfaces.ics_neovi.ICSOperationError + + diff --git a/doc/interfaces/nican.rst b/doc/interfaces/nican.rst index 4d2a40717..b2214371f 100644 --- a/doc/interfaces/nican.rst +++ b/doc/interfaces/nican.rst @@ -21,7 +21,6 @@ Bus .. autoclass:: can.interfaces.nican.NicanBus .. autoexception:: can.interfaces.nican.NicanError -.. autoexception:: can.interfaces.nican.NicanInitializationError .. _National Instruments: http://www.ni.com/can/ diff --git a/doc/interfaces/pcan.rst b/doc/interfaces/pcan.rst index feb40b195..ff82ba9f4 100644 --- a/doc/interfaces/pcan.rst +++ b/doc/interfaces/pcan.rst @@ -48,4 +48,3 @@ Bus --- .. autoclass:: can.interfaces.pcan.PcanBus - :members: diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst index da4d86995..5c86fa688 100644 --- a/doc/interfaces/seeedstudio.rst +++ b/doc/interfaces/seeedstudio.rst @@ -1,8 +1,9 @@ .. _seeeddoc: -Seeed Studio USB-CAN Analyzer -============================= +USB-CAN Analyzer +================ +...by Seeed Studio SKU: 114991193 diff --git a/doc/interfaces/serial.rst b/doc/interfaces/serial.rst index 99ee54df6..59ffef21a 100644 --- a/doc/interfaces/serial.rst +++ b/doc/interfaces/serial.rst @@ -21,8 +21,6 @@ Bus .. autoclass:: can.interfaces.serial.serial_can.SerialBus - .. automethod:: _recv_internal - Internals --------- The frames that will be sent and received over the serial interface consist of diff --git a/doc/interfaces/socketcan.rst b/doc/interfaces/socketcan.rst index f6cc1ba5f..1e82d8827 100644 --- a/doc/interfaces/socketcan.rst +++ b/doc/interfaces/socketcan.rst @@ -1,9 +1,7 @@ -.. _SocketCAN: - SocketCAN ========= -The SocketCAN documentation can be found in the `Linux kernel docs`_ at +The `SocketCAN`_ documentation can be found in the Linux kernel docs at ``networking`` directory. Quoting from the SocketCAN Linux documentation:: > The socketcan package is an implementation of CAN protocols @@ -286,7 +284,7 @@ to ensure usage of SocketCAN Linux API. The most important differences are: .. External references -.. _Linux kernel docs: https://www.kernel.org/doc/Documentation/networking/can.txt +.. _SocketCAN: https://www.kernel.org/doc/Documentation/networking/can.txt .. _Intrepid kernel module: https://github.com/intrepidcs/intrepid-socketcan-kernel-module .. _Intrepid user-space daemon: https://github.com/intrepidcs/icsscand .. _can-utils: https://github.com/linux-can/can-utils diff --git a/doc/interfaces/socketcand.rst b/doc/interfaces/socketcand.rst index 3c05bcc85..e50f134e1 100644 --- a/doc/interfaces/socketcand.rst +++ b/doc/interfaces/socketcand.rst @@ -28,8 +28,8 @@ daemon running on a remote Raspberry Pi: except KeyboardInterrupt: pass -The output may look like this:: - +The output may look like this: +:: Timestamp: 1637791111.209224 ID: 000006fd X Rx DLC: 8 c4 10 e3 2d 96 ff 25 6b Timestamp: 1637791111.233951 ID: 000001ad X Rx DLC: 4 4d 47 c7 64 Timestamp: 1637791111.409415 ID: 000005f7 X Rx DLC: 8 86 de e6 0f 42 55 5d 39 @@ -47,9 +47,8 @@ However, it will also work with any other socketcan device. Install CAN Interface for a MCP2515 based interface on a Raspberry Pi ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Add the following lines to ``/boot/config.txt``. -Please take care on the frequency of the crystal on your MCP2515 board:: - +Add the following lines to ``/boot/config.txt``. Please take care on the frequency of the crystal on your MCP2515 board: +:: dtparam=spi=on dtoverlay=mcp2515-can0,oscillator=12000000,interrupt=25,spimaxfrequency=1000000 @@ -107,8 +106,8 @@ Run socketcand ./socketcand -v -i can0 -During start, socketcand will prompt its IP address and port it listens to:: - +During start, socketcand will prompt its IP address and port it listens to: +:: Verbose output activated Using network interface 'eth0' diff --git a/doc/interfaces/usb2can.rst b/doc/interfaces/usb2can.rst index 56243d41d..e2e8d7517 100644 --- a/doc/interfaces/usb2can.rst +++ b/doc/interfaces/usb2can.rst @@ -86,5 +86,3 @@ Internals .. autoclass:: can.interfaces.usb2can.Usb2CanAbstractionLayer :members: :undoc-members: - -.. autoexception:: can.interfaces.usb2can.usb2canabstractionlayer.CanalError diff --git a/doc/interfaces/vector.rst b/doc/interfaces/vector.rst index 7b5ede616..dcd45f1bf 100644 --- a/doc/interfaces/vector.rst +++ b/doc/interfaces/vector.rst @@ -19,6 +19,8 @@ application named "python-can":: channel = 0, 1 app_name = python-can +If you are using Python 2.7 it is recommended to install pywin32_, otherwise a +slow and CPU intensive polling will be used when waiting for new messages. Bus @@ -27,7 +29,7 @@ Bus .. autoclass:: can.interfaces.vector.VectorBus .. autoexception:: can.interfaces.vector.VectorError -.. autoexception:: can.interfaces.vector.VectorInitializationError -.. autoexception:: can.interfaces.vector.VectorOperationError + .. _Vector: https://vector.com/ +.. _pywin32: https://sourceforge.net/projects/pywin32/ diff --git a/doc/interfaces/virtual.rst b/doc/interfaces/virtual.rst index 29976ed47..9258c9bbd 100644 --- a/doc/interfaces/virtual.rst +++ b/doc/interfaces/virtual.rst @@ -70,8 +70,8 @@ arrive at the recipients exactly once. Both is not guaranteed to hold for the be these guarantees of message delivery and message ordering. The central servers receive and distribute the CAN messages to all other bus participants, unlike in a real physical CAN network. The first intra-process ``virtual`` interface only runs within one Python process, effectively the -Python instance of :class:`~can.interfaces.virtual.VirtualBus` acts as a central server. -Notably the ``udp_multicast`` bus does not require a central server. +Python instance of :class:`VirtualBus` acts as a central server. Notably the ``udp_multicast`` bus +does not require a central server. **Arbitration and throughput** are two interrelated functions/properties of CAN networks which are typically abstracted in virtual interfaces. In all four interfaces, an unlimited amount @@ -133,5 +133,3 @@ Bus Class Documentation .. autoclass:: can.interfaces.virtual.VirtualBus :members: - - .. automethod:: _detect_available_configs diff --git a/doc/internal-api.rst b/doc/internal-api.rst index 1367dca50..c43db3394 100644 --- a/doc/internal-api.rst +++ b/doc/internal-api.rst @@ -70,7 +70,7 @@ methods: About the IO module ------------------- -Handling of the different file formats is implemented in ``can.io``. +Handling of the different file formats is implemented in :mod:`can.io`. Each file/IO type is within a separate module and ideally implements both a *Reader* and a *Writer*. The reader usually extends :class:`can.io.generic.BaseIOHandler`, while the writer often additionally extends :class:`can.Listener`, diff --git a/doc/message.rst b/doc/message.rst index 78ccc0b50..e5745f6b5 100644 --- a/doc/message.rst +++ b/doc/message.rst @@ -200,5 +200,3 @@ Message Each of the bytes in the data field (when present) are represented as two-digit hexadecimal numbers. - - .. automethod:: equals diff --git a/doc/scripts.rst b/doc/scripts.rst index 6b9bdf504..ace8c1e39 100644 --- a/doc/scripts.rst +++ b/doc/scripts.rst @@ -55,6 +55,6 @@ The full usage page can be seen below: can.logconvert --------------- +---------- .. command-output:: python -m can.logconvert -h diff --git a/setup.cfg b/setup.cfg index 043fff73a..b402ee645 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,7 +12,6 @@ warn_unused_ignores = True exclude = (?x)( venv - |^doc/conf.py$ |^test |^setup.py$ |^can/interfaces/__init__.py From d76b6747e3d9596dbd8b2aef513635f38654a842 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:27 -0600 Subject: [PATCH 28/38] Revert "Modify `file_size` help doc string (#1401)" This reverts commit b639560594d9dbb570efd67d64877b743fdf9aef. --- can/logger.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/can/logger.py b/can/logger.py index 8cb201987..0b73dd785 100644 --- a/can/logger.py +++ b/can/logger.py @@ -193,10 +193,9 @@ def main() -> None: "--file_size", dest="file_size", type=int, - help="Maximum file size in bytes. Rotate log file when size threshold " - "is reached. (The resulting file sizes will be consistent, but are not " - "guaranteed to be exactly what is specified here due to the rollover " - "conditions being logger implementation specific.)", + help="Maximum file size in bytes (or for the case of blf, maximum " + "buffer size before compression and flush to file). Rotate log " + "file when size threshold is reached.", default=None, ) From 149a20cd0f8f802c30f1836d14b8741ce28d8fbe Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:28 -0600 Subject: [PATCH 29/38] Revert "Test load_config() (#1396)" This reverts commit a144f25689728bad7ed39de4a70f2260c04df9be. --- test/listener_test.py | 8 ++-- test/network_test.py | 11 +---- test/test_load_config.py | 86 ----------------------------------- test/test_load_file_config.py | 2 +- 4 files changed, 6 insertions(+), 101 deletions(-) delete mode 100644 test/test_load_config.py diff --git a/test/listener_test.py b/test/listener_test.py index 0e64a266a..e5abd94a2 100644 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -15,6 +15,9 @@ from .data.example_data import generate_message +channel = "virtual_channel_0" +can.rc["interface"] = "virtual" + logging.basicConfig(level=logging.DEBUG) # makes the random number generator deterministic @@ -52,15 +55,10 @@ def testClassesImportable(self): class BusTest(unittest.TestCase): def setUp(self): - # Save all can.rc defaults - self._can_rc = can.rc - can.rc = {"interface": "virtual"} self.bus = can.interface.Bus() def tearDown(self): self.bus.shutdown() - # Restore the defaults - can.rc = self._can_rc class ListenerTest(BusTest): diff --git a/test/network_test.py b/test/network_test.py index 58c305a38..5900cd10f 100644 --- a/test/network_test.py +++ b/test/network_test.py @@ -14,8 +14,10 @@ import can channel = "vcan0" +can.rc["interface"] = "virtual" +@unittest.skipIf("interface" not in can.rc, "Need a CAN interface") class ControllerAreaNetworkTestCase(unittest.TestCase): """ This test ensures that what messages go in to the bus is what comes out. @@ -40,15 +42,6 @@ class ControllerAreaNetworkTestCase(unittest.TestCase): for b in range(num_messages) ) - def setUp(self): - # Save all can.rc defaults - self._can_rc = can.rc - can.rc = {"interface": "virtual"} - - def tearDown(self): - # Restore the defaults - can.rc = self._can_rc - def producer(self, ready_event, msg_read): self.client_bus = can.interface.Bus(channel=channel) ready_event.wait() diff --git a/test/test_load_config.py b/test/test_load_config.py deleted file mode 100644 index a2969b0a5..000000000 --- a/test/test_load_config.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python - -import os -import shutil -import tempfile -import unittest -from tempfile import NamedTemporaryFile - -import can - - -class LoadConfigTest(unittest.TestCase): - configuration = { - "default": {"interface": "serial", "channel": "0"}, - "one": {"interface": "kvaser", "channel": "1", "bitrate": 100000}, - "two": {"channel": "2"}, - } - - def setUp(self): - # Create a temporary directory - self.test_dir = tempfile.mkdtemp() - - def tearDown(self): - # Remove the directory after the test - shutil.rmtree(self.test_dir) - - def _gen_configration_file(self, sections): - with NamedTemporaryFile( - mode="w", dir=self.test_dir, delete=False - ) as tmp_config_file: - content = [] - for section in sections: - content.append("[{}]".format(section)) - for k, v in self.configuration[section].items(): - content.append("{} = {}".format(k, v)) - tmp_config_file.write("\n".join(content)) - return tmp_config_file.name - - def _dict_to_env(self, d): - return {f"CAN_{k.upper()}": str(v) for k, v in d.items()} - - def test_config_default(self): - tmp_config = self._gen_configration_file(["default"]) - config = can.util.load_config(path=tmp_config) - self.assertEqual(config, self.configuration["default"]) - - def test_config_whole_default(self): - tmp_config = self._gen_configration_file(self.configuration) - config = can.util.load_config(path=tmp_config) - self.assertEqual(config, self.configuration["default"]) - - def test_config_whole_context(self): - tmp_config = self._gen_configration_file(self.configuration) - config = can.util.load_config(path=tmp_config, context="one") - self.assertEqual(config, self.configuration["one"]) - - def test_config_merge_context(self): - tmp_config = self._gen_configration_file(self.configuration) - config = can.util.load_config(path=tmp_config, context="two") - expected = self.configuration["default"] - expected.update(self.configuration["two"]) - self.assertEqual(config, expected) - - def test_config_merge_environment_to_context(self): - tmp_config = self._gen_configration_file(self.configuration) - env_data = {"interface": "serial", "bitrate": 125000} - env_dict = self._dict_to_env(env_data) - with unittest.mock.patch.dict("os.environ", env_dict): - config = can.util.load_config(path=tmp_config, context="one") - expected = self.configuration["one"] - expected.update(env_data) - self.assertEqual(config, expected) - - def test_config_whole_environment(self): - tmp_config = self._gen_configration_file(self.configuration) - env_data = {"interface": "socketcan", "channel": "3", "bitrate": 250000} - env_dict = self._dict_to_env(env_data) - with unittest.mock.patch.dict("os.environ", env_dict): - config = can.util.load_config(path=tmp_config, context="one") - expected = self.configuration["one"] - expected.update(env_data) - self.assertEqual(config, expected) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_load_file_config.py b/test/test_load_file_config.py index 6b1d5a382..c71e6ccd6 100644 --- a/test/test_load_file_config.py +++ b/test/test_load_file_config.py @@ -11,7 +11,7 @@ class LoadFileConfigTest(unittest.TestCase): configuration = { "default": {"interface": "virtual", "channel": "0"}, - "one": {"interface": "kvaser", "channel": "1"}, + "one": {"interface": "virtual", "channel": "1"}, "two": {"channel": "2"}, "three": {"extra": "extra value"}, } From fc01cf0fe500b09f5acbef8de97196e2051e82c9 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:28 -0600 Subject: [PATCH 30/38] Revert "Move windows-curses dependency to an optional extra (#1395)" This reverts commit 89c395fd315179b1eb2ca8e21a852c825bb385b8. --- doc/installation.rst | 10 ---------- setup.py | 4 +--- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/doc/installation.rst b/doc/installation.rst index bfce72180..fe7204ba6 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -103,16 +103,6 @@ If ``python-can`` is already installed, the CANtact backend can be installed sep Additional CANtact documentation is available at `cantact.io `__. -CanViewer -~~~~~~~~~ - -``python-can`` has support for showing a simple CAN viewer terminal application -by running ``python -m can.viewer``. On Windows, this depends on the -`windows-curses library `__ which can -be installed with: - -``python -m pip install "python-can[viewer]"`` - Installing python-can in development mode ----------------------------------------- diff --git a/setup.py b/setup.py index adbd61f91..c9defecab 100644 --- a/setup.py +++ b/setup.py @@ -33,9 +33,6 @@ "gs_usb": ["gs_usb>=0.2.1"], "nixnet": ["nixnet>=0.3.1"], "pcan": ["uptime~=3.0.1"], - "viewer": [ - 'windows-curses;platform_system=="Windows" and platform_python_implementation=="CPython"' - ], } setup( @@ -89,6 +86,7 @@ install_requires=[ "setuptools", "wrapt~=1.10", + 'windows-curses;platform_system=="Windows" and platform_python_implementation=="CPython"', "typing_extensions>=3.10.0.0", 'pywin32;platform_system=="Windows" and platform_python_implementation=="CPython"', 'msgpack~=1.0.0;platform_system!="Windows"', From 3f1de8df80ce6508b3dcc090b823fa3efdcc785a Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:29 -0600 Subject: [PATCH 31/38] Revert "Update BufferedReader.get_message docstring (#1397)" This reverts commit 23b6b1916532b6e6ba2e5cc6f2e0d139c428a02a. --- can/listener.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/can/listener.py b/can/listener.py index 6c9cdf0be..12836a83c 100644 --- a/can/listener.py +++ b/can/listener.py @@ -105,13 +105,13 @@ def on_message_received(self, msg: Message) -> None: def get_message(self, timeout: float = 0.5) -> Optional[Message]: """ - Attempts to retrieve the message that has been in the queue for the longest amount - of time (FIFO). If no message is available, it blocks for given timeout or until a - message is received (whichever is shorter), or else returns None. This method does - not block after :meth:`can.BufferedReader.stop` has been called. + Attempts to retrieve the latest message received by the instance. If no message is + available it blocks for given timeout or until a message is received, or else + returns None (whichever is shorter). This method does not block after + :meth:`can.BufferedReader.stop` has been called. :param timeout: The number of seconds to wait for a new message. - :return: the received :class:`can.Message` or `None`, if the queue is empty. + :return: the Message if there is one, or None if there is not. """ try: if self.is_stopped: From 429bcced863abfcb6bb84c322781cf4b48688bd7 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:29 -0600 Subject: [PATCH 32/38] Revert "Pass file mode to compress function (#1384)" This reverts commit b2b2a80486bb2c165b710baed509a6654df76703. --- can/io/logger.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 30216bb24..478651953 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -98,18 +98,13 @@ def __new__( # type: ignore ) from None @staticmethod - def compress( - filename: StringPathLike, *args: Any, **kwargs: Any - ) -> Tuple[str, FileLike]: + def compress(filename: StringPathLike) -> Tuple[str, FileLike]: """ Return the suffix and io object of the decompressed file. File will automatically recompress upon close. """ real_suffix = pathlib.Path(filename).suffixes[-2].lower() - if kwargs.get("append", False): - mode = "ab" if real_suffix == ".blf" else "at" - else: - mode = "wb" if real_suffix == ".blf" else "wt" + mode = "ab" if real_suffix == ".blf" else "at" return real_suffix, gzip.open(filename, mode) From 547e8858c39bc9869ac8f1a6fa432b81ff6f4c32 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:30 -0600 Subject: [PATCH 33/38] Revert "Provide meaningful error message for xlGetApplConfig error (#1392)" This reverts commit c3a5c7ab969cd40711dba992e6a95ec8df4551b6. --- can/interfaces/vector/canlib.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 7737a99d3..ae60c5754 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -791,24 +791,14 @@ def get_application_config( hw_channel = ctypes.c_uint() _app_channel = ctypes.c_uint(app_channel) - try: - xldriver.xlGetApplConfig( - app_name.encode(), - _app_channel, - hw_type, - hw_index, - hw_channel, - xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, - ) - except VectorError as e: - raise VectorInitializationError( - error_code=e.error_code, - error_string=( - f"Vector HW Config: Channel '{app_channel}' of " - f"application '{app_name}' is not assigned to any interface" - ), - function="xlGetApplConfig", - ) from None + xldriver.xlGetApplConfig( + app_name.encode(), + _app_channel, + hw_type, + hw_index, + hw_channel, + xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, + ) return xldefine.XL_HardwareType(hw_type.value), hw_index.value, hw_channel.value @staticmethod From 614f6507d29e215a08378989759208284d0901c2 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:30 -0600 Subject: [PATCH 34/38] Revert "Test on vector virtual bus if XL API is available (#1390)" This reverts commit 366e2391a517c3b828e90925508a9ea3c1bfd6f2. --- can/interfaces/vector/canlib.py | 21 +- test/test_vector.py | 1118 +++++++++---------------------- 2 files changed, 337 insertions(+), 802 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index ae60c5754..333380a6f 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -881,25 +881,16 @@ class VectorChannelConfig(NamedTuple): transceiverName: str -def _get_xl_driver_config() -> xlclass.XLdriverConfig: +def get_channel_configs() -> List[VectorChannelConfig]: if xldriver is None: - raise VectorError( - error_code=xldefine.XL_Status.XL_ERR_DLL_NOT_FOUND, - error_string="xldriver is unavailable", - function="_get_xl_driver_config", - ) + return [] driver_config = xlclass.XLdriverConfig() - xldriver.xlOpenDriver() - xldriver.xlGetDriverConfig(driver_config) - xldriver.xlCloseDriver() - return driver_config - - -def get_channel_configs() -> List[VectorChannelConfig]: try: - driver_config = _get_xl_driver_config() + xldriver.xlOpenDriver() + xldriver.xlGetDriverConfig(driver_config) + xldriver.xlCloseDriver() except VectorError: - return [] + pass channel_list: List[VectorChannelConfig] = [] for i in range(driver_config.channelCount): diff --git a/test/test_vector.py b/test/test_vector.py index c4ae21f4e..338783136 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -5,9 +5,9 @@ """ import ctypes -import functools +import os import pickle -import time +import unittest from unittest.mock import Mock import pytest @@ -24,792 +24,332 @@ ) from test.config import IS_WINDOWS -XLDRIVER_FOUND = canlib.xldriver is not None - -@pytest.fixture() -def mock_xldriver() -> None: - # basic mock for XLDriver - xldriver_mock = Mock() - - # bus creation functions - xldriver_mock.xlOpenDriver = Mock() - xldriver_mock.xlGetApplConfig = Mock(side_effect=xlGetApplConfig) - xldriver_mock.xlGetChannelIndex = Mock(side_effect=xlGetChannelIndex) - xldriver_mock.xlOpenPort = Mock(side_effect=xlOpenPort) - xldriver_mock.xlCanFdSetConfiguration = Mock(return_value=0) - xldriver_mock.xlCanSetChannelMode = Mock(return_value=0) - xldriver_mock.xlActivateChannel = Mock(return_value=0) - xldriver_mock.xlGetSyncTime = Mock(side_effect=xlGetSyncTime) - xldriver_mock.xlCanSetChannelAcceptance = Mock(return_value=0) - xldriver_mock.xlCanSetChannelBitrate = Mock(return_value=0) - xldriver_mock.xlSetNotification = Mock(side_effect=xlSetNotification) - - # bus deactivation functions - xldriver_mock.xlDeactivateChannel = Mock(return_value=0) - xldriver_mock.xlClosePort = Mock(return_value=0) - xldriver_mock.xlCloseDriver = Mock() - - # sender functions - xldriver_mock.xlCanTransmit = Mock(return_value=0) - xldriver_mock.xlCanTransmitEx = Mock(return_value=0) - - # various functions - xldriver_mock.xlCanFlushTransmitQueue = Mock() - - # backup unmodified values - real_xldriver = canlib.xldriver - real_waitforsingleobject = canlib.WaitForSingleObject - - # set mock - canlib.xldriver = xldriver_mock - canlib.HAS_EVENTS = False - - yield - - # cleanup - canlib.xldriver = real_xldriver - canlib.WaitForSingleObject = real_waitforsingleobject - - -def test_bus_creation_mocked(mock_xldriver) -> None: - bus = can.Bus(channel=0, bustype="vector", _testing=True) - assert isinstance(bus, canlib.VectorBus) - can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() - can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() - - can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() - xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] - assert xlOpenPort_args[5] == xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION.value - assert xlOpenPort_args[6] == xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value - - can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_not_called() - can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_bus_creation() -> None: - bus = can.Bus(channel=0, serial=_find_virtual_can_serial(), bustype="vector") - assert isinstance(bus, canlib.VectorBus) - bus.shutdown() - - xl_channel_config = _find_xl_channel_config( - serial=_find_virtual_can_serial(), channel=0 - ) - assert bus.channel_masks[0] == xl_channel_config.channelMask - assert ( - xl_channel_config.busParams.data.can.canOpMode - & xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CAN20 - ) - - bus = canlib.VectorBus(channel=0, serial=_find_virtual_can_serial()) - assert isinstance(bus, canlib.VectorBus) - bus.shutdown() - - -def test_bus_creation_bitrate_mocked(mock_xldriver) -> None: - bus = can.Bus(channel=0, bustype="vector", bitrate=200_000, _testing=True) - assert isinstance(bus, canlib.VectorBus) - can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() - can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() - - can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() - xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] - assert xlOpenPort_args[5] == xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION.value - assert xlOpenPort_args[6] == xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value - - can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_not_called() - can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_called() - xlCanSetChannelBitrate_args = ( - can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.call_args[0] - ) - assert xlCanSetChannelBitrate_args[2] == 200_000 - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_bus_creation_bitrate() -> None: - bus = can.Bus( - channel=0, serial=_find_virtual_can_serial(), bustype="vector", bitrate=200_000 - ) - assert isinstance(bus, canlib.VectorBus) - - xl_channel_config = _find_xl_channel_config( - serial=_find_virtual_can_serial(), channel=0 - ) - assert xl_channel_config.busParams.data.can.bitRate == 200_000 - - bus.shutdown() - - -def test_bus_creation_fd_mocked(mock_xldriver) -> None: - bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) - assert isinstance(bus, canlib.VectorBus) - can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() - can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() - - can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() - xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] - assert ( - xlOpenPort_args[5] == xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4.value - ) - assert xlOpenPort_args[6] == xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value - - can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_called() - can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_bus_creation_fd() -> None: - bus = can.Bus( - channel=0, serial=_find_virtual_can_serial(), bustype="vector", fd=True - ) - assert isinstance(bus, canlib.VectorBus) - - xl_channel_config = _find_xl_channel_config( - serial=_find_virtual_can_serial(), channel=0 - ) - assert ( - xl_channel_config.interfaceVersion - == xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4 - ) - assert ( - xl_channel_config.busParams.data.canFD.canOpMode - & xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CANFD - ) - bus.shutdown() - - -def test_bus_creation_fd_bitrate_timings_mocked(mock_xldriver) -> None: - bus = can.Bus( - channel=0, - bustype="vector", - fd=True, - bitrate=500_000, - data_bitrate=2_000_000, - sjw_abr=10, - tseg1_abr=11, - tseg2_abr=12, - sjw_dbr=13, - tseg1_dbr=14, - tseg2_dbr=15, - _testing=True, - ) - assert isinstance(bus, canlib.VectorBus) - can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() - can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() - - can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() - xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] - assert ( - xlOpenPort_args[5] == xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4.value - ) - - assert xlOpenPort_args[6] == xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value - - can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_called() - can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() - - xlCanFdSetConfiguration_args = ( - can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.call_args[0] - ) - canFdConf = xlCanFdSetConfiguration_args[2] - assert canFdConf.arbitrationBitRate == 500000 - assert canFdConf.dataBitRate == 2000000 - assert canFdConf.sjwAbr == 10 - assert canFdConf.tseg1Abr == 11 - assert canFdConf.tseg2Abr == 12 - assert canFdConf.sjwDbr == 13 - assert canFdConf.tseg1Dbr == 14 - assert canFdConf.tseg2Dbr == 15 - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_bus_creation_fd_bitrate_timings() -> None: - bus = can.Bus( - channel=0, - serial=_find_virtual_can_serial(), - bustype="vector", - fd=True, - bitrate=500_000, - data_bitrate=2_000_000, - sjw_abr=10, - tseg1_abr=11, - tseg2_abr=12, - sjw_dbr=13, - tseg1_dbr=14, - tseg2_dbr=15, - ) - - xl_channel_config = _find_xl_channel_config( - serial=_find_virtual_can_serial(), channel=0 - ) - assert ( - xl_channel_config.interfaceVersion - == xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4 - ) - assert ( - xl_channel_config.busParams.data.canFD.canOpMode - & xldefine.XL_CANFD_BusParams_CanOpMode.XL_BUS_PARAMS_CANOPMODE_CANFD - ) - assert xl_channel_config.busParams.data.canFD.arbitrationBitRate == 500_000 - assert xl_channel_config.busParams.data.canFD.sjwAbr == 10 - assert xl_channel_config.busParams.data.canFD.tseg1Abr == 11 - assert xl_channel_config.busParams.data.canFD.tseg2Abr == 12 - assert xl_channel_config.busParams.data.canFD.sjwDbr == 13 - assert xl_channel_config.busParams.data.canFD.tseg1Dbr == 14 - assert xl_channel_config.busParams.data.canFD.tseg2Dbr == 15 - assert xl_channel_config.busParams.data.canFD.dataBitRate == 2_000_000 - - bus.shutdown() - - -def test_send_mocked(mock_xldriver) -> None: - bus = can.Bus(channel=0, bustype="vector", _testing=True) - msg = can.Message( - arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True - ) - bus.send(msg) - can.interfaces.vector.canlib.xldriver.xlCanTransmit.assert_called() - can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_not_called() - - -def test_send_fd_mocked(mock_xldriver) -> None: - bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) - msg = can.Message( - arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True - ) - bus.send(msg) - can.interfaces.vector.canlib.xldriver.xlCanTransmit.assert_not_called() - can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_called() - - -def test_receive_mocked(mock_xldriver) -> None: - can.interfaces.vector.canlib.xldriver.xlReceive = Mock(side_effect=xlReceive) - bus = can.Bus(channel=0, bustype="vector", _testing=True) - bus.recv(timeout=0.05) - can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() - can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() - - -def test_receive_fd_mocked(mock_xldriver) -> None: - can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock(side_effect=xlCanReceive) - bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) - bus.recv(timeout=0.05) - can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() - can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_send_and_receive() -> None: - bus1 = can.Bus(channel=0, serial=_find_virtual_can_serial(), bustype="vector") - bus2 = can.Bus(channel=0, serial=_find_virtual_can_serial(), bustype="vector") - - msg_std = can.Message( - channel=0, arbitration_id=0xFF, data=list(range(8)), is_extended_id=False - ) - msg_ext = can.Message( - channel=0, arbitration_id=0xFFFFFF, data=list(range(8)), is_extended_id=True - ) - - bus1.send(msg_std) - msg_std_recv = bus2.recv(None) - assert msg_std.equals(msg_std_recv, timestamp_delta=None) - - bus1.send(msg_ext) - msg_ext_recv = bus2.recv(None) - assert msg_ext.equals(msg_ext_recv, timestamp_delta=None) - - bus1.shutdown() - bus2.shutdown() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_send_and_receive_fd() -> None: - bus1 = can.Bus( - channel=0, serial=_find_virtual_can_serial(), fd=True, bustype="vector" - ) - bus2 = can.Bus( - channel=0, serial=_find_virtual_can_serial(), fd=True, bustype="vector" - ) - - msg_std = can.Message( - channel=0, - arbitration_id=0xFF, - data=list(range(64)), - is_extended_id=False, - is_fd=True, - ) - msg_ext = can.Message( - channel=0, - arbitration_id=0xFFFFFF, - data=list(range(64)), - is_extended_id=True, - is_fd=True, - ) - - bus1.send(msg_std) - msg_std_recv = bus2.recv(None) - assert msg_std.equals(msg_std_recv, timestamp_delta=None) - - bus1.send(msg_ext) - msg_ext_recv = bus2.recv(None) - assert msg_ext.equals(msg_ext_recv, timestamp_delta=None) - - bus1.shutdown() - bus2.shutdown() - - -def test_receive_non_msg_event_mocked(mock_xldriver) -> None: - can.interfaces.vector.canlib.xldriver.xlReceive = Mock( - side_effect=xlReceive_chipstate - ) - bus = can.Bus(channel=0, bustype="vector", _testing=True) - bus.handle_can_event = Mock() - bus.recv(timeout=0.05) - can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() - can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() - bus.handle_can_event.assert_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_receive_non_msg_event() -> None: - bus = canlib.VectorBus( - channel=0, serial=_find_virtual_can_serial(), bustype="vector" - ) - bus.handle_can_event = Mock() - bus.xldriver.xlCanRequestChipState(bus.port_handle, bus.channel_masks[0]) - bus.recv(timeout=0.5) - bus.handle_can_event.assert_called() - bus.shutdown() - - -def test_receive_fd_non_msg_event_mocked(mock_xldriver) -> None: - can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock( - side_effect=xlCanReceive_chipstate - ) - bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) - bus.handle_canfd_event = Mock() - bus.recv(timeout=0.05) - can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() - can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() - bus.handle_canfd_event.assert_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_receive_fd_non_msg_event() -> None: - bus = canlib.VectorBus( - channel=0, serial=_find_virtual_can_serial(), fd=True, bustype="vector" - ) - bus.handle_canfd_event = Mock() - bus.xldriver.xlCanRequestChipState(bus.port_handle, bus.channel_masks[0]) - bus.recv(timeout=0.5) - bus.handle_canfd_event.assert_called() - bus.shutdown() - - -def test_flush_tx_buffer_mocked(mock_xldriver) -> None: - bus = can.Bus(channel=0, bustype="vector", _testing=True) - bus.flush_tx_buffer() - can.interfaces.vector.canlib.xldriver.xlCanFlushTransmitQueue.assert_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_flush_tx_buffer() -> None: - bus = can.Bus(channel=0, serial=_find_virtual_can_serial(), bustype="vector") - bus.flush_tx_buffer() - bus.shutdown() - - -def test_shutdown_mocked(mock_xldriver) -> None: - bus = can.Bus(channel=0, bustype="vector", _testing=True) - bus.shutdown() - can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() - can.interfaces.vector.canlib.xldriver.xlClosePort.assert_called() - can.interfaces.vector.canlib.xldriver.xlCloseDriver.assert_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_shutdown() -> None: - bus = can.Bus(channel=0, serial=_find_virtual_can_serial(), bustype="vector") - - xl_channel_config = _find_xl_channel_config( - serial=_find_virtual_can_serial(), channel=0 - ) - assert xl_channel_config.isOnBus != 0 - bus.shutdown() - - xl_channel_config = _find_xl_channel_config( - serial=_find_virtual_can_serial(), channel=0 - ) - assert xl_channel_config.isOnBus == 0 - - -def test_reset_mocked(mock_xldriver) -> None: - bus = canlib.VectorBus(channel=0, bustype="vector", _testing=True) - bus.reset() - can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() - can.interfaces.vector.canlib.xldriver.xlActivateChannel.assert_called() - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_reset_mocked() -> None: - bus = canlib.VectorBus( - channel=0, serial=_find_virtual_can_serial(), bustype="vector" - ) - bus.reset() - bus.shutdown() - - -def test_popup_hw_cfg_mocked(mock_xldriver) -> None: - canlib.xldriver.xlPopupHwConfig = Mock() - canlib.VectorBus.popup_vector_hw_configuration(10) - assert canlib.xldriver.xlPopupHwConfig.called - args, kwargs = canlib.xldriver.xlPopupHwConfig.call_args - assert isinstance(args[0], ctypes.c_char_p) - assert isinstance(args[1], ctypes.c_uint) - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_popup_hw_cfg() -> None: - with pytest.raises(VectorOperationError): - canlib.VectorBus.popup_vector_hw_configuration(1) - - -def test_get_application_config_mocked(mock_xldriver) -> None: - canlib.xldriver.xlGetApplConfig = Mock() - canlib.VectorBus.get_application_config(app_name="CANalyzer", app_channel=0) - assert canlib.xldriver.xlGetApplConfig.called - - -def test_set_application_config_mocked(mock_xldriver) -> None: - canlib.xldriver.xlSetApplConfig = Mock() - canlib.VectorBus.set_application_config( - app_name="CANalyzer", - app_channel=0, - hw_type=xldefine.XL_HardwareType.XL_HWTYPE_VN1610, - hw_index=0, - hw_channel=0, - ) - assert canlib.xldriver.xlSetApplConfig.called - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_set_and_get_application_config() -> None: - xl_channel_config = _find_xl_channel_config( - serial=_find_virtual_can_serial(), channel=1 - ) - canlib.VectorBus.set_application_config( - app_name="python-can::test_vector", - app_channel=5, - hw_channel=xl_channel_config.hwChannel, - hw_index=xl_channel_config.hwIndex, - hw_type=xldefine.XL_HardwareType(xl_channel_config.hwType), - ) - hw_type, hw_index, hw_channel = canlib.VectorBus.get_application_config( - app_name="python-can::test_vector", - app_channel=5, - ) - assert hw_type == xldefine.XL_HardwareType(xl_channel_config.hwType) - assert hw_index == xl_channel_config.hwIndex - assert hw_channel == xl_channel_config.hwChannel - - -def test_set_timer_mocked(mock_xldriver) -> None: - canlib.xldriver.xlSetTimerRate = Mock() - bus = canlib.VectorBus(channel=0, bustype="vector", fd=True, _testing=True) - bus.set_timer_rate(timer_rate_ms=1) - assert canlib.xldriver.xlSetTimerRate.called - - -@pytest.mark.skipif(not XLDRIVER_FOUND, reason="Vector XL API is unavailable") -def test_set_timer() -> None: - bus = canlib.VectorBus( - channel=0, serial=_find_virtual_can_serial(), bustype="vector" - ) - bus.handle_can_event = Mock() - bus.set_timer_rate(timer_rate_ms=1) - t0 = time.perf_counter() - while time.perf_counter() - t0 < 0.5: - bus.recv(timeout=-1) - - # call_count is incorrect when using virtual bus - # assert bus.handle_can_event.call_count > 498 - # assert bus.handle_can_event.call_count < 502 - - -@pytest.mark.skipif(IS_WINDOWS, reason="Not relevant for Windows.") -def test_called_without_testing_argument() -> None: - """This tests if an exception is thrown when we are not running on Windows.""" - with pytest.raises(can.CanInterfaceNotImplementedError): - # do not set the _testing argument, since it would suppress the exception - can.Bus(channel=0, bustype="vector") - - -def test_vector_error_pickle() -> None: - for error_type in [ - VectorError, - VectorInitializationError, - VectorOperationError, - ]: - error_code = 118 - error_string = "XL_ERROR" - function = "function_name" - - exc = error_type(error_code, error_string, function) - - # pickle and unpickle - p = pickle.dumps(exc) - exc_unpickled: VectorError = pickle.loads(p) - - assert str(exc) == str(exc_unpickled) - assert error_code == exc_unpickled.error_code - - with pytest.raises(error_type): - raise exc_unpickled - - -def test_vector_subtype_error_from_generic() -> None: - for error_type in [VectorInitializationError, VectorOperationError]: - error_code = 118 - error_string = "XL_ERROR" - function = "function_name" - - generic = VectorError(error_code, error_string, function) - - # pickle and unpickle - specific: VectorError = error_type.from_generic(generic) - - assert str(generic) == str(specific) - assert error_code == specific.error_code - - with pytest.raises(error_type): - raise specific - - -def test_get_channel_configs() -> None: - _original_func = canlib._get_xl_driver_config - canlib._get_xl_driver_config = _get_predefined_xl_driver_config - - channel_configs = canlib.get_channel_configs() - assert len(channel_configs) == 12 - - 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 - assert canlib.INFINITE is not None - - -def test_vector_channel_config_attributes(): - assert hasattr(VectorChannelConfig, "name") - assert hasattr(VectorChannelConfig, "hwType") - assert hasattr(VectorChannelConfig, "hwIndex") - assert hasattr(VectorChannelConfig, "hwChannel") - assert hasattr(VectorChannelConfig, "channelIndex") - assert hasattr(VectorChannelConfig, "channelMask") - assert hasattr(VectorChannelConfig, "channelCapabilities") - assert hasattr(VectorChannelConfig, "channelBusCapabilities") - assert hasattr(VectorChannelConfig, "isOnBus") - assert hasattr(VectorChannelConfig, "connectedBusType") - assert hasattr(VectorChannelConfig, "serialNumber") - assert hasattr(VectorChannelConfig, "articleNumber") - assert hasattr(VectorChannelConfig, "transceiverName") - - -# ***************************************************************************** -# Utility functions -# ***************************************************************************** - - -def _find_xl_channel_config(serial: int, channel: int) -> xlclass.XLchannelConfig: - """Helper function""" - xl_driver_config = xlclass.XLdriverConfig() - canlib.xldriver.xlOpenDriver() - canlib.xldriver.xlGetDriverConfig(xl_driver_config) - canlib.xldriver.xlCloseDriver() - - for i in range(xl_driver_config.channelCount): - xl_channel_config: xlclass.XLchannelConfig = xl_driver_config.channel[i] - - if xl_channel_config.serialNumber != serial: - continue - - if xl_channel_config.hwChannel != channel: - continue - - return xl_channel_config - - raise LookupError("XLchannelConfig not found.") - - -@functools.lru_cache() -def _find_virtual_can_serial() -> int: - """Serial number might be 0 or 100 depending on driver version.""" - xl_driver_config = xlclass.XLdriverConfig() - canlib.xldriver.xlOpenDriver() - canlib.xldriver.xlGetDriverConfig(xl_driver_config) - canlib.xldriver.xlCloseDriver() - - for i in range(xl_driver_config.channelCount): - xl_channel_config: xlclass.XLchannelConfig = xl_driver_config.channel[i] - - if xl_channel_config.transceiverName.decode() == "Virtual CAN": - return xl_channel_config.serialNumber - - raise LookupError("Vector virtual CAN not found") - - -XL_DRIVER_CONFIG_EXAMPLE = ( - b"\x0E\x00\x1E\x14\x0C\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x56\x4E\x38\x39\x31\x34\x20\x43\x68\x61\x6E\x6E" - b"\x65\x6C\x20\x53\x74\x72\x65\x61\x6D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x04" - b"\x0A\x40\x00\x02\x00\x02\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x02\x0A\x04\x00\x00\x00\x00\x00\x00\x00\x8E" - b"\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE9\x03\x00\x00\x08" - b"\x1C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x4E\x38\x39\x31" - b"\x34\x20\x43\x68\x61\x6E\x6E\x65\x6C\x20\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x2D\x00\x01\x03\x02\x00\x00\x00\x00\x01\x02\x00\x00" - b"\x00\x00\x00\x00\x00\x02\x10\x00\x08\x07\x01\x04\x00\x00\x00\x00\x00\x00\x04\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x02\x0A\x04\x00" - b"\x00\x00\x00\x00\x00\x00\x8E\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\xE9\x03\x00\x00\x08\x1C\x00\x00\x46\x52\x70\x69\x67\x67\x79\x20\x31\x30" - b"\x38\x30\x41\x6D\x61\x67\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x05\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x56\x4E\x38\x39\x31\x34\x20\x43\x68\x61\x6E\x6E\x65\x6C\x20\x32\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2D\x00\x02\x3C\x01\x00" - b"\x00\x00\x00\x02\x04\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\xA2\x03\x05\x01\x00" - b"\x00\x00\x04\x00\x00\x01\x00\x00\x00\x20\xA1\x07\x00\x01\x04\x03\x01\x01\x00\x00" - b"\x00\x00\x00\x00\x00\x01\x80\x00\x00\x00\x68\x89\x09\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x0C\x00\x02\x0A\x04\x00\x00\x00\x00\x00\x00\x00\x8E\x00\x02\x0A\x00\x00\x00" - b"\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE9\x03\x00\x00\x08\x1C\x00\x00\x4F\x6E\x20" - b"\x62\x6F\x61\x72\x64\x20\x43\x41\x4E\x20\x31\x30\x35\x31\x63\x61\x70\x28\x48\x69" - b"\x67\x68\x73\x70\x65\x65\x64\x29\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03" - b"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x4E\x38\x39\x31\x34\x20\x43\x68\x61\x6E" - b"\x6E\x65\x6C\x20\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x2D\x00\x03\x3C\x01\x00\x00\x00\x00\x03\x08\x00\x00\x00\x00\x00\x00\x00\x12" - b"\x00\x00\xA2\x03\x09\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x20\xA1\x07\x00" - b"\x01\x04\x03\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x9B\x00\x00\x00\x68\x89\x09" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x02\x0A\x04\x00\x00\x00\x00\x00\x00\x00" - b"\x8E\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE9\x03\x00\x00" - b"\x08\x1C\x00\x00\x4F\x6E\x20\x62\x6F\x61\x72\x64\x20\x43\x41\x4E\x20\x31\x30\x35" - b"\x31\x63\x61\x70\x28\x48\x69\x67\x68\x73\x70\x65\x65\x64\x29\x00\x04\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x4E\x38\x39" - b"\x31\x34\x20\x43\x68\x61\x6E\x6E\x65\x6C\x20\x34\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x2D\x00\x04\x33\x01\x00\x00\x00\x00\x04\x10\x00" - b"\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x09\x02\x08\x00\x00\x00\x00\x00\x02" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x02\x0A\x03" - b"\x00\x00\x00\x00\x00\x00\x00\x8E\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x01\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\xE9\x03\x00\x00\x08\x1C\x00\x00\x4C\x49\x4E\x70\x69\x67\x67\x79\x20" - b"\x37\x32\x36\x39\x6D\x61\x67\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x07\x00\x00\x00\x70\x17\x00\x00\x0C\x09\x03\x04\x58\x02\x10\x0E\x30" - b"\x57\x05\x00\x00\x00\x00\x00\x88\x13\x88\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x56\x4E\x38\x39\x31\x34\x20\x43\x68\x61\x6E\x6E\x65\x6C\x20\x35\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2D\x00\x05\x00\x00" - b"\x00\x00\x02\x00\x05\x20\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x0C\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x00\x8E\x00\x02\x0A\x00\x00" - b"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE9\x03\x00\x00\x08\x1C\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x4E\x38\x39\x31\x34\x20\x43\x68\x61" - b"\x6E\x6E\x65\x6C\x20\x36\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x2D\x00\x06\x00\x00\x00\x00\x02\x00\x06\x40\x00\x00\x00\x00\x00\x00\x00" - b"\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x8E\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE9\x03\x00" - b"\x00\x08\x1C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x4E\x38" - b"\x39\x31\x34\x20\x43\x68\x61\x6E\x6E\x65\x6C\x20\x37\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2D\x00\x07\x00\x00\x00\x00\x02\x00\x07\x80" - b"\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x02\x0A" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x8E\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x01" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\xE9\x03\x00\x00\x08\x1C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x04\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x56\x4E\x38\x39\x31\x34\x20\x43\x68\x61\x6E\x6E\x65\x6C\x20\x38" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2D\x00\x08\x3C" - b"\x01\x00\x00\x00\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00\x12\x00\x00\xA2\x01\x00" - b"\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x20\xA1\x07\x00\x01\x04\x03\x01\x01" - b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x68\x89\x09\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x0C\x00\x02\x0A\x04\x00\x00\x00\x00\x00\x00\x00\x8E\x00\x02\x0A\x00" - b"\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE9\x03\x00\x00\x08\x1C\x00\x00\x4F" - b"\x6E\x20\x62\x6F\x61\x72\x64\x20\x43\x41\x4E\x20\x31\x30\x35\x31\x63\x61\x70\x28" - b"\x48\x69\x67\x68\x73\x70\x65\x65\x64\x29\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x4E\x38\x39\x31\x34\x20\x43\x68" - b"\x61\x6E\x6E\x65\x6C\x20\x39\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x2D\x00\x09\x80\x02\x00\x00\x00\x00\x09\x00\x02\x00\x00\x00\x00\x00" - b"\x00\x02\x00\x00\x00\x40\x00\x40\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x02\x0A\x03\x00\x00\x00\x00\x00" - b"\x00\x00\x8E\x00\x02\x0A\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE9\x03" - b"\x00\x00\x08\x1C\x00\x00\x44\x2F\x41\x20\x49\x4F\x70\x69\x67\x67\x79\x20\x38\x36" - b"\x34\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x69" - b"\x72\x74\x75\x61\x6C\x20\x43\x68\x61\x6E\x6E\x65\x6C\x20\x31\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x16\x00\x00\x00\x00\x00\x0A" - b"\x00\x04\x00\x00\x00\x00\x00\x00\x07\x00\x00\xA0\x01\x00\x01\x00\x00\x00\x00\x00" - b"\x00\x01\x00\x00\x00\x20\xA1\x07\x00\x01\x04\x03\x01\x01\x00\x00\x00\x00\x00\x00" - b"\x00\x01\x00\x00\x00\x00\x68\x89\x09\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x1E" - b"\x14\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x56\x69\x72\x74\x75\x61\x6C" - b"\x20\x43\x41\x4E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x56\x69\x72\x74\x75\x61\x6C\x20\x43\x68\x61\x6E\x6E\x65\x6C" - b"\x20\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01" - b"\x16\x00\x00\x00\x00\x00\x0B\x00\x08\x00\x00\x00\x00\x00\x00\x07\x00\x00\xA0\x01" - b"\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x20\xA1\x07\x00\x01\x04\x03\x01" - b"\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x68\x89\x09\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x10\x00\x1E\x14\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x56\x69\x72\x74\x75\x61\x6C\x20\x43\x41\x4E\x00\x00\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00" - b"\x00\x00\x00\x02" + 11832 * b"\x00" -) - - -def _get_predefined_xl_driver_config() -> xlclass.XLdriverConfig: - return xlclass.XLdriverConfig.from_buffer_copy(XL_DRIVER_CONFIG_EXAMPLE) - - -# ***************************************************************************** -# Mock functions/side effects -# ***************************************************************************** +class TestVectorBus(unittest.TestCase): + def setUp(self) -> None: + # basic mock for XLDriver + can.interfaces.vector.canlib.xldriver = Mock() + + # bus creation functions + can.interfaces.vector.canlib.xldriver.xlOpenDriver = Mock() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig = Mock( + side_effect=xlGetApplConfig + ) + can.interfaces.vector.canlib.xldriver.xlGetChannelIndex = Mock( + side_effect=xlGetChannelIndex + ) + can.interfaces.vector.canlib.xldriver.xlOpenPort = Mock(side_effect=xlOpenPort) + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration = Mock( + return_value=0 + ) + can.interfaces.vector.canlib.xldriver.xlCanSetChannelMode = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlActivateChannel = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlGetSyncTime = Mock( + side_effect=xlGetSyncTime + ) + can.interfaces.vector.canlib.xldriver.xlCanSetChannelAcceptance = Mock( + return_value=0 + ) + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate = Mock( + return_value=0 + ) + can.interfaces.vector.canlib.xldriver.xlSetNotification = Mock( + side_effect=xlSetNotification + ) + + # bus deactivation functions + can.interfaces.vector.canlib.xldriver.xlDeactivateChannel = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlClosePort = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlCloseDriver = Mock() + + # sender functions + can.interfaces.vector.canlib.xldriver.xlCanTransmit = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlCanTransmitEx = Mock(return_value=0) + + # various functions + can.interfaces.vector.canlib.xldriver.xlCanFlushTransmitQueue = Mock() + can.interfaces.vector.canlib.WaitForSingleObject = Mock() + + self.bus = None + + def tearDown(self) -> None: + if self.bus: + self.bus.shutdown() + self.bus = None + + def test_bus_creation(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION.value + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() + + def test_bus_creation_bitrate(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", bitrate=200000, _testing=True) + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION.value + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_called() + xlCanSetChannelBitrate_args = ( + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.call_args[0] + ) + self.assertEqual(xlCanSetChannelBitrate_args[2], 200000) + + def test_bus_creation_fd(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4.value, + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() + + def test_bus_creation_fd_bitrate_timings(self) -> None: + self.bus = can.Bus( + channel=0, + bustype="vector", + fd=True, + bitrate=500000, + data_bitrate=2000000, + sjw_abr=10, + tseg1_abr=11, + tseg2_abr=12, + sjw_dbr=13, + tseg1_dbr=14, + tseg2_dbr=15, + _testing=True, + ) + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4.value, + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() + + xlCanFdSetConfiguration_args = ( + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.call_args[0] + ) + canFdConf = xlCanFdSetConfiguration_args[2] + self.assertEqual(canFdConf.arbitrationBitRate, 500000) + self.assertEqual(canFdConf.dataBitRate, 2000000) + self.assertEqual(canFdConf.sjwAbr, 10) + self.assertEqual(canFdConf.tseg1Abr, 11) + self.assertEqual(canFdConf.tseg2Abr, 12) + self.assertEqual(canFdConf.sjwDbr, 13) + self.assertEqual(canFdConf.tseg1Dbr, 14) + self.assertEqual(canFdConf.tseg2Dbr, 15) + + def test_receive(self) -> None: + can.interfaces.vector.canlib.xldriver.xlReceive = Mock(side_effect=xlReceive) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() + + def test_receive_fd(self) -> None: + can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock( + side_effect=xlCanReceive + ) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() + + def test_receive_non_msg_event(self) -> None: + can.interfaces.vector.canlib.xldriver.xlReceive = Mock( + side_effect=xlReceive_chipstate + ) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + self.bus.handle_can_event = Mock() + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() + self.bus.handle_can_event.assert_called() + + def test_receive_fd_non_msg_event(self) -> None: + can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock( + side_effect=xlCanReceive_chipstate + ) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) + self.bus.handle_canfd_event = Mock() + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() + self.bus.handle_canfd_event.assert_called() + + def test_send(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + msg = can.Message( + arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True + ) + self.bus.send(msg) + can.interfaces.vector.canlib.xldriver.xlCanTransmit.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_not_called() + + def test_send_fd(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) + msg = can.Message( + arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True + ) + self.bus.send(msg) + can.interfaces.vector.canlib.xldriver.xlCanTransmit.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_called() + + def test_flush_tx_buffer(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + self.bus.flush_tx_buffer() + can.interfaces.vector.canlib.xldriver.xlCanFlushTransmitQueue.assert_called() + + def test_shutdown(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + self.bus.shutdown() + can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() + can.interfaces.vector.canlib.xldriver.xlClosePort.assert_called() + can.interfaces.vector.canlib.xldriver.xlCloseDriver.assert_called() + + def test_reset(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + self.bus.reset() + can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() + can.interfaces.vector.canlib.xldriver.xlActivateChannel.assert_called() + + def test_popup_hw_cfg(self) -> None: + canlib.xldriver.xlPopupHwConfig = Mock() + canlib.VectorBus.popup_vector_hw_configuration(10) + assert canlib.xldriver.xlPopupHwConfig.called + args, kwargs = canlib.xldriver.xlPopupHwConfig.call_args + assert isinstance(args[0], ctypes.c_char_p) + assert isinstance(args[1], ctypes.c_uint) + + def test_get_application_config(self) -> None: + canlib.xldriver.xlGetApplConfig = Mock() + canlib.VectorBus.get_application_config(app_name="CANalyzer", app_channel=0) + assert canlib.xldriver.xlGetApplConfig.called + + def test_set_application_config(self) -> None: + canlib.xldriver.xlSetApplConfig = Mock() + canlib.VectorBus.set_application_config( + app_name="CANalyzer", + app_channel=0, + hw_type=xldefine.XL_HardwareType.XL_HWTYPE_VN1610, + hw_index=0, + hw_channel=0, + ) + assert canlib.xldriver.xlSetApplConfig.called + + def test_set_timer_rate(self) -> None: + canlib.xldriver.xlSetTimerRate = Mock() + bus: canlib.VectorBus = can.Bus( + channel=0, bustype="vector", fd=True, _testing=True + ) + bus.set_timer_rate(timer_rate_ms=1) + assert canlib.xldriver.xlSetTimerRate.called + + def test_called_without_testing_argument(self) -> None: + """This tests if an exception is thrown when we are not running on Windows.""" + if os.name != "nt": + with self.assertRaises(can.CanInterfaceNotImplementedError): + # do not set the _testing argument, since it would suppress the exception + can.Bus(channel=0, bustype="vector") + + def test_vector_error_pickle(self) -> None: + for error_type in [ + VectorError, + VectorInitializationError, + VectorOperationError, + ]: + with self.subTest(f"error_type = {error_type.__name__}"): + + error_code = 118 + error_string = "XL_ERROR" + function = "function_name" + + exc = error_type(error_code, error_string, function) + + # pickle and unpickle + p = pickle.dumps(exc) + exc_unpickled: VectorError = pickle.loads(p) + + self.assertEqual(str(exc), str(exc_unpickled)) + self.assertEqual(error_code, exc_unpickled.error_code) + + with pytest.raises(error_type): + raise exc_unpickled + + def test_vector_subtype_error_from_generic(self) -> None: + for error_type in [VectorInitializationError, VectorOperationError]: + with self.subTest(f"error_type = {error_type.__name__}"): + + error_code = 118 + error_string = "XL_ERROR" + function = "function_name" + + generic = VectorError(error_code, error_string, function) + + # pickle and unpickle + specific: VectorError = error_type.from_generic(generic) + + self.assertEqual(str(generic), str(specific)) + self.assertEqual(error_code, specific.error_code) + + with pytest.raises(error_type): + raise specific + + @unittest.skipUnless(IS_WINDOWS, "Windows specific test") + def test_winapi_availability(self) -> None: + self.assertIsNotNone(canlib.WaitForSingleObject) + self.assertIsNotNone(canlib.INFINITE) + + +class TestVectorChannelConfig: + def test_attributes(self): + assert hasattr(VectorChannelConfig, "name") + assert hasattr(VectorChannelConfig, "hwType") + assert hasattr(VectorChannelConfig, "hwIndex") + assert hasattr(VectorChannelConfig, "hwChannel") + assert hasattr(VectorChannelConfig, "channelIndex") + assert hasattr(VectorChannelConfig, "channelMask") + assert hasattr(VectorChannelConfig, "channelCapabilities") + assert hasattr(VectorChannelConfig, "channelBusCapabilities") + assert hasattr(VectorChannelConfig, "isOnBus") + assert hasattr(VectorChannelConfig, "connectedBusType") + assert hasattr(VectorChannelConfig, "serialNumber") + assert hasattr(VectorChannelConfig, "articleNumber") + assert hasattr(VectorChannelConfig, "transceiverName") def xlGetApplConfig( @@ -914,3 +454,7 @@ def xlCanReceive_chipstate( event.timeStamp = 0 event.chanIndex = 2 return 0 + + +if __name__ == "__main__": + unittest.main() From 366cd45ea92d8414ba4ebb135c17a1bf72ce7037 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:31 -0600 Subject: [PATCH 35/38] Revert "VectorBus init refactoring (#1389)" This reverts commit 1e11f21189a297cc08c3e24df52dff9f762a1b60. --- can/interfaces/vector/canlib.py | 316 ++++++++++++-------------------- 1 file changed, 116 insertions(+), 200 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 333380a6f..9cecaa83d 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -21,7 +21,6 @@ Any, Dict, Callable, - cast, ) WaitForSingleObject: Optional[Callable[[int, int], int]] @@ -44,7 +43,7 @@ deprecated_args_alias, time_perfcounter_correlation, ) -from can.typechecking import AutoDetectedConfig, CanFilters +from can.typechecking import AutoDetectedConfig, CanFilters, Channel # Define Module Logger # ==================== @@ -153,7 +152,6 @@ def __init__( if xldriver is None: raise CanInterfaceNotImplementedError("The Vector API has not been loaded") self.xldriver = xldriver # keep reference so mypy knows it is not None - self.xldriver.xlOpenDriver() self.poll_interval = poll_interval @@ -167,7 +165,7 @@ def __init__( self.channels = [int(ch) for ch in channel] else: raise TypeError( - f"Invalid type for parameter 'channel': {type(channel).__name__}" + f"Invalid type for channels parameter: {type(channel).__name__}" ) self._app_name = app_name.encode() if app_name is not None else b"" @@ -176,71 +174,136 @@ def __init__( ", ".join(f"CAN {ch + 1}" for ch in self.channels), ) - channel_configs = get_channel_configs() + if serial is not None: + app_name = None + channel_index = [] + channel_configs = get_channel_configs() + for channel_config in channel_configs: + if channel_config.serialNumber == serial: + if channel_config.hwChannel in self.channels: + channel_index.append(channel_config.channelIndex) + if channel_index: + if len(channel_index) != len(self.channels): + LOG.info( + "At least one defined channel wasn't found on the specified hardware." + ) + self.channels = channel_index + else: + # Is there any better way to raise the error? + raise CanInitializationError( + "None of the configured channels could be found on the specified hardware." + ) + self.xldriver.xlOpenDriver() + self.port_handle = xlclass.XLportHandle(xldefine.XL_INVALID_PORTHANDLE) self.mask = 0 self.fd = fd - self.channel_masks: Dict[int, int] = {} - self.index_to_channel: Dict[int, int] = {} + # Get channels masks + self.channel_masks: Dict[Optional[Channel], int] = {} + self.index_to_channel = {} for channel in self.channels: - 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 - self.channel_masks[channel] = channel_mask - self.index_to_channel[channel_index] = channel - self.mask |= channel_mask + if app_name: + # Get global channel index from application channel + hw_type, hw_index, hw_channel = self.get_application_config( + app_name, channel + ) + LOG.debug("Channel index %d found", channel) + idx = self.xldriver.xlGetChannelIndex(hw_type, hw_index, hw_channel) + if idx < 0: + # Undocumented behavior! See issue #353. + # If hardware is unavailable, this function returns -1. + # Raise an exception as if the driver + # would have signalled XL_ERR_HW_NOT_PRESENT. + raise VectorInitializationError( + xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT, + xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT.name, + "xlGetChannelIndex", + ) + else: + # Channel already given as global channel + idx = channel + mask = 1 << idx + self.channel_masks[channel] = mask + self.index_to_channel[idx] = channel + self.mask |= mask permission_mask = xlclass.XLaccess() # Set mask to request channel init permission if needed if bitrate or fd: permission_mask.value = self.mask - - interface_version = ( - xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4 - if fd - else xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION - ) - - self.port_handle = xlclass.XLportHandle(xldefine.XL_INVALID_PORTHANDLE) - self.xldriver.xlOpenPort( - self.port_handle, - self._app_name, - self.mask, - permission_mask, - rx_queue_size, - interface_version, - xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, - ) - + if fd: + self.xldriver.xlOpenPort( + self.port_handle, + self._app_name, + self.mask, + permission_mask, + rx_queue_size, + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4, + xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, + ) + else: + self.xldriver.xlOpenPort( + self.port_handle, + self._app_name, + self.mask, + permission_mask, + rx_queue_size, + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION, + xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, + ) LOG.debug( "Open Port: PortHandle: %d, PermissionMask: 0x%X", self.port_handle.value, permission_mask.value, ) - for channel in self.channels: - if permission_mask.value & self.channel_masks[channel]: - if fd: - self._set_bitrate_canfd( - channel=channel, - bitrate=bitrate, - data_bitrate=data_bitrate, - sjw_abr=sjw_abr, - tseg1_abr=tseg1_abr, - tseg2_abr=tseg2_abr, - sjw_dbr=sjw_dbr, - tseg1_dbr=tseg1_dbr, - tseg2_dbr=tseg2_dbr, + if permission_mask.value == self.mask: + if fd: + self.canFdConf = xlclass.XLcanFdConf() + if bitrate: + self.canFdConf.arbitrationBitRate = int(bitrate) + else: + self.canFdConf.arbitrationBitRate = 500000 + self.canFdConf.sjwAbr = int(sjw_abr) + self.canFdConf.tseg1Abr = int(tseg1_abr) + self.canFdConf.tseg2Abr = int(tseg2_abr) + if data_bitrate: + self.canFdConf.dataBitRate = int(data_bitrate) + else: + self.canFdConf.dataBitRate = self.canFdConf.arbitrationBitRate + self.canFdConf.sjwDbr = int(sjw_dbr) + self.canFdConf.tseg1Dbr = int(tseg1_dbr) + self.canFdConf.tseg2Dbr = int(tseg2_dbr) + + self.xldriver.xlCanFdSetConfiguration( + self.port_handle, self.mask, self.canFdConf + ) + LOG.info( + "SetFdConfig.: ABaudr.=%u, DBaudr.=%u", + self.canFdConf.arbitrationBitRate, + self.canFdConf.dataBitRate, + ) + LOG.info( + "SetFdConfig.: sjwAbr=%u, tseg1Abr=%u, tseg2Abr=%u", + self.canFdConf.sjwAbr, + self.canFdConf.tseg1Abr, + self.canFdConf.tseg2Abr, + ) + LOG.info( + "SetFdConfig.: sjwDbr=%u, tseg1Dbr=%u, tseg2Dbr=%u", + self.canFdConf.sjwDbr, + self.canFdConf.tseg1Dbr, + self.canFdConf.tseg2Dbr, + ) + else: + if bitrate: + self.xldriver.xlCanSetChannelBitrate( + self.port_handle, permission_mask, bitrate ) - elif bitrate: - self._set_bitrate_can(channel=channel, bitrate=bitrate) + LOG.info("SetChannelBitrate: baudr.=%u", bitrate) + else: + LOG.info("No init access!") # Enable/disable TX receipts tx_receipts = 1 if receive_own_messages else 0 @@ -285,153 +348,6 @@ def __init__( self._is_filtered = False super().__init__(channel=channel, can_filters=can_filters, **kwargs) - def _find_global_channel_idx( - self, - channel: int, - serial: Optional[int], - app_name: Optional[str], - channel_configs: List["VectorChannelConfig"], - ) -> int: - if serial is not None: - hw_type: Optional[xldefine.XL_HardwareType] = None - for channel_config in channel_configs: - if channel_config.serialNumber != serial: - continue - - hw_type = xldefine.XL_HardwareType(channel_config.hwType) - if channel_config.hwChannel == channel: - return channel_config.channelIndex - - if hw_type is None: - err_msg = f"No interface with serial {serial} found." - else: - err_msg = f"Channel {channel} not found on interface {hw_type.name} ({serial})." - raise CanInitializationError( - err_msg, error_code=xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT - ) - - if app_name: - hw_type, hw_index, hw_channel = self.get_application_config( - app_name, channel - ) - idx = cast( - int, self.xldriver.xlGetChannelIndex(hw_type, hw_index, hw_channel) - ) - if idx < 0: - # Undocumented behavior! See issue #353. - # If hardware is unavailable, this function returns -1. - # Raise an exception as if the driver - # would have signalled XL_ERR_HW_NOT_PRESENT. - raise VectorInitializationError( - xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT, - xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT.name, - "xlGetChannelIndex", - ) - return idx - - # check if channel is a valid global channel index - for channel_config in channel_configs: - if channel == channel_config.channelIndex: - return channel - - raise CanInitializationError( - f"Channel {channel} not found. The 'channel' parameter must be " - f"a valid global channel index if neither 'app_name' nor 'serial' were given.", - error_code=xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT, - ) - - def _set_bitrate_can( - self, - channel: int, - bitrate: int, - sjw: Optional[int] = None, - tseg1: Optional[int] = None, - tseg2: Optional[int] = None, - sam: int = 1, - ) -> None: - kwargs = [sjw, tseg1, tseg2] - if any(kwargs) and not all(kwargs): - raise ValueError( - f"Either all of sjw, tseg1, tseg2 must be set or none of them." - ) - - # set parameters if channel has init access - if any(kwargs): - chip_params = xlclass.XLchipParams() - chip_params.bitRate = bitrate - chip_params.sjw = sjw - chip_params.tseg1 = tseg1 - chip_params.tseg2 = tseg2 - chip_params.sam = sam - self.xldriver.xlCanSetChannelParams( - self.port_handle, - self.channel_masks[channel], - chip_params, - ) - LOG.info( - "xlCanSetChannelParams: baudr.=%u, sjwAbr=%u, tseg1Abr=%u, tseg2Abr=%u", - chip_params.bitRate, - chip_params.sjw, - chip_params.tseg1, - chip_params.tseg2, - ) - else: - self.xldriver.xlCanSetChannelBitrate( - self.port_handle, - self.channel_masks[channel], - bitrate, - ) - LOG.info("xlCanSetChannelBitrate: baudr.=%u", bitrate) - - def _set_bitrate_canfd( - self, - channel: int, - bitrate: Optional[int] = None, - data_bitrate: Optional[int] = None, - sjw_abr: int = 2, - tseg1_abr: int = 6, - tseg2_abr: int = 3, - sjw_dbr: int = 2, - tseg1_dbr: int = 6, - tseg2_dbr: int = 3, - ) -> None: - # set parameters if channel has init access - canfd_conf = xlclass.XLcanFdConf() - if bitrate: - canfd_conf.arbitrationBitRate = int(bitrate) - else: - canfd_conf.arbitrationBitRate = 500_000 - canfd_conf.sjwAbr = int(sjw_abr) - canfd_conf.tseg1Abr = int(tseg1_abr) - canfd_conf.tseg2Abr = int(tseg2_abr) - if data_bitrate: - canfd_conf.dataBitRate = int(data_bitrate) - else: - canfd_conf.dataBitRate = int(canfd_conf.arbitrationBitRate) - canfd_conf.sjwDbr = int(sjw_dbr) - canfd_conf.tseg1Dbr = int(tseg1_dbr) - canfd_conf.tseg2Dbr = int(tseg2_dbr) - self.xldriver.xlCanFdSetConfiguration( - self.port_handle, self.channel_masks[channel], canfd_conf - ) - LOG.info( - "xlCanFdSetConfiguration.: ABaudr.=%u, DBaudr.=%u", - canfd_conf.arbitrationBitRate, - canfd_conf.dataBitRate, - ) - LOG.info( - "xlCanFdSetConfiguration.: sjwAbr=%u, tseg1Abr=%u, tseg2Abr=%u", - canfd_conf.sjwAbr, - canfd_conf.tseg1Abr, - canfd_conf.tseg2Abr, - ) - LOG.info( - "xlCanFdSetConfiguration.: sjwDbr=%u, tseg1Dbr=%u, tseg2Dbr=%u", - canfd_conf.sjwDbr, - canfd_conf.tseg1Dbr, - canfd_conf.tseg2Dbr, - ) - def _apply_filters(self, filters: Optional[CanFilters]) -> None: if filters: # Only up to one filter per ID type allowed @@ -628,7 +544,7 @@ def _send_sequence(self, msgs: Sequence[Message]) -> int: def _get_tx_channel_mask(self, msgs: Sequence[Message]) -> int: if len(msgs) == 1: - return self.channel_masks.get(msgs[0].channel, self.mask) # type: ignore[arg-type] + return self.channel_masks.get(msgs[0].channel, self.mask) else: return self.mask From b7217416041388419507caaba27a60596c173531 Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:31 -0600 Subject: [PATCH 36/38] Revert "extend XL api wrapper (#1387)" This reverts commit 40f6cce796ef5969764e18e746d0dab8d1adab63. --- can/interfaces/vector/xlclass.py | 13 +------------ can/interfaces/vector/xldefine.py | 8 +------- can/interfaces/vector/xldriver.py | 15 --------------- 3 files changed, 2 insertions(+), 34 deletions(-) diff --git a/can/interfaces/vector/xlclass.py b/can/interfaces/vector/xlclass.py index 6441ad4e5..c0ef9fa48 100644 --- a/can/interfaces/vector/xlclass.py +++ b/can/interfaces/vector/xlclass.py @@ -45,13 +45,6 @@ class s_xl_chip_state(ctypes.Structure): ] -class s_xl_sync_pulse(ctypes.Structure): - _fields_ = [ - ("pulseCode", ctypes.c_ubyte), - ("time", XLuint64), - ] - - class s_xl_can_ev_chip_state(ctypes.Structure): _fields_ = [ ("busStatus", ctypes.c_ubyte), @@ -72,11 +65,7 @@ class s_xl_can_ev_sync_pulse(ctypes.Structure): # BASIC bus message structure class s_xl_tag_data(ctypes.Union): - _fields_ = [ - ("msg", s_xl_can_msg), - ("chipState", s_xl_chip_state), - ("syncPulse", s_xl_sync_pulse), - ] + _fields_ = [("msg", s_xl_can_msg), ("chipState", s_xl_chip_state)] # CAN FD messages diff --git a/can/interfaces/vector/xldefine.py b/can/interfaces/vector/xldefine.py index 5a1084f48..032f08318 100644 --- a/can/interfaces/vector/xldefine.py +++ b/can/interfaces/vector/xldefine.py @@ -64,7 +64,7 @@ class XL_BusTypes(IntFlag): XL_BUS_TYPE_A429 = 8192 # =0x00002000 -class XL_CANFD_BusParams_CanOpMode(IntFlag): +class XL_CANFD_BusParams_CanOpMode(IntEnum): XL_BUS_PARAMS_CANOPMODE_CAN20 = 1 XL_BUS_PARAMS_CANOPMODE_CANFD = 2 XL_BUS_PARAMS_CANOPMODE_CANFD_NO_ISO = 8 @@ -318,9 +318,3 @@ class XL_HardwareType(IntEnum): XL_HWTYPE_VX1161A = 114 XL_HWTYPE_VX1161B = 115 XL_MAX_HWTYPE = 120 - - -class XL_SyncPulseSource(IntEnum): - XL_SYNC_PULSE_EXTERNAL = 0 - XL_SYNC_PULSE_OUR = 1 - XL_SYNC_PULSE_OUR_SHARED = 2 diff --git a/can/interfaces/vector/xldriver.py b/can/interfaces/vector/xldriver.py index 8df39e9dc..3243fa4a0 100644 --- a/can/interfaces/vector/xldriver.py +++ b/can/interfaces/vector/xldriver.py @@ -272,18 +272,3 @@ def check_status_initialization(result, function, arguments): xlCanGetEventString = _xlapi_dll.xlCanGetEventString xlCanGetEventString.argtypes = [ctypes.POINTER(xlclass.XLcanRxEvent)] xlCanGetEventString.restype = xlclass.XLstringType - -xlGetReceiveQueueLevel = _xlapi_dll.xlGetReceiveQueueLevel -xlGetReceiveQueueLevel.argtypes = [xlclass.XLportHandle, ctypes.POINTER(ctypes.c_int)] -xlGetReceiveQueueLevel.restype = xlclass.XLstatus -xlGetReceiveQueueLevel.errcheck = check_status_operation - -xlGenerateSyncPulse = _xlapi_dll.xlGenerateSyncPulse -xlGenerateSyncPulse.argtypes = [xlclass.XLportHandle, xlclass.XLaccess] -xlGenerateSyncPulse.restype = xlclass.XLstatus -xlGenerateSyncPulse.errcheck = check_status_operation - -xlFlushReceiveQueue = _xlapi_dll.xlFlushReceiveQueue -xlFlushReceiveQueue.argtypes = [xlclass.XLportHandle] -xlFlushReceiveQueue.restype = xlclass.XLstatus -xlFlushReceiveQueue.errcheck = check_status_operation From ceaed9daed4ecd69e1175d7094a599faea7dec3a Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:32 -0600 Subject: [PATCH 37/38] Revert "socketcan: Make find_available_interfaces() find slcanX interfaces (#1369)" This reverts commit ad8b9482ba9fe76f1a4b746f20a9e7a216f6d9f9. --- can/interfaces/socketcan/utils.py | 2 +- doc/interfaces/socketcan.rst | 22 ---------------------- test/open_vcan.sh | 4 ---- test/test_socketcan_helpers.py | 6 ++---- 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/can/interfaces/socketcan/utils.py b/can/interfaces/socketcan/utils.py index 55e7eb392..b718bb69e 100644 --- a/can/interfaces/socketcan/utils.py +++ b/can/interfaces/socketcan/utils.py @@ -38,7 +38,7 @@ def pack_filters(can_filters: Optional[typechecking.CanFilters] = None) -> bytes return struct.pack(can_filter_fmt, *filter_data) -_PATTERN_CAN_INTERFACE = re.compile(r"(sl|v|vx)?can\d+") +_PATTERN_CAN_INTERFACE = re.compile(r"v?can\d+") def find_available_interfaces() -> Iterable[str]: diff --git a/doc/interfaces/socketcan.rst b/doc/interfaces/socketcan.rst index 1e82d8827..d3a583d75 100644 --- a/doc/interfaces/socketcan.rst +++ b/doc/interfaces/socketcan.rst @@ -57,28 +57,6 @@ existing ``can0`` interface with a bitrate of 1MB: sudo ip link set can0 up type can bitrate 1000000 -CAN over Serial / SLCAN -~~~~~~~~~~~~~~~~~~~~~~~ - -SLCAN adapters can be used directly via :doc:`/interfaces/slcan`, or -via :doc:`/interfaces/socketcan` with some help from the ``slcand`` utility -which can be found in the `can-utils `_ package. - -To create a socketcan interface for an SLCAN adapter run the following: - -.. code-block:: bash - - slcand -f -o -c -s5 /dev/ttyAMA0 - ip link set up slcan0 - -Names of the interfaces created by ``slcand`` match the ``slcan\d+`` regex. -If a custom name is required, it can be specified as the last argument. E.g.: - -.. code-block:: bash - - slcand -f -o -c -s5 /dev/ttyAMA0 can0 - ip link set up can0 - .. _socketcan-pcan: PCAN diff --git a/test/open_vcan.sh b/test/open_vcan.sh index b6f4676b7..bd02ad752 100755 --- a/test/open_vcan.sh +++ b/test/open_vcan.sh @@ -5,7 +5,3 @@ modprobe vcan ip link add dev vcan0 type vcan ip link set up vcan0 mtu 72 -ip link add dev vxcan0 type vcan -ip link set up vxcan0 mtu 72 -ip link add dev slcan0 type vcan -ip link set up slcan0 mtu 72 diff --git a/test/test_socketcan_helpers.py b/test/test_socketcan_helpers.py index ad53836f2..f3fbe6d26 100644 --- a/test/test_socketcan_helpers.py +++ b/test/test_socketcan_helpers.py @@ -31,12 +31,10 @@ def test_find_available_interfaces(self): result = list(find_available_interfaces()) self.assertGreaterEqual(len(result), 0) for entry in result: - self.assertRegex(entry, r"(sl|v|vx)?can\d+") + self.assertRegex(entry, r"v?can\d+") if TEST_INTERFACE_SOCKETCAN: - self.assertGreaterEqual(len(result), 3) + self.assertGreaterEqual(len(result), 1) self.assertIn("vcan0", result) - self.assertIn("vxcan0", result) - self.assertIn("slcan0", result) if __name__ == "__main__": From f6edc4aab96bbb78536708effbfbf9260da8736e Mon Sep 17 00:00:00 2001 From: j-c-cook Date: Fri, 11 Nov 2022 15:36:32 -0600 Subject: [PATCH 38/38] Revert "Fix _default_name for compressed files (#1383)" This reverts commit 9d9cb16f474f837bd46eabb1350ecf86e8d2c42b. --- can/io/logger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 478651953..b50825d3f 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -345,11 +345,11 @@ def _default_name(self) -> StringPathLike: """Generate the default rotation filename.""" path = pathlib.Path(self.base_filename) new_name = ( - path.stem.split(".")[0] + path.stem + "_" + datetime.now().strftime("%Y-%m-%dT%H%M%S") + "_" + f"#{self.rollover_count:03}" - + "".join(path.suffixes[-2:]) + + path.suffix ) return str(path.parent / new_name)