From 76de62528ea1ef5f9951a1a55a8d679030e471d7 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 16 Aug 2021 10:49:04 +0200 Subject: [PATCH 01/22] Added: Ixxat interface copy before start changing for canfd. --- can/interfaces/ixxat_fd/__init__.py | 7 + can/interfaces/ixxat_fd/canlib.py | 848 ++++++++++++++++++++++++++ can/interfaces/ixxat_fd/constants.py | 148 +++++ can/interfaces/ixxat_fd/exceptions.py | 44 ++ can/interfaces/ixxat_fd/structures.py | 166 +++++ 5 files changed, 1213 insertions(+) create mode 100644 can/interfaces/ixxat_fd/__init__.py create mode 100644 can/interfaces/ixxat_fd/canlib.py create mode 100644 can/interfaces/ixxat_fd/constants.py create mode 100644 can/interfaces/ixxat_fd/exceptions.py create mode 100644 can/interfaces/ixxat_fd/structures.py diff --git a/can/interfaces/ixxat_fd/__init__.py b/can/interfaces/ixxat_fd/__init__.py new file mode 100644 index 000000000..fc2cae0f3 --- /dev/null +++ b/can/interfaces/ixxat_fd/__init__.py @@ -0,0 +1,7 @@ +""" +Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + +Copyright (C) 2016 Giuseppe Corbelli +""" + +from can.interfaces.ixxat.canlib import IXXATBus, get_ixxat_hwids diff --git a/can/interfaces/ixxat_fd/canlib.py b/can/interfaces/ixxat_fd/canlib.py new file mode 100644 index 000000000..13709c71c --- /dev/null +++ b/can/interfaces/ixxat_fd/canlib.py @@ -0,0 +1,848 @@ +""" +Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + +TODO: We could implement this interface such that setting other filters + could work when the initial filters were set to zero using the + software fallback. Or could the software filters even be changed + after the connection was opened? We need to document that bahaviour! + See also the NICAN interface. + +""" + +import ctypes +import functools +import logging +import sys +from typing import Optional + +from can import BusABC, Message +from can.exceptions import CanInterfaceNotImplementedError, CanInitializationError +from can.broadcastmanager import ( + LimitedDurationCyclicSendTaskABC, + RestartableCyclicTaskABC, +) +from can.ctypesutil import CLibrary, HANDLE, PHANDLE, HRESULT as ctypes_HRESULT + +from . import constants, structures +from .exceptions import * + +__all__ = [ + "VCITimeout", + "VCIError", + "VCIBusOffError", + "VCIDeviceNotFoundError", + "IXXATBus", + "vciFormatError", +] + +log = logging.getLogger("can.ixxat") + +from time import perf_counter as _timer_function + +# Hack to have vciFormatError as a free function, see below +vciFormatError = None + +# main ctypes instance +_canlib = None +# TODO: Use ECI driver for linux +if sys.platform == "win32" or sys.platform == "cygwin": + try: + _canlib = CLibrary("vcinpl.dll") + except Exception as e: + log.warning("Cannot load IXXAT vcinpl library: %s", e) +else: + # Will not work on other systems, but have it importable anyway for + # tests/sphinx + log.warning("IXXAT VCI library does not work on %s platform", sys.platform) + + +def __vciFormatErrorExtended(library_instance, function, HRESULT, arguments): + """Format a VCI error and attach failed function, decoded HRESULT and arguments + :param CLibrary library_instance: + Mapped instance of IXXAT vcinpl library + :param callable function: + Failed function + :param HRESULT HRESULT: + HRESULT returned by vcinpl call + :param arguments: + Arbitrary arguments tuple + :return: + Formatted string + """ + # TODO: make sure we don't generate another exception + return "{} - arguments were {}".format( + __vciFormatError(library_instance, function, HRESULT), arguments + ) + + +def __vciFormatError(library_instance, function, HRESULT): + """Format a VCI error and attach failed function and decoded HRESULT + :param CLibrary library_instance: + Mapped instance of IXXAT vcinpl library + :param callable function: + Failed function + :param HRESULT HRESULT: + HRESULT returned by vcinpl call + :return: + Formatted string + """ + buf = ctypes.create_string_buffer(constants.VCI_MAX_ERRSTRLEN) + ctypes.memset(buf, 0, constants.VCI_MAX_ERRSTRLEN) + library_instance.vciFormatError(HRESULT, buf, constants.VCI_MAX_ERRSTRLEN) + return "function {} failed ({})".format( + function._name, buf.value.decode("utf-8", "replace") + ) + + +def __check_status(result, function, arguments): + """ + Check the result of a vcinpl function call and raise appropriate exception + in case of an error. Used as errcheck function when mapping C functions + with ctypes. + :param result: + Function call numeric result + :param callable function: + Called function + :param arguments: + Arbitrary arguments tuple + :raise: + :class:VCITimeout + :class:VCIRxQueueEmptyError + :class:StopIteration + :class:VCIError + """ + if isinstance(result, int): + # Real return value is an unsigned long + result = ctypes.c_ulong(result).value + + if result == constants.VCI_E_TIMEOUT: + raise VCITimeout("Function {} timed out".format(function._name)) + elif result == constants.VCI_E_RXQUEUE_EMPTY: + raise VCIRxQueueEmptyError() + elif result == constants.VCI_E_NO_MORE_ITEMS: + raise StopIteration() + elif result == constants.VCI_E_ACCESSDENIED: + pass # not a real error, might happen if another program has initialized the bus + elif result != constants.VCI_OK: + raise VCIError(vciFormatError(function, result)) + + return result + + +try: + # Map all required symbols and initialize library --------------------------- + # HRESULT VCIAPI vciInitialize ( void ); + _canlib.map_symbol("vciInitialize", ctypes.c_long, (), __check_status) + + # void VCIAPI vciFormatError (HRESULT hrError, PCHAR pszText, UINT32 dwsize); + _canlib.map_symbol( + "vciFormatError", None, (ctypes_HRESULT, ctypes.c_char_p, ctypes.c_uint32) + ) + # Hack to have vciFormatError as a free function + vciFormatError = functools.partial(__vciFormatError, _canlib) + + # HRESULT VCIAPI vciEnumDeviceOpen( OUT PHANDLE hEnum ); + _canlib.map_symbol("vciEnumDeviceOpen", ctypes.c_long, (PHANDLE,), __check_status) + # HRESULT VCIAPI vciEnumDeviceClose ( IN HANDLE hEnum ); + _canlib.map_symbol("vciEnumDeviceClose", ctypes.c_long, (HANDLE,), __check_status) + # HRESULT VCIAPI vciEnumDeviceNext( IN HANDLE hEnum, OUT PVCIDEVICEINFO pInfo ); + _canlib.map_symbol( + "vciEnumDeviceNext", + ctypes.c_long, + (HANDLE, structures.PVCIDEVICEINFO), + __check_status, + ) + + # HRESULT VCIAPI vciDeviceOpen( IN REFVCIID rVciid, OUT PHANDLE phDevice ); + _canlib.map_symbol( + "vciDeviceOpen", ctypes.c_long, (structures.PVCIID, PHANDLE), __check_status + ) + # HRESULT vciDeviceClose( HANDLE hDevice ) + _canlib.map_symbol("vciDeviceClose", ctypes.c_long, (HANDLE,), __check_status) + + # HRESULT VCIAPI canChannelOpen( IN HANDLE hDevice, IN UINT32 dwCanNo, IN BOOL fExclusive, OUT PHANDLE phCanChn ); + _canlib.map_symbol( + "canChannelOpen", + ctypes.c_long, + (HANDLE, ctypes.c_uint32, ctypes.c_long, PHANDLE), + __check_status, + ) + # EXTERN_C HRESULT VCIAPI canChannelInitialize( IN HANDLE hCanChn, IN UINT16 wRxFifoSize, IN UINT16 wRxThreshold, IN UINT16 wTxFifoSize, IN UINT16 wTxThreshold ); + _canlib.map_symbol( + "canChannelInitialize", + ctypes.c_long, + (HANDLE, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16), + __check_status, + ) + # EXTERN_C HRESULT VCIAPI canChannelActivate( IN HANDLE hCanChn, IN BOOL fEnable ); + _canlib.map_symbol( + "canChannelActivate", ctypes.c_long, (HANDLE, ctypes.c_long), __check_status + ) + # HRESULT canChannelClose( HANDLE hChannel ) + _canlib.map_symbol("canChannelClose", ctypes.c_long, (HANDLE,), __check_status) + # EXTERN_C HRESULT VCIAPI canChannelReadMessage( IN HANDLE hCanChn, IN UINT32 dwMsTimeout, OUT PCANMSG pCanMsg ); + _canlib.map_symbol( + "canChannelReadMessage", + ctypes.c_long, + (HANDLE, ctypes.c_uint32, structures.PCANMSG), + __check_status, + ) + # HRESULT canChannelPeekMessage(HANDLE hChannel,PCANMSG pCanMsg ); + _canlib.map_symbol( + "canChannelPeekMessage", + ctypes.c_long, + (HANDLE, structures.PCANMSG), + __check_status, + ) + # HRESULT canChannelWaitTxEvent (HANDLE hChannel UINT32 dwMsTimeout ); + _canlib.map_symbol( + "canChannelWaitTxEvent", + ctypes.c_long, + (HANDLE, ctypes.c_uint32), + __check_status, + ) + # HRESULT canChannelWaitRxEvent (HANDLE hChannel, UINT32 dwMsTimeout ); + _canlib.map_symbol( + "canChannelWaitRxEvent", + ctypes.c_long, + (HANDLE, ctypes.c_uint32), + __check_status, + ) + # HRESULT canChannelPostMessage (HANDLE hChannel, PCANMSG pCanMsg ); + _canlib.map_symbol( + "canChannelPostMessage", + ctypes.c_long, + (HANDLE, structures.PCANMSG), + __check_status, + ) + # HRESULT canChannelSendMessage (HANDLE hChannel, UINT32 dwMsTimeout, PCANMSG pCanMsg ); + _canlib.map_symbol( + "canChannelSendMessage", + ctypes.c_long, + (HANDLE, ctypes.c_uint32, structures.PCANMSG), + __check_status, + ) + + # EXTERN_C HRESULT VCIAPI canControlOpen( IN HANDLE hDevice, IN UINT32 dwCanNo, OUT PHANDLE phCanCtl ); + _canlib.map_symbol( + "canControlOpen", + ctypes.c_long, + (HANDLE, ctypes.c_uint32, PHANDLE), + __check_status, + ) + # EXTERN_C HRESULT VCIAPI canControlInitialize( IN HANDLE hCanCtl, IN UINT8 bMode, IN UINT8 bBtr0, IN UINT8 bBtr1 ); + _canlib.map_symbol( + "canControlInitialize", + ctypes.c_long, + (HANDLE, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint8), + __check_status, + ) + # EXTERN_C HRESULT VCIAPI canControlClose( IN HANDLE hCanCtl ); + _canlib.map_symbol("canControlClose", ctypes.c_long, (HANDLE,), __check_status) + # EXTERN_C HRESULT VCIAPI canControlReset( IN HANDLE hCanCtl ); + _canlib.map_symbol("canControlReset", ctypes.c_long, (HANDLE,), __check_status) + # EXTERN_C HRESULT VCIAPI canControlStart( IN HANDLE hCanCtl, IN BOOL fStart ); + _canlib.map_symbol( + "canControlStart", ctypes.c_long, (HANDLE, ctypes.c_long), __check_status + ) + # EXTERN_C HRESULT VCIAPI canControlGetStatus( IN HANDLE hCanCtl, OUT PCANLINESTATUS pStatus ); + _canlib.map_symbol( + "canControlGetStatus", + ctypes.c_long, + (HANDLE, structures.PCANLINESTATUS), + __check_status, + ) + # EXTERN_C HRESULT VCIAPI canControlGetCaps( IN HANDLE hCanCtl, OUT PCANCAPABILITIES pCanCaps ); + _canlib.map_symbol( + "canControlGetCaps", + ctypes.c_long, + (HANDLE, structures.PCANCAPABILITIES), + __check_status, + ) + # EXTERN_C HRESULT VCIAPI canControlSetAccFilter( IN HANDLE hCanCtl, IN BOOL fExtend, IN UINT32 dwCode, IN UINT32 dwMask ); + _canlib.map_symbol( + "canControlSetAccFilter", + ctypes.c_long, + (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), + __check_status, + ) + # EXTERN_C HRESULT canControlAddFilterIds (HANDLE hControl, BOOL fExtended, UINT32 dwCode, UINT32 dwMask); + _canlib.map_symbol( + "canControlAddFilterIds", + ctypes.c_long, + (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), + __check_status, + ) + # EXTERN_C HRESULT canControlRemFilterIds (HANDLE hControl, BOOL fExtendend, UINT32 dwCode, UINT32 dwMask ); + _canlib.map_symbol( + "canControlRemFilterIds", + ctypes.c_long, + (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), + __check_status, + ) + # EXTERN_C HRESULT canSchedulerOpen (HANDLE hDevice, UINT32 dwCanNo, PHANDLE phScheduler ); + _canlib.map_symbol( + "canSchedulerOpen", + ctypes.c_long, + (HANDLE, ctypes.c_uint32, PHANDLE), + __check_status, + ) + # EXTERN_C HRESULT canSchedulerClose (HANDLE hScheduler ); + _canlib.map_symbol("canSchedulerClose", ctypes.c_long, (HANDLE,), __check_status) + # EXTERN_C HRESULT canSchedulerGetCaps (HANDLE hScheduler, PCANCAPABILITIES pCaps ); + _canlib.map_symbol( + "canSchedulerGetCaps", + ctypes.c_long, + (HANDLE, structures.PCANCAPABILITIES), + __check_status, + ) + # EXTERN_C HRESULT canSchedulerActivate ( HANDLE hScheduler, BOOL fEnable ); + _canlib.map_symbol( + "canSchedulerActivate", ctypes.c_long, (HANDLE, ctypes.c_int), __check_status + ) + # EXTERN_C HRESULT canSchedulerAddMessage (HANDLE hScheduler, PCANCYCLICTXMSG pMessage, PUINT32 pdwIndex ); + _canlib.map_symbol( + "canSchedulerAddMessage", + ctypes.c_long, + (HANDLE, structures.PCANCYCLICTXMSG, ctypes.POINTER(ctypes.c_uint32)), + __check_status, + ) + # EXTERN_C HRESULT canSchedulerRemMessage (HANDLE hScheduler, UINT32 dwIndex ); + _canlib.map_symbol( + "canSchedulerRemMessage", + ctypes.c_long, + (HANDLE, ctypes.c_uint32), + __check_status, + ) + # EXTERN_C HRESULT canSchedulerStartMessage (HANDLE hScheduler, UINT32 dwIndex, UINT16 dwCount ); + _canlib.map_symbol( + "canSchedulerStartMessage", + ctypes.c_long, + (HANDLE, ctypes.c_uint32, ctypes.c_uint16), + __check_status, + ) + # EXTERN_C HRESULT canSchedulerStopMessage (HANDLE hScheduler, UINT32 dwIndex ); + _canlib.map_symbol( + "canSchedulerStopMessage", + ctypes.c_long, + (HANDLE, ctypes.c_uint32), + __check_status, + ) + _canlib.vciInitialize() +except AttributeError: + # In case _canlib == None meaning we're not on win32/no lib found + pass +except Exception as e: + log.warning("Could not initialize IXXAT VCI library: %s", e) +# --------------------------------------------------------------------------- + + +CAN_INFO_MESSAGES = { + constants.CAN_INFO_START: "CAN started", + constants.CAN_INFO_STOP: "CAN stopped", + constants.CAN_INFO_RESET: "CAN reset", +} + +CAN_ERROR_MESSAGES = { + constants.CAN_ERROR_STUFF: "CAN bit stuff error", + constants.CAN_ERROR_FORM: "CAN form error", + constants.CAN_ERROR_ACK: "CAN acknowledgment error", + constants.CAN_ERROR_BIT: "CAN bit error", + constants.CAN_ERROR_CRC: "CAN CRC error", + constants.CAN_ERROR_OTHER: "Other (unknown) CAN error", +} + +CAN_STATUS_FLAGS = { + constants.CAN_STATUS_TXPEND: "transmission pending", + constants.CAN_STATUS_OVRRUN: "data overrun occurred", + constants.CAN_STATUS_ERRLIM: "error warning limit exceeded", + constants.CAN_STATUS_BUSOFF: "bus off", + constants.CAN_STATUS_ININIT: "init mode active", + constants.CAN_STATUS_BUSCERR: "bus coupling error", +} +# ---------------------------------------------------------------------------- + + +class IXXATBus(BusABC): + """The CAN Bus implemented for the IXXAT interface. + + .. warning:: + + This interface does implement efficient filtering of messages, but + 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 = { + 0: { + 10000: constants.CAN_BT0_10KB, + 20000: constants.CAN_BT0_20KB, + 50000: constants.CAN_BT0_50KB, + 100000: constants.CAN_BT0_100KB, + 125000: constants.CAN_BT0_125KB, + 250000: constants.CAN_BT0_250KB, + 500000: constants.CAN_BT0_500KB, + 666000: constants.CAN_BT0_667KB, + 666666: constants.CAN_BT0_667KB, + 666667: constants.CAN_BT0_667KB, + 667000: constants.CAN_BT0_667KB, + 800000: constants.CAN_BT0_800KB, + 1000000: constants.CAN_BT0_1000KB, + }, + 1: { + 10000: constants.CAN_BT1_10KB, + 20000: constants.CAN_BT1_20KB, + 50000: constants.CAN_BT1_50KB, + 100000: constants.CAN_BT1_100KB, + 125000: constants.CAN_BT1_125KB, + 250000: constants.CAN_BT1_250KB, + 500000: constants.CAN_BT1_500KB, + 666000: constants.CAN_BT1_667KB, + 666666: constants.CAN_BT1_667KB, + 666667: constants.CAN_BT1_667KB, + 667000: constants.CAN_BT1_667KB, + 800000: constants.CAN_BT1_800KB, + 1000000: constants.CAN_BT1_1000KB, + }, + } + + def __init__(self, channel, can_filters=None, **kwargs): + """ + :param int channel: + The Channel id to create this bus with. + + :param list can_filters: + See :meth:`can.BusABC.set_filters`. + + :param bool receive_own_messages: + Enable self-reception of sent messages. + + :param int UniqueHardwareId: + UniqueHardwareId to connect (optional, will use the first found if not supplied) + + :param int bitrate: + Channel bitrate in bit/s + """ + if _canlib is None: + raise CanInterfaceNotImplementedError( + "The IXXAT VCI library has not been initialized. Check the logs for more details." + ) + log.info("CAN Filters: %s", can_filters) + log.info("Got configuration of: %s", kwargs) + # Configuration options + bitrate = kwargs.get("bitrate", 500000) + UniqueHardwareId = kwargs.get("UniqueHardwareId", None) + rxFifoSize = kwargs.get("rxFifoSize", 16) + txFifoSize = kwargs.get("txFifoSize", 16) + self._receive_own_messages = kwargs.get("receive_own_messages", False) + # Usually comes as a string from the config file + channel = int(channel) + + if bitrate not in self.CHANNEL_BITRATES[0]: + raise ValueError("Invalid bitrate {}".format(bitrate)) + + if rxFifoSize <= 0: + raise ValueError("rxFifoSize must be > 0") + + if txFifoSize <= 0: + raise ValueError("txFifoSize must be > 0") + + if channel < 0: + raise ValueError("channel number must be >= 0") + + self._device_handle = HANDLE() + self._device_info = structures.VCIDEVICEINFO() + self._control_handle = HANDLE() + self._channel_handle = HANDLE() + self._channel_capabilities = structures.CANCAPABILITIES() + self._message = structures.CANMSG() + self._payload = (ctypes.c_byte * 8)() + + # Search for supplied device + if UniqueHardwareId is None: + log.info("Searching for first available device") + else: + log.info("Searching for unique HW ID %s", UniqueHardwareId) + _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle)) + while True: + try: + _canlib.vciEnumDeviceNext( + self._device_handle, ctypes.byref(self._device_info) + ) + except StopIteration: + if UniqueHardwareId is None: + raise VCIDeviceNotFoundError( + "No IXXAT device(s) connected or device(s) in use by other process(es)." + ) + else: + raise VCIDeviceNotFoundError( + "Unique HW ID {} not connected or not available.".format( + UniqueHardwareId + ) + ) + else: + if (UniqueHardwareId is None) or ( + self._device_info.UniqueHardwareId.AsChar + == bytes(UniqueHardwareId, "ascii") + ): + break + else: + log.debug( + "Ignoring IXXAT with hardware id '%s'.", + self._device_info.UniqueHardwareId.AsChar.decode("ascii"), + ) + _canlib.vciEnumDeviceClose(self._device_handle) + + try: + _canlib.vciDeviceOpen( + ctypes.byref(self._device_info.VciObjectId), + ctypes.byref(self._device_handle), + ) + except Exception as exception: + raise CanInitializationError(f"Could not open device: {exception}") + + log.info("Using unique HW ID %s", self._device_info.UniqueHardwareId.AsChar) + + log.info( + "Initializing channel %d in shared mode, %d rx buffers, %d tx buffers", + channel, + rxFifoSize, + txFifoSize, + ) + + try: + _canlib.canChannelOpen( + self._device_handle, + channel, + constants.FALSE, + ctypes.byref(self._channel_handle), + ) + except Exception as exception: + raise CanInitializationError( + f"Could not open and initialize channel: {exception}" + ) + + # Signal TX/RX events when at least one frame has been handled + _canlib.canChannelInitialize(self._channel_handle, rxFifoSize, 1, txFifoSize, 1) + _canlib.canChannelActivate(self._channel_handle, constants.TRUE) + + log.info("Initializing control %d bitrate %d", channel, bitrate) + _canlib.canControlOpen( + self._device_handle, channel, ctypes.byref(self._control_handle) + ) + _canlib.canControlInitialize( + self._control_handle, + constants.CAN_OPMODE_STANDARD + | constants.CAN_OPMODE_EXTENDED + | constants.CAN_OPMODE_ERRFRAME, + self.CHANNEL_BITRATES[0][bitrate], + self.CHANNEL_BITRATES[1][bitrate], + ) + _canlib.canControlGetCaps( + self._control_handle, ctypes.byref(self._channel_capabilities) + ) + + # With receive messages, this field contains the relative reception time of + # the message in ticks. The resolution of a tick can be calculated from the fields + # dwClockFreq and dwTscDivisor of the structure CANCAPABILITIES in accordance with the following formula: + # frequency [1/s] = dwClockFreq / dwTscDivisor + # We explicitly cast to float for Python 2.x users + self._tick_resolution = float( + self._channel_capabilities.dwClockFreq + / self._channel_capabilities.dwTscDivisor + ) + + # Setup filters before starting the channel + if can_filters: + log.info("The IXXAT VCI backend is filtering messages") + # Disable every message coming in + for extended in (0, 1): + _canlib.canControlSetAccFilter( + self._control_handle, + extended, + constants.CAN_ACC_CODE_NONE, + constants.CAN_ACC_MASK_NONE, + ) + for can_filter in can_filters: + # Filters define what messages are accepted + code = int(can_filter["can_id"]) + mask = int(can_filter["can_mask"]) + extended = can_filter.get("extended", False) + _canlib.canControlAddFilterIds( + self._control_handle, 1 if extended else 0, code << 1, mask << 1 + ) + log.info("Accepting ID: 0x%X MASK: 0x%X", code, mask) + + # Start the CAN controller. Messages will be forwarded to the channel + _canlib.canControlStart(self._control_handle, constants.TRUE) + + # For cyclic transmit list. Set when .send_periodic() is first called + self._scheduler = None + self._scheduler_resolution = None + self.channel = channel + + # Usually you get back 3 messages like "CAN initialized" ecc... + # Clear the FIFO by filter them out with low timeout + for _ in range(rxFifoSize): + try: + _canlib.canChannelReadMessage( + self._channel_handle, 0, ctypes.byref(self._message) + ) + except (VCITimeout, VCIRxQueueEmptyError): + break + + super().__init__(channel=channel, can_filters=None, **kwargs) + + def _inWaiting(self): + try: + _canlib.canChannelWaitRxEvent(self._channel_handle, 0) + except VCITimeout: + return 0 + else: + return 1 + + def flush_tx_buffer(self): + """ Flushes the transmit buffer on the IXXAT """ + # TODO #64: no timeout? + _canlib.canChannelWaitTxEvent(self._channel_handle, constants.INFINITE) + + def _recv_internal(self, timeout): + """ Read a message from IXXAT device. """ + + # TODO: handling CAN error messages? + data_received = False + + if timeout == 0: + # Peek without waiting + try: + _canlib.canChannelPeekMessage( + self._channel_handle, ctypes.byref(self._message) + ) + except (VCITimeout, VCIRxQueueEmptyError): + return None, True + else: + if self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_DATA: + data_received = True + else: + # Wait if no message available + if timeout is None or timeout < 0: + remaining_ms = constants.INFINITE + t0 = None + else: + timeout_ms = int(timeout * 1000) + remaining_ms = timeout_ms + t0 = _timer_function() + + while True: + try: + _canlib.canChannelReadMessage( + self._channel_handle, remaining_ms, ctypes.byref(self._message) + ) + except (VCITimeout, VCIRxQueueEmptyError): + # Ignore the 2 errors, the timeout is handled manually with the _timer_function() + pass + else: + # See if we got a data or info/error messages + if self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_DATA: + data_received = True + break + elif self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_INFO: + log.info( + CAN_INFO_MESSAGES.get( + self._message.abData[0], + "Unknown CAN info message code {}".format( + self._message.abData[0] + ), + ) + ) + + elif ( + self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_ERROR + ): + log.warning( + CAN_ERROR_MESSAGES.get( + self._message.abData[0], + "Unknown CAN error message code {}".format( + self._message.abData[0] + ), + ) + ) + + elif ( + self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_STATUS + ): + log.info(_format_can_status(self._message.abData[0])) + if self._message.abData[0] & constants.CAN_STATUS_BUSOFF: + raise VCIBusOffError() + + elif ( + self._message.uMsgInfo.Bits.type + == constants.CAN_MSGTYPE_TIMEOVR + ): + pass + else: + log.warning("Unexpected message info type") + + if t0 is not None: + remaining_ms = timeout_ms - int((_timer_function() - t0) * 1000) + if remaining_ms < 0: + break + + if not data_received: + # Timed out / can message type is not DATA + return None, True + + # The _message.dwTime is a 32bit tick value and will overrun, + # so expect to see the value restarting from 0 + rx_msg = Message( + timestamp=self._message.dwTime + / self._tick_resolution, # Relative time in s + is_remote_frame=bool(self._message.uMsgInfo.Bits.rtr), + is_extended_id=bool(self._message.uMsgInfo.Bits.ext), + arbitration_id=self._message.dwMsgId, + dlc=self._message.uMsgInfo.Bits.dlc, + data=self._message.abData[: self._message.uMsgInfo.Bits.dlc], + channel=self.channel, + ) + + return rx_msg, True + + def send(self, msg: Message, timeout: Optional[float] = None) -> None: + """ + Sends a message on the bus. The interface may buffer the message. + + :param msg: + The message to send. + :param timeout: + Timeout after some time. + :raise: + :class:CanTimeoutError + :class:CanOperationError + """ + # This system is not designed to be very efficient + message = structures.CANMSG() + message.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA + message.uMsgInfo.Bits.rtr = 1 if msg.is_remote_frame else 0 + message.uMsgInfo.Bits.ext = 1 if msg.is_extended_id else 0 + message.uMsgInfo.Bits.srr = 1 if self._receive_own_messages else 0 + message.dwMsgId = msg.arbitration_id + if msg.dlc: + message.uMsgInfo.Bits.dlc = msg.dlc + adapter = (ctypes.c_uint8 * len(msg.data)).from_buffer(msg.data) + ctypes.memmove(message.abData, adapter, len(msg.data)) + + if timeout: + _canlib.canChannelSendMessage( + self._channel_handle, int(timeout * 1000), message + ) + + else: + _canlib.canChannelPostMessage(self._channel_handle, message) + + def _send_periodic_internal(self, msg, period, duration=None): + """Send a message using built-in cyclic transmit list functionality.""" + if self._scheduler is None: + self._scheduler = HANDLE() + _canlib.canSchedulerOpen(self._device_handle, self.channel, self._scheduler) + caps = structures.CANCAPABILITIES() + _canlib.canSchedulerGetCaps(self._scheduler, caps) + self._scheduler_resolution = float(caps.dwClockFreq) / caps.dwCmsDivisor + _canlib.canSchedulerActivate(self._scheduler, constants.TRUE) + return CyclicSendTask( + self._scheduler, msg, period, duration, self._scheduler_resolution + ) + + def shutdown(self): + if self._scheduler is not None: + _canlib.canSchedulerClose(self._scheduler) + _canlib.canChannelClose(self._channel_handle) + _canlib.canControlStart(self._control_handle, constants.FALSE) + _canlib.canControlClose(self._control_handle) + _canlib.vciDeviceClose(self._device_handle) + + +class CyclicSendTask(LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC): + """A message in the cyclic transmit list.""" + + def __init__(self, scheduler, msgs, period, duration, resolution): + super().__init__(msgs, period, duration) + if len(self.messages) != 1: + raise ValueError( + "IXXAT Interface only supports periodic transmission of 1 element" + ) + + self._scheduler = scheduler + self._index = None + self._count = int(duration / period) if duration else 0 + + self._msg = structures.CANCYCLICTXMSG() + self._msg.wCycleTime = int(round(period * resolution)) + self._msg.dwMsgId = self.messages[0].arbitration_id + self._msg.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA + self._msg.uMsgInfo.Bits.ext = 1 if self.messages[0].is_extended_id else 0 + self._msg.uMsgInfo.Bits.rtr = 1 if self.messages[0].is_remote_frame else 0 + self._msg.uMsgInfo.Bits.dlc = self.messages[0].dlc + for i, b in enumerate(self.messages[0].data): + self._msg.abData[i] = b + self.start() + + def start(self): + """Start transmitting message (add to list if needed).""" + if self._index is None: + self._index = ctypes.c_uint32() + _canlib.canSchedulerAddMessage(self._scheduler, self._msg, self._index) + _canlib.canSchedulerStartMessage(self._scheduler, self._index, self._count) + + def pause(self): + """Pause transmitting message (keep it in the list).""" + _canlib.canSchedulerStopMessage(self._scheduler, self._index) + + def stop(self): + """Stop transmitting message (remove from list).""" + # Remove it completely instead of just stopping it to avoid filling up + # the list with permanently stopped messages + _canlib.canSchedulerRemMessage(self._scheduler, self._index) + self._index = None + + +def _format_can_status(status_flags: int): + """ + Format a status bitfield found in CAN_MSGTYPE_STATUS messages or in dwStatus + field in CANLINESTATUS. + + Valid states are defined in the CAN_STATUS_* constants in cantype.h + """ + states = [] + for flag, description in CAN_STATUS_FLAGS.items(): + if status_flags & flag: + states.append(description) + status_flags &= ~flag + + if status_flags: + states.append("unknown state 0x{:02x}".format(status_flags)) + + if states: + return "CAN status message: {}".format(", ".join(states)) + else: + return "Empty CAN status message" + + +def get_ixxat_hwids(): + """Get a list of hardware ids of all available IXXAT devices.""" + hwids = [] + device_handle = HANDLE() + device_info = structures.VCIDEVICEINFO() + + _canlib.vciEnumDeviceOpen(ctypes.byref(device_handle)) + while True: + try: + _canlib.vciEnumDeviceNext(device_handle, ctypes.byref(device_info)) + except StopIteration: + break + else: + hwids.append(device_info.UniqueHardwareId.AsChar.decode("ascii")) + _canlib.vciEnumDeviceClose(device_handle) + + return hwids diff --git a/can/interfaces/ixxat_fd/constants.py b/can/interfaces/ixxat_fd/constants.py new file mode 100644 index 000000000..e911a580a --- /dev/null +++ b/can/interfaces/ixxat_fd/constants.py @@ -0,0 +1,148 @@ +""" +Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + +Copyright (C) 2016 Giuseppe Corbelli +""" + +FALSE = 0 +TRUE = 1 + +INFINITE = 0xFFFFFFFF + +VCI_MAX_ERRSTRLEN = 256 + +# Bitrates +CAN_BT0_10KB = 0x31 +CAN_BT1_10KB = 0x1C +CAN_BT0_20KB = 0x18 +CAN_BT1_20KB = 0x1C +CAN_BT0_50KB = 0x09 +CAN_BT1_50KB = 0x1C +CAN_BT0_100KB = 0x04 +CAN_BT1_100KB = 0x1C +CAN_BT0_125KB = 0x03 +CAN_BT1_125KB = 0x1C +CAN_BT0_250KB = 0x01 +CAN_BT1_250KB = 0x1C +CAN_BT0_500KB = 0x00 +CAN_BT1_500KB = 0x1C +CAN_BT0_667KB = 0x00 +CAN_BT1_667KB = 0x18 +CAN_BT0_800KB = 0x00 +CAN_BT1_800KB = 0x16 +CAN_BT0_1000KB = 0x00 +CAN_BT1_1000KB = 0x14 + +# Facilities/severities +SEV_INFO = 0x40000000 +SEV_WARN = 0x80000000 +SEV_ERROR = 0xC0000000 +SEV_MASK = 0xC0000000 +SEV_SUCCESS = 0x00000000 + +RESERVED_FLAG = 0x10000000 +CUSTOMER_FLAG = 0x20000000 + +STATUS_MASK = 0x0000FFFF +FACILITY_MASK = 0x0FFF0000 + +# Or so I hope +FACILITY_STD = 0 + +SEV_STD_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_STD +SEV_STD_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_STD +SEV_STD_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_STD + +FACILITY_VCI = 0x00010000 +SEV_VCI_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_VCI +SEV_VCI_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_VCI +SEV_VCI_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_VCI + +FACILITY_DAL = 0x00020000 +SEV_DAL_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_DAL +SEV_DAL_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_DAL +SEV_DAL_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_DAL + +FACILITY_CCL = 0x00030000 +SEV_CCL_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_CCL +SEV_CCL_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_CCL +SEV_CCL_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_CCL + +FACILITY_BAL = 0x00040000 +SEV_BAL_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_BAL +SEV_BAL_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_BAL +SEV_BAL_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_BAL + +# Errors +VCI_SUCCESS = 0x00 +VCI_OK = 0x00 +VCI_E_UNEXPECTED = SEV_VCI_ERROR | 0x0001 +VCI_E_NOT_IMPLEMENTED = SEV_VCI_ERROR | 0x0002 +VCI_E_OUTOFMEMORY = SEV_VCI_ERROR | 0x0003 +VCI_E_INVALIDARG = SEV_VCI_ERROR | 0x0004 +VCI_E_NOINTERFACE = SEV_VCI_ERROR | 0x0005 +VCI_E_INVPOINTER = SEV_VCI_ERROR | 0x0006 +VCI_E_INVHANDLE = SEV_VCI_ERROR | 0x0007 +VCI_E_ABORT = SEV_VCI_ERROR | 0x0008 +VCI_E_FAIL = SEV_VCI_ERROR | 0x0009 +VCI_E_ACCESSDENIED = SEV_VCI_ERROR | 0x000A +VCI_E_TIMEOUT = SEV_VCI_ERROR | 0x000B +VCI_E_BUSY = SEV_VCI_ERROR | 0x000C +VCI_E_PENDING = SEV_VCI_ERROR | 0x000D +VCI_E_NO_DATA = SEV_VCI_ERROR | 0x000E +VCI_E_NO_MORE_ITEMS = SEV_VCI_ERROR | 0x000F +VCI_E_NOT_INITIALIZED = SEV_VCI_ERROR | 0x0010 +VCI_E_ALREADY_INITIALIZED = SEV_VCI_ERROR | 0x00011 +VCI_E_RXQUEUE_EMPTY = SEV_VCI_ERROR | 0x00012 +VCI_E_TXQUEUE_FULL = SEV_VCI_ERROR | 0x0013 +VCI_E_BUFFER_OVERFLOW = SEV_VCI_ERROR | 0x0014 +VCI_E_INVALID_STATE = SEV_VCI_ERROR | 0x0015 +VCI_E_OBJECT_ALREADY_EXISTS = SEV_VCI_ERROR | 0x0016 +VCI_E_INVALID_INDEX = SEV_VCI_ERROR | 0x0017 +VCI_E_END_OF_FILE = SEV_VCI_ERROR | 0x0018 +VCI_E_DISCONNECTED = SEV_VCI_ERROR | 0x0019 +VCI_E_WRONG_FLASHFWVERSION = SEV_VCI_ERROR | 0x001A + +# Controller status +CAN_STATUS_TXPEND = 0x01 +CAN_STATUS_OVRRUN = 0x02 +CAN_STATUS_ERRLIM = 0x04 +CAN_STATUS_BUSOFF = 0x08 +CAN_STATUS_ININIT = 0x10 +CAN_STATUS_BUSCERR = 0x20 + +# Controller operating modes +CAN_OPMODE_UNDEFINED = 0x00 +CAN_OPMODE_STANDARD = 0x01 +CAN_OPMODE_EXTENDED = 0x02 +CAN_OPMODE_ERRFRAME = 0x04 +CAN_OPMODE_LISTONLY = 0x08 +CAN_OPMODE_LOWSPEED = 0x10 + +# Message types +CAN_MSGTYPE_DATA = 0 +CAN_MSGTYPE_INFO = 1 +CAN_MSGTYPE_ERROR = 2 +CAN_MSGTYPE_STATUS = 3 +CAN_MSGTYPE_WAKEUP = 4 +CAN_MSGTYPE_TIMEOVR = 5 +CAN_MSGTYPE_TIMERST = 6 + +# Information supplied in the abData[0] field of info frames +# (CANMSGINFO.Bytes.bType = CAN_MSGTYPE_INFO). +CAN_INFO_START = 1 +CAN_INFO_STOP = 2 +CAN_INFO_RESET = 3 + +# Information supplied in the abData[0] field of info frames +# (CANMSGINFO.Bytes.bType = CAN_MSGTYPE_ERROR). +CAN_ERROR_STUFF = 1 # stuff error +CAN_ERROR_FORM = 2 # form error +CAN_ERROR_ACK = 3 # acknowledgment error +CAN_ERROR_BIT = 4 # bit error +CAN_ERROR_CRC = 6 # CRC error +CAN_ERROR_OTHER = 7 # other (unspecified) error + +# acceptance code and mask to reject all CAN IDs +CAN_ACC_MASK_NONE = 0xFFFFFFFF +CAN_ACC_CODE_NONE = 0x80000000 diff --git a/can/interfaces/ixxat_fd/exceptions.py b/can/interfaces/ixxat_fd/exceptions.py new file mode 100644 index 000000000..3bc0e1111 --- /dev/null +++ b/can/interfaces/ixxat_fd/exceptions.py @@ -0,0 +1,44 @@ +""" +Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + +Copyright (C) 2016 Giuseppe Corbelli +Copyright (C) 2019 Marcel Kanter +""" + +from can import ( + CanInitializationError, + CanOperationError, + CanTimeoutError, +) + +__all__ = [ + "VCITimeout", + "VCIError", + "VCIRxQueueEmptyError", + "VCIBusOffError", + "VCIDeviceNotFoundError", +] + + +class VCITimeout(CanTimeoutError): + """ Wraps the VCI_E_TIMEOUT error """ + + +class VCIError(CanOperationError): + """ Try to display errors that occur within the wrapped C library nicely. """ + + +class VCIRxQueueEmptyError(VCIError): + """ Wraps the VCI_E_RXQUEUE_EMPTY error """ + + def __init__(self): + super().__init__("Receive queue is empty") + + +class VCIBusOffError(VCIError): + def __init__(self): + super().__init__("Controller is in BUSOFF state") + + +class VCIDeviceNotFoundError(CanInitializationError): + pass diff --git a/can/interfaces/ixxat_fd/structures.py b/can/interfaces/ixxat_fd/structures.py new file mode 100644 index 000000000..73c01823d --- /dev/null +++ b/can/interfaces/ixxat_fd/structures.py @@ -0,0 +1,166 @@ +""" +Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + +Copyright (C) 2016 Giuseppe Corbelli +""" + +import ctypes + + +class LUID(ctypes.Structure): + _fields_ = [("LowPart", ctypes.c_uint32), ("HighPart", ctypes.c_int32)] + + +PLUID = ctypes.POINTER(LUID) + + +class VCIID(ctypes.Union): + _fields_ = [("AsLuid", LUID), ("AsInt64", ctypes.c_int64)] + + +PVCIID = ctypes.POINTER(VCIID) + + +class GUID(ctypes.Structure): + _fields_ = [ + ("Data1", ctypes.c_uint32), + ("Data2", ctypes.c_uint16), + ("Data3", ctypes.c_uint16), + ("Data4", ctypes.c_char * 8), + ] + + +class VCIDEVICEINFO(ctypes.Structure): + class UniqueHardwareId(ctypes.Union): + _fields_ = [("AsChar", ctypes.c_char * 16), ("AsGuid", GUID)] + + _fields_ = [ + ("VciObjectId", VCIID), + ("DeviceClass", GUID), + ("DriverMajorVersion", ctypes.c_uint8), + ("DriverMinorVersion", ctypes.c_uint8), + ("DriverBuildVersion", ctypes.c_uint16), + ("HardwareBranchVersion", ctypes.c_uint8), + ("HardwareMajorVersion", ctypes.c_uint8), + ("HardwareMinorVersion", ctypes.c_uint8), + ("HardwareBuildVersion", ctypes.c_uint8), + ("UniqueHardwareId", UniqueHardwareId), + ("Description", ctypes.c_char * 128), + ("Manufacturer", ctypes.c_char * 126), + ("DriverReleaseVersion", ctypes.c_uint16), + ] + + def __str__(self): + return "Mfg: {}, Dev: {} HW: {}.{}.{}.{} Drv: {}.{}.{}.{}".format( + self.Manufacturer, + self.Description, + self.HardwareBranchVersion, + self.HardwareMajorVersion, + self.HardwareMinorVersion, + self.HardwareBuildVersion, + self.DriverReleaseVersion, + self.DriverMajorVersion, + self.DriverMinorVersion, + self.DriverBuildVersion, + ) + + +PVCIDEVICEINFO = ctypes.POINTER(VCIDEVICEINFO) + + +class CANLINESTATUS(ctypes.Structure): + _fields_ = [ + ("bOpMode", ctypes.c_uint8), + ("bBtReg0", ctypes.c_uint8), + ("bBtReg1", ctypes.c_uint8), + ("bBusLoad", ctypes.c_uint8), + ("dwStatus", ctypes.c_uint32), + ] + + +PCANLINESTATUS = ctypes.POINTER(CANLINESTATUS) + + +class CANCHANSTATUS(ctypes.Structure): + _fields_ = [ + ("sLineStatus", CANLINESTATUS), + ("fActivated", ctypes.c_uint32), + ("fRxOverrun", ctypes.c_uint32), + ("bRxFifoLoad", ctypes.c_uint8), + ("bTxFifoLoad", ctypes.c_uint8), + ] + + +PCANCHANSTATUS = ctypes.POINTER(CANCHANSTATUS) + + +class CANCAPABILITIES(ctypes.Structure): + _fields_ = [ + ("wCtrlType", ctypes.c_uint16), + ("wBusCoupling", ctypes.c_uint16), + ("dwFeatures", ctypes.c_uint32), + ("dwClockFreq", ctypes.c_uint32), + ("dwTscDivisor", ctypes.c_uint32), + ("dwCmsDivisor", ctypes.c_uint32), + ("dwCmsMaxTicks", ctypes.c_uint32), + ("dwDtxDivisor", ctypes.c_uint32), + ("dwDtxMaxTicks", ctypes.c_uint32), + ] + + +PCANCAPABILITIES = ctypes.POINTER(CANCAPABILITIES) + + +class CANMSGINFO(ctypes.Union): + class Bytes(ctypes.Structure): + _fields_ = [ + ("bType", ctypes.c_uint8), + ("bAddFlags", ctypes.c_uint8), + ("bFlags", ctypes.c_uint8), + ("bAccept", ctypes.c_uint8), + ] + + class Bits(ctypes.Structure): + _fields_ = [ + ("type", ctypes.c_uint32, 8), + ("ssm", ctypes.c_uint32, 1), + ("hi", ctypes.c_uint32, 2), + ("res", ctypes.c_uint32, 5), + ("dlc", ctypes.c_uint32, 4), + ("ovr", ctypes.c_uint32, 1), + ("srr", ctypes.c_uint32, 1), + ("rtr", ctypes.c_uint32, 1), + ("ext", ctypes.c_uint32, 1), + ("afc", ctypes.c_uint32, 8), + ] + + _fields_ = [("Bytes", Bytes), ("Bits", Bits)] + + +PCANMSGINFO = ctypes.POINTER(CANMSGINFO) + + +class CANMSG(ctypes.Structure): + _fields_ = [ + ("dwTime", ctypes.c_uint32), + ("dwMsgId", ctypes.c_uint32), + ("uMsgInfo", CANMSGINFO), + ("abData", ctypes.c_uint8 * 8), + ] + + +PCANMSG = ctypes.POINTER(CANMSG) + + +class CANCYCLICTXMSG(ctypes.Structure): + _fields_ = [ + ("wCycleTime", ctypes.c_uint16), + ("bIncrMode", ctypes.c_uint8), + ("bByteIndex", ctypes.c_uint8), + ("dwMsgId", ctypes.c_uint32), + ("uMsgInfo", CANMSGINFO), + ("abData", ctypes.c_uint8 * 8), + ] + + +PCANCYCLICTXMSG = ctypes.POINTER(CANCYCLICTXMSG) From 7a52d8fca9a135dde33cbc4ad69c3b559d49dc8b Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 16 Aug 2021 11:19:46 +0200 Subject: [PATCH 02/22] Changed: ixxat_fd adapting for usage with vcinpl2.dll instead of vcinpl.dll. --- can/interfaces/__init__.py | 1 + can/interfaces/ixxat_fd/__init__.py | 2 +- can/interfaces/ixxat_fd/canlib.py | 109 ++++++++++++++++--------- can/interfaces/ixxat_fd/constants.py | 38 +++++++++ can/interfaces/ixxat_fd/structures.py | 113 ++++++++++++++++++++++---- 5 files changed, 211 insertions(+), 52 deletions(-) diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index bfedea60a..8a805334f 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -14,6 +14,7 @@ "pcan": ("can.interfaces.pcan", "PcanBus"), "usb2can": ("can.interfaces.usb2can", "Usb2canBus"), "ixxat": ("can.interfaces.ixxat", "IXXATBus"), + "ixxat_fd": ("can.interfaces.ixxat_fd", "IXXATBus"), "nican": ("can.interfaces.nican", "NicanBus"), "iscan": ("can.interfaces.iscan", "IscanBus"), "virtual": ("can.interfaces.virtual", "VirtualBus"), diff --git a/can/interfaces/ixxat_fd/__init__.py b/can/interfaces/ixxat_fd/__init__.py index fc2cae0f3..08c3fdf95 100644 --- a/can/interfaces/ixxat_fd/__init__.py +++ b/can/interfaces/ixxat_fd/__init__.py @@ -4,4 +4,4 @@ Copyright (C) 2016 Giuseppe Corbelli """ -from can.interfaces.ixxat.canlib import IXXATBus, get_ixxat_hwids +from can.interfaces.ixxat_fd.canlib import IXXATBus, get_ixxat_hwids diff --git a/can/interfaces/ixxat_fd/canlib.py b/can/interfaces/ixxat_fd/canlib.py index 13709c71c..7f0bb263c 100644 --- a/can/interfaces/ixxat_fd/canlib.py +++ b/can/interfaces/ixxat_fd/canlib.py @@ -23,6 +23,8 @@ ) from can.ctypesutil import CLibrary, HANDLE, PHANDLE, HRESULT as ctypes_HRESULT +import can.util + from . import constants, structures from .exceptions import * @@ -35,7 +37,7 @@ "vciFormatError", ] -log = logging.getLogger("can.ixxat") +log = logging.getLogger("can.ixxat_fd") from time import perf_counter as _timer_function @@ -47,7 +49,7 @@ # TODO: Use ECI driver for linux if sys.platform == "win32" or sys.platform == "cygwin": try: - _canlib = CLibrary("vcinpl.dll") + _canlib = CLibrary("vcinpl2.dll") except Exception as e: log.warning("Cannot load IXXAT vcinpl library: %s", e) else: @@ -135,9 +137,15 @@ def __check_status(result, function, arguments): _canlib.map_symbol("vciInitialize", ctypes.c_long, (), __check_status) # void VCIAPI vciFormatError (HRESULT hrError, PCHAR pszText, UINT32 dwsize); - _canlib.map_symbol( - "vciFormatError", None, (ctypes_HRESULT, ctypes.c_char_p, ctypes.c_uint32) - ) + try: + _canlib.map_symbol( + "vciFormatError", None, (ctypes_HRESULT, ctypes.c_char_p, ctypes.c_uint32) + ) + except: + _canlib.map_symbol( + "vciFormatErrorA", None, (ctypes_HRESULT, ctypes.c_char_p, ctypes.c_uint32) + ) + _canlib.vciFormatError = _canlib.vciFormatErrorA # Hack to have vciFormatError as a free function vciFormatError = functools.partial(__vciFormatError, _canlib) @@ -171,7 +179,7 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canChannelInitialize", ctypes.c_long, - (HANDLE, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16), + (HANDLE, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint32, ctypes.c_uint8), __check_status, ) # EXTERN_C HRESULT VCIAPI canChannelActivate( IN HANDLE hCanChn, IN BOOL fEnable ); @@ -184,14 +192,14 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canChannelReadMessage", ctypes.c_long, - (HANDLE, ctypes.c_uint32, structures.PCANMSG), + (HANDLE, ctypes.c_uint32, structures.PCANMSG2), __check_status, ) # HRESULT canChannelPeekMessage(HANDLE hChannel,PCANMSG pCanMsg ); _canlib.map_symbol( "canChannelPeekMessage", ctypes.c_long, - (HANDLE, structures.PCANMSG), + (HANDLE, structures.PCANMSG2), __check_status, ) # HRESULT canChannelWaitTxEvent (HANDLE hChannel UINT32 dwMsTimeout ); @@ -212,14 +220,14 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canChannelPostMessage", ctypes.c_long, - (HANDLE, structures.PCANMSG), + (HANDLE, structures.PCANMSG2), __check_status, ) # HRESULT canChannelSendMessage (HANDLE hChannel, UINT32 dwMsTimeout, PCANMSG pCanMsg ); _canlib.map_symbol( "canChannelSendMessage", ctypes.c_long, - (HANDLE, ctypes.c_uint32, structures.PCANMSG), + (HANDLE, ctypes.c_uint32, structures.PCANMSG2), __check_status, ) @@ -234,7 +242,7 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canControlInitialize", ctypes.c_long, - (HANDLE, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint8), + (HANDLE, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint32, ctypes.c_uint32, structures.PCANBTP, structures.PCANBTP), __check_status, ) # EXTERN_C HRESULT VCIAPI canControlClose( IN HANDLE hCanCtl ); @@ -249,14 +257,14 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canControlGetStatus", ctypes.c_long, - (HANDLE, structures.PCANLINESTATUS), + (HANDLE, structures.PCANLINESTATUS2), __check_status, ) # EXTERN_C HRESULT VCIAPI canControlGetCaps( IN HANDLE hCanCtl, OUT PCANCAPABILITIES pCanCaps ); _canlib.map_symbol( "canControlGetCaps", ctypes.c_long, - (HANDLE, structures.PCANCAPABILITIES), + (HANDLE, structures.PCANCAPABILITIES2), __check_status, ) # EXTERN_C HRESULT VCIAPI canControlSetAccFilter( IN HANDLE hCanCtl, IN BOOL fExtend, IN UINT32 dwCode, IN UINT32 dwMask ); @@ -293,7 +301,7 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canSchedulerGetCaps", ctypes.c_long, - (HANDLE, structures.PCANCAPABILITIES), + (HANDLE, structures.PCANCAPABILITIES2), __check_status, ) # EXTERN_C HRESULT canSchedulerActivate ( HANDLE hScheduler, BOOL fEnable ); @@ -304,7 +312,7 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canSchedulerAddMessage", ctypes.c_long, - (HANDLE, structures.PCANCYCLICTXMSG, ctypes.POINTER(ctypes.c_uint32)), + (HANDLE, structures.PCANCYCLICTXMSG2, ctypes.POINTER(ctypes.c_uint32)), __check_status, ) # EXTERN_C HRESULT canSchedulerRemMessage (HANDLE hScheduler, UINT32 dwIndex ); @@ -434,8 +442,8 @@ def __init__(self, channel, can_filters=None, **kwargs): # Configuration options bitrate = kwargs.get("bitrate", 500000) UniqueHardwareId = kwargs.get("UniqueHardwareId", None) - rxFifoSize = kwargs.get("rxFifoSize", 16) - txFifoSize = kwargs.get("txFifoSize", 16) + rxFifoSize = kwargs.get("rxFifoSize", 1024) + txFifoSize = kwargs.get("txFifoSize", 128) self._receive_own_messages = kwargs.get("receive_own_messages", False) # Usually comes as a string from the config file channel = int(channel) @@ -456,9 +464,9 @@ def __init__(self, channel, can_filters=None, **kwargs): self._device_info = structures.VCIDEVICEINFO() self._control_handle = HANDLE() self._channel_handle = HANDLE() - self._channel_capabilities = structures.CANCAPABILITIES() - self._message = structures.CANMSG() - self._payload = (ctypes.c_byte * 8)() + self._channel_capabilities = structures.CANCAPABILITIES2() + self._message = structures.CANMSG2() + self._payload = (ctypes.c_byte * 64)() # Search for supplied device if UniqueHardwareId is None: @@ -525,21 +533,43 @@ def __init__(self, channel, can_filters=None, **kwargs): ) # Signal TX/RX events when at least one frame has been handled - _canlib.canChannelInitialize(self._channel_handle, rxFifoSize, 1, txFifoSize, 1) + _canlib.canChannelInitialize(self._channel_handle, rxFifoSize, 1, txFifoSize, 1, 0, constants.CAN_FILTER_PASS) _canlib.canChannelActivate(self._channel_handle, constants.TRUE) log.info("Initializing control %d bitrate %d", channel, bitrate) _canlib.canControlOpen( self._device_handle, channel, ctypes.byref(self._control_handle) ) + # TODO: fill from parametters + # TODO: confirm 500/2000 + pBtpSDR = structures.CANBTP() + pBtpSDR.dwMode = 0 #constants.CAN_BTMODE_RAW + pBtpSDR.dwBPS = 500000 + pBtpSDR.wTS1 = 6400 + pBtpSDR.wTS2 = 1600 + pBtpSDR.wSJW = 1600 + pBtpSDR.wTDO = 0 + + pBtpFDR = structures.CANBTP() + pBtpFDR.dwMode = 0 #constants.CAN_BTMODE_RAW + pBtpFDR.dwBPS = 2000000 + pBtpFDR.wTS1 = 1600 + pBtpFDR.wTS2 = 400 + pBtpFDR.wSJW = 400 + pBtpFDR.wTDO = 1600 + _canlib.canControlInitialize( self._control_handle, - constants.CAN_OPMODE_STANDARD - | constants.CAN_OPMODE_EXTENDED - | constants.CAN_OPMODE_ERRFRAME, - self.CHANNEL_BITRATES[0][bitrate], - self.CHANNEL_BITRATES[1][bitrate], + constants.CAN_OPMODE_STANDARD | constants.CAN_OPMODE_EXTENDED | constants.CAN_OPMODE_ERRFRAME, + constants.CAN_EXMODE_FASTDATA | constants.CAN_EXMODE_EXTDATALEN, + constants.CAN_FILTER_PASS, + constants.CAN_FILTER_PASS, + 0, + 0, + ctypes.byref(pBtpSDR), + ctypes.byref(pBtpFDR) ) + _canlib.canControlGetCaps( self._control_handle, ctypes.byref(self._channel_capabilities) ) @@ -550,7 +580,7 @@ def __init__(self, channel, can_filters=None, **kwargs): # frequency [1/s] = dwClockFreq / dwTscDivisor # We explicitly cast to float for Python 2.x users self._tick_resolution = float( - self._channel_capabilities.dwClockFreq + self._channel_capabilities.dwTscClkFreq # TODO confirm / self._channel_capabilities.dwTscDivisor ) @@ -694,6 +724,7 @@ def _recv_internal(self, timeout): # Timed out / can message type is not DATA return None, True + data_len = can.util.dlc2len(self._message.uMsgInfo.Bits.dlc) # The _message.dwTime is a 32bit tick value and will overrun, # so expect to see the value restarting from 0 rx_msg = Message( @@ -702,8 +733,8 @@ def _recv_internal(self, timeout): is_remote_frame=bool(self._message.uMsgInfo.Bits.rtr), is_extended_id=bool(self._message.uMsgInfo.Bits.ext), arbitration_id=self._message.dwMsgId, - dlc=self._message.uMsgInfo.Bits.dlc, - data=self._message.abData[: self._message.uMsgInfo.Bits.dlc], + dlc=data_len, + data=self._message.abData[: data_len], channel=self.channel, ) @@ -722,16 +753,20 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None: :class:CanOperationError """ # This system is not designed to be very efficient - message = structures.CANMSG() + message = structures.CANMSG2() message.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA message.uMsgInfo.Bits.rtr = 1 if msg.is_remote_frame else 0 message.uMsgInfo.Bits.ext = 1 if msg.is_extended_id else 0 message.uMsgInfo.Bits.srr = 1 if self._receive_own_messages else 0 + message.uMsgInfo.Bits.fdr = 1 if msg.bitrate_switch else 0 + message.uMsgInfo.Bits.edl = 1 if msg.is_fd else 0 message.dwMsgId = msg.arbitration_id - if msg.dlc: - message.uMsgInfo.Bits.dlc = msg.dlc - adapter = (ctypes.c_uint8 * len(msg.data)).from_buffer(msg.data) - ctypes.memmove(message.abData, adapter, len(msg.data)) + if msg.dlc: # this dlc means number of bytes of payload + message.uMsgInfo.Bits.dlc = can.util.len2dlc(msg.dlc) + data_len_dif = msg.dlc - len(msg.data) + data = msg.data + bytearray([0] * data_len_dif) # pad with zeros until required length + adapter = (ctypes.c_uint8 * msg.dlc).from_buffer(data) + ctypes.memmove(message.abData, adapter, msg.dlc) if timeout: _canlib.canChannelSendMessage( @@ -746,9 +781,9 @@ def _send_periodic_internal(self, msg, period, duration=None): if self._scheduler is None: self._scheduler = HANDLE() _canlib.canSchedulerOpen(self._device_handle, self.channel, self._scheduler) - caps = structures.CANCAPABILITIES() + caps = structures.CANCAPABILITIES2() _canlib.canSchedulerGetCaps(self._scheduler, caps) - self._scheduler_resolution = float(caps.dwClockFreq) / caps.dwCmsDivisor + self._scheduler_resolution = float(caps.dwCmsClkFreq) / caps.dwCmsDivisor # TODO: confirm _canlib.canSchedulerActivate(self._scheduler, constants.TRUE) return CyclicSendTask( self._scheduler, msg, period, duration, self._scheduler_resolution @@ -777,7 +812,7 @@ def __init__(self, scheduler, msgs, period, duration, resolution): self._index = None self._count = int(duration / period) if duration else 0 - self._msg = structures.CANCYCLICTXMSG() + self._msg = structures.CANCYCLICTXMSG2() self._msg.wCycleTime = int(round(period * resolution)) self._msg.dwMsgId = self.messages[0].arbitration_id self._msg.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA diff --git a/can/interfaces/ixxat_fd/constants.py b/can/interfaces/ixxat_fd/constants.py index e911a580a..36ca944cb 100644 --- a/can/interfaces/ixxat_fd/constants.py +++ b/can/interfaces/ixxat_fd/constants.py @@ -119,6 +119,11 @@ CAN_OPMODE_LISTONLY = 0x08 CAN_OPMODE_LOWSPEED = 0x10 +# Extended operating modes +CAN_EXMODE_EXTDATALEN = 0x01 +CAN_EXMODE_FASTDATA = 0x02 +CAN_EXMODE_NONISOCANFD = 0x04 + # Message types CAN_MSGTYPE_DATA = 0 CAN_MSGTYPE_INFO = 1 @@ -146,3 +151,36 @@ # acceptance code and mask to reject all CAN IDs CAN_ACC_MASK_NONE = 0xFFFFFFFF CAN_ACC_CODE_NONE = 0x80000000 + +# BTMODEs +CAN_BTMODE_RAW = 0x00000001 # raw mode +CAN_BTMODE_TSM = 0x00000002 # triple sampling mode + + +CAN_FILTER_VOID = 0x00 # invalid or unknown filter mode (do not use for initialization) +CAN_FILTER_LOCK = 0x01 # lock filter (inhibit all IDs) +CAN_FILTER_PASS = 0x02 # bypass filter (pass all IDs) +CAN_FILTER_INCL = 0x03 # inclusive filtering (pass registered IDs) +CAN_FILTER_EXCL = 0x04 # exclusive filtering (inhibit registered IDs) + + +CAN_MSGFLAGS_DLC = 0x0F # [bit 0] data length code +CAN_MSGFLAGS_OVR = 0x10 # [bit 4] data overrun flag +CAN_MSGFLAGS_SRR = 0x20 # [bit 5] self reception request +CAN_MSGFLAGS_RTR = 0x40 # [bit 6] remote transmission request +CAN_MSGFLAGS_EXT = 0x80 # [bit 7] frame format (0=11-bit, 1=29-bit) + + +CAN_MSGFLAGS2_SSM = 0x01 # [bit 0] single shot mode +CAN_MSGFLAGS2_HPM = 0x02 # [bit 1] high priority message +CAN_MSGFLAGS2_EDL = 0x04 # [bit 2] extended data length +CAN_MSGFLAGS2_FDR = 0x08 # [bit 3] fast data bit rate +CAN_MSGFLAGS2_ESI = 0x10 # [bit 4] error state indicator +CAN_MSGFLAGS2_RES = 0xE0 # [bit 5..7] reserved bits + + +CAN_ACCEPT_REJECT = 0x00 # message not accepted +CAN_ACCEPT_ALWAYS = 0xFF # message always accepted +CAN_ACCEPT_FILTER_1 = 0x01 # message accepted by filter 1 +CAN_ACCEPT_FILTER_2 = 0x02 # message accepted by filter 2 +CAN_ACCEPT_PASSEXCL = 0x03 # message passes exclusion filter diff --git a/can/interfaces/ixxat_fd/structures.py b/can/interfaces/ixxat_fd/structures.py index 73c01823d..c688b2831 100644 --- a/can/interfaces/ixxat_fd/structures.py +++ b/can/interfaces/ixxat_fd/structures.py @@ -108,30 +108,34 @@ class CANCAPABILITIES(ctypes.Structure): ] + PCANCAPABILITIES = ctypes.POINTER(CANCAPABILITIES) class CANMSGINFO(ctypes.Union): class Bytes(ctypes.Structure): _fields_ = [ - ("bType", ctypes.c_uint8), - ("bAddFlags", ctypes.c_uint8), - ("bFlags", ctypes.c_uint8), - ("bAccept", ctypes.c_uint8), + ("bType", ctypes.c_uint8), # type (see CAN_MSGTYPE_ constants) + ("bAddFlags", ctypes.c_uint8), # extended flags (see CAN_MSGFLAGS2_ constants) + ("bFlags", ctypes.c_uint8), # flags (see CAN_MSGFLAGS_ constants) + ("bAccept", ctypes.c_uint8), # accept code (see CAN_ACCEPT_ constants) ] class Bits(ctypes.Structure): _fields_ = [ - ("type", ctypes.c_uint32, 8), - ("ssm", ctypes.c_uint32, 1), - ("hi", ctypes.c_uint32, 2), - ("res", ctypes.c_uint32, 5), - ("dlc", ctypes.c_uint32, 4), - ("ovr", ctypes.c_uint32, 1), - ("srr", ctypes.c_uint32, 1), - ("rtr", ctypes.c_uint32, 1), - ("ext", ctypes.c_uint32, 1), - ("afc", ctypes.c_uint32, 8), + ("type", ctypes.c_uint32, 8), # type (see CAN_MSGTYPE_ constants) + ("ssm", ctypes.c_uint32, 1), # single shot mode + ("hpm", ctypes.c_uint32, 1), # high priority message + ("edl", ctypes.c_uint32, 1), # extended data length + ("fdr", ctypes.c_uint32, 1), # fast data bit rate + ("esi", ctypes.c_uint32, 1), # error state indicator + ("res", ctypes.c_uint32, 3), # reserved set to 0 + ("dlc", ctypes.c_uint32, 4), # data length code + ("ovr", ctypes.c_uint32, 1), # data overrun + ("srr", ctypes.c_uint32, 1), # self reception request + ("rtr", ctypes.c_uint32, 1), # remote transmission request + ("ext", ctypes.c_uint32, 1), # extended frame format (0=standard, 1=extended) + ("afc", ctypes.c_uint32, 8), # accept code (see CAN_ACCEPT_ constants) ] _fields_ = [("Bytes", Bytes), ("Bits", Bits)] @@ -164,3 +168,84 @@ class CANCYCLICTXMSG(ctypes.Structure): PCANCYCLICTXMSG = ctypes.POINTER(CANCYCLICTXMSG) + + +class CANBTP(ctypes.Structure): + _fields_ = [ + ("dwMode", ctypes.c_uint32), # timing mode (see CAN_BTMODE_ const) + ("dwBPS", ctypes.c_uint32), # bits per second or prescaler (see CAN_BTMODE_RAW) + ("wTS1", ctypes.c_uint16), # length of time segment 1 in quanta + ("wTS2", ctypes.c_uint16), # length of time segment 2 in quanta + ("wSJW", ctypes.c_uint16), # re-synchronization jump width im quanta + ("wTDO", ctypes.c_uint16), # transceiver delay offset (SSP offset) in quanta (0 = disabled, 0xFFFF = simplified SSP positioning) + ] + + +PCANBTP = ctypes.POINTER(CANBTP) + + +class CANCAPABILITIES2(ctypes.Structure): + _fields_ = [ + ("wCtrlType", ctypes.c_uint16), # Type of CAN controller (see CAN_CTRL_ const) + ("wBusCoupling", ctypes.c_uint16), # Type of Bus coupling (see CAN_BUSC_ const) + ("dwFeatures", ctypes.c_uint32), # supported features (see CAN_FEATURE_ constants) + ("dwCanClkFreq" , ctypes.c_uint32), # CAN clock frequency [Hz] + ("sSdrRangeMin" , CANBTP), # minimum bit timing values for standard bit rate + ("sSdrRangeMax" , CANBTP), # maximum bit timing values for standard bit rate + ("sFdrRangeMin" , CANBTP), # minimum bit timing values for fast data bit rate + ("sFdrRangeMax" , CANBTP), # maximum bit timing values for fast data bit rate + ("dwTscClkFreq" , ctypes.c_uint32), # clock frequency of the time stamp counter [Hz] + ("dwTscDivisor" , ctypes.c_uint32), # divisor for the message time stamp counter + ("dwCmsClkFreq" , ctypes.c_uint32), # clock frequency of cyclic message scheduler [Hz] + ("dwCmsDivisor" , ctypes.c_uint32), # divisor for the cyclic message scheduler + ("dwCmsMaxTicks", ctypes.c_uint32), # maximum tick count value of the cyclic message + ("dwDtxClkFreq" , ctypes.c_uint32), # clock frequency of the delayed message transmitter [Hz] + ("dwDtxDivisor" , ctypes.c_uint32), # divisor for the delayed message transmitter + ("dwDtxMaxTicks", ctypes.c_uint32), # maximum tick count value of the delayed message transmitter + ] + + +PCANCAPABILITIES2 = ctypes.POINTER(CANCAPABILITIES2) + + +class CANLINESTATUS2(ctypes.Structure): + _fields_ = [ + ("bOpMode", ctypes.c_uint8), # current CAN operating mode + ("bExMode", ctypes.c_uint8), # current CAN extended operating mode + ("bBusLoad", ctypes.c_uint8), # average bus load in percent (0..100) + ("bReserved", ctypes.c_uint8), # reserved set to 0 + ("sBtpSdr", ctypes.c_uint8), # standard bit rate timing + ("sBtpFdr", ctypes.c_uint8), # fast data bit rate timing + ("dwStatus", ctypes.c_uint32), # status of the CAN controller (see CAN_STATUS_) + ] + + +PCANLINESTATUS2 = ctypes.POINTER(CANLINESTATUS2) + +class CANMSG2(ctypes.Structure): + _fields_ = [ + ("dwTime", ctypes.c_uint32), # time stamp for receive message + ("rsvd", ctypes.c_uint32), # reserved (set to 0) + ("dwMsgId", ctypes.c_uint32), # CAN message identifier (INTEL format) + ("uMsgInfo", CANMSGINFO), # message information (bit field) + ("abData", ctypes.c_uint8 * 64), # message data + ] + + +PCANMSG2 = ctypes.POINTER(CANMSG2) + + +class CANCYCLICTXMSG2(ctypes.Structure): + _fields_ = [ + ("wCycleTime", ctypes.c_uint16), # cycle time for the message in ticks + ("bIncrMode", ctypes.c_uint8), # auto increment mode (see CAN_CTXMSG_INC_ const) + ("bByteIndex", ctypes.c_uint8), # index of the byte within abData[] to increment + ("dwMsgId", ctypes.c_uint32), # message identifier (INTEL format) + ("uMsgInfo", CANMSGINFO), # message information (bit field) + ("abData", ctypes.c_uint8 * 64), # message data + ] + + +PCANCYCLICTXMSG2 = ctypes.POINTER(CANCYCLICTXMSG2) + + From 178b813f64d8d746f18c4b746fe91b39f6022c26 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 16 Aug 2021 11:21:50 +0200 Subject: [PATCH 03/22] Fixed: ixxat fd receive function with timeout = 0 was causing to receive always last frame. Now, if function does not return VCI_OK, no new message is returned. --- can/interfaces/ixxat_fd/canlib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/can/interfaces/ixxat_fd/canlib.py b/can/interfaces/ixxat_fd/canlib.py index 7f0bb263c..229560dc5 100644 --- a/can/interfaces/ixxat_fd/canlib.py +++ b/can/interfaces/ixxat_fd/canlib.py @@ -650,7 +650,8 @@ def _recv_internal(self, timeout): _canlib.canChannelPeekMessage( self._channel_handle, ctypes.byref(self._message) ) - except (VCITimeout, VCIRxQueueEmptyError): + except (VCITimeout, VCIRxQueueEmptyError, VCIError): + # VCIError means no frame available (canChannelPeekMessage returned different from zero) return None, True else: if self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_DATA: From 0a31bde465e52c3884af9e14bf225ee0ecaa8597 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Wed, 18 Aug 2021 10:50:43 +0200 Subject: [PATCH 04/22] Added: ixxat_fd baudrate from user options instead of hardcoded. --- can/interfaces/ixxat_fd/canlib.py | 64 +++++---------------------- can/interfaces/ixxat_fd/constants.py | 20 +++++++++ can/interfaces/ixxat_fd/structures.py | 3 ++ 3 files changed, 35 insertions(+), 52 deletions(-) diff --git a/can/interfaces/ixxat_fd/canlib.py b/can/interfaces/ixxat_fd/canlib.py index 229560dc5..0aa90b2ed 100644 --- a/can/interfaces/ixxat_fd/canlib.py +++ b/can/interfaces/ixxat_fd/canlib.py @@ -383,39 +383,6 @@ class IXXATBus(BusABC): """ - CHANNEL_BITRATES = { - 0: { - 10000: constants.CAN_BT0_10KB, - 20000: constants.CAN_BT0_20KB, - 50000: constants.CAN_BT0_50KB, - 100000: constants.CAN_BT0_100KB, - 125000: constants.CAN_BT0_125KB, - 250000: constants.CAN_BT0_250KB, - 500000: constants.CAN_BT0_500KB, - 666000: constants.CAN_BT0_667KB, - 666666: constants.CAN_BT0_667KB, - 666667: constants.CAN_BT0_667KB, - 667000: constants.CAN_BT0_667KB, - 800000: constants.CAN_BT0_800KB, - 1000000: constants.CAN_BT0_1000KB, - }, - 1: { - 10000: constants.CAN_BT1_10KB, - 20000: constants.CAN_BT1_20KB, - 50000: constants.CAN_BT1_50KB, - 100000: constants.CAN_BT1_100KB, - 125000: constants.CAN_BT1_125KB, - 250000: constants.CAN_BT1_250KB, - 500000: constants.CAN_BT1_500KB, - 666000: constants.CAN_BT1_667KB, - 666666: constants.CAN_BT1_667KB, - 666667: constants.CAN_BT1_667KB, - 667000: constants.CAN_BT1_667KB, - 800000: constants.CAN_BT1_800KB, - 1000000: constants.CAN_BT1_1000KB, - }, - } - def __init__(self, channel, can_filters=None, **kwargs): """ :param int channel: @@ -441,6 +408,7 @@ def __init__(self, channel, can_filters=None, **kwargs): log.info("Got configuration of: %s", kwargs) # Configuration options bitrate = kwargs.get("bitrate", 500000) + data_bitrate = kwargs.get("data_bitrate", bitrate) UniqueHardwareId = kwargs.get("UniqueHardwareId", None) rxFifoSize = kwargs.get("rxFifoSize", 1024) txFifoSize = kwargs.get("txFifoSize", 128) @@ -448,7 +416,10 @@ def __init__(self, channel, can_filters=None, **kwargs): # Usually comes as a string from the config file channel = int(channel) - if bitrate not in self.CHANNEL_BITRATES[0]: + if bitrate not in constants.CAN_BITRATE_PRESETS: + raise ValueError("Invalid bitrate {}".format(bitrate)) + + if data_bitrate not in constants.CAN_DATABITRATE_PRESETS: raise ValueError("Invalid bitrate {}".format(bitrate)) if rxFifoSize <= 0: @@ -536,27 +507,16 @@ def __init__(self, channel, can_filters=None, **kwargs): _canlib.canChannelInitialize(self._channel_handle, rxFifoSize, 1, txFifoSize, 1, 0, constants.CAN_FILTER_PASS) _canlib.canChannelActivate(self._channel_handle, constants.TRUE) - log.info("Initializing control %d bitrate %d", channel, bitrate) + pBtpSDR = constants.CAN_BITRATE_PRESETS[bitrate] + pBtpFDR = constants.CAN_DATABITRATE_PRESETS[data_bitrate] + log.info("Initializing control %d with SDR={%s}, FDR={%s}", + channel, + pBtpSDR, + pBtpFDR, + ) _canlib.canControlOpen( self._device_handle, channel, ctypes.byref(self._control_handle) ) - # TODO: fill from parametters - # TODO: confirm 500/2000 - pBtpSDR = structures.CANBTP() - pBtpSDR.dwMode = 0 #constants.CAN_BTMODE_RAW - pBtpSDR.dwBPS = 500000 - pBtpSDR.wTS1 = 6400 - pBtpSDR.wTS2 = 1600 - pBtpSDR.wSJW = 1600 - pBtpSDR.wTDO = 0 - - pBtpFDR = structures.CANBTP() - pBtpFDR.dwMode = 0 #constants.CAN_BTMODE_RAW - pBtpFDR.dwBPS = 2000000 - pBtpFDR.wTS1 = 1600 - pBtpFDR.wTS2 = 400 - pBtpFDR.wSJW = 400 - pBtpFDR.wTDO = 1600 _canlib.canControlInitialize( self._control_handle, diff --git a/can/interfaces/ixxat_fd/constants.py b/can/interfaces/ixxat_fd/constants.py index 36ca944cb..842a5a4c1 100644 --- a/can/interfaces/ixxat_fd/constants.py +++ b/can/interfaces/ixxat_fd/constants.py @@ -4,6 +4,8 @@ Copyright (C) 2016 Giuseppe Corbelli """ +from . import structures + FALSE = 0 TRUE = 1 @@ -184,3 +186,21 @@ CAN_ACCEPT_FILTER_1 = 0x01 # message accepted by filter 1 CAN_ACCEPT_FILTER_2 = 0x02 # message accepted by filter 2 CAN_ACCEPT_PASSEXCL = 0x03 # message passes exclusion filter + + +CAN_BITRATE_PRESETS = { + 500000: structures.CANBTP(dwMode=0, dwBPS= 500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0), # SP = 80,0% + 1000000: structures.CANBTP(dwMode=0, dwBPS=1000000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0), # SP = 80,0% +} + +CAN_DATABITRATE_PRESETS = { + 1000000: structures.CANBTP(dwMode=0, dwBPS= 1000000, wTS1=1600, wTS2= 400, wSJW= 400, wTDO=1600), # SP = 80,0% + 2000000: structures.CANBTP(dwMode=0, dwBPS= 2000000, wTS1=1600, wTS2= 400, wSJW= 400, wTDO=1600), # SP = 80,0% + 4000000: structures.CANBTP(dwMode=0, dwBPS= 4000000, wTS1= 800, wTS2= 200, wSJW= 200, wTDO= 800), # SP = 80,0% + 5000000: structures.CANBTP(dwMode=0, dwBPS= 5000000, wTS1= 600, wTS2= 200, wSJW= 200, wTDO= 600), # SP = 75,0% + 6666666: structures.CANBTP(dwMode=0, dwBPS= 6666666, wTS1= 400, wTS2= 200, wSJW= 200, wTDO= 402), # SP = 66,7% + 8000000: structures.CANBTP(dwMode=0, dwBPS= 8000000, wTS1= 400, wTS2= 100, wSJW= 100, wTDO= 250), # SP = 80,0% + 10000000: structures.CANBTP(dwMode=0, dwBPS=10000000, wTS1= 300, wTS2= 100, wSJW= 100, wTDO= 200), # SP = 75,0% +} + + diff --git a/can/interfaces/ixxat_fd/structures.py b/can/interfaces/ixxat_fd/structures.py index c688b2831..68ace5da3 100644 --- a/can/interfaces/ixxat_fd/structures.py +++ b/can/interfaces/ixxat_fd/structures.py @@ -180,6 +180,9 @@ class CANBTP(ctypes.Structure): ("wTDO", ctypes.c_uint16), # transceiver delay offset (SSP offset) in quanta (0 = disabled, 0xFFFF = simplified SSP positioning) ] + def __str__(self): + return "dwMode=%d, dwBPS=%d, wTS1=%d, wTS2=%d, wSJW=%d, wTDO=%d" % (self.dwMode, self.dwBPS, self.wTS1, self.wTS2, self.wSJW, self.wTDO) + PCANBTP = ctypes.POINTER(CANBTP) From 6dbaa1c788c54bb6133f2a8d12674c07f70adcd4 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Wed, 18 Aug 2021 11:23:57 +0200 Subject: [PATCH 05/22] Added: ixxat_fd channel capabilities check. --- can/interfaces/ixxat_fd/canlib.py | 38 +++++++++++++++++++++++----- can/interfaces/ixxat_fd/constants.py | 21 +++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/can/interfaces/ixxat_fd/canlib.py b/can/interfaces/ixxat_fd/canlib.py index 0aa90b2ed..7b686b499 100644 --- a/can/interfaces/ixxat_fd/canlib.py +++ b/can/interfaces/ixxat_fd/canlib.py @@ -412,6 +412,7 @@ def __init__(self, channel, can_filters=None, **kwargs): UniqueHardwareId = kwargs.get("UniqueHardwareId", None) rxFifoSize = kwargs.get("rxFifoSize", 1024) txFifoSize = kwargs.get("txFifoSize", 128) + extended = kwargs.get("extended", False) self._receive_own_messages = kwargs.get("receive_own_messages", False) # Usually comes as a string from the config file channel = int(channel) @@ -518,10 +519,39 @@ def __init__(self, channel, can_filters=None, **kwargs): self._device_handle, channel, ctypes.byref(self._control_handle) ) + _canlib.canControlGetCaps( + self._control_handle, ctypes.byref(self._channel_capabilities) + ) + + # check capabilities + bOpMode = constants.CAN_OPMODE_UNDEFINED + if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_STDANDEXT) != 0: + # controller supportes CAN_OPMODE_STANDARD and CAN_OPMODE_EXTENDED at the same time + bOpMode |= constants.CAN_OPMODE_STANDARD # enable both 11 bits reception + if extended: # parameter from configuration + bOpMode |= constants.CAN_OPMODE_EXTENDED # enable 29 bits reception + elif (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_STDANDEXT) != 0: + log.warning("Channel %d capabilities allow either basic or extended IDs, but not both. using %s according to parameter [extended=%s]", + channel, + "extended" if extended else "basic", + "True" if extended else "False", + ) + bOpMode |= constants.CAN_OPMODE_EXTENDED if extended else constants.CAN_OPMODE_STANDARD + + if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_ERRFRAME) != 0: + bOpMode |= constants.CAN_OPMODE_ERRFRAME + + bExMode = constants.CAN_EXMODE_DISABLED + if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_EXTDATA) != 0: + bExMode |= constants.CAN_EXMODE_EXTDATALEN + + if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_FASTDATA) != 0: + bExMode |= constants.CAN_EXMODE_FASTDATA + _canlib.canControlInitialize( self._control_handle, - constants.CAN_OPMODE_STANDARD | constants.CAN_OPMODE_EXTENDED | constants.CAN_OPMODE_ERRFRAME, - constants.CAN_EXMODE_FASTDATA | constants.CAN_EXMODE_EXTDATALEN, + bOpMode, + bExMode, constants.CAN_FILTER_PASS, constants.CAN_FILTER_PASS, 0, @@ -530,10 +560,6 @@ def __init__(self, channel, can_filters=None, **kwargs): ctypes.byref(pBtpFDR) ) - _canlib.canControlGetCaps( - self._control_handle, ctypes.byref(self._channel_capabilities) - ) - # With receive messages, this field contains the relative reception time of # the message in ticks. The resolution of a tick can be calculated from the fields # dwClockFreq and dwTscDivisor of the structure CANCAPABILITIES in accordance with the following formula: diff --git a/can/interfaces/ixxat_fd/constants.py b/can/interfaces/ixxat_fd/constants.py index 842a5a4c1..7262713d4 100644 --- a/can/interfaces/ixxat_fd/constants.py +++ b/can/interfaces/ixxat_fd/constants.py @@ -122,6 +122,7 @@ CAN_OPMODE_LOWSPEED = 0x10 # Extended operating modes +CAN_EXMODE_DISABLED = 0x00 CAN_EXMODE_EXTDATALEN = 0x01 CAN_EXMODE_FASTDATA = 0x02 CAN_EXMODE_NONISOCANFD = 0x04 @@ -188,6 +189,26 @@ CAN_ACCEPT_PASSEXCL = 0x03 # message passes exclusion filter +CAN_FEATURE_STDOREXT = 0x00000001 # 11 OR 29 bit (exclusive) +CAN_FEATURE_STDANDEXT = 0x00000002 # 11 AND 29 bit (simultaneous) +CAN_FEATURE_RMTFRAME = 0x00000004 # reception of remote frames +CAN_FEATURE_ERRFRAME = 0x00000008 # reception of error frames +CAN_FEATURE_BUSLOAD = 0x00000010 # bus load measurement +CAN_FEATURE_IDFILTER = 0x00000020 # exact message filter +CAN_FEATURE_LISTONLY = 0x00000040 # listen only mode +CAN_FEATURE_SCHEDULER = 0x00000080 # cyclic message scheduler +CAN_FEATURE_GENERRFRM = 0x00000100 # error frame generation +CAN_FEATURE_DELAYEDTX = 0x00000200 # delayed message transmitter +CAN_FEATURE_SINGLESHOT = 0x00000400 # single shot mode +CAN_FEATURE_HIGHPRIOR = 0x00000800 # high priority message +CAN_FEATURE_AUTOBAUD = 0x00001000 # automatic bit rate detection +CAN_FEATURE_EXTDATA = 0x00002000 # extended data length (CANFD) +CAN_FEATURE_FASTDATA = 0x00004000 # fast data bit rate (CANFD) +CAN_FEATURE_ISOFRAME = 0x00008000 # ISO conform frame (CANFD) +CAN_FEATURE_NONISOFRM = 0x00010000 # non ISO conform frame (CANFD) (different CRC computation) +CAN_FEATURE_64BITTSC = 0x00020000 # 64-bit time stamp counter + + CAN_BITRATE_PRESETS = { 500000: structures.CANBTP(dwMode=0, dwBPS= 500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0), # SP = 80,0% 1000000: structures.CANBTP(dwMode=0, dwBPS=1000000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0), # SP = 80,0% From a0af8319770c1299ba1749d2a49c82dda0a49b98 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Wed, 18 Aug 2021 19:43:24 +0200 Subject: [PATCH 06/22] Added: Myself to contributors list. Added: Documentation for ixxat_fd usage. --- CONTRIBUTORS.txt | 1 + doc/interfaces/ixxat.rst | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index b7ac9fbd4..c4bd2b76a 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -28,3 +28,4 @@ Jan Goeteyn "ykzheng" Lear Corporation Nick Black +Francisco Javier Burgos Macia diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index 929221733..f90df65ce 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -80,3 +80,15 @@ explicitly instantiated by the caller. RX and TX FIFO sizes are configurable with ``rxFifoSize`` and ``txFifoSize`` options, defaulting to 16 for both. + + +CAN FD +------ + +Experimental support for CAN-FD can be enabled by using interface name "ixxat_fd" instead of "ixxat". + +Additional parameters when using this interface are: + +* ``data_bitrate`` (defaults to same value passed for bitrate) Channel data bitrate (to use when +message bitrate_switch is used). + From 2dfd3cba0d7d6ba07084808707e581655c03c36d Mon Sep 17 00:00:00 2001 From: fjburgos Date: Sat, 21 Aug 2021 13:42:53 +0200 Subject: [PATCH 07/22] Related to ixxat_fd: Added: Format with black. Added: test to improve coverage. Changed: Default baudrate for data segment is now 2 Mbps. --- can/interfaces/ixxat_fd/canlib.py | 94 +++++++++++---- can/interfaces/ixxat_fd/constants.py | 154 +++++++++++++++---------- can/interfaces/ixxat_fd/exceptions.py | 6 +- can/interfaces/ixxat_fd/structures.py | 158 ++++++++++++++++---------- doc/interfaces/ixxat.rst | 2 +- test/test_interface_ixxat_fd.py | 62 ++++++++++ 6 files changed, 327 insertions(+), 149 deletions(-) create mode 100644 test/test_interface_ixxat_fd.py diff --git a/can/interfaces/ixxat_fd/canlib.py b/can/interfaces/ixxat_fd/canlib.py index 7b686b499..df5690e95 100644 --- a/can/interfaces/ixxat_fd/canlib.py +++ b/can/interfaces/ixxat_fd/canlib.py @@ -179,7 +179,15 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canChannelInitialize", ctypes.c_long, - (HANDLE, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint32, ctypes.c_uint8), + ( + HANDLE, + ctypes.c_uint16, + ctypes.c_uint16, + ctypes.c_uint16, + ctypes.c_uint16, + ctypes.c_uint32, + ctypes.c_uint8, + ), __check_status, ) # EXTERN_C HRESULT VCIAPI canChannelActivate( IN HANDLE hCanChn, IN BOOL fEnable ); @@ -242,7 +250,17 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canControlInitialize", ctypes.c_long, - (HANDLE, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint8, ctypes.c_uint32, ctypes.c_uint32, structures.PCANBTP, structures.PCANBTP), + ( + HANDLE, + ctypes.c_uint8, + ctypes.c_uint8, + ctypes.c_uint8, + ctypes.c_uint8, + ctypes.c_uint32, + ctypes.c_uint32, + structures.PCANBTP, + structures.PCANBTP, + ), __check_status, ) # EXTERN_C HRESULT VCIAPI canControlClose( IN HANDLE hCanCtl ); @@ -408,7 +426,7 @@ def __init__(self, channel, can_filters=None, **kwargs): log.info("Got configuration of: %s", kwargs) # Configuration options bitrate = kwargs.get("bitrate", 500000) - data_bitrate = kwargs.get("data_bitrate", bitrate) + data_bitrate = kwargs.get("data_bitrate", 2000000) UniqueHardwareId = kwargs.get("UniqueHardwareId", None) rxFifoSize = kwargs.get("rxFifoSize", 1024) txFifoSize = kwargs.get("txFifoSize", 128) @@ -505,15 +523,24 @@ def __init__(self, channel, can_filters=None, **kwargs): ) # Signal TX/RX events when at least one frame has been handled - _canlib.canChannelInitialize(self._channel_handle, rxFifoSize, 1, txFifoSize, 1, 0, constants.CAN_FILTER_PASS) + _canlib.canChannelInitialize( + self._channel_handle, + rxFifoSize, + 1, + txFifoSize, + 1, + 0, + constants.CAN_FILTER_PASS, + ) _canlib.canChannelActivate(self._channel_handle, constants.TRUE) pBtpSDR = constants.CAN_BITRATE_PRESETS[bitrate] pBtpFDR = constants.CAN_DATABITRATE_PRESETS[data_bitrate] - log.info("Initializing control %d with SDR={%s}, FDR={%s}", - channel, - pBtpSDR, - pBtpFDR, + log.info( + "Initializing control %d with SDR={%s}, FDR={%s}", + channel, + pBtpSDR, + pBtpFDR, ) _canlib.canControlOpen( self._device_handle, channel, ctypes.byref(self._control_handle) @@ -525,27 +552,40 @@ def __init__(self, channel, can_filters=None, **kwargs): # check capabilities bOpMode = constants.CAN_OPMODE_UNDEFINED - if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_STDANDEXT) != 0: + if ( + self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_STDANDEXT + ) != 0: # controller supportes CAN_OPMODE_STANDARD and CAN_OPMODE_EXTENDED at the same time - bOpMode |= constants.CAN_OPMODE_STANDARD # enable both 11 bits reception - if extended: # parameter from configuration - bOpMode |= constants.CAN_OPMODE_EXTENDED # enable 29 bits reception - elif (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_STDANDEXT) != 0: - log.warning("Channel %d capabilities allow either basic or extended IDs, but not both. using %s according to parameter [extended=%s]", + bOpMode |= constants.CAN_OPMODE_STANDARD # enable both 11 bits reception + if extended: # parameter from configuration + bOpMode |= constants.CAN_OPMODE_EXTENDED # enable 29 bits reception + elif ( + self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_STDANDEXT + ) != 0: + log.warning( + "Channel %d capabilities allow either basic or extended IDs, but not both. using %s according to parameter [extended=%s]", channel, "extended" if extended else "basic", "True" if extended else "False", ) - bOpMode |= constants.CAN_OPMODE_EXTENDED if extended else constants.CAN_OPMODE_STANDARD + bOpMode |= ( + constants.CAN_OPMODE_EXTENDED + if extended + else constants.CAN_OPMODE_STANDARD + ) - if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_ERRFRAME) != 0: + if ( + self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_ERRFRAME + ) != 0: bOpMode |= constants.CAN_OPMODE_ERRFRAME bExMode = constants.CAN_EXMODE_DISABLED if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_EXTDATA) != 0: bExMode |= constants.CAN_EXMODE_EXTDATALEN - if (self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_FASTDATA) != 0: + if ( + self._channel_capabilities.dwFeatures & constants.CAN_FEATURE_FASTDATA + ) != 0: bExMode |= constants.CAN_EXMODE_FASTDATA _canlib.canControlInitialize( @@ -557,7 +597,7 @@ def __init__(self, channel, can_filters=None, **kwargs): 0, 0, ctypes.byref(pBtpSDR), - ctypes.byref(pBtpFDR) + ctypes.byref(pBtpFDR), ) # With receive messages, this field contains the relative reception time of @@ -566,7 +606,7 @@ def __init__(self, channel, can_filters=None, **kwargs): # frequency [1/s] = dwClockFreq / dwTscDivisor # We explicitly cast to float for Python 2.x users self._tick_resolution = float( - self._channel_capabilities.dwTscClkFreq # TODO confirm + self._channel_capabilities.dwTscClkFreq # TODO confirm / self._channel_capabilities.dwTscDivisor ) @@ -620,12 +660,12 @@ def _inWaiting(self): return 1 def flush_tx_buffer(self): - """ Flushes the transmit buffer on the IXXAT """ + """Flushes the transmit buffer on the IXXAT""" # TODO #64: no timeout? _canlib.canChannelWaitTxEvent(self._channel_handle, constants.INFINITE) def _recv_internal(self, timeout): - """ Read a message from IXXAT device. """ + """Read a message from IXXAT device.""" # TODO: handling CAN error messages? data_received = False @@ -721,7 +761,7 @@ def _recv_internal(self, timeout): is_extended_id=bool(self._message.uMsgInfo.Bits.ext), arbitration_id=self._message.dwMsgId, dlc=data_len, - data=self._message.abData[: data_len], + data=self._message.abData[:data_len], channel=self.channel, ) @@ -748,10 +788,12 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None: message.uMsgInfo.Bits.fdr = 1 if msg.bitrate_switch else 0 message.uMsgInfo.Bits.edl = 1 if msg.is_fd else 0 message.dwMsgId = msg.arbitration_id - if msg.dlc: # this dlc means number of bytes of payload + if msg.dlc: # this dlc means number of bytes of payload message.uMsgInfo.Bits.dlc = can.util.len2dlc(msg.dlc) data_len_dif = msg.dlc - len(msg.data) - data = msg.data + bytearray([0] * data_len_dif) # pad with zeros until required length + data = msg.data + bytearray( + [0] * data_len_dif + ) # pad with zeros until required length adapter = (ctypes.c_uint8 * msg.dlc).from_buffer(data) ctypes.memmove(message.abData, adapter, msg.dlc) @@ -770,7 +812,9 @@ def _send_periodic_internal(self, msg, period, duration=None): _canlib.canSchedulerOpen(self._device_handle, self.channel, self._scheduler) caps = structures.CANCAPABILITIES2() _canlib.canSchedulerGetCaps(self._scheduler, caps) - self._scheduler_resolution = float(caps.dwCmsClkFreq) / caps.dwCmsDivisor # TODO: confirm + self._scheduler_resolution = ( + float(caps.dwCmsClkFreq) / caps.dwCmsDivisor + ) # TODO: confirm _canlib.canSchedulerActivate(self._scheduler, constants.TRUE) return CyclicSendTask( self._scheduler, msg, period, duration, self._scheduler_resolution diff --git a/can/interfaces/ixxat_fd/constants.py b/can/interfaces/ixxat_fd/constants.py index 7262713d4..1dbc22a44 100644 --- a/can/interfaces/ixxat_fd/constants.py +++ b/can/interfaces/ixxat_fd/constants.py @@ -156,72 +156,102 @@ CAN_ACC_CODE_NONE = 0x80000000 # BTMODEs -CAN_BTMODE_RAW = 0x00000001 # raw mode -CAN_BTMODE_TSM = 0x00000002 # triple sampling mode - - -CAN_FILTER_VOID = 0x00 # invalid or unknown filter mode (do not use for initialization) -CAN_FILTER_LOCK = 0x01 # lock filter (inhibit all IDs) -CAN_FILTER_PASS = 0x02 # bypass filter (pass all IDs) -CAN_FILTER_INCL = 0x03 # inclusive filtering (pass registered IDs) -CAN_FILTER_EXCL = 0x04 # exclusive filtering (inhibit registered IDs) - - -CAN_MSGFLAGS_DLC = 0x0F # [bit 0] data length code -CAN_MSGFLAGS_OVR = 0x10 # [bit 4] data overrun flag -CAN_MSGFLAGS_SRR = 0x20 # [bit 5] self reception request -CAN_MSGFLAGS_RTR = 0x40 # [bit 6] remote transmission request -CAN_MSGFLAGS_EXT = 0x80 # [bit 7] frame format (0=11-bit, 1=29-bit) - - -CAN_MSGFLAGS2_SSM = 0x01 # [bit 0] single shot mode -CAN_MSGFLAGS2_HPM = 0x02 # [bit 1] high priority message -CAN_MSGFLAGS2_EDL = 0x04 # [bit 2] extended data length -CAN_MSGFLAGS2_FDR = 0x08 # [bit 3] fast data bit rate -CAN_MSGFLAGS2_ESI = 0x10 # [bit 4] error state indicator -CAN_MSGFLAGS2_RES = 0xE0 # [bit 5..7] reserved bits - - -CAN_ACCEPT_REJECT = 0x00 # message not accepted -CAN_ACCEPT_ALWAYS = 0xFF # message always accepted -CAN_ACCEPT_FILTER_1 = 0x01 # message accepted by filter 1 -CAN_ACCEPT_FILTER_2 = 0x02 # message accepted by filter 2 -CAN_ACCEPT_PASSEXCL = 0x03 # message passes exclusion filter - - -CAN_FEATURE_STDOREXT = 0x00000001 # 11 OR 29 bit (exclusive) -CAN_FEATURE_STDANDEXT = 0x00000002 # 11 AND 29 bit (simultaneous) -CAN_FEATURE_RMTFRAME = 0x00000004 # reception of remote frames -CAN_FEATURE_ERRFRAME = 0x00000008 # reception of error frames -CAN_FEATURE_BUSLOAD = 0x00000010 # bus load measurement -CAN_FEATURE_IDFILTER = 0x00000020 # exact message filter -CAN_FEATURE_LISTONLY = 0x00000040 # listen only mode -CAN_FEATURE_SCHEDULER = 0x00000080 # cyclic message scheduler -CAN_FEATURE_GENERRFRM = 0x00000100 # error frame generation -CAN_FEATURE_DELAYEDTX = 0x00000200 # delayed message transmitter -CAN_FEATURE_SINGLESHOT = 0x00000400 # single shot mode -CAN_FEATURE_HIGHPRIOR = 0x00000800 # high priority message -CAN_FEATURE_AUTOBAUD = 0x00001000 # automatic bit rate detection -CAN_FEATURE_EXTDATA = 0x00002000 # extended data length (CANFD) -CAN_FEATURE_FASTDATA = 0x00004000 # fast data bit rate (CANFD) -CAN_FEATURE_ISOFRAME = 0x00008000 # ISO conform frame (CANFD) -CAN_FEATURE_NONISOFRM = 0x00010000 # non ISO conform frame (CANFD) (different CRC computation) -CAN_FEATURE_64BITTSC = 0x00020000 # 64-bit time stamp counter +CAN_BTMODE_RAW = 0x00000001 # raw mode +CAN_BTMODE_TSM = 0x00000002 # triple sampling mode + + +CAN_FILTER_VOID = 0x00 # invalid or unknown filter mode (do not use for initialization) +CAN_FILTER_LOCK = 0x01 # lock filter (inhibit all IDs) +CAN_FILTER_PASS = 0x02 # bypass filter (pass all IDs) +CAN_FILTER_INCL = 0x03 # inclusive filtering (pass registered IDs) +CAN_FILTER_EXCL = 0x04 # exclusive filtering (inhibit registered IDs) + + +CAN_MSGFLAGS_DLC = 0x0F # [bit 0] data length code +CAN_MSGFLAGS_OVR = 0x10 # [bit 4] data overrun flag +CAN_MSGFLAGS_SRR = 0x20 # [bit 5] self reception request +CAN_MSGFLAGS_RTR = 0x40 # [bit 6] remote transmission request +CAN_MSGFLAGS_EXT = 0x80 # [bit 7] frame format (0=11-bit, 1=29-bit) + + +CAN_MSGFLAGS2_SSM = 0x01 # [bit 0] single shot mode +CAN_MSGFLAGS2_HPM = 0x02 # [bit 1] high priority message +CAN_MSGFLAGS2_EDL = 0x04 # [bit 2] extended data length +CAN_MSGFLAGS2_FDR = 0x08 # [bit 3] fast data bit rate +CAN_MSGFLAGS2_ESI = 0x10 # [bit 4] error state indicator +CAN_MSGFLAGS2_RES = 0xE0 # [bit 5..7] reserved bits + + +CAN_ACCEPT_REJECT = 0x00 # message not accepted +CAN_ACCEPT_ALWAYS = 0xFF # message always accepted +CAN_ACCEPT_FILTER_1 = 0x01 # message accepted by filter 1 +CAN_ACCEPT_FILTER_2 = 0x02 # message accepted by filter 2 +CAN_ACCEPT_PASSEXCL = 0x03 # message passes exclusion filter + + +CAN_FEATURE_STDOREXT = 0x00000001 # 11 OR 29 bit (exclusive) +CAN_FEATURE_STDANDEXT = 0x00000002 # 11 AND 29 bit (simultaneous) +CAN_FEATURE_RMTFRAME = 0x00000004 # reception of remote frames +CAN_FEATURE_ERRFRAME = 0x00000008 # reception of error frames +CAN_FEATURE_BUSLOAD = 0x00000010 # bus load measurement +CAN_FEATURE_IDFILTER = 0x00000020 # exact message filter +CAN_FEATURE_LISTONLY = 0x00000040 # listen only mode +CAN_FEATURE_SCHEDULER = 0x00000080 # cyclic message scheduler +CAN_FEATURE_GENERRFRM = 0x00000100 # error frame generation +CAN_FEATURE_DELAYEDTX = 0x00000200 # delayed message transmitter +CAN_FEATURE_SINGLESHOT = 0x00000400 # single shot mode +CAN_FEATURE_HIGHPRIOR = 0x00000800 # high priority message +CAN_FEATURE_AUTOBAUD = 0x00001000 # automatic bit rate detection +CAN_FEATURE_EXTDATA = 0x00002000 # extended data length (CANFD) +CAN_FEATURE_FASTDATA = 0x00004000 # fast data bit rate (CANFD) +CAN_FEATURE_ISOFRAME = 0x00008000 # ISO conform frame (CANFD) +CAN_FEATURE_NONISOFRM = ( + 0x00010000 # non ISO conform frame (CANFD) (different CRC computation) +) +CAN_FEATURE_64BITTSC = 0x00020000 # 64-bit time stamp counter CAN_BITRATE_PRESETS = { - 500000: structures.CANBTP(dwMode=0, dwBPS= 500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0), # SP = 80,0% - 1000000: structures.CANBTP(dwMode=0, dwBPS=1000000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0), # SP = 80,0% + 250000: structures.CANBTP( + dwMode=0, dwBPS=250000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 + ), # SP = 80,0% + 500000: structures.CANBTP( + dwMode=0, dwBPS=500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 + ), # SP = 80,0% + 1000000: structures.CANBTP( + dwMode=0, dwBPS=1000000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 + ), # SP = 80,0% } CAN_DATABITRATE_PRESETS = { - 1000000: structures.CANBTP(dwMode=0, dwBPS= 1000000, wTS1=1600, wTS2= 400, wSJW= 400, wTDO=1600), # SP = 80,0% - 2000000: structures.CANBTP(dwMode=0, dwBPS= 2000000, wTS1=1600, wTS2= 400, wSJW= 400, wTDO=1600), # SP = 80,0% - 4000000: structures.CANBTP(dwMode=0, dwBPS= 4000000, wTS1= 800, wTS2= 200, wSJW= 200, wTDO= 800), # SP = 80,0% - 5000000: structures.CANBTP(dwMode=0, dwBPS= 5000000, wTS1= 600, wTS2= 200, wSJW= 200, wTDO= 600), # SP = 75,0% - 6666666: structures.CANBTP(dwMode=0, dwBPS= 6666666, wTS1= 400, wTS2= 200, wSJW= 200, wTDO= 402), # SP = 66,7% - 8000000: structures.CANBTP(dwMode=0, dwBPS= 8000000, wTS1= 400, wTS2= 100, wSJW= 100, wTDO= 250), # SP = 80,0% - 10000000: structures.CANBTP(dwMode=0, dwBPS=10000000, wTS1= 300, wTS2= 100, wSJW= 100, wTDO= 200), # SP = 75,0% + 500000: structures.CANBTP( + dwMode=0, dwBPS=500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=6400 + ), # SP = 80,0% + 833333: structures.CANBTP( + dwMode=0, dwBPS=833333, wTS1=1600, wTS2=400, wSJW=400, wTDO=1620 + ), # SP = 80,0% + 1000000: structures.CANBTP( + dwMode=0, dwBPS=1000000, wTS1=1600, wTS2=400, wSJW=400, wTDO=1600 + ), # SP = 80,0% + 1538461: structures.CANBTP( + dwMode=0, dwBPS=1538461, wTS1=1000, wTS2=300, wSJW=300, wTDO=1040 + ), # SP = 76,9% + 2000000: structures.CANBTP( + dwMode=0, dwBPS=2000000, wTS1=1600, wTS2=400, wSJW=400, wTDO=1600 + ), # SP = 80,0% + 4000000: structures.CANBTP( + dwMode=0, dwBPS=4000000, wTS1=800, wTS2=200, wSJW=200, wTDO=800 + ), # SP = 80,0% + 5000000: structures.CANBTP( + dwMode=0, dwBPS=5000000, wTS1=600, wTS2=200, wSJW=200, wTDO=600 + ), # SP = 75,0% + 6666666: structures.CANBTP( + dwMode=0, dwBPS=6666666, wTS1=400, wTS2=200, wSJW=200, wTDO=402 + ), # SP = 66,7% + 8000000: structures.CANBTP( + dwMode=0, dwBPS=8000000, wTS1=400, wTS2=100, wSJW=100, wTDO=250 + ), # SP = 80,0% + 10000000: structures.CANBTP( + dwMode=0, dwBPS=10000000, wTS1=300, wTS2=100, wSJW=100, wTDO=200 + ), # SP = 75,0% } - - diff --git a/can/interfaces/ixxat_fd/exceptions.py b/can/interfaces/ixxat_fd/exceptions.py index 3bc0e1111..babe08e3b 100644 --- a/can/interfaces/ixxat_fd/exceptions.py +++ b/can/interfaces/ixxat_fd/exceptions.py @@ -21,15 +21,15 @@ class VCITimeout(CanTimeoutError): - """ Wraps the VCI_E_TIMEOUT error """ + """Wraps the VCI_E_TIMEOUT error""" class VCIError(CanOperationError): - """ Try to display errors that occur within the wrapped C library nicely. """ + """Try to display errors that occur within the wrapped C library nicely.""" class VCIRxQueueEmptyError(VCIError): - """ Wraps the VCI_E_RXQUEUE_EMPTY error """ + """Wraps the VCI_E_RXQUEUE_EMPTY error""" def __init__(self): super().__init__("Receive queue is empty") diff --git a/can/interfaces/ixxat_fd/structures.py b/can/interfaces/ixxat_fd/structures.py index 68ace5da3..f76a39a38 100644 --- a/can/interfaces/ixxat_fd/structures.py +++ b/can/interfaces/ixxat_fd/structures.py @@ -108,34 +108,40 @@ class CANCAPABILITIES(ctypes.Structure): ] - PCANCAPABILITIES = ctypes.POINTER(CANCAPABILITIES) class CANMSGINFO(ctypes.Union): class Bytes(ctypes.Structure): _fields_ = [ - ("bType", ctypes.c_uint8), # type (see CAN_MSGTYPE_ constants) - ("bAddFlags", ctypes.c_uint8), # extended flags (see CAN_MSGFLAGS2_ constants) - ("bFlags", ctypes.c_uint8), # flags (see CAN_MSGFLAGS_ constants) - ("bAccept", ctypes.c_uint8), # accept code (see CAN_ACCEPT_ constants) + ("bType", ctypes.c_uint8), # type (see CAN_MSGTYPE_ constants) + ( + "bAddFlags", + ctypes.c_uint8, + ), # extended flags (see CAN_MSGFLAGS2_ constants) + ("bFlags", ctypes.c_uint8), # flags (see CAN_MSGFLAGS_ constants) + ("bAccept", ctypes.c_uint8), # accept code (see CAN_ACCEPT_ constants) ] class Bits(ctypes.Structure): _fields_ = [ ("type", ctypes.c_uint32, 8), # type (see CAN_MSGTYPE_ constants) - ("ssm", ctypes.c_uint32, 1), # single shot mode - ("hpm", ctypes.c_uint32, 1), # high priority message - ("edl", ctypes.c_uint32, 1), # extended data length - ("fdr", ctypes.c_uint32, 1), # fast data bit rate - ("esi", ctypes.c_uint32, 1), # error state indicator - ("res", ctypes.c_uint32, 3), # reserved set to 0 - ("dlc", ctypes.c_uint32, 4), # data length code - ("ovr", ctypes.c_uint32, 1), # data overrun - ("srr", ctypes.c_uint32, 1), # self reception request - ("rtr", ctypes.c_uint32, 1), # remote transmission request - ("ext", ctypes.c_uint32, 1), # extended frame format (0=standard, 1=extended) - ("afc", ctypes.c_uint32, 8), # accept code (see CAN_ACCEPT_ constants) + ("ssm", ctypes.c_uint32, 1), # single shot mode + ("hpm", ctypes.c_uint32, 1), # high priority message + ("edl", ctypes.c_uint32, 1), # extended data length + ("fdr", ctypes.c_uint32, 1), # fast data bit rate + ("esi", ctypes.c_uint32, 1), # error state indicator + ("res", ctypes.c_uint32, 3), # reserved set to 0 + ("dlc", ctypes.c_uint32, 4), # data length code + ("ovr", ctypes.c_uint32, 1), # data overrun + ("srr", ctypes.c_uint32, 1), # self reception request + ("rtr", ctypes.c_uint32, 1), # remote transmission request + ( + "ext", + ctypes.c_uint32, + 1, + ), # extended frame format (0=standard, 1=extended) + ("afc", ctypes.c_uint32, 8), # accept code (see CAN_ACCEPT_ constants) ] _fields_ = [("Bytes", Bytes), ("Bits", Bits)] @@ -172,16 +178,26 @@ class CANCYCLICTXMSG(ctypes.Structure): class CANBTP(ctypes.Structure): _fields_ = [ - ("dwMode", ctypes.c_uint32), # timing mode (see CAN_BTMODE_ const) + ("dwMode", ctypes.c_uint32), # timing mode (see CAN_BTMODE_ const) ("dwBPS", ctypes.c_uint32), # bits per second or prescaler (see CAN_BTMODE_RAW) - ("wTS1", ctypes.c_uint16), # length of time segment 1 in quanta - ("wTS2", ctypes.c_uint16), # length of time segment 2 in quanta - ("wSJW", ctypes.c_uint16), # re-synchronization jump width im quanta - ("wTDO", ctypes.c_uint16), # transceiver delay offset (SSP offset) in quanta (0 = disabled, 0xFFFF = simplified SSP positioning) + ("wTS1", ctypes.c_uint16), # length of time segment 1 in quanta + ("wTS2", ctypes.c_uint16), # length of time segment 2 in quanta + ("wSJW", ctypes.c_uint16), # re-synchronization jump width im quanta + ( + "wTDO", + ctypes.c_uint16, + ), # transceiver delay offset (SSP offset) in quanta (0 = disabled, 0xFFFF = simplified SSP positioning) ] def __str__(self): - return "dwMode=%d, dwBPS=%d, wTS1=%d, wTS2=%d, wSJW=%d, wTDO=%d" % (self.dwMode, self.dwBPS, self.wTS1, self.wTS2, self.wSJW, self.wTDO) + return "dwMode=%d, dwBPS=%d, wTS1=%d, wTS2=%d, wSJW=%d, wTDO=%d" % ( + self.dwMode, + self.dwBPS, + self.wTS1, + self.wTS2, + self.wSJW, + self.wTDO, + ) PCANBTP = ctypes.POINTER(CANBTP) @@ -189,22 +205,43 @@ def __str__(self): class CANCAPABILITIES2(ctypes.Structure): _fields_ = [ - ("wCtrlType", ctypes.c_uint16), # Type of CAN controller (see CAN_CTRL_ const) - ("wBusCoupling", ctypes.c_uint16), # Type of Bus coupling (see CAN_BUSC_ const) - ("dwFeatures", ctypes.c_uint32), # supported features (see CAN_FEATURE_ constants) - ("dwCanClkFreq" , ctypes.c_uint32), # CAN clock frequency [Hz] - ("sSdrRangeMin" , CANBTP), # minimum bit timing values for standard bit rate - ("sSdrRangeMax" , CANBTP), # maximum bit timing values for standard bit rate - ("sFdrRangeMin" , CANBTP), # minimum bit timing values for fast data bit rate - ("sFdrRangeMax" , CANBTP), # maximum bit timing values for fast data bit rate - ("dwTscClkFreq" , ctypes.c_uint32), # clock frequency of the time stamp counter [Hz] - ("dwTscDivisor" , ctypes.c_uint32), # divisor for the message time stamp counter - ("dwCmsClkFreq" , ctypes.c_uint32), # clock frequency of cyclic message scheduler [Hz] - ("dwCmsDivisor" , ctypes.c_uint32), # divisor for the cyclic message scheduler - ("dwCmsMaxTicks", ctypes.c_uint32), # maximum tick count value of the cyclic message - ("dwDtxClkFreq" , ctypes.c_uint32), # clock frequency of the delayed message transmitter [Hz] - ("dwDtxDivisor" , ctypes.c_uint32), # divisor for the delayed message transmitter - ("dwDtxMaxTicks", ctypes.c_uint32), # maximum tick count value of the delayed message transmitter + ("wCtrlType", ctypes.c_uint16), # Type of CAN controller (see CAN_CTRL_ const) + ("wBusCoupling", ctypes.c_uint16), # Type of Bus coupling (see CAN_BUSC_ const) + ( + "dwFeatures", + ctypes.c_uint32, + ), # supported features (see CAN_FEATURE_ constants) + ("dwCanClkFreq", ctypes.c_uint32), # CAN clock frequency [Hz] + ("sSdrRangeMin", CANBTP), # minimum bit timing values for standard bit rate + ("sSdrRangeMax", CANBTP), # maximum bit timing values for standard bit rate + ("sFdrRangeMin", CANBTP), # minimum bit timing values for fast data bit rate + ("sFdrRangeMax", CANBTP), # maximum bit timing values for fast data bit rate + ( + "dwTscClkFreq", + ctypes.c_uint32, + ), # clock frequency of the time stamp counter [Hz] + ("dwTscDivisor", ctypes.c_uint32), # divisor for the message time stamp counter + ( + "dwCmsClkFreq", + ctypes.c_uint32, + ), # clock frequency of cyclic message scheduler [Hz] + ("dwCmsDivisor", ctypes.c_uint32), # divisor for the cyclic message scheduler + ( + "dwCmsMaxTicks", + ctypes.c_uint32, + ), # maximum tick count value of the cyclic message + ( + "dwDtxClkFreq", + ctypes.c_uint32, + ), # clock frequency of the delayed message transmitter [Hz] + ( + "dwDtxDivisor", + ctypes.c_uint32, + ), # divisor for the delayed message transmitter + ( + "dwDtxMaxTicks", + ctypes.c_uint32, + ), # maximum tick count value of the delayed message transmitter ] @@ -213,25 +250,26 @@ class CANCAPABILITIES2(ctypes.Structure): class CANLINESTATUS2(ctypes.Structure): _fields_ = [ - ("bOpMode", ctypes.c_uint8), # current CAN operating mode - ("bExMode", ctypes.c_uint8), # current CAN extended operating mode + ("bOpMode", ctypes.c_uint8), # current CAN operating mode + ("bExMode", ctypes.c_uint8), # current CAN extended operating mode ("bBusLoad", ctypes.c_uint8), # average bus load in percent (0..100) - ("bReserved", ctypes.c_uint8), # reserved set to 0 - ("sBtpSdr", ctypes.c_uint8), # standard bit rate timing - ("sBtpFdr", ctypes.c_uint8), # fast data bit rate timing - ("dwStatus", ctypes.c_uint32), # status of the CAN controller (see CAN_STATUS_) + ("bReserved", ctypes.c_uint8), # reserved set to 0 + ("sBtpSdr", ctypes.c_uint8), # standard bit rate timing + ("sBtpFdr", ctypes.c_uint8), # fast data bit rate timing + ("dwStatus", ctypes.c_uint32), # status of the CAN controller (see CAN_STATUS_) ] PCANLINESTATUS2 = ctypes.POINTER(CANLINESTATUS2) + class CANMSG2(ctypes.Structure): _fields_ = [ - ("dwTime", ctypes.c_uint32), # time stamp for receive message - ("rsvd", ctypes.c_uint32), # reserved (set to 0) - ("dwMsgId", ctypes.c_uint32), # CAN message identifier (INTEL format) - ("uMsgInfo", CANMSGINFO), # message information (bit field) - ("abData", ctypes.c_uint8 * 64), # message data + ("dwTime", ctypes.c_uint32), # time stamp for receive message + ("rsvd", ctypes.c_uint32), # reserved (set to 0) + ("dwMsgId", ctypes.c_uint32), # CAN message identifier (INTEL format) + ("uMsgInfo", CANMSGINFO), # message information (bit field) + ("abData", ctypes.c_uint8 * 64), # message data ] @@ -240,15 +278,19 @@ class CANMSG2(ctypes.Structure): class CANCYCLICTXMSG2(ctypes.Structure): _fields_ = [ - ("wCycleTime", ctypes.c_uint16), # cycle time for the message in ticks - ("bIncrMode", ctypes.c_uint8), # auto increment mode (see CAN_CTXMSG_INC_ const) - ("bByteIndex", ctypes.c_uint8), # index of the byte within abData[] to increment - ("dwMsgId", ctypes.c_uint32), # message identifier (INTEL format) - ("uMsgInfo", CANMSGINFO), # message information (bit field) - ("abData", ctypes.c_uint8 * 64), # message data + ("wCycleTime", ctypes.c_uint16), # cycle time for the message in ticks + ( + "bIncrMode", + ctypes.c_uint8, + ), # auto increment mode (see CAN_CTXMSG_INC_ const) + ( + "bByteIndex", + ctypes.c_uint8, + ), # index of the byte within abData[] to increment + ("dwMsgId", ctypes.c_uint32), # message identifier (INTEL format) + ("uMsgInfo", CANMSGINFO), # message information (bit field) + ("abData", ctypes.c_uint8 * 64), # message data ] PCANCYCLICTXMSG2 = ctypes.POINTER(CANCYCLICTXMSG2) - - diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index f90df65ce..cb88d5da2 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -89,6 +89,6 @@ Experimental support for CAN-FD can be enabled by using interface name "ixxat_fd Additional parameters when using this interface are: -* ``data_bitrate`` (defaults to same value passed for bitrate) Channel data bitrate (to use when +* ``data_bitrate`` (defaults to 2Mbps) Channel data bitrate (to use when message bitrate_switch is used). diff --git a/test/test_interface_ixxat_fd.py b/test/test_interface_ixxat_fd.py new file mode 100644 index 000000000..0d55a5906 --- /dev/null +++ b/test/test_interface_ixxat_fd.py @@ -0,0 +1,62 @@ +""" +Unittest for ixxat_fd interface. + +Run only this test: +python setup.py test --addopts "--verbose -s test/test_interface_ixxat_fd.py" +""" + +import unittest +import can + + +class SoftwareTestCase(unittest.TestCase): + """ + Test cases that test the software only and do not rely on an existing/connected hardware. + """ + + def setUp(self): + try: + bus = can.Bus(interface="ixxat_fd", channel=0) + bus.shutdown() + except can.CanInterfaceNotImplementedError: + raise unittest.SkipTest("not available on this platform") + + def test_bus_creation(self): + # channel must be >= 0 + with self.assertRaises(ValueError): + can.Bus(interface="ixxat_fd", channel=-1) + + # rxFifoSize must be > 0 + with self.assertRaises(ValueError): + can.Bus(interface="ixxat_fd", channel=0, rxFifoSize=0) + + # txFifoSize must be > 0 + with self.assertRaises(ValueError): + can.Bus(interface="ixxat_fd", channel=0, txFifoSize=0) + + +class HardwareTestCase(unittest.TestCase): + """ + Test cases that rely on an existing/connected hardware. + """ + + def setUp(self): + try: + bus = can.Bus(interface="ixxat_fd", channel=0) + bus.shutdown() + except can.CanInterfaceNotImplementedError: + raise unittest.SkipTest("not available on this platform") + + def test_bus_creation(self): + # non-existent channel -> use arbitrary high value + with self.assertRaises(can.CanInitializationError): + can.Bus(interface="ixxat_fd", channel=0xFFFF) + + def test_send_after_shutdown(self): + with can.Bus(interface="ixxat_fd", channel=0) as bus: + with self.assertRaises(can.CanOperationError): + bus.send(can.Message(arbitration_id=0x3FF, dlc=0)) + + +if __name__ == "__main__": + unittest.main() From d9f64ec76753e7aac5ef8b048807cbaba70e38df Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 13 Sep 2021 17:22:24 +0200 Subject: [PATCH 08/22] Changed: renamed files before "merging" ixxat and ixxat_fd interfaces. --- can/interfaces/ixxat/{canlib.py => canlib_vcinpl.py} | 4 ++-- .../{ixxat_fd/canlib.py => ixxat/canlib_vcinpl2.py} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename can/interfaces/ixxat/{canlib.py => canlib_vcinpl.py} (99%) rename can/interfaces/{ixxat_fd/canlib.py => ixxat/canlib_vcinpl2.py} (100%) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib_vcinpl.py similarity index 99% rename from can/interfaces/ixxat/canlib.py rename to can/interfaces/ixxat/canlib_vcinpl.py index 13709c71c..ced840ff3 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -604,12 +604,12 @@ def _inWaiting(self): return 1 def flush_tx_buffer(self): - """ Flushes the transmit buffer on the IXXAT """ + """Flushes the transmit buffer on the IXXAT""" # TODO #64: no timeout? _canlib.canChannelWaitTxEvent(self._channel_handle, constants.INFINITE) def _recv_internal(self, timeout): - """ Read a message from IXXAT device. """ + """Read a message from IXXAT device.""" # TODO: handling CAN error messages? data_received = False diff --git a/can/interfaces/ixxat_fd/canlib.py b/can/interfaces/ixxat/canlib_vcinpl2.py similarity index 100% rename from can/interfaces/ixxat_fd/canlib.py rename to can/interfaces/ixxat/canlib_vcinpl2.py From b5653cfa3cd52bf16725d04ef3bdb61521561281 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 13 Sep 2021 17:24:58 +0200 Subject: [PATCH 09/22] Changed: Merged structures, exceptions and constants from ixxat-fd to ixxat. --- can/interfaces/ixxat/constants.py | 109 ++++++++++ can/interfaces/ixxat/exceptions.py | 6 +- can/interfaces/ixxat/structures.py | 158 ++++++++++++-- can/interfaces/ixxat_fd/__init__.py | 7 - can/interfaces/ixxat_fd/constants.py | 257 ---------------------- can/interfaces/ixxat_fd/exceptions.py | 44 ---- can/interfaces/ixxat_fd/structures.py | 296 -------------------------- 7 files changed, 256 insertions(+), 621 deletions(-) delete mode 100644 can/interfaces/ixxat_fd/__init__.py delete mode 100644 can/interfaces/ixxat_fd/constants.py delete mode 100644 can/interfaces/ixxat_fd/exceptions.py delete mode 100644 can/interfaces/ixxat_fd/structures.py diff --git a/can/interfaces/ixxat/constants.py b/can/interfaces/ixxat/constants.py index e911a580a..1dbc22a44 100644 --- a/can/interfaces/ixxat/constants.py +++ b/can/interfaces/ixxat/constants.py @@ -4,6 +4,8 @@ Copyright (C) 2016 Giuseppe Corbelli """ +from . import structures + FALSE = 0 TRUE = 1 @@ -119,6 +121,12 @@ CAN_OPMODE_LISTONLY = 0x08 CAN_OPMODE_LOWSPEED = 0x10 +# Extended operating modes +CAN_EXMODE_DISABLED = 0x00 +CAN_EXMODE_EXTDATALEN = 0x01 +CAN_EXMODE_FASTDATA = 0x02 +CAN_EXMODE_NONISOCANFD = 0x04 + # Message types CAN_MSGTYPE_DATA = 0 CAN_MSGTYPE_INFO = 1 @@ -146,3 +154,104 @@ # acceptance code and mask to reject all CAN IDs CAN_ACC_MASK_NONE = 0xFFFFFFFF CAN_ACC_CODE_NONE = 0x80000000 + +# BTMODEs +CAN_BTMODE_RAW = 0x00000001 # raw mode +CAN_BTMODE_TSM = 0x00000002 # triple sampling mode + + +CAN_FILTER_VOID = 0x00 # invalid or unknown filter mode (do not use for initialization) +CAN_FILTER_LOCK = 0x01 # lock filter (inhibit all IDs) +CAN_FILTER_PASS = 0x02 # bypass filter (pass all IDs) +CAN_FILTER_INCL = 0x03 # inclusive filtering (pass registered IDs) +CAN_FILTER_EXCL = 0x04 # exclusive filtering (inhibit registered IDs) + + +CAN_MSGFLAGS_DLC = 0x0F # [bit 0] data length code +CAN_MSGFLAGS_OVR = 0x10 # [bit 4] data overrun flag +CAN_MSGFLAGS_SRR = 0x20 # [bit 5] self reception request +CAN_MSGFLAGS_RTR = 0x40 # [bit 6] remote transmission request +CAN_MSGFLAGS_EXT = 0x80 # [bit 7] frame format (0=11-bit, 1=29-bit) + + +CAN_MSGFLAGS2_SSM = 0x01 # [bit 0] single shot mode +CAN_MSGFLAGS2_HPM = 0x02 # [bit 1] high priority message +CAN_MSGFLAGS2_EDL = 0x04 # [bit 2] extended data length +CAN_MSGFLAGS2_FDR = 0x08 # [bit 3] fast data bit rate +CAN_MSGFLAGS2_ESI = 0x10 # [bit 4] error state indicator +CAN_MSGFLAGS2_RES = 0xE0 # [bit 5..7] reserved bits + + +CAN_ACCEPT_REJECT = 0x00 # message not accepted +CAN_ACCEPT_ALWAYS = 0xFF # message always accepted +CAN_ACCEPT_FILTER_1 = 0x01 # message accepted by filter 1 +CAN_ACCEPT_FILTER_2 = 0x02 # message accepted by filter 2 +CAN_ACCEPT_PASSEXCL = 0x03 # message passes exclusion filter + + +CAN_FEATURE_STDOREXT = 0x00000001 # 11 OR 29 bit (exclusive) +CAN_FEATURE_STDANDEXT = 0x00000002 # 11 AND 29 bit (simultaneous) +CAN_FEATURE_RMTFRAME = 0x00000004 # reception of remote frames +CAN_FEATURE_ERRFRAME = 0x00000008 # reception of error frames +CAN_FEATURE_BUSLOAD = 0x00000010 # bus load measurement +CAN_FEATURE_IDFILTER = 0x00000020 # exact message filter +CAN_FEATURE_LISTONLY = 0x00000040 # listen only mode +CAN_FEATURE_SCHEDULER = 0x00000080 # cyclic message scheduler +CAN_FEATURE_GENERRFRM = 0x00000100 # error frame generation +CAN_FEATURE_DELAYEDTX = 0x00000200 # delayed message transmitter +CAN_FEATURE_SINGLESHOT = 0x00000400 # single shot mode +CAN_FEATURE_HIGHPRIOR = 0x00000800 # high priority message +CAN_FEATURE_AUTOBAUD = 0x00001000 # automatic bit rate detection +CAN_FEATURE_EXTDATA = 0x00002000 # extended data length (CANFD) +CAN_FEATURE_FASTDATA = 0x00004000 # fast data bit rate (CANFD) +CAN_FEATURE_ISOFRAME = 0x00008000 # ISO conform frame (CANFD) +CAN_FEATURE_NONISOFRM = ( + 0x00010000 # non ISO conform frame (CANFD) (different CRC computation) +) +CAN_FEATURE_64BITTSC = 0x00020000 # 64-bit time stamp counter + + +CAN_BITRATE_PRESETS = { + 250000: structures.CANBTP( + dwMode=0, dwBPS=250000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 + ), # SP = 80,0% + 500000: structures.CANBTP( + dwMode=0, dwBPS=500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 + ), # SP = 80,0% + 1000000: structures.CANBTP( + dwMode=0, dwBPS=1000000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 + ), # SP = 80,0% +} + +CAN_DATABITRATE_PRESETS = { + 500000: structures.CANBTP( + dwMode=0, dwBPS=500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=6400 + ), # SP = 80,0% + 833333: structures.CANBTP( + dwMode=0, dwBPS=833333, wTS1=1600, wTS2=400, wSJW=400, wTDO=1620 + ), # SP = 80,0% + 1000000: structures.CANBTP( + dwMode=0, dwBPS=1000000, wTS1=1600, wTS2=400, wSJW=400, wTDO=1600 + ), # SP = 80,0% + 1538461: structures.CANBTP( + dwMode=0, dwBPS=1538461, wTS1=1000, wTS2=300, wSJW=300, wTDO=1040 + ), # SP = 76,9% + 2000000: structures.CANBTP( + dwMode=0, dwBPS=2000000, wTS1=1600, wTS2=400, wSJW=400, wTDO=1600 + ), # SP = 80,0% + 4000000: structures.CANBTP( + dwMode=0, dwBPS=4000000, wTS1=800, wTS2=200, wSJW=200, wTDO=800 + ), # SP = 80,0% + 5000000: structures.CANBTP( + dwMode=0, dwBPS=5000000, wTS1=600, wTS2=200, wSJW=200, wTDO=600 + ), # SP = 75,0% + 6666666: structures.CANBTP( + dwMode=0, dwBPS=6666666, wTS1=400, wTS2=200, wSJW=200, wTDO=402 + ), # SP = 66,7% + 8000000: structures.CANBTP( + dwMode=0, dwBPS=8000000, wTS1=400, wTS2=100, wSJW=100, wTDO=250 + ), # SP = 80,0% + 10000000: structures.CANBTP( + dwMode=0, dwBPS=10000000, wTS1=300, wTS2=100, wSJW=100, wTDO=200 + ), # SP = 75,0% +} diff --git a/can/interfaces/ixxat/exceptions.py b/can/interfaces/ixxat/exceptions.py index 3bc0e1111..babe08e3b 100644 --- a/can/interfaces/ixxat/exceptions.py +++ b/can/interfaces/ixxat/exceptions.py @@ -21,15 +21,15 @@ class VCITimeout(CanTimeoutError): - """ Wraps the VCI_E_TIMEOUT error """ + """Wraps the VCI_E_TIMEOUT error""" class VCIError(CanOperationError): - """ Try to display errors that occur within the wrapped C library nicely. """ + """Try to display errors that occur within the wrapped C library nicely.""" class VCIRxQueueEmptyError(VCIError): - """ Wraps the VCI_E_RXQUEUE_EMPTY error """ + """Wraps the VCI_E_RXQUEUE_EMPTY error""" def __init__(self): super().__init__("Receive queue is empty") diff --git a/can/interfaces/ixxat/structures.py b/can/interfaces/ixxat/structures.py index 73c01823d..f76a39a38 100644 --- a/can/interfaces/ixxat/structures.py +++ b/can/interfaces/ixxat/structures.py @@ -114,24 +114,34 @@ class CANCAPABILITIES(ctypes.Structure): class CANMSGINFO(ctypes.Union): class Bytes(ctypes.Structure): _fields_ = [ - ("bType", ctypes.c_uint8), - ("bAddFlags", ctypes.c_uint8), - ("bFlags", ctypes.c_uint8), - ("bAccept", ctypes.c_uint8), + ("bType", ctypes.c_uint8), # type (see CAN_MSGTYPE_ constants) + ( + "bAddFlags", + ctypes.c_uint8, + ), # extended flags (see CAN_MSGFLAGS2_ constants) + ("bFlags", ctypes.c_uint8), # flags (see CAN_MSGFLAGS_ constants) + ("bAccept", ctypes.c_uint8), # accept code (see CAN_ACCEPT_ constants) ] class Bits(ctypes.Structure): _fields_ = [ - ("type", ctypes.c_uint32, 8), - ("ssm", ctypes.c_uint32, 1), - ("hi", ctypes.c_uint32, 2), - ("res", ctypes.c_uint32, 5), - ("dlc", ctypes.c_uint32, 4), - ("ovr", ctypes.c_uint32, 1), - ("srr", ctypes.c_uint32, 1), - ("rtr", ctypes.c_uint32, 1), - ("ext", ctypes.c_uint32, 1), - ("afc", ctypes.c_uint32, 8), + ("type", ctypes.c_uint32, 8), # type (see CAN_MSGTYPE_ constants) + ("ssm", ctypes.c_uint32, 1), # single shot mode + ("hpm", ctypes.c_uint32, 1), # high priority message + ("edl", ctypes.c_uint32, 1), # extended data length + ("fdr", ctypes.c_uint32, 1), # fast data bit rate + ("esi", ctypes.c_uint32, 1), # error state indicator + ("res", ctypes.c_uint32, 3), # reserved set to 0 + ("dlc", ctypes.c_uint32, 4), # data length code + ("ovr", ctypes.c_uint32, 1), # data overrun + ("srr", ctypes.c_uint32, 1), # self reception request + ("rtr", ctypes.c_uint32, 1), # remote transmission request + ( + "ext", + ctypes.c_uint32, + 1, + ), # extended frame format (0=standard, 1=extended) + ("afc", ctypes.c_uint32, 8), # accept code (see CAN_ACCEPT_ constants) ] _fields_ = [("Bytes", Bytes), ("Bits", Bits)] @@ -164,3 +174,123 @@ class CANCYCLICTXMSG(ctypes.Structure): PCANCYCLICTXMSG = ctypes.POINTER(CANCYCLICTXMSG) + + +class CANBTP(ctypes.Structure): + _fields_ = [ + ("dwMode", ctypes.c_uint32), # timing mode (see CAN_BTMODE_ const) + ("dwBPS", ctypes.c_uint32), # bits per second or prescaler (see CAN_BTMODE_RAW) + ("wTS1", ctypes.c_uint16), # length of time segment 1 in quanta + ("wTS2", ctypes.c_uint16), # length of time segment 2 in quanta + ("wSJW", ctypes.c_uint16), # re-synchronization jump width im quanta + ( + "wTDO", + ctypes.c_uint16, + ), # transceiver delay offset (SSP offset) in quanta (0 = disabled, 0xFFFF = simplified SSP positioning) + ] + + def __str__(self): + return "dwMode=%d, dwBPS=%d, wTS1=%d, wTS2=%d, wSJW=%d, wTDO=%d" % ( + self.dwMode, + self.dwBPS, + self.wTS1, + self.wTS2, + self.wSJW, + self.wTDO, + ) + + +PCANBTP = ctypes.POINTER(CANBTP) + + +class CANCAPABILITIES2(ctypes.Structure): + _fields_ = [ + ("wCtrlType", ctypes.c_uint16), # Type of CAN controller (see CAN_CTRL_ const) + ("wBusCoupling", ctypes.c_uint16), # Type of Bus coupling (see CAN_BUSC_ const) + ( + "dwFeatures", + ctypes.c_uint32, + ), # supported features (see CAN_FEATURE_ constants) + ("dwCanClkFreq", ctypes.c_uint32), # CAN clock frequency [Hz] + ("sSdrRangeMin", CANBTP), # minimum bit timing values for standard bit rate + ("sSdrRangeMax", CANBTP), # maximum bit timing values for standard bit rate + ("sFdrRangeMin", CANBTP), # minimum bit timing values for fast data bit rate + ("sFdrRangeMax", CANBTP), # maximum bit timing values for fast data bit rate + ( + "dwTscClkFreq", + ctypes.c_uint32, + ), # clock frequency of the time stamp counter [Hz] + ("dwTscDivisor", ctypes.c_uint32), # divisor for the message time stamp counter + ( + "dwCmsClkFreq", + ctypes.c_uint32, + ), # clock frequency of cyclic message scheduler [Hz] + ("dwCmsDivisor", ctypes.c_uint32), # divisor for the cyclic message scheduler + ( + "dwCmsMaxTicks", + ctypes.c_uint32, + ), # maximum tick count value of the cyclic message + ( + "dwDtxClkFreq", + ctypes.c_uint32, + ), # clock frequency of the delayed message transmitter [Hz] + ( + "dwDtxDivisor", + ctypes.c_uint32, + ), # divisor for the delayed message transmitter + ( + "dwDtxMaxTicks", + ctypes.c_uint32, + ), # maximum tick count value of the delayed message transmitter + ] + + +PCANCAPABILITIES2 = ctypes.POINTER(CANCAPABILITIES2) + + +class CANLINESTATUS2(ctypes.Structure): + _fields_ = [ + ("bOpMode", ctypes.c_uint8), # current CAN operating mode + ("bExMode", ctypes.c_uint8), # current CAN extended operating mode + ("bBusLoad", ctypes.c_uint8), # average bus load in percent (0..100) + ("bReserved", ctypes.c_uint8), # reserved set to 0 + ("sBtpSdr", ctypes.c_uint8), # standard bit rate timing + ("sBtpFdr", ctypes.c_uint8), # fast data bit rate timing + ("dwStatus", ctypes.c_uint32), # status of the CAN controller (see CAN_STATUS_) + ] + + +PCANLINESTATUS2 = ctypes.POINTER(CANLINESTATUS2) + + +class CANMSG2(ctypes.Structure): + _fields_ = [ + ("dwTime", ctypes.c_uint32), # time stamp for receive message + ("rsvd", ctypes.c_uint32), # reserved (set to 0) + ("dwMsgId", ctypes.c_uint32), # CAN message identifier (INTEL format) + ("uMsgInfo", CANMSGINFO), # message information (bit field) + ("abData", ctypes.c_uint8 * 64), # message data + ] + + +PCANMSG2 = ctypes.POINTER(CANMSG2) + + +class CANCYCLICTXMSG2(ctypes.Structure): + _fields_ = [ + ("wCycleTime", ctypes.c_uint16), # cycle time for the message in ticks + ( + "bIncrMode", + ctypes.c_uint8, + ), # auto increment mode (see CAN_CTXMSG_INC_ const) + ( + "bByteIndex", + ctypes.c_uint8, + ), # index of the byte within abData[] to increment + ("dwMsgId", ctypes.c_uint32), # message identifier (INTEL format) + ("uMsgInfo", CANMSGINFO), # message information (bit field) + ("abData", ctypes.c_uint8 * 64), # message data + ] + + +PCANCYCLICTXMSG2 = ctypes.POINTER(CANCYCLICTXMSG2) diff --git a/can/interfaces/ixxat_fd/__init__.py b/can/interfaces/ixxat_fd/__init__.py deleted file mode 100644 index 08c3fdf95..000000000 --- a/can/interfaces/ixxat_fd/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems - -Copyright (C) 2016 Giuseppe Corbelli -""" - -from can.interfaces.ixxat_fd.canlib import IXXATBus, get_ixxat_hwids diff --git a/can/interfaces/ixxat_fd/constants.py b/can/interfaces/ixxat_fd/constants.py deleted file mode 100644 index 1dbc22a44..000000000 --- a/can/interfaces/ixxat_fd/constants.py +++ /dev/null @@ -1,257 +0,0 @@ -""" -Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems - -Copyright (C) 2016 Giuseppe Corbelli -""" - -from . import structures - -FALSE = 0 -TRUE = 1 - -INFINITE = 0xFFFFFFFF - -VCI_MAX_ERRSTRLEN = 256 - -# Bitrates -CAN_BT0_10KB = 0x31 -CAN_BT1_10KB = 0x1C -CAN_BT0_20KB = 0x18 -CAN_BT1_20KB = 0x1C -CAN_BT0_50KB = 0x09 -CAN_BT1_50KB = 0x1C -CAN_BT0_100KB = 0x04 -CAN_BT1_100KB = 0x1C -CAN_BT0_125KB = 0x03 -CAN_BT1_125KB = 0x1C -CAN_BT0_250KB = 0x01 -CAN_BT1_250KB = 0x1C -CAN_BT0_500KB = 0x00 -CAN_BT1_500KB = 0x1C -CAN_BT0_667KB = 0x00 -CAN_BT1_667KB = 0x18 -CAN_BT0_800KB = 0x00 -CAN_BT1_800KB = 0x16 -CAN_BT0_1000KB = 0x00 -CAN_BT1_1000KB = 0x14 - -# Facilities/severities -SEV_INFO = 0x40000000 -SEV_WARN = 0x80000000 -SEV_ERROR = 0xC0000000 -SEV_MASK = 0xC0000000 -SEV_SUCCESS = 0x00000000 - -RESERVED_FLAG = 0x10000000 -CUSTOMER_FLAG = 0x20000000 - -STATUS_MASK = 0x0000FFFF -FACILITY_MASK = 0x0FFF0000 - -# Or so I hope -FACILITY_STD = 0 - -SEV_STD_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_STD -SEV_STD_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_STD -SEV_STD_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_STD - -FACILITY_VCI = 0x00010000 -SEV_VCI_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_VCI -SEV_VCI_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_VCI -SEV_VCI_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_VCI - -FACILITY_DAL = 0x00020000 -SEV_DAL_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_DAL -SEV_DAL_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_DAL -SEV_DAL_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_DAL - -FACILITY_CCL = 0x00030000 -SEV_CCL_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_CCL -SEV_CCL_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_CCL -SEV_CCL_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_CCL - -FACILITY_BAL = 0x00040000 -SEV_BAL_INFO = SEV_INFO | CUSTOMER_FLAG | FACILITY_BAL -SEV_BAL_WARN = SEV_WARN | CUSTOMER_FLAG | FACILITY_BAL -SEV_BAL_ERROR = SEV_ERROR | CUSTOMER_FLAG | FACILITY_BAL - -# Errors -VCI_SUCCESS = 0x00 -VCI_OK = 0x00 -VCI_E_UNEXPECTED = SEV_VCI_ERROR | 0x0001 -VCI_E_NOT_IMPLEMENTED = SEV_VCI_ERROR | 0x0002 -VCI_E_OUTOFMEMORY = SEV_VCI_ERROR | 0x0003 -VCI_E_INVALIDARG = SEV_VCI_ERROR | 0x0004 -VCI_E_NOINTERFACE = SEV_VCI_ERROR | 0x0005 -VCI_E_INVPOINTER = SEV_VCI_ERROR | 0x0006 -VCI_E_INVHANDLE = SEV_VCI_ERROR | 0x0007 -VCI_E_ABORT = SEV_VCI_ERROR | 0x0008 -VCI_E_FAIL = SEV_VCI_ERROR | 0x0009 -VCI_E_ACCESSDENIED = SEV_VCI_ERROR | 0x000A -VCI_E_TIMEOUT = SEV_VCI_ERROR | 0x000B -VCI_E_BUSY = SEV_VCI_ERROR | 0x000C -VCI_E_PENDING = SEV_VCI_ERROR | 0x000D -VCI_E_NO_DATA = SEV_VCI_ERROR | 0x000E -VCI_E_NO_MORE_ITEMS = SEV_VCI_ERROR | 0x000F -VCI_E_NOT_INITIALIZED = SEV_VCI_ERROR | 0x0010 -VCI_E_ALREADY_INITIALIZED = SEV_VCI_ERROR | 0x00011 -VCI_E_RXQUEUE_EMPTY = SEV_VCI_ERROR | 0x00012 -VCI_E_TXQUEUE_FULL = SEV_VCI_ERROR | 0x0013 -VCI_E_BUFFER_OVERFLOW = SEV_VCI_ERROR | 0x0014 -VCI_E_INVALID_STATE = SEV_VCI_ERROR | 0x0015 -VCI_E_OBJECT_ALREADY_EXISTS = SEV_VCI_ERROR | 0x0016 -VCI_E_INVALID_INDEX = SEV_VCI_ERROR | 0x0017 -VCI_E_END_OF_FILE = SEV_VCI_ERROR | 0x0018 -VCI_E_DISCONNECTED = SEV_VCI_ERROR | 0x0019 -VCI_E_WRONG_FLASHFWVERSION = SEV_VCI_ERROR | 0x001A - -# Controller status -CAN_STATUS_TXPEND = 0x01 -CAN_STATUS_OVRRUN = 0x02 -CAN_STATUS_ERRLIM = 0x04 -CAN_STATUS_BUSOFF = 0x08 -CAN_STATUS_ININIT = 0x10 -CAN_STATUS_BUSCERR = 0x20 - -# Controller operating modes -CAN_OPMODE_UNDEFINED = 0x00 -CAN_OPMODE_STANDARD = 0x01 -CAN_OPMODE_EXTENDED = 0x02 -CAN_OPMODE_ERRFRAME = 0x04 -CAN_OPMODE_LISTONLY = 0x08 -CAN_OPMODE_LOWSPEED = 0x10 - -# Extended operating modes -CAN_EXMODE_DISABLED = 0x00 -CAN_EXMODE_EXTDATALEN = 0x01 -CAN_EXMODE_FASTDATA = 0x02 -CAN_EXMODE_NONISOCANFD = 0x04 - -# Message types -CAN_MSGTYPE_DATA = 0 -CAN_MSGTYPE_INFO = 1 -CAN_MSGTYPE_ERROR = 2 -CAN_MSGTYPE_STATUS = 3 -CAN_MSGTYPE_WAKEUP = 4 -CAN_MSGTYPE_TIMEOVR = 5 -CAN_MSGTYPE_TIMERST = 6 - -# Information supplied in the abData[0] field of info frames -# (CANMSGINFO.Bytes.bType = CAN_MSGTYPE_INFO). -CAN_INFO_START = 1 -CAN_INFO_STOP = 2 -CAN_INFO_RESET = 3 - -# Information supplied in the abData[0] field of info frames -# (CANMSGINFO.Bytes.bType = CAN_MSGTYPE_ERROR). -CAN_ERROR_STUFF = 1 # stuff error -CAN_ERROR_FORM = 2 # form error -CAN_ERROR_ACK = 3 # acknowledgment error -CAN_ERROR_BIT = 4 # bit error -CAN_ERROR_CRC = 6 # CRC error -CAN_ERROR_OTHER = 7 # other (unspecified) error - -# acceptance code and mask to reject all CAN IDs -CAN_ACC_MASK_NONE = 0xFFFFFFFF -CAN_ACC_CODE_NONE = 0x80000000 - -# BTMODEs -CAN_BTMODE_RAW = 0x00000001 # raw mode -CAN_BTMODE_TSM = 0x00000002 # triple sampling mode - - -CAN_FILTER_VOID = 0x00 # invalid or unknown filter mode (do not use for initialization) -CAN_FILTER_LOCK = 0x01 # lock filter (inhibit all IDs) -CAN_FILTER_PASS = 0x02 # bypass filter (pass all IDs) -CAN_FILTER_INCL = 0x03 # inclusive filtering (pass registered IDs) -CAN_FILTER_EXCL = 0x04 # exclusive filtering (inhibit registered IDs) - - -CAN_MSGFLAGS_DLC = 0x0F # [bit 0] data length code -CAN_MSGFLAGS_OVR = 0x10 # [bit 4] data overrun flag -CAN_MSGFLAGS_SRR = 0x20 # [bit 5] self reception request -CAN_MSGFLAGS_RTR = 0x40 # [bit 6] remote transmission request -CAN_MSGFLAGS_EXT = 0x80 # [bit 7] frame format (0=11-bit, 1=29-bit) - - -CAN_MSGFLAGS2_SSM = 0x01 # [bit 0] single shot mode -CAN_MSGFLAGS2_HPM = 0x02 # [bit 1] high priority message -CAN_MSGFLAGS2_EDL = 0x04 # [bit 2] extended data length -CAN_MSGFLAGS2_FDR = 0x08 # [bit 3] fast data bit rate -CAN_MSGFLAGS2_ESI = 0x10 # [bit 4] error state indicator -CAN_MSGFLAGS2_RES = 0xE0 # [bit 5..7] reserved bits - - -CAN_ACCEPT_REJECT = 0x00 # message not accepted -CAN_ACCEPT_ALWAYS = 0xFF # message always accepted -CAN_ACCEPT_FILTER_1 = 0x01 # message accepted by filter 1 -CAN_ACCEPT_FILTER_2 = 0x02 # message accepted by filter 2 -CAN_ACCEPT_PASSEXCL = 0x03 # message passes exclusion filter - - -CAN_FEATURE_STDOREXT = 0x00000001 # 11 OR 29 bit (exclusive) -CAN_FEATURE_STDANDEXT = 0x00000002 # 11 AND 29 bit (simultaneous) -CAN_FEATURE_RMTFRAME = 0x00000004 # reception of remote frames -CAN_FEATURE_ERRFRAME = 0x00000008 # reception of error frames -CAN_FEATURE_BUSLOAD = 0x00000010 # bus load measurement -CAN_FEATURE_IDFILTER = 0x00000020 # exact message filter -CAN_FEATURE_LISTONLY = 0x00000040 # listen only mode -CAN_FEATURE_SCHEDULER = 0x00000080 # cyclic message scheduler -CAN_FEATURE_GENERRFRM = 0x00000100 # error frame generation -CAN_FEATURE_DELAYEDTX = 0x00000200 # delayed message transmitter -CAN_FEATURE_SINGLESHOT = 0x00000400 # single shot mode -CAN_FEATURE_HIGHPRIOR = 0x00000800 # high priority message -CAN_FEATURE_AUTOBAUD = 0x00001000 # automatic bit rate detection -CAN_FEATURE_EXTDATA = 0x00002000 # extended data length (CANFD) -CAN_FEATURE_FASTDATA = 0x00004000 # fast data bit rate (CANFD) -CAN_FEATURE_ISOFRAME = 0x00008000 # ISO conform frame (CANFD) -CAN_FEATURE_NONISOFRM = ( - 0x00010000 # non ISO conform frame (CANFD) (different CRC computation) -) -CAN_FEATURE_64BITTSC = 0x00020000 # 64-bit time stamp counter - - -CAN_BITRATE_PRESETS = { - 250000: structures.CANBTP( - dwMode=0, dwBPS=250000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 - ), # SP = 80,0% - 500000: structures.CANBTP( - dwMode=0, dwBPS=500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 - ), # SP = 80,0% - 1000000: structures.CANBTP( - dwMode=0, dwBPS=1000000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=0 - ), # SP = 80,0% -} - -CAN_DATABITRATE_PRESETS = { - 500000: structures.CANBTP( - dwMode=0, dwBPS=500000, wTS1=6400, wTS2=1600, wSJW=1600, wTDO=6400 - ), # SP = 80,0% - 833333: structures.CANBTP( - dwMode=0, dwBPS=833333, wTS1=1600, wTS2=400, wSJW=400, wTDO=1620 - ), # SP = 80,0% - 1000000: structures.CANBTP( - dwMode=0, dwBPS=1000000, wTS1=1600, wTS2=400, wSJW=400, wTDO=1600 - ), # SP = 80,0% - 1538461: structures.CANBTP( - dwMode=0, dwBPS=1538461, wTS1=1000, wTS2=300, wSJW=300, wTDO=1040 - ), # SP = 76,9% - 2000000: structures.CANBTP( - dwMode=0, dwBPS=2000000, wTS1=1600, wTS2=400, wSJW=400, wTDO=1600 - ), # SP = 80,0% - 4000000: structures.CANBTP( - dwMode=0, dwBPS=4000000, wTS1=800, wTS2=200, wSJW=200, wTDO=800 - ), # SP = 80,0% - 5000000: structures.CANBTP( - dwMode=0, dwBPS=5000000, wTS1=600, wTS2=200, wSJW=200, wTDO=600 - ), # SP = 75,0% - 6666666: structures.CANBTP( - dwMode=0, dwBPS=6666666, wTS1=400, wTS2=200, wSJW=200, wTDO=402 - ), # SP = 66,7% - 8000000: structures.CANBTP( - dwMode=0, dwBPS=8000000, wTS1=400, wTS2=100, wSJW=100, wTDO=250 - ), # SP = 80,0% - 10000000: structures.CANBTP( - dwMode=0, dwBPS=10000000, wTS1=300, wTS2=100, wSJW=100, wTDO=200 - ), # SP = 75,0% -} diff --git a/can/interfaces/ixxat_fd/exceptions.py b/can/interfaces/ixxat_fd/exceptions.py deleted file mode 100644 index babe08e3b..000000000 --- a/can/interfaces/ixxat_fd/exceptions.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems - -Copyright (C) 2016 Giuseppe Corbelli -Copyright (C) 2019 Marcel Kanter -""" - -from can import ( - CanInitializationError, - CanOperationError, - CanTimeoutError, -) - -__all__ = [ - "VCITimeout", - "VCIError", - "VCIRxQueueEmptyError", - "VCIBusOffError", - "VCIDeviceNotFoundError", -] - - -class VCITimeout(CanTimeoutError): - """Wraps the VCI_E_TIMEOUT error""" - - -class VCIError(CanOperationError): - """Try to display errors that occur within the wrapped C library nicely.""" - - -class VCIRxQueueEmptyError(VCIError): - """Wraps the VCI_E_RXQUEUE_EMPTY error""" - - def __init__(self): - super().__init__("Receive queue is empty") - - -class VCIBusOffError(VCIError): - def __init__(self): - super().__init__("Controller is in BUSOFF state") - - -class VCIDeviceNotFoundError(CanInitializationError): - pass diff --git a/can/interfaces/ixxat_fd/structures.py b/can/interfaces/ixxat_fd/structures.py deleted file mode 100644 index f76a39a38..000000000 --- a/can/interfaces/ixxat_fd/structures.py +++ /dev/null @@ -1,296 +0,0 @@ -""" -Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems - -Copyright (C) 2016 Giuseppe Corbelli -""" - -import ctypes - - -class LUID(ctypes.Structure): - _fields_ = [("LowPart", ctypes.c_uint32), ("HighPart", ctypes.c_int32)] - - -PLUID = ctypes.POINTER(LUID) - - -class VCIID(ctypes.Union): - _fields_ = [("AsLuid", LUID), ("AsInt64", ctypes.c_int64)] - - -PVCIID = ctypes.POINTER(VCIID) - - -class GUID(ctypes.Structure): - _fields_ = [ - ("Data1", ctypes.c_uint32), - ("Data2", ctypes.c_uint16), - ("Data3", ctypes.c_uint16), - ("Data4", ctypes.c_char * 8), - ] - - -class VCIDEVICEINFO(ctypes.Structure): - class UniqueHardwareId(ctypes.Union): - _fields_ = [("AsChar", ctypes.c_char * 16), ("AsGuid", GUID)] - - _fields_ = [ - ("VciObjectId", VCIID), - ("DeviceClass", GUID), - ("DriverMajorVersion", ctypes.c_uint8), - ("DriverMinorVersion", ctypes.c_uint8), - ("DriverBuildVersion", ctypes.c_uint16), - ("HardwareBranchVersion", ctypes.c_uint8), - ("HardwareMajorVersion", ctypes.c_uint8), - ("HardwareMinorVersion", ctypes.c_uint8), - ("HardwareBuildVersion", ctypes.c_uint8), - ("UniqueHardwareId", UniqueHardwareId), - ("Description", ctypes.c_char * 128), - ("Manufacturer", ctypes.c_char * 126), - ("DriverReleaseVersion", ctypes.c_uint16), - ] - - def __str__(self): - return "Mfg: {}, Dev: {} HW: {}.{}.{}.{} Drv: {}.{}.{}.{}".format( - self.Manufacturer, - self.Description, - self.HardwareBranchVersion, - self.HardwareMajorVersion, - self.HardwareMinorVersion, - self.HardwareBuildVersion, - self.DriverReleaseVersion, - self.DriverMajorVersion, - self.DriverMinorVersion, - self.DriverBuildVersion, - ) - - -PVCIDEVICEINFO = ctypes.POINTER(VCIDEVICEINFO) - - -class CANLINESTATUS(ctypes.Structure): - _fields_ = [ - ("bOpMode", ctypes.c_uint8), - ("bBtReg0", ctypes.c_uint8), - ("bBtReg1", ctypes.c_uint8), - ("bBusLoad", ctypes.c_uint8), - ("dwStatus", ctypes.c_uint32), - ] - - -PCANLINESTATUS = ctypes.POINTER(CANLINESTATUS) - - -class CANCHANSTATUS(ctypes.Structure): - _fields_ = [ - ("sLineStatus", CANLINESTATUS), - ("fActivated", ctypes.c_uint32), - ("fRxOverrun", ctypes.c_uint32), - ("bRxFifoLoad", ctypes.c_uint8), - ("bTxFifoLoad", ctypes.c_uint8), - ] - - -PCANCHANSTATUS = ctypes.POINTER(CANCHANSTATUS) - - -class CANCAPABILITIES(ctypes.Structure): - _fields_ = [ - ("wCtrlType", ctypes.c_uint16), - ("wBusCoupling", ctypes.c_uint16), - ("dwFeatures", ctypes.c_uint32), - ("dwClockFreq", ctypes.c_uint32), - ("dwTscDivisor", ctypes.c_uint32), - ("dwCmsDivisor", ctypes.c_uint32), - ("dwCmsMaxTicks", ctypes.c_uint32), - ("dwDtxDivisor", ctypes.c_uint32), - ("dwDtxMaxTicks", ctypes.c_uint32), - ] - - -PCANCAPABILITIES = ctypes.POINTER(CANCAPABILITIES) - - -class CANMSGINFO(ctypes.Union): - class Bytes(ctypes.Structure): - _fields_ = [ - ("bType", ctypes.c_uint8), # type (see CAN_MSGTYPE_ constants) - ( - "bAddFlags", - ctypes.c_uint8, - ), # extended flags (see CAN_MSGFLAGS2_ constants) - ("bFlags", ctypes.c_uint8), # flags (see CAN_MSGFLAGS_ constants) - ("bAccept", ctypes.c_uint8), # accept code (see CAN_ACCEPT_ constants) - ] - - class Bits(ctypes.Structure): - _fields_ = [ - ("type", ctypes.c_uint32, 8), # type (see CAN_MSGTYPE_ constants) - ("ssm", ctypes.c_uint32, 1), # single shot mode - ("hpm", ctypes.c_uint32, 1), # high priority message - ("edl", ctypes.c_uint32, 1), # extended data length - ("fdr", ctypes.c_uint32, 1), # fast data bit rate - ("esi", ctypes.c_uint32, 1), # error state indicator - ("res", ctypes.c_uint32, 3), # reserved set to 0 - ("dlc", ctypes.c_uint32, 4), # data length code - ("ovr", ctypes.c_uint32, 1), # data overrun - ("srr", ctypes.c_uint32, 1), # self reception request - ("rtr", ctypes.c_uint32, 1), # remote transmission request - ( - "ext", - ctypes.c_uint32, - 1, - ), # extended frame format (0=standard, 1=extended) - ("afc", ctypes.c_uint32, 8), # accept code (see CAN_ACCEPT_ constants) - ] - - _fields_ = [("Bytes", Bytes), ("Bits", Bits)] - - -PCANMSGINFO = ctypes.POINTER(CANMSGINFO) - - -class CANMSG(ctypes.Structure): - _fields_ = [ - ("dwTime", ctypes.c_uint32), - ("dwMsgId", ctypes.c_uint32), - ("uMsgInfo", CANMSGINFO), - ("abData", ctypes.c_uint8 * 8), - ] - - -PCANMSG = ctypes.POINTER(CANMSG) - - -class CANCYCLICTXMSG(ctypes.Structure): - _fields_ = [ - ("wCycleTime", ctypes.c_uint16), - ("bIncrMode", ctypes.c_uint8), - ("bByteIndex", ctypes.c_uint8), - ("dwMsgId", ctypes.c_uint32), - ("uMsgInfo", CANMSGINFO), - ("abData", ctypes.c_uint8 * 8), - ] - - -PCANCYCLICTXMSG = ctypes.POINTER(CANCYCLICTXMSG) - - -class CANBTP(ctypes.Structure): - _fields_ = [ - ("dwMode", ctypes.c_uint32), # timing mode (see CAN_BTMODE_ const) - ("dwBPS", ctypes.c_uint32), # bits per second or prescaler (see CAN_BTMODE_RAW) - ("wTS1", ctypes.c_uint16), # length of time segment 1 in quanta - ("wTS2", ctypes.c_uint16), # length of time segment 2 in quanta - ("wSJW", ctypes.c_uint16), # re-synchronization jump width im quanta - ( - "wTDO", - ctypes.c_uint16, - ), # transceiver delay offset (SSP offset) in quanta (0 = disabled, 0xFFFF = simplified SSP positioning) - ] - - def __str__(self): - return "dwMode=%d, dwBPS=%d, wTS1=%d, wTS2=%d, wSJW=%d, wTDO=%d" % ( - self.dwMode, - self.dwBPS, - self.wTS1, - self.wTS2, - self.wSJW, - self.wTDO, - ) - - -PCANBTP = ctypes.POINTER(CANBTP) - - -class CANCAPABILITIES2(ctypes.Structure): - _fields_ = [ - ("wCtrlType", ctypes.c_uint16), # Type of CAN controller (see CAN_CTRL_ const) - ("wBusCoupling", ctypes.c_uint16), # Type of Bus coupling (see CAN_BUSC_ const) - ( - "dwFeatures", - ctypes.c_uint32, - ), # supported features (see CAN_FEATURE_ constants) - ("dwCanClkFreq", ctypes.c_uint32), # CAN clock frequency [Hz] - ("sSdrRangeMin", CANBTP), # minimum bit timing values for standard bit rate - ("sSdrRangeMax", CANBTP), # maximum bit timing values for standard bit rate - ("sFdrRangeMin", CANBTP), # minimum bit timing values for fast data bit rate - ("sFdrRangeMax", CANBTP), # maximum bit timing values for fast data bit rate - ( - "dwTscClkFreq", - ctypes.c_uint32, - ), # clock frequency of the time stamp counter [Hz] - ("dwTscDivisor", ctypes.c_uint32), # divisor for the message time stamp counter - ( - "dwCmsClkFreq", - ctypes.c_uint32, - ), # clock frequency of cyclic message scheduler [Hz] - ("dwCmsDivisor", ctypes.c_uint32), # divisor for the cyclic message scheduler - ( - "dwCmsMaxTicks", - ctypes.c_uint32, - ), # maximum tick count value of the cyclic message - ( - "dwDtxClkFreq", - ctypes.c_uint32, - ), # clock frequency of the delayed message transmitter [Hz] - ( - "dwDtxDivisor", - ctypes.c_uint32, - ), # divisor for the delayed message transmitter - ( - "dwDtxMaxTicks", - ctypes.c_uint32, - ), # maximum tick count value of the delayed message transmitter - ] - - -PCANCAPABILITIES2 = ctypes.POINTER(CANCAPABILITIES2) - - -class CANLINESTATUS2(ctypes.Structure): - _fields_ = [ - ("bOpMode", ctypes.c_uint8), # current CAN operating mode - ("bExMode", ctypes.c_uint8), # current CAN extended operating mode - ("bBusLoad", ctypes.c_uint8), # average bus load in percent (0..100) - ("bReserved", ctypes.c_uint8), # reserved set to 0 - ("sBtpSdr", ctypes.c_uint8), # standard bit rate timing - ("sBtpFdr", ctypes.c_uint8), # fast data bit rate timing - ("dwStatus", ctypes.c_uint32), # status of the CAN controller (see CAN_STATUS_) - ] - - -PCANLINESTATUS2 = ctypes.POINTER(CANLINESTATUS2) - - -class CANMSG2(ctypes.Structure): - _fields_ = [ - ("dwTime", ctypes.c_uint32), # time stamp for receive message - ("rsvd", ctypes.c_uint32), # reserved (set to 0) - ("dwMsgId", ctypes.c_uint32), # CAN message identifier (INTEL format) - ("uMsgInfo", CANMSGINFO), # message information (bit field) - ("abData", ctypes.c_uint8 * 64), # message data - ] - - -PCANMSG2 = ctypes.POINTER(CANMSG2) - - -class CANCYCLICTXMSG2(ctypes.Structure): - _fields_ = [ - ("wCycleTime", ctypes.c_uint16), # cycle time for the message in ticks - ( - "bIncrMode", - ctypes.c_uint8, - ), # auto increment mode (see CAN_CTXMSG_INC_ const) - ( - "bByteIndex", - ctypes.c_uint8, - ), # index of the byte within abData[] to increment - ("dwMsgId", ctypes.c_uint32), # message identifier (INTEL format) - ("uMsgInfo", CANMSGINFO), # message information (bit field) - ("abData", ctypes.c_uint8 * 64), # message data - ] - - -PCANCYCLICTXMSG2 = ctypes.POINTER(CANCYCLICTXMSG2) From ef82e6c094249de74ee30e2ad113fd6cc5c57236 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 13 Sep 2021 19:35:35 +0200 Subject: [PATCH 10/22] Added: Ixxat interface proxy class delegating to related implementation where required. Changed: Documentation updated accordingly. --- can/interfaces/__init__.py | 1 - can/interfaces/ixxat/__init__.py | 5 +- can/interfaces/ixxat/canlib.py | 63 ++++++++++++++++++++++++++ can/interfaces/ixxat/canlib_vcinpl2.py | 8 +++- doc/interfaces/ixxat.rst | 19 +++----- 5 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 can/interfaces/ixxat/canlib.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 0959a20bd..884665934 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -10,7 +10,6 @@ "pcan": ("can.interfaces.pcan", "PcanBus"), "usb2can": ("can.interfaces.usb2can", "Usb2canBus"), "ixxat": ("can.interfaces.ixxat", "IXXATBus"), - "ixxat_fd": ("can.interfaces.ixxat_fd", "IXXATBus"), "nican": ("can.interfaces.nican", "NicanBus"), "iscan": ("can.interfaces.iscan", "IscanBus"), "virtual": ("can.interfaces.virtual", "VirtualBus"), diff --git a/can/interfaces/ixxat/__init__.py b/can/interfaces/ixxat/__init__.py index fc2cae0f3..347caed50 100644 --- a/can/interfaces/ixxat/__init__.py +++ b/can/interfaces/ixxat/__init__.py @@ -4,4 +4,7 @@ Copyright (C) 2016 Giuseppe Corbelli """ -from can.interfaces.ixxat.canlib import IXXATBus, get_ixxat_hwids +from can.interfaces.ixxat.canlib import IXXATBus +from can.interfaces.ixxat.canlib_vcinpl import ( + get_ixxat_hwids, +) # import this and not the one from vcinpl2 for backward compatibility diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py new file mode 100644 index 000000000..d61521fdb --- /dev/null +++ b/can/interfaces/ixxat/canlib.py @@ -0,0 +1,63 @@ +import can.interfaces.ixxat.canlib_vcinpl as vcinpl +import can.interfaces.ixxat.canlib_vcinpl2 as vcinpl2 + +from can import BusABC, Message +from typing import Optional + + +class IXXATBus(BusABC): + """The CAN Bus implemented for the IXXAT interface. + + Based on the C implementation of IXXAT, two different dlls are provided by IXXAT, one to work with CAN, + the other with CAN-FD. + + This class only delegates to related implementation (in calib_vcinpl or canlib_vcinpl2) class depending on fd user option. + """ + + def __init__(self, channel, can_filters=None, **kwargs): + """ + :param int channel: + The Channel id to create this bus with. + + :param list can_filters: + See :meth:`can.BusABC.set_filters`. + + :param bool receive_own_messages: + Enable self-reception of sent messages. + + :param int UniqueHardwareId: + UniqueHardwareId to connect (optional, will use the first found if not supplied) + + : param bool fd: + Default False, enables CAN-FD usage. + + :param int bitrate: + Channel bitrate in bit/s + + :param int data_bitrate: + Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled). + + :param int extended: + Default False, enables the capability to use extended IDs. + + """ + if kwargs.get("fd", False): + self.bus = vcinpl2.IXXATBus(channel, can_filters=None, **kwargs) + else: + self.bus = vcinpl.IXXATBus(channel, can_filters=None, **kwargs) + + def flush_tx_buffer(self): + """Flushes the transmit buffer on the IXXAT""" + return self.bus.flush_tx_buffer() + + def _recv_internal(self, timeout): + return self.bus._recv_internal(timeout) + + def send(self, msg: Message, timeout: Optional[float] = None) -> None: + return self.bus.send(msg, timeout) + + def _send_periodic_internal(self, msg, period, duration=None): + return self.bus._send_periodic_internal(msg, period, duration) + + def shutdown(self): + return self.bus.shutdown() diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index df5690e95..da61a9ac0 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -37,7 +37,7 @@ "vciFormatError", ] -log = logging.getLogger("can.ixxat_fd") +log = logging.getLogger("can.ixxat") from time import perf_counter as _timer_function @@ -417,6 +417,12 @@ def __init__(self, channel, can_filters=None, **kwargs): :param int bitrate: Channel bitrate in bit/s + + :param int data_bitrate: + Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled). + + :param int extended: + Default False, enables the capability to use extended IDs. """ if _canlib is None: raise CanInterfaceNotImplementedError( diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index cb88d5da2..0204d2a67 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -38,9 +38,13 @@ module, while the following parameters are optional and are interpreted by IXXAT * ``bitrate`` (default 500000) Channel bitrate * ``UniqueHardwareId`` (default first device) Unique hardware ID of the IXXAT device -* ``rxFifoSize`` (default 16) Number of RX mailboxes -* ``txFifoSize`` (default 16) Number of TX mailboxes +* ``rxFifoSize`` (default 16 for CAN, 1024 for CAN-FD) Number of RX mailboxes +* ``txFifoSize`` (default 16 for CAN, 128 for CAN-FD) Number of TX mailboxes * ``extended`` (default False) Allow usage of extended IDs +* ``fd`` Enable CAN-FD capabilities. +* ``data_bitrate`` (defaults to 2Mbps) Channel data bitrate (to use when +message bitrate_switch is used). + Filtering @@ -81,14 +85,3 @@ explicitly instantiated by the caller. RX and TX FIFO sizes are configurable with ``rxFifoSize`` and ``txFifoSize`` options, defaulting to 16 for both. - -CAN FD ------- - -Experimental support for CAN-FD can be enabled by using interface name "ixxat_fd" instead of "ixxat". - -Additional parameters when using this interface are: - -* ``data_bitrate`` (defaults to 2Mbps) Channel data bitrate (to use when -message bitrate_switch is used). - From 34fc77b5ffd42a8129db1eb0af904e420477c6f7 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 13 Sep 2021 20:00:07 +0200 Subject: [PATCH 11/22] Changed: Tests for CAN-FD in ixxat updated according to last changes. --- test/test_interface_ixxat_fd.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/test_interface_ixxat_fd.py b/test/test_interface_ixxat_fd.py index 0d55a5906..23452b9d7 100644 --- a/test/test_interface_ixxat_fd.py +++ b/test/test_interface_ixxat_fd.py @@ -1,5 +1,5 @@ """ -Unittest for ixxat_fd interface. +Unittest for ixxat interface using fd option. Run only this test: python setup.py test --addopts "--verbose -s test/test_interface_ixxat_fd.py" @@ -16,7 +16,7 @@ class SoftwareTestCase(unittest.TestCase): def setUp(self): try: - bus = can.Bus(interface="ixxat_fd", channel=0) + bus = can.Bus(interface="ixxat", fd=True, channel=0) bus.shutdown() except can.CanInterfaceNotImplementedError: raise unittest.SkipTest("not available on this platform") @@ -24,15 +24,15 @@ def setUp(self): def test_bus_creation(self): # channel must be >= 0 with self.assertRaises(ValueError): - can.Bus(interface="ixxat_fd", channel=-1) + can.Bus(interface="ixxat", fd=True, channel=-1) # rxFifoSize must be > 0 with self.assertRaises(ValueError): - can.Bus(interface="ixxat_fd", channel=0, rxFifoSize=0) + can.Bus(interface="ixxat", fd=True, channel=0, rxFifoSize=0) # txFifoSize must be > 0 with self.assertRaises(ValueError): - can.Bus(interface="ixxat_fd", channel=0, txFifoSize=0) + can.Bus(interface="ixxat", fd=True, channel=0, txFifoSize=0) class HardwareTestCase(unittest.TestCase): @@ -42,7 +42,7 @@ class HardwareTestCase(unittest.TestCase): def setUp(self): try: - bus = can.Bus(interface="ixxat_fd", channel=0) + bus = can.Bus(interface="ixxat", fd=True, channel=0) bus.shutdown() except can.CanInterfaceNotImplementedError: raise unittest.SkipTest("not available on this platform") @@ -50,10 +50,10 @@ def setUp(self): def test_bus_creation(self): # non-existent channel -> use arbitrary high value with self.assertRaises(can.CanInitializationError): - can.Bus(interface="ixxat_fd", channel=0xFFFF) + can.Bus(interface="ixxat", fd=True, channel=0xFFFF) def test_send_after_shutdown(self): - with can.Bus(interface="ixxat_fd", channel=0) as bus: + with can.Bus(interface="ixxat", fd=True, channel=0) as bus: with self.assertRaises(can.CanOperationError): bus.send(can.Message(arbitration_id=0x3FF, dlc=0)) From b59c8b1a5d95eabb6439dda68ed0e770ff681930 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 27 Sep 2021 18:47:33 +0200 Subject: [PATCH 12/22] Changed: Suggested changes from pull request review. --- can/interfaces/ixxat/canlib_vcinpl2.py | 52 ++++++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index da61a9ac0..7089c6f21 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -175,7 +175,14 @@ def __check_status(result, function, arguments): (HANDLE, ctypes.c_uint32, ctypes.c_long, PHANDLE), __check_status, ) - # EXTERN_C HRESULT VCIAPI canChannelInitialize( IN HANDLE hCanChn, IN UINT16 wRxFifoSize, IN UINT16 wRxThreshold, IN UINT16 wTxFifoSize, IN UINT16 wTxThreshold ); + # EXTERN_C HRESULT VCIAPI + # canChannelInitialize( IN HANDLE hCanChn, + # IN UINT16 wRxFifoSize, + # IN UINT16 wRxThreshold, + # IN UINT16 wTxFifoSize, + # IN UINT16 wTxThreshold, + # IN UINT32 dwFilterSize, + # IN UINT8 bFilterMode ); _canlib.map_symbol( "canChannelInitialize", ctypes.c_long, @@ -196,14 +203,14 @@ def __check_status(result, function, arguments): ) # HRESULT canChannelClose( HANDLE hChannel ) _canlib.map_symbol("canChannelClose", ctypes.c_long, (HANDLE,), __check_status) - # EXTERN_C HRESULT VCIAPI canChannelReadMessage( IN HANDLE hCanChn, IN UINT32 dwMsTimeout, OUT PCANMSG pCanMsg ); + # EXTERN_C HRESULT VCIAPI canChannelReadMessage( IN HANDLE hCanChn, IN UINT32 dwMsTimeout, OUT PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelReadMessage", ctypes.c_long, (HANDLE, ctypes.c_uint32, structures.PCANMSG2), __check_status, ) - # HRESULT canChannelPeekMessage(HANDLE hChannel,PCANMSG pCanMsg ); + # HRESULT canChannelPeekMessage(HANDLE hChannel,PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelPeekMessage", ctypes.c_long, @@ -224,14 +231,14 @@ def __check_status(result, function, arguments): (HANDLE, ctypes.c_uint32), __check_status, ) - # HRESULT canChannelPostMessage (HANDLE hChannel, PCANMSG pCanMsg ); + # HRESULT canChannelPostMessage (HANDLE hChannel, PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelPostMessage", ctypes.c_long, (HANDLE, structures.PCANMSG2), __check_status, ) - # HRESULT canChannelSendMessage (HANDLE hChannel, UINT32 dwMsTimeout, PCANMSG pCanMsg ); + # HRESULT canChannelSendMessage (HANDLE hChannel, UINT32 dwMsTimeout, PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelSendMessage", ctypes.c_long, @@ -246,7 +253,16 @@ def __check_status(result, function, arguments): (HANDLE, ctypes.c_uint32, PHANDLE), __check_status, ) - # EXTERN_C HRESULT VCIAPI canControlInitialize( IN HANDLE hCanCtl, IN UINT8 bMode, IN UINT8 bBtr0, IN UINT8 bBtr1 ); + # EXTERN_C HRESULT VCIAPI + # canControlInitialize( IN HANDLE hCanCtl, + # IN UINT8 bOpMode, + # IN UINT8 bExMode, + # IN UINT8 bSFMode, + # IN UINT8 bEFMode, + # IN UINT32 dwSFIds, + # IN UINT32 dwEFIds, + # IN PCANBTP pBtpSDR, + # IN PCANBTP pBtpFDR ); _canlib.map_symbol( "canControlInitialize", ctypes.c_long, @@ -271,14 +287,14 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canControlStart", ctypes.c_long, (HANDLE, ctypes.c_long), __check_status ) - # EXTERN_C HRESULT VCIAPI canControlGetStatus( IN HANDLE hCanCtl, OUT PCANLINESTATUS pStatus ); + # EXTERN_C HRESULT VCIAPI canControlGetStatus( IN HANDLE hCanCtl, OUT PCANLINESTATUS2 pStatus ); _canlib.map_symbol( "canControlGetStatus", ctypes.c_long, (HANDLE, structures.PCANLINESTATUS2), __check_status, ) - # EXTERN_C HRESULT VCIAPI canControlGetCaps( IN HANDLE hCanCtl, OUT PCANCAPABILITIES pCanCaps ); + # EXTERN_C HRESULT VCIAPI canControlGetCaps( IN HANDLE hCanCtl, OUT PCANCAPABILITIES2 pCanCaps ); _canlib.map_symbol( "canControlGetCaps", ctypes.c_long, @@ -315,7 +331,7 @@ def __check_status(result, function, arguments): ) # EXTERN_C HRESULT canSchedulerClose (HANDLE hScheduler ); _canlib.map_symbol("canSchedulerClose", ctypes.c_long, (HANDLE,), __check_status) - # EXTERN_C HRESULT canSchedulerGetCaps (HANDLE hScheduler, PCANCAPABILITIES pCaps ); + # EXTERN_C HRESULT canSchedulerGetCaps (HANDLE hScheduler, PCANCAPABILITIES2 pCaps ); _canlib.map_symbol( "canSchedulerGetCaps", ctypes.c_long, @@ -326,7 +342,7 @@ def __check_status(result, function, arguments): _canlib.map_symbol( "canSchedulerActivate", ctypes.c_long, (HANDLE, ctypes.c_int), __check_status ) - # EXTERN_C HRESULT canSchedulerAddMessage (HANDLE hScheduler, PCANCYCLICTXMSG pMessage, PUINT32 pdwIndex ); + # EXTERN_C HRESULT canSchedulerAddMessage (HANDLE hScheduler, PCANCYCLICTXMSG2 pMessage, PUINT32 pdwIndex ); _canlib.map_symbol( "canSchedulerAddMessage", ctypes.c_long, @@ -612,7 +628,7 @@ def __init__(self, channel, can_filters=None, **kwargs): # frequency [1/s] = dwClockFreq / dwTscDivisor # We explicitly cast to float for Python 2.x users self._tick_resolution = float( - self._channel_capabilities.dwTscClkFreq # TODO confirm + self._channel_capabilities.dwTscClkFreq / self._channel_capabilities.dwTscDivisor ) @@ -764,6 +780,13 @@ def _recv_internal(self, timeout): timestamp=self._message.dwTime / self._tick_resolution, # Relative time in s is_remote_frame=bool(self._message.uMsgInfo.Bits.rtr), + is_fd=bool(self._message.uMsgInfo.Bits.edl), + is_rx=True, + is_error_frame=bool( + self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_ERROR + ), + bitrate_switch=bool(self._message.uMsgInfo.Bits.fdr), + error_state_indicator=bool(self._message.uMsgInfo.Bits.esi), is_extended_id=bool(self._message.uMsgInfo.Bits.ext), arbitration_id=self._message.dwMsgId, dlc=data_len, @@ -787,11 +810,16 @@ def send(self, msg: Message, timeout: Optional[float] = None) -> None: """ # This system is not designed to be very efficient message = structures.CANMSG2() - message.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA + message.uMsgInfo.Bits.type = ( + constants.CAN_MSGTYPE_ERROR + if msg.is_error_frame + else constants.CAN_MSGTYPE_DATA + ) message.uMsgInfo.Bits.rtr = 1 if msg.is_remote_frame else 0 message.uMsgInfo.Bits.ext = 1 if msg.is_extended_id else 0 message.uMsgInfo.Bits.srr = 1 if self._receive_own_messages else 0 message.uMsgInfo.Bits.fdr = 1 if msg.bitrate_switch else 0 + message.uMsgInfo.Bits.esi = 1 if msg.error_state_indicator else 0 message.uMsgInfo.Bits.edl = 1 if msg.is_fd else 0 message.dwMsgId = msg.arbitration_id if msg.dlc: # this dlc means number of bytes of payload From e177c94fc7968b0f28db7b634bb4b15cbbfa5e1a Mon Sep 17 00:00:00 2001 From: fjburgos Date: Tue, 16 Nov 2021 20:09:32 +0100 Subject: [PATCH 13/22] Changed: Suggested changes from pull request review. --- can/interfaces/ixxat/canlib.py | 12 +++--- can/interfaces/ixxat/canlib_vcinpl.py | 55 +++++++++++++----------- can/interfaces/ixxat/canlib_vcinpl2.py | 59 ++++++++++++++------------ 3 files changed, 69 insertions(+), 57 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 23a82ae91..c396191f9 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -14,7 +14,7 @@ class IXXATBus(BusABC): This class only delegates to related implementation (in calib_vcinpl or canlib_vcinpl2) class depending on fd user option. """ - def __init__(self, channel, can_filters=None, **kwargs): + def __init__(self, channel, can_filters=None, fd=False, **kwargs): """ :param int channel: The Channel id to create this bus with. @@ -28,8 +28,8 @@ def __init__(self, channel, can_filters=None, **kwargs): :param int UniqueHardwareId: UniqueHardwareId to connect (optional, will use the first found if not supplied) - : param bool fd: - Default False, enables CAN-FD usage. + :param bool fd: + Default False, enables CAN-FD usage. :param int bitrate: Channel bitrate in bit/s @@ -41,10 +41,8 @@ def __init__(self, channel, can_filters=None, **kwargs): Default False, enables the capability to use extended IDs. """ - if kwargs.get("fd", False): - self.bus = vcinpl2.IXXATBus(channel, can_filters=None, **kwargs) - else: - self.bus = vcinpl.IXXATBus(channel, can_filters=None, **kwargs) + bus_class = vcinpl2.IXXATBus if fd else vcinpl.IXXATBus + self.bus = bus_class(channel, can_filters=can_filters, fd=fd, **kwargs) def flush_tx_buffer(self): """Flushes the transmit buffer on the IXXAT""" diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index ced840ff3..d49c82b82 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -13,7 +13,7 @@ import functools import logging import sys -from typing import Optional +from typing import Optional, Callable from can import BusABC, Message from can.exceptions import CanInterfaceNotImplementedError, CanInitializationError @@ -37,7 +37,7 @@ log = logging.getLogger("can.ixxat") -from time import perf_counter as _timer_function +from time import perf_counter # Hack to have vciFormatError as a free function, see below vciFormatError = None @@ -56,45 +56,47 @@ log.warning("IXXAT VCI library does not work on %s platform", sys.platform) -def __vciFormatErrorExtended(library_instance, function, HRESULT, arguments): +def __vciFormatErrorExtended( + library_instance: CLibrary, function: Callable, vret: int, args: tuple +): """Format a VCI error and attach failed function, decoded HRESULT and arguments :param CLibrary library_instance: Mapped instance of IXXAT vcinpl library :param callable function: Failed function - :param HRESULT HRESULT: + :param HRESULT vret: HRESULT returned by vcinpl call - :param arguments: + :param args: Arbitrary arguments tuple :return: Formatted string """ # TODO: make sure we don't generate another exception return "{} - arguments were {}".format( - __vciFormatError(library_instance, function, HRESULT), arguments + __vciFormatError(library_instance, function, vret), args ) -def __vciFormatError(library_instance, function, HRESULT): +def __vciFormatError(library_instance: CLibrary, function: Callable, vret: int): """Format a VCI error and attach failed function and decoded HRESULT :param CLibrary library_instance: Mapped instance of IXXAT vcinpl library :param callable function: Failed function - :param HRESULT HRESULT: + :param HRESULT vret: HRESULT returned by vcinpl call :return: Formatted string """ buf = ctypes.create_string_buffer(constants.VCI_MAX_ERRSTRLEN) ctypes.memset(buf, 0, constants.VCI_MAX_ERRSTRLEN) - library_instance.vciFormatError(HRESULT, buf, constants.VCI_MAX_ERRSTRLEN) + library_instance.vciFormatError(vret, buf, constants.VCI_MAX_ERRSTRLEN) return "function {} failed ({})".format( function._name, buf.value.decode("utf-8", "replace") ) -def __check_status(result, function, arguments): +def __check_status(result, function, args): """ Check the result of a vcinpl function call and raise appropriate exception in case of an error. Used as errcheck function when mapping C functions @@ -103,7 +105,7 @@ def __check_status(result, function, arguments): Function call numeric result :param callable function: Called function - :param arguments: + :param args: Arbitrary arguments tuple :raise: :class:VCITimeout @@ -112,7 +114,7 @@ def __check_status(result, function, arguments): :class:VCIError """ if isinstance(result, int): - # Real return value is an unsigned long + # Real return value is an unsigned long, the following line converts the number to unsigned result = ctypes.c_ulong(result).value if result == constants.VCI_E_TIMEOUT: @@ -408,7 +410,17 @@ class IXXATBus(BusABC): }, } - def __init__(self, channel, can_filters=None, **kwargs): + def __init__( + self, + channel, + can_filters=None, + bitrate=500000, + UniqueHardwareId=None, + rxFifoSize=16, + txFifoSize=16, + receive_own_messages=False, + **kwargs, + ): """ :param int channel: The Channel id to create this bus with. @@ -432,11 +444,7 @@ def __init__(self, channel, can_filters=None, **kwargs): log.info("CAN Filters: %s", can_filters) log.info("Got configuration of: %s", kwargs) # Configuration options - bitrate = kwargs.get("bitrate", 500000) - UniqueHardwareId = kwargs.get("UniqueHardwareId", None) - rxFifoSize = kwargs.get("rxFifoSize", 16) - txFifoSize = kwargs.get("txFifoSize", 16) - self._receive_own_messages = kwargs.get("receive_own_messages", False) + self._receive_own_messages = receive_own_messages # Usually comes as a string from the config file channel = int(channel) @@ -548,8 +556,7 @@ def __init__(self, channel, can_filters=None, **kwargs): # the message in ticks. The resolution of a tick can be calculated from the fields # dwClockFreq and dwTscDivisor of the structure CANCAPABILITIES in accordance with the following formula: # frequency [1/s] = dwClockFreq / dwTscDivisor - # We explicitly cast to float for Python 2.x users - self._tick_resolution = float( + self._tick_resolution = ( self._channel_capabilities.dwClockFreq / self._channel_capabilities.dwTscDivisor ) @@ -633,7 +640,7 @@ def _recv_internal(self, timeout): else: timeout_ms = int(timeout * 1000) remaining_ms = timeout_ms - t0 = _timer_function() + t0 = perf_counter() while True: try: @@ -641,7 +648,7 @@ def _recv_internal(self, timeout): self._channel_handle, remaining_ms, ctypes.byref(self._message) ) except (VCITimeout, VCIRxQueueEmptyError): - # Ignore the 2 errors, the timeout is handled manually with the _timer_function() + # Ignore the 2 errors, the timeout is handled manually with the perf_counter() pass else: # See if we got a data or info/error messages @@ -686,7 +693,7 @@ def _recv_internal(self, timeout): log.warning("Unexpected message info type") if t0 is not None: - remaining_ms = timeout_ms - int((_timer_function() - t0) * 1000) + remaining_ms = timeout_ms - int((perf_counter() - t0) * 1000) if remaining_ms < 0: break @@ -748,7 +755,7 @@ def _send_periodic_internal(self, msg, period, duration=None): _canlib.canSchedulerOpen(self._device_handle, self.channel, self._scheduler) caps = structures.CANCAPABILITIES() _canlib.canSchedulerGetCaps(self._scheduler, caps) - self._scheduler_resolution = float(caps.dwClockFreq) / caps.dwCmsDivisor + self._scheduler_resolution = caps.dwClockFreq / caps.dwCmsDivisor _canlib.canSchedulerActivate(self._scheduler, constants.TRUE) return CyclicSendTask( self._scheduler, msg, period, duration, self._scheduler_resolution diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 7089c6f21..4706326ef 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -13,7 +13,7 @@ import functools import logging import sys -from typing import Optional +from typing import Optional, Callable from can import BusABC, Message from can.exceptions import CanInterfaceNotImplementedError, CanInitializationError @@ -39,7 +39,7 @@ log = logging.getLogger("can.ixxat") -from time import perf_counter as _timer_function +from time import perf_counter # Hack to have vciFormatError as a free function, see below vciFormatError = None @@ -58,45 +58,47 @@ log.warning("IXXAT VCI library does not work on %s platform", sys.platform) -def __vciFormatErrorExtended(library_instance, function, HRESULT, arguments): +def __vciFormatErrorExtended( + library_instance: CLibrary, function: Callable, vret: int, args: tuple +): """Format a VCI error and attach failed function, decoded HRESULT and arguments :param CLibrary library_instance: Mapped instance of IXXAT vcinpl library :param callable function: Failed function - :param HRESULT HRESULT: + :param HRESULT vret: HRESULT returned by vcinpl call - :param arguments: + :param args: Arbitrary arguments tuple :return: Formatted string """ # TODO: make sure we don't generate another exception return "{} - arguments were {}".format( - __vciFormatError(library_instance, function, HRESULT), arguments + __vciFormatError(library_instance, function, vret), args ) -def __vciFormatError(library_instance, function, HRESULT): +def __vciFormatError(library_instance: CLibrary, function: Callable, vret: int): """Format a VCI error and attach failed function and decoded HRESULT :param CLibrary library_instance: Mapped instance of IXXAT vcinpl library :param callable function: Failed function - :param HRESULT HRESULT: + :param HRESULT vret: HRESULT returned by vcinpl call :return: Formatted string """ buf = ctypes.create_string_buffer(constants.VCI_MAX_ERRSTRLEN) ctypes.memset(buf, 0, constants.VCI_MAX_ERRSTRLEN) - library_instance.vciFormatError(HRESULT, buf, constants.VCI_MAX_ERRSTRLEN) + library_instance.vciFormatError(vret, buf, constants.VCI_MAX_ERRSTRLEN) return "function {} failed ({})".format( function._name, buf.value.decode("utf-8", "replace") ) -def __check_status(result, function, arguments): +def __check_status(result, function, args): """ Check the result of a vcinpl function call and raise appropriate exception in case of an error. Used as errcheck function when mapping C functions @@ -105,7 +107,7 @@ def __check_status(result, function, arguments): Function call numeric result :param callable function: Called function - :param arguments: + :param args: Arbitrary arguments tuple :raise: :class:VCITimeout @@ -114,7 +116,7 @@ def __check_status(result, function, arguments): :class:VCIError """ if isinstance(result, int): - # Real return value is an unsigned long + # Real return value is an unsigned long, the following line converts the number to unsigned result = ctypes.c_ulong(result).value if result == constants.VCI_E_TIMEOUT: @@ -417,7 +419,19 @@ class IXXATBus(BusABC): """ - def __init__(self, channel, can_filters=None, **kwargs): + def __init__( + self, + channel, + can_filters=None, + bitrate=500000, + data_bitrate=2000000, + extended=False, + UniqueHardwareId=None, + rxFifoSize=1024, + txFifoSize=128, + receive_own_messages=False, + **kwargs, + ): """ :param int channel: The Channel id to create this bus with. @@ -447,13 +461,7 @@ def __init__(self, channel, can_filters=None, **kwargs): log.info("CAN Filters: %s", can_filters) log.info("Got configuration of: %s", kwargs) # Configuration options - bitrate = kwargs.get("bitrate", 500000) - data_bitrate = kwargs.get("data_bitrate", 2000000) - UniqueHardwareId = kwargs.get("UniqueHardwareId", None) - rxFifoSize = kwargs.get("rxFifoSize", 1024) - txFifoSize = kwargs.get("txFifoSize", 128) - extended = kwargs.get("extended", False) - self._receive_own_messages = kwargs.get("receive_own_messages", False) + self._receive_own_messages = receive_own_messages # Usually comes as a string from the config file channel = int(channel) @@ -626,8 +634,7 @@ def __init__(self, channel, can_filters=None, **kwargs): # the message in ticks. The resolution of a tick can be calculated from the fields # dwClockFreq and dwTscDivisor of the structure CANCAPABILITIES in accordance with the following formula: # frequency [1/s] = dwClockFreq / dwTscDivisor - # We explicitly cast to float for Python 2.x users - self._tick_resolution = float( + self._tick_resolution = ( self._channel_capabilities.dwTscClkFreq / self._channel_capabilities.dwTscDivisor ) @@ -712,7 +719,7 @@ def _recv_internal(self, timeout): else: timeout_ms = int(timeout * 1000) remaining_ms = timeout_ms - t0 = _timer_function() + t0 = perf_counter() while True: try: @@ -720,7 +727,7 @@ def _recv_internal(self, timeout): self._channel_handle, remaining_ms, ctypes.byref(self._message) ) except (VCITimeout, VCIRxQueueEmptyError): - # Ignore the 2 errors, the timeout is handled manually with the _timer_function() + # Ignore the 2 errors, the timeout is handled manually with the perf_counter() pass else: # See if we got a data or info/error messages @@ -765,7 +772,7 @@ def _recv_internal(self, timeout): log.warning("Unexpected message info type") if t0 is not None: - remaining_ms = timeout_ms - int((_timer_function() - t0) * 1000) + remaining_ms = timeout_ms - int((perf_counter() - t0) * 1000) if remaining_ms < 0: break @@ -847,7 +854,7 @@ def _send_periodic_internal(self, msg, period, duration=None): caps = structures.CANCAPABILITIES2() _canlib.canSchedulerGetCaps(self._scheduler, caps) self._scheduler_resolution = ( - float(caps.dwCmsClkFreq) / caps.dwCmsDivisor + caps.dwCmsClkFreq / caps.dwCmsDivisor ) # TODO: confirm _canlib.canSchedulerActivate(self._scheduler, constants.TRUE) return CyclicSendTask( From dd196ae51bcf2e461ba5aa7fab013fc40183dd10 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Thu, 18 Nov 2021 19:36:59 +0100 Subject: [PATCH 14/22] Changed: Type hint, typing.Tuple instead of tuple. --- can/interfaces/ixxat/canlib_vcinpl.py | 4 ++-- can/interfaces/ixxat/canlib_vcinpl2.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index d49c82b82..13badb409 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -13,7 +13,7 @@ import functools import logging import sys -from typing import Optional, Callable +from typing import Optional, Callable, Tuple from can import BusABC, Message from can.exceptions import CanInterfaceNotImplementedError, CanInitializationError @@ -57,7 +57,7 @@ def __vciFormatErrorExtended( - library_instance: CLibrary, function: Callable, vret: int, args: tuple + library_instance: CLibrary, function: Callable, vret: int, args: Tuple ): """Format a VCI error and attach failed function, decoded HRESULT and arguments :param CLibrary library_instance: diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 4706326ef..23703b618 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -13,7 +13,7 @@ import functools import logging import sys -from typing import Optional, Callable +from typing import Optional, Callable, Tuple from can import BusABC, Message from can.exceptions import CanInterfaceNotImplementedError, CanInitializationError @@ -59,7 +59,7 @@ def __vciFormatErrorExtended( - library_instance: CLibrary, function: Callable, vret: int, args: tuple + library_instance: CLibrary, function: Callable, vret: int, args: Tuple ): """Format a VCI error and attach failed function, decoded HRESULT and arguments :param CLibrary library_instance: From 4f52c81b576cd11e017e0e699d6cb497af95a46f Mon Sep 17 00:00:00 2001 From: fjburgos Date: Tue, 23 Nov 2021 18:24:05 +0100 Subject: [PATCH 15/22] Changed: TSEG1, TSEG2 and SJW parameters available as optional advanced settings. Changed: Deprecated parameters UniqueHardwareId, rxFifoSize, txFifoSize (use unique_hardware_id, rx_fifo_size, tx_fifo_size instead). Changed: Replaced ctypes.c_long return value for ctypes functions returning HRESULT with an alias "hresult_type" that is a ctypes.c_ulong. --- can/interfaces/ixxat/canlib_vcinpl.py | 46 +++--- can/interfaces/ixxat/canlib_vcinpl2.py | 213 +++++++++++++++++-------- doc/interfaces/ixxat.rst | 13 +- 3 files changed, 185 insertions(+), 87 deletions(-) diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 13badb409..403880164 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -22,6 +22,7 @@ RestartableCyclicTaskABC, ) from can.ctypesutil import CLibrary, HANDLE, PHANDLE, HRESULT as ctypes_HRESULT +from can.util import deprecated_args_alias from . import constants, structures from .exceptions import * @@ -410,14 +411,19 @@ class IXXATBus(BusABC): }, } + @deprecated_args_alias( + UniqueHardwareId="unique_hardware_id", + rxFifoSize="rx_fifo_size", + txFifoSize="tx_fifo_size", + ) def __init__( self, channel, can_filters=None, bitrate=500000, - UniqueHardwareId=None, - rxFifoSize=16, - txFifoSize=16, + unique_hardware_id=None, + rx_fifo_size=16, + tx_fifo_size=16, receive_own_messages=False, **kwargs, ): @@ -431,8 +437,8 @@ def __init__( :param bool receive_own_messages: Enable self-reception of sent messages. - :param int UniqueHardwareId: - UniqueHardwareId to connect (optional, will use the first found if not supplied) + :param int unique_hardware_id: + unique_hardware_id to connect (optional, will use the first found if not supplied) :param int bitrate: Channel bitrate in bit/s @@ -451,11 +457,11 @@ def __init__( if bitrate not in self.CHANNEL_BITRATES[0]: raise ValueError("Invalid bitrate {}".format(bitrate)) - if rxFifoSize <= 0: - raise ValueError("rxFifoSize must be > 0") + if rx_fifo_size <= 0: + raise ValueError("rx_fifo_size must be > 0") - if txFifoSize <= 0: - raise ValueError("txFifoSize must be > 0") + if tx_fifo_size <= 0: + raise ValueError("tx_fifo_size must be > 0") if channel < 0: raise ValueError("channel number must be >= 0") @@ -469,10 +475,10 @@ def __init__( self._payload = (ctypes.c_byte * 8)() # Search for supplied device - if UniqueHardwareId is None: + if unique_hardware_id is None: log.info("Searching for first available device") else: - log.info("Searching for unique HW ID %s", UniqueHardwareId) + log.info("Searching for unique HW ID %s", unique_hardware_id) _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle)) while True: try: @@ -480,20 +486,20 @@ def __init__( self._device_handle, ctypes.byref(self._device_info) ) except StopIteration: - if UniqueHardwareId is None: + if unique_hardware_id is None: raise VCIDeviceNotFoundError( "No IXXAT device(s) connected or device(s) in use by other process(es)." ) else: raise VCIDeviceNotFoundError( "Unique HW ID {} not connected or not available.".format( - UniqueHardwareId + unique_hardware_id ) ) else: - if (UniqueHardwareId is None) or ( + if (unique_hardware_id is None) or ( self._device_info.UniqueHardwareId.AsChar - == bytes(UniqueHardwareId, "ascii") + == bytes(unique_hardware_id, "ascii") ): break else: @@ -516,8 +522,8 @@ def __init__( log.info( "Initializing channel %d in shared mode, %d rx buffers, %d tx buffers", channel, - rxFifoSize, - txFifoSize, + rx_fifo_size, + tx_fifo_size, ) try: @@ -533,7 +539,9 @@ def __init__( ) # Signal TX/RX events when at least one frame has been handled - _canlib.canChannelInitialize(self._channel_handle, rxFifoSize, 1, txFifoSize, 1) + _canlib.canChannelInitialize( + self._channel_handle, rx_fifo_size, 1, tx_fifo_size, 1 + ) _canlib.canChannelActivate(self._channel_handle, constants.TRUE) log.info("Initializing control %d bitrate %d", channel, bitrate) @@ -592,7 +600,7 @@ def __init__( # Usually you get back 3 messages like "CAN initialized" ecc... # Clear the FIFO by filter them out with low timeout - for _ in range(rxFifoSize): + for _ in range(rx_fifo_size): try: _canlib.canChannelReadMessage( self._channel_handle, 0, ctypes.byref(self._message) diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 23703b618..195281bd5 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -24,6 +24,7 @@ from can.ctypesutil import CLibrary, HANDLE, PHANDLE, HRESULT as ctypes_HRESULT import can.util +from can.util import deprecated_args_alias from . import constants, structures from .exceptions import * @@ -115,10 +116,6 @@ def __check_status(result, function, args): :class:StopIteration :class:VCIError """ - if isinstance(result, int): - # Real return value is an unsigned long, the following line converts the number to unsigned - result = ctypes.c_ulong(result).value - if result == constants.VCI_E_TIMEOUT: raise VCITimeout("Function {} timed out".format(function._name)) elif result == constants.VCI_E_RXQUEUE_EMPTY: @@ -134,9 +131,10 @@ def __check_status(result, function, args): try: + hresult_type = ctypes.c_ulong # Map all required symbols and initialize library --------------------------- # HRESULT VCIAPI vciInitialize ( void ); - _canlib.map_symbol("vciInitialize", ctypes.c_long, (), __check_status) + _canlib.map_symbol("vciInitialize", hresult_type, (), __check_status) # void VCIAPI vciFormatError (HRESULT hrError, PCHAR pszText, UINT32 dwsize); try: @@ -152,28 +150,28 @@ def __check_status(result, function, args): vciFormatError = functools.partial(__vciFormatError, _canlib) # HRESULT VCIAPI vciEnumDeviceOpen( OUT PHANDLE hEnum ); - _canlib.map_symbol("vciEnumDeviceOpen", ctypes.c_long, (PHANDLE,), __check_status) + _canlib.map_symbol("vciEnumDeviceOpen", hresult_type, (PHANDLE,), __check_status) # HRESULT VCIAPI vciEnumDeviceClose ( IN HANDLE hEnum ); - _canlib.map_symbol("vciEnumDeviceClose", ctypes.c_long, (HANDLE,), __check_status) + _canlib.map_symbol("vciEnumDeviceClose", hresult_type, (HANDLE,), __check_status) # HRESULT VCIAPI vciEnumDeviceNext( IN HANDLE hEnum, OUT PVCIDEVICEINFO pInfo ); _canlib.map_symbol( "vciEnumDeviceNext", - ctypes.c_long, + hresult_type, (HANDLE, structures.PVCIDEVICEINFO), __check_status, ) # HRESULT VCIAPI vciDeviceOpen( IN REFVCIID rVciid, OUT PHANDLE phDevice ); _canlib.map_symbol( - "vciDeviceOpen", ctypes.c_long, (structures.PVCIID, PHANDLE), __check_status + "vciDeviceOpen", hresult_type, (structures.PVCIID, PHANDLE), __check_status ) # HRESULT vciDeviceClose( HANDLE hDevice ) - _canlib.map_symbol("vciDeviceClose", ctypes.c_long, (HANDLE,), __check_status) + _canlib.map_symbol("vciDeviceClose", hresult_type, (HANDLE,), __check_status) # HRESULT VCIAPI canChannelOpen( IN HANDLE hDevice, IN UINT32 dwCanNo, IN BOOL fExclusive, OUT PHANDLE phCanChn ); _canlib.map_symbol( "canChannelOpen", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32, ctypes.c_long, PHANDLE), __check_status, ) @@ -187,7 +185,7 @@ def __check_status(result, function, args): # IN UINT8 bFilterMode ); _canlib.map_symbol( "canChannelInitialize", - ctypes.c_long, + hresult_type, ( HANDLE, ctypes.c_uint16, @@ -201,49 +199,49 @@ def __check_status(result, function, args): ) # EXTERN_C HRESULT VCIAPI canChannelActivate( IN HANDLE hCanChn, IN BOOL fEnable ); _canlib.map_symbol( - "canChannelActivate", ctypes.c_long, (HANDLE, ctypes.c_long), __check_status + "canChannelActivate", hresult_type, (HANDLE, ctypes.c_long), __check_status ) # HRESULT canChannelClose( HANDLE hChannel ) - _canlib.map_symbol("canChannelClose", ctypes.c_long, (HANDLE,), __check_status) + _canlib.map_symbol("canChannelClose", hresult_type, (HANDLE,), __check_status) # EXTERN_C HRESULT VCIAPI canChannelReadMessage( IN HANDLE hCanChn, IN UINT32 dwMsTimeout, OUT PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelReadMessage", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32, structures.PCANMSG2), __check_status, ) # HRESULT canChannelPeekMessage(HANDLE hChannel,PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelPeekMessage", - ctypes.c_long, + hresult_type, (HANDLE, structures.PCANMSG2), __check_status, ) # HRESULT canChannelWaitTxEvent (HANDLE hChannel UINT32 dwMsTimeout ); _canlib.map_symbol( "canChannelWaitTxEvent", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32), __check_status, ) # HRESULT canChannelWaitRxEvent (HANDLE hChannel, UINT32 dwMsTimeout ); _canlib.map_symbol( "canChannelWaitRxEvent", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32), __check_status, ) # HRESULT canChannelPostMessage (HANDLE hChannel, PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelPostMessage", - ctypes.c_long, + hresult_type, (HANDLE, structures.PCANMSG2), __check_status, ) # HRESULT canChannelSendMessage (HANDLE hChannel, UINT32 dwMsTimeout, PCANMSG2 pCanMsg ); _canlib.map_symbol( "canChannelSendMessage", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32, structures.PCANMSG2), __check_status, ) @@ -251,7 +249,7 @@ def __check_status(result, function, args): # EXTERN_C HRESULT VCIAPI canControlOpen( IN HANDLE hDevice, IN UINT32 dwCanNo, OUT PHANDLE phCanCtl ); _canlib.map_symbol( "canControlOpen", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32, PHANDLE), __check_status, ) @@ -267,7 +265,7 @@ def __check_status(result, function, args): # IN PCANBTP pBtpFDR ); _canlib.map_symbol( "canControlInitialize", - ctypes.c_long, + hresult_type, ( HANDLE, ctypes.c_uint8, @@ -282,93 +280,93 @@ def __check_status(result, function, args): __check_status, ) # EXTERN_C HRESULT VCIAPI canControlClose( IN HANDLE hCanCtl ); - _canlib.map_symbol("canControlClose", ctypes.c_long, (HANDLE,), __check_status) + _canlib.map_symbol("canControlClose", hresult_type, (HANDLE,), __check_status) # EXTERN_C HRESULT VCIAPI canControlReset( IN HANDLE hCanCtl ); - _canlib.map_symbol("canControlReset", ctypes.c_long, (HANDLE,), __check_status) + _canlib.map_symbol("canControlReset", hresult_type, (HANDLE,), __check_status) # EXTERN_C HRESULT VCIAPI canControlStart( IN HANDLE hCanCtl, IN BOOL fStart ); _canlib.map_symbol( - "canControlStart", ctypes.c_long, (HANDLE, ctypes.c_long), __check_status + "canControlStart", hresult_type, (HANDLE, ctypes.c_long), __check_status ) # EXTERN_C HRESULT VCIAPI canControlGetStatus( IN HANDLE hCanCtl, OUT PCANLINESTATUS2 pStatus ); _canlib.map_symbol( "canControlGetStatus", - ctypes.c_long, + hresult_type, (HANDLE, structures.PCANLINESTATUS2), __check_status, ) # EXTERN_C HRESULT VCIAPI canControlGetCaps( IN HANDLE hCanCtl, OUT PCANCAPABILITIES2 pCanCaps ); _canlib.map_symbol( "canControlGetCaps", - ctypes.c_long, + hresult_type, (HANDLE, structures.PCANCAPABILITIES2), __check_status, ) # EXTERN_C HRESULT VCIAPI canControlSetAccFilter( IN HANDLE hCanCtl, IN BOOL fExtend, IN UINT32 dwCode, IN UINT32 dwMask ); _canlib.map_symbol( "canControlSetAccFilter", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), __check_status, ) # EXTERN_C HRESULT canControlAddFilterIds (HANDLE hControl, BOOL fExtended, UINT32 dwCode, UINT32 dwMask); _canlib.map_symbol( "canControlAddFilterIds", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), __check_status, ) # EXTERN_C HRESULT canControlRemFilterIds (HANDLE hControl, BOOL fExtendend, UINT32 dwCode, UINT32 dwMask ); _canlib.map_symbol( "canControlRemFilterIds", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), __check_status, ) # EXTERN_C HRESULT canSchedulerOpen (HANDLE hDevice, UINT32 dwCanNo, PHANDLE phScheduler ); _canlib.map_symbol( "canSchedulerOpen", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32, PHANDLE), __check_status, ) # EXTERN_C HRESULT canSchedulerClose (HANDLE hScheduler ); - _canlib.map_symbol("canSchedulerClose", ctypes.c_long, (HANDLE,), __check_status) + _canlib.map_symbol("canSchedulerClose", hresult_type, (HANDLE,), __check_status) # EXTERN_C HRESULT canSchedulerGetCaps (HANDLE hScheduler, PCANCAPABILITIES2 pCaps ); _canlib.map_symbol( "canSchedulerGetCaps", - ctypes.c_long, + hresult_type, (HANDLE, structures.PCANCAPABILITIES2), __check_status, ) # EXTERN_C HRESULT canSchedulerActivate ( HANDLE hScheduler, BOOL fEnable ); _canlib.map_symbol( - "canSchedulerActivate", ctypes.c_long, (HANDLE, ctypes.c_int), __check_status + "canSchedulerActivate", hresult_type, (HANDLE, ctypes.c_int), __check_status ) # EXTERN_C HRESULT canSchedulerAddMessage (HANDLE hScheduler, PCANCYCLICTXMSG2 pMessage, PUINT32 pdwIndex ); _canlib.map_symbol( "canSchedulerAddMessage", - ctypes.c_long, + hresult_type, (HANDLE, structures.PCANCYCLICTXMSG2, ctypes.POINTER(ctypes.c_uint32)), __check_status, ) # EXTERN_C HRESULT canSchedulerRemMessage (HANDLE hScheduler, UINT32 dwIndex ); _canlib.map_symbol( "canSchedulerRemMessage", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32), __check_status, ) # EXTERN_C HRESULT canSchedulerStartMessage (HANDLE hScheduler, UINT32 dwIndex, UINT16 dwCount ); _canlib.map_symbol( "canSchedulerStartMessage", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32, ctypes.c_uint16), __check_status, ) # EXTERN_C HRESULT canSchedulerStopMessage (HANDLE hScheduler, UINT32 dwIndex ); _canlib.map_symbol( "canSchedulerStopMessage", - ctypes.c_long, + hresult_type, (HANDLE, ctypes.c_uint32), __check_status, ) @@ -419,16 +417,28 @@ class IXXATBus(BusABC): """ + @deprecated_args_alias( + UniqueHardwareId="unique_hardware_id", + rxFifoSize="rx_fifo_size", + txFifoSize="tx_fifo_size", + ) def __init__( self, channel, can_filters=None, bitrate=500000, + sjw_abr: int = None, + tseg1_abr: int = None, + tseg2_abr: int = None, data_bitrate=2000000, + sjw_dbr: int = None, + tseg1_dbr: int = None, + tseg2_dbr: int = None, + ssp_dbr: int = None, extended=False, - UniqueHardwareId=None, - rxFifoSize=1024, - txFifoSize=128, + unique_hardware_id=None, + rx_fifo_size=1024, + tx_fifo_size=128, receive_own_messages=False, **kwargs, ): @@ -442,8 +452,8 @@ def __init__( :param bool receive_own_messages: Enable self-reception of sent messages. - :param int UniqueHardwareId: - UniqueHardwareId to connect (optional, will use the first found if not supplied) + :param int unique_hardware_id: + unique_hardware_id to connect (optional, will use the first found if not supplied) :param int bitrate: Channel bitrate in bit/s @@ -453,6 +463,28 @@ def __init__( :param int extended: Default False, enables the capability to use extended IDs. + + :param int sjw_abr: + Bus timing value sample jump width (arbitration). + + :param int tseg1_abr: + Bus timing value tseg1 (arbitration) + + :param int tseg2_abr: + Bus timing value tseg2 (arbitration) + + :param int sjw_dbr: + Bus timing value sample jump width (data) + + :param int tseg1_dbr: + Bus timing value tseg1 (data) + + :param int tseg2_dbr: + Bus timing value tseg2 (data) + + :param int ssp_dbr: + Bus timing value tseg2 (data) + """ if _canlib is None: raise CanInterfaceNotImplementedError( @@ -465,17 +497,28 @@ def __init__( # Usually comes as a string from the config file channel = int(channel) - if bitrate not in constants.CAN_BITRATE_PRESETS: - raise ValueError("Invalid bitrate {}".format(bitrate)) - - if data_bitrate not in constants.CAN_DATABITRATE_PRESETS: - raise ValueError("Invalid bitrate {}".format(bitrate)) + if bitrate not in constants.CAN_BITRATE_PRESETS and ( + tseg1_abr is None or tseg2_abr is None or sjw_abr is None + ): + raise ValueError( + "To use bitrate {} (that has not predefined preset) is mandatory to use also parameters tseg1_abr, tseg2_abr and swj_abr".format( + bitrate + ) + ) + if data_bitrate not in constants.CAN_DATABITRATE_PRESETS and ( + tseg1_dbr is None or tseg2_dbr is None or sjw_dbr is None + ): + raise ValueError( + "To use data_bitrate {} (that has not predefined preset) is mandatory to use also parameters tseg1_dbr, tseg2_dbr and swj_dbr".format( + data_bitrate + ) + ) - if rxFifoSize <= 0: - raise ValueError("rxFifoSize must be > 0") + if rx_fifo_size <= 0: + raise ValueError("rx_fifo_size must be > 0") - if txFifoSize <= 0: - raise ValueError("txFifoSize must be > 0") + if tx_fifo_size <= 0: + raise ValueError("tx_fifo_size must be > 0") if channel < 0: raise ValueError("channel number must be >= 0") @@ -489,10 +532,10 @@ def __init__( self._payload = (ctypes.c_byte * 64)() # Search for supplied device - if UniqueHardwareId is None: + if unique_hardware_id is None: log.info("Searching for first available device") else: - log.info("Searching for unique HW ID %s", UniqueHardwareId) + log.info("Searching for unique HW ID %s", unique_hardware_id) _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle)) while True: try: @@ -500,20 +543,20 @@ def __init__( self._device_handle, ctypes.byref(self._device_info) ) except StopIteration: - if UniqueHardwareId is None: + if unique_hardware_id is None: raise VCIDeviceNotFoundError( "No IXXAT device(s) connected or device(s) in use by other process(es)." ) else: raise VCIDeviceNotFoundError( "Unique HW ID {} not connected or not available.".format( - UniqueHardwareId + unique_hardware_id ) ) else: - if (UniqueHardwareId is None) or ( + if (unique_hardware_id is None) or ( self._device_info.UniqueHardwareId.AsChar - == bytes(UniqueHardwareId, "ascii") + == bytes(unique_hardware_id, "ascii") ): break else: @@ -536,8 +579,8 @@ def __init__( log.info( "Initializing channel %d in shared mode, %d rx buffers, %d tx buffers", channel, - rxFifoSize, - txFifoSize, + rx_fifo_size, + tx_fifo_size, ) try: @@ -555,17 +598,32 @@ def __init__( # Signal TX/RX events when at least one frame has been handled _canlib.canChannelInitialize( self._channel_handle, - rxFifoSize, + rx_fifo_size, 1, - txFifoSize, + tx_fifo_size, 1, 0, constants.CAN_FILTER_PASS, ) _canlib.canChannelActivate(self._channel_handle, constants.TRUE) - pBtpSDR = constants.CAN_BITRATE_PRESETS[bitrate] - pBtpFDR = constants.CAN_DATABITRATE_PRESETS[data_bitrate] + pBtpSDR = IXXATBus._canptb_build( + defaults=constants.CAN_BITRATE_PRESETS, + bitrate=bitrate, + tseg1=tseg1_abr, + tseg2=tseg2_abr, + sjw=sjw_abr, + ssp=0, + ) + pBtpFDR = IXXATBus._canptb_build( + defaults=constants.CAN_DATABITRATE_PRESETS, + bitrate=data_bitrate, + tseg1=tseg1_dbr, + tseg2=tseg2_dbr, + sjw=sjw_dbr, + ssp=ssp_dbr if ssp_dbr is not None else tseg1_dbr, + ) + log.info( "Initializing control %d with SDR={%s}, FDR={%s}", channel, @@ -670,7 +728,7 @@ def __init__( # Usually you get back 3 messages like "CAN initialized" ecc... # Clear the FIFO by filter them out with low timeout - for _ in range(rxFifoSize): + for _ in range(rx_fifo_size): try: _canlib.canChannelReadMessage( self._channel_handle, 0, ctypes.byref(self._message) @@ -680,6 +738,31 @@ def __init__( super().__init__(channel=channel, can_filters=None, **kwargs) + @staticmethod + def _canptb_build(defaults, bitrate, tseg1, tseg2, sjw, ssp): + if bitrate in defaults: + d = defaults[bitrate] + if tseg1 is None: + tseg1 = d.wTS1 + if tseg2 is None: + tseg2 = d.wTS2 + if sjw is None: + sjw = d.wSJW + if ssp is None: + ssp = d.wTDO + dw_mode = d.dwMode + else: + dw_mode = 0 + + return structures.CANBTP( + dwMode=dw_mode, + dwBPS=bitrate, + wTS1=tseg1, + wTS2=tseg2, + wSJW=sjw, + wTDO=ssp, + ) + def _inWaiting(self): try: _canlib.canChannelWaitRxEvent(self._channel_handle, 0) diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index 0204d2a67..d4d7b3145 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -37,13 +37,20 @@ Python-can will search for the first IXXAT device available and open the first c module, while the following parameters are optional and are interpreted by IXXAT implementation. * ``bitrate`` (default 500000) Channel bitrate -* ``UniqueHardwareId`` (default first device) Unique hardware ID of the IXXAT device -* ``rxFifoSize`` (default 16 for CAN, 1024 for CAN-FD) Number of RX mailboxes -* ``txFifoSize`` (default 16 for CAN, 128 for CAN-FD) Number of TX mailboxes +* ``unique_hardware_id`` (default first device) Unique hardware ID of the IXXAT device +* ``rx_fifo_size`` (default 16 for CAN, 1024 for CAN-FD) Number of RX mailboxes +* ``tx_fifo_size`` (default 16 for CAN, 128 for CAN-FD) Number of TX mailboxes * ``extended`` (default False) Allow usage of extended IDs * ``fd`` Enable CAN-FD capabilities. * ``data_bitrate`` (defaults to 2Mbps) Channel data bitrate (to use when message bitrate_switch is used). +* ``sjw_abr`` (optional, only canfd) Bus timing value sample jump width (arbitration). +* ``tseg1_abr`` (optional, only canfd) Bus timing value tseg1 (arbitration). +* ``tseg2_abr`` (optional, only canfd) Bus timing value tseg2 (arbitration). +* ``sjw_dbr`` (optional, only used if baudrate switch enabled) Bus timing value sample jump width (data). +* ``tseg1_dbr`` (optional, only used if baudrate switch enabled) Bus timing value tseg1 (data). +* ``tseg2_dbr`` (optional, only used if baudrate switch enabled) Bus timing value tseg2 (data). +* ``ssp_dbr`` (optional, only used if baudrate switch enabled) Secondary sample point (data). From e54b735252d06362de7cb26314c79d11c2d1034e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20Burgos=20Maci=C3=A1?= Date: Thu, 25 Nov 2021 18:51:03 +0100 Subject: [PATCH 16/22] Update doc/interfaces/ixxat.rst Co-authored-by: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> --- doc/interfaces/ixxat.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index d4d7b3145..6b518c947 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -89,6 +89,6 @@ explicitly instantiated by the caller. - ``recv()`` is a blocking call with optional timeout. - ``send()`` is not blocking but may raise a VCIError if the TX FIFO is full -RX and TX FIFO sizes are configurable with ``rxFifoSize`` and ``txFifoSize`` +RX and TX FIFO sizes are configurable with ``rx_fifo_size`` and ``tx_fifo_size`` options, defaulting to 16 for both. From 81b8697b46d6edce71d8a40f811c306be6b73f64 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Sat, 27 Nov 2021 17:58:32 +0100 Subject: [PATCH 17/22] Changed: Documentation. --- can/interfaces/ixxat/canlib.py | 94 ++++++++++++++++++++++++-- can/interfaces/ixxat/canlib_vcinpl.py | 20 +++--- can/interfaces/ixxat/canlib_vcinpl2.py | 36 +++++----- doc/interfaces/ixxat.rst | 10 +-- 4 files changed, 125 insertions(+), 35 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index c396191f9..29edabb19 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -14,7 +14,27 @@ class IXXATBus(BusABC): This class only delegates to related implementation (in calib_vcinpl or canlib_vcinpl2) class depending on fd user option. """ - def __init__(self, channel, can_filters=None, fd=False, **kwargs): + def __init__( + self, + channel: int, + can_filters=None, + receive_own_messages: bool = False, + unique_hardware_id: int = None, + extended: bool = False, + fd: bool = False, + rx_fifo_size: int = None, + tx_fifo_size: int = None, + bitrate: int = 500000, + data_bitrate: int = 2000000, + sjw_abr: int = None, + tseg1_abr: int = None, + tseg2_abr: int = None, + sjw_dbr: int = None, + tseg1_dbr: int = None, + tseg2_dbr: int = None, + ssp_dbr: int = None, + **kwargs + ): """ :param int channel: The Channel id to create this bus with. @@ -25,24 +45,86 @@ def __init__(self, channel, can_filters=None, fd=False, **kwargs): :param bool receive_own_messages: Enable self-reception of sent messages. - :param int UniqueHardwareId: + :param int unique_hardware_id: UniqueHardwareId to connect (optional, will use the first found if not supplied) + :param int extended: + Default False, enables the capability to use extended IDs. + :param bool fd: Default False, enables CAN-FD usage. + :param int rx_fifo_size: + Receive fifo size (default 1024 for fd, else 16) + + :param int tx_fifo_size: + Transmit fifo size (default 128 for fd, else 16) + :param int bitrate: Channel bitrate in bit/s :param int data_bitrate: Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled). - :param int extended: - Default False, enables the capability to use extended IDs. + :param int sjw_abr: + Bus timing value sample jump width (arbitration). Only takes effect with fd enabled. + + :param int tseg1_abr: + Bus timing value tseg1 (arbitration). Only takes effect with fd enabled. + + :param int tseg2_abr: + Bus timing value tseg2 (arbitration). Only takes effect with fd enabled. + + :param int sjw_dbr: + Bus timing value sample jump width (data). Only takes effect with fd and baudrate switch enabled. + + :param int tseg1_dbr: + Bus timing value tseg1 (data). Only takes effect with fd and bitrate switch enabled. + + :param int tseg2_dbr: + Bus timing value tseg2 (data). Only takes effect with fd and bitrate switch enabled. + + :param int ssp_dbr: + Secondary sample point (data). Only takes effect with fd and bitrate switch enabled. """ - bus_class = vcinpl2.IXXATBus if fd else vcinpl.IXXATBus - self.bus = bus_class(channel, can_filters=can_filters, fd=fd, **kwargs) + if fd: + if rx_fifo_size is None: + rx_fifo_size = 1024 + if tx_fifo_size is None: + tx_fifo_size = 128 + self.bus = vcinpl2.IXXATBus( + channel=channel, + can_filters=can_filters, + receive_own_messages=receive_own_messages, + unique_hardware_id=unique_hardware_id, + extended=extended, + rx_fifo_size=rx_fifo_size, + tx_fifo_size=tx_fifo_size, + 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, + ssp_dbr=ssp_dbr, + ) + else: + if rx_fifo_size is None: + rx_fifo_size = 16 + if tx_fifo_size is None: + tx_fifo_size = 16 + self.bus = vcinpl.IXXATBus( + channel=channel, + can_filters=can_filters, + receive_own_messages=receive_own_messages, + unique_hardware_id=unique_hardware_id, + rx_fifo_size=rx_fifo_size, + tx_fifo_size=tx_fifo_size, + bitrate=bitrate, + ) def flush_tx_buffer(self): """Flushes the transmit buffer on the IXXAT""" diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 403880164..289d95c31 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -418,14 +418,13 @@ class IXXATBus(BusABC): ) def __init__( self, - channel, + channel: int, can_filters=None, - bitrate=500000, - unique_hardware_id=None, - rx_fifo_size=16, - tx_fifo_size=16, - receive_own_messages=False, - **kwargs, + receive_own_messages: bool = False, + unique_hardware_id: int = None, + rx_fifo_size: int = 16, + tx_fifo_size: int = 16, + bitrate: int = 500000, ): """ :param int channel: @@ -440,6 +439,12 @@ def __init__( :param int unique_hardware_id: unique_hardware_id to connect (optional, will use the first found if not supplied) + :param int rx_fifo_size: + Receive fifo size (default 16) + + :param int tx_fifo_size: + Transmit fifo size (default 16) + :param int bitrate: Channel bitrate in bit/s """ @@ -448,7 +453,6 @@ def __init__( "The IXXAT VCI library has not been initialized. Check the logs for more details." ) log.info("CAN Filters: %s", can_filters) - log.info("Got configuration of: %s", kwargs) # Configuration options self._receive_own_messages = receive_own_messages # Usually comes as a string from the config file diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 195281bd5..c42e6763c 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -424,23 +424,22 @@ class IXXATBus(BusABC): ) def __init__( self, - channel, + channel: int, can_filters=None, - bitrate=500000, + receive_own_messages: int = False, + unique_hardware_id: int = None, + extended: bool = False, + rx_fifo_size: int = 1024, + tx_fifo_size: int = 128, + bitrate: int = 500000, + data_bitrate: int = 2000000, sjw_abr: int = None, tseg1_abr: int = None, tseg2_abr: int = None, - data_bitrate=2000000, sjw_dbr: int = None, tseg1_dbr: int = None, tseg2_dbr: int = None, ssp_dbr: int = None, - extended=False, - unique_hardware_id=None, - rx_fifo_size=1024, - tx_fifo_size=128, - receive_own_messages=False, - **kwargs, ): """ :param int channel: @@ -455,15 +454,21 @@ def __init__( :param int unique_hardware_id: unique_hardware_id to connect (optional, will use the first found if not supplied) + :param int extended: + Default False, enables the capability to use extended IDs. + + :param int rx_fifo_size: + Receive fifo size (default 1024) + + :param int tx_fifo_size: + Transmit fifo size (default 128) + :param int bitrate: Channel bitrate in bit/s :param int data_bitrate: Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled). - :param int extended: - Default False, enables the capability to use extended IDs. - :param int sjw_abr: Bus timing value sample jump width (arbitration). @@ -477,13 +482,13 @@ def __init__( Bus timing value sample jump width (data) :param int tseg1_dbr: - Bus timing value tseg1 (data) + Bus timing value tseg1 (data). Only takes effect with fd and bitrate switch enabled. :param int tseg2_dbr: - Bus timing value tseg2 (data) + Bus timing value tseg2 (data). Only takes effect with fd and bitrate switch enabled. :param int ssp_dbr: - Bus timing value tseg2 (data) + Secondary sample point (data). Only takes effect with fd and bitrate switch enabled. """ if _canlib is None: @@ -491,7 +496,6 @@ def __init__( "The IXXAT VCI library has not been initialized. Check the logs for more details." ) log.info("CAN Filters: %s", can_filters) - log.info("Got configuration of: %s", kwargs) # Configuration options self._receive_own_messages = receive_own_messages # Usually comes as a string from the config file diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index 6b518c947..67faaad23 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -36,14 +36,14 @@ Python-can will search for the first IXXAT device available and open the first c ``interface`` and ``channel`` parameters are interpreted by frontend ``can.interfaces.interface`` module, while the following parameters are optional and are interpreted by IXXAT implementation. -* ``bitrate`` (default 500000) Channel bitrate +* ``receive_own_messages`` (default False) Enable self-reception of sent messages. * ``unique_hardware_id`` (default first device) Unique hardware ID of the IXXAT device +* ``extended`` (default False) Allow usage of extended IDs +* ``fd`` (default False) Enable CAN-FD capabilities. * ``rx_fifo_size`` (default 16 for CAN, 1024 for CAN-FD) Number of RX mailboxes * ``tx_fifo_size`` (default 16 for CAN, 128 for CAN-FD) Number of TX mailboxes -* ``extended`` (default False) Allow usage of extended IDs -* ``fd`` Enable CAN-FD capabilities. -* ``data_bitrate`` (defaults to 2Mbps) Channel data bitrate (to use when -message bitrate_switch is used). +* ``bitrate`` (default 500000) Channel bitrate. +* ``data_bitrate`` (defaults to 2Mbps) Channel data bitrate (only canfd, to use when message bitrate_switch is used). * ``sjw_abr`` (optional, only canfd) Bus timing value sample jump width (arbitration). * ``tseg1_abr`` (optional, only canfd) Bus timing value tseg1 (arbitration). * ``tseg2_abr`` (optional, only canfd) Bus timing value tseg2 (arbitration). From 1e81fed9681e70eebac291780fb7dcfc0824d40b Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 6 Dec 2021 13:38:20 +0100 Subject: [PATCH 18/22] Changed: Ixxat extended parameter default from False to True. Now it is also considered in "no fd" implementation. --- can/interfaces/ixxat/canlib.py | 5 +++-- can/interfaces/ixxat/canlib_vcinpl.py | 15 ++++++++++++--- can/interfaces/ixxat/canlib_vcinpl2.py | 4 ++-- doc/interfaces/ixxat.rst | 8 ++++---- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 29edabb19..25026619e 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -20,7 +20,7 @@ def __init__( can_filters=None, receive_own_messages: bool = False, unique_hardware_id: int = None, - extended: bool = False, + extended: bool = True, fd: bool = False, rx_fifo_size: int = None, tx_fifo_size: int = None, @@ -49,7 +49,7 @@ def __init__( UniqueHardwareId to connect (optional, will use the first found if not supplied) :param int extended: - Default False, enables the capability to use extended IDs. + Default True, enables the capability to use extended IDs. :param bool fd: Default False, enables CAN-FD usage. @@ -121,6 +121,7 @@ def __init__( can_filters=can_filters, receive_own_messages=receive_own_messages, unique_hardware_id=unique_hardware_id, + extended=extended, rx_fifo_size=rx_fifo_size, tx_fifo_size=tx_fifo_size, bitrate=bitrate, diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 289d95c31..55f013b08 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -422,6 +422,7 @@ def __init__( can_filters=None, receive_own_messages: bool = False, unique_hardware_id: int = None, + extended: bool = True, rx_fifo_size: int = 16, tx_fifo_size: int = 16, bitrate: int = 500000, @@ -439,6 +440,9 @@ def __init__( :param int unique_hardware_id: unique_hardware_id to connect (optional, will use the first found if not supplied) + :param int extended: + Default True, enables the capability to use extended IDs. + :param int rx_fifo_size: Receive fifo size (default 16) @@ -552,11 +556,16 @@ def __init__( _canlib.canControlOpen( self._device_handle, channel, ctypes.byref(self._control_handle) ) + + # compute opmode before control initialize + opmode = constants.CAN_OPMODE_STANDARD | constants.CAN_OPMODE_ERRFRAME + if extended: + opmode |= constants.CAN_OPMODE_EXTENDED + + # control initialize _canlib.canControlInitialize( self._control_handle, - constants.CAN_OPMODE_STANDARD - | constants.CAN_OPMODE_EXTENDED - | constants.CAN_OPMODE_ERRFRAME, + opmode, self.CHANNEL_BITRATES[0][bitrate], self.CHANNEL_BITRATES[1][bitrate], ) diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index c42e6763c..562630038 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -428,7 +428,7 @@ def __init__( can_filters=None, receive_own_messages: int = False, unique_hardware_id: int = None, - extended: bool = False, + extended: bool = True, rx_fifo_size: int = 1024, tx_fifo_size: int = 128, bitrate: int = 500000, @@ -455,7 +455,7 @@ def __init__( unique_hardware_id to connect (optional, will use the first found if not supplied) :param int extended: - Default False, enables the capability to use extended IDs. + Default True, enables the capability to use extended IDs. :param int rx_fifo_size: Receive fifo size (default 1024) diff --git a/doc/interfaces/ixxat.rst b/doc/interfaces/ixxat.rst index 67faaad23..28fb6f314 100644 --- a/doc/interfaces/ixxat.rst +++ b/doc/interfaces/ixxat.rst @@ -37,11 +37,11 @@ Python-can will search for the first IXXAT device available and open the first c module, while the following parameters are optional and are interpreted by IXXAT implementation. * ``receive_own_messages`` (default False) Enable self-reception of sent messages. -* ``unique_hardware_id`` (default first device) Unique hardware ID of the IXXAT device -* ``extended`` (default False) Allow usage of extended IDs +* ``unique_hardware_id`` (default first device) Unique hardware ID of the IXXAT device. +* ``extended`` (default True) Allow usage of extended IDs. * ``fd`` (default False) Enable CAN-FD capabilities. -* ``rx_fifo_size`` (default 16 for CAN, 1024 for CAN-FD) Number of RX mailboxes -* ``tx_fifo_size`` (default 16 for CAN, 128 for CAN-FD) Number of TX mailboxes +* ``rx_fifo_size`` (default 16 for CAN, 1024 for CAN-FD) Number of RX mailboxes. +* ``tx_fifo_size`` (default 16 for CAN, 128 for CAN-FD) Number of TX mailboxes. * ``bitrate`` (default 500000) Channel bitrate. * ``data_bitrate`` (defaults to 2Mbps) Channel data bitrate (only canfd, to use when message bitrate_switch is used). * ``sjw_abr`` (optional, only canfd) Bus timing value sample jump width (arbitration). From 2da4757184a03f1bf0d5981a52431e8d24b32fdc Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 6 Dec 2021 13:39:19 +0100 Subject: [PATCH 19/22] Changed: Ixxat parameter unique_hardware_id type hint is now optional. --- can/interfaces/ixxat/canlib.py | 2 +- can/interfaces/ixxat/canlib_vcinpl.py | 2 +- can/interfaces/ixxat/canlib_vcinpl2.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 25026619e..0da24e54b 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -19,7 +19,7 @@ def __init__( channel: int, can_filters=None, receive_own_messages: bool = False, - unique_hardware_id: int = None, + unique_hardware_id: Optional[int] = None, extended: bool = True, fd: bool = False, rx_fifo_size: int = None, diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 55f013b08..5640cb912 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -421,7 +421,7 @@ def __init__( channel: int, can_filters=None, receive_own_messages: bool = False, - unique_hardware_id: int = None, + unique_hardware_id: Optional[int] = None, extended: bool = True, rx_fifo_size: int = 16, tx_fifo_size: int = 16, diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 562630038..9ab90ad1c 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -427,7 +427,7 @@ def __init__( channel: int, can_filters=None, receive_own_messages: int = False, - unique_hardware_id: int = None, + unique_hardware_id: Optional[int] = None, extended: bool = True, rx_fifo_size: int = 1024, tx_fifo_size: int = 128, From 189fb3b9628b54efe458ec0692b7551580ebe38b Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 6 Dec 2021 13:43:54 +0100 Subject: [PATCH 20/22] Changed: Ixxat doc comment types removed (let Sphinx derive them from the method signature). --- can/interfaces/ixxat/canlib.py | 34 +++++++++++++------------- can/interfaces/ixxat/canlib_vcinpl.py | 16 ++++++------ can/interfaces/ixxat/canlib_vcinpl2.py | 32 ++++++++++++------------ 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 0da24e54b..bf52e7ed5 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -36,55 +36,55 @@ def __init__( **kwargs ): """ - :param int channel: + :param channel: The Channel id to create this bus with. - :param list can_filters: + :param can_filters: See :meth:`can.BusABC.set_filters`. - :param bool receive_own_messages: + :param receive_own_messages: Enable self-reception of sent messages. - :param int unique_hardware_id: + :param unique_hardware_id: UniqueHardwareId to connect (optional, will use the first found if not supplied) - :param int extended: + :param extended: Default True, enables the capability to use extended IDs. - :param bool fd: + :param fd: Default False, enables CAN-FD usage. - :param int rx_fifo_size: + :param rx_fifo_size: Receive fifo size (default 1024 for fd, else 16) - :param int tx_fifo_size: + :param tx_fifo_size: Transmit fifo size (default 128 for fd, else 16) - :param int bitrate: + :param bitrate: Channel bitrate in bit/s - :param int data_bitrate: + :param data_bitrate: Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled). - :param int sjw_abr: + :param sjw_abr: Bus timing value sample jump width (arbitration). Only takes effect with fd enabled. - :param int tseg1_abr: + :param tseg1_abr: Bus timing value tseg1 (arbitration). Only takes effect with fd enabled. - :param int tseg2_abr: + :param tseg2_abr: Bus timing value tseg2 (arbitration). Only takes effect with fd enabled. - :param int sjw_dbr: + :param sjw_dbr: Bus timing value sample jump width (data). Only takes effect with fd and baudrate switch enabled. - :param int tseg1_dbr: + :param tseg1_dbr: Bus timing value tseg1 (data). Only takes effect with fd and bitrate switch enabled. - :param int tseg2_dbr: + :param tseg2_dbr: Bus timing value tseg2 (data). Only takes effect with fd and bitrate switch enabled. - :param int ssp_dbr: + :param ssp_dbr: Secondary sample point (data). Only takes effect with fd and bitrate switch enabled. """ diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index 5640cb912..edf940862 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -428,28 +428,28 @@ def __init__( bitrate: int = 500000, ): """ - :param int channel: + :param channel: The Channel id to create this bus with. - :param list can_filters: + :param can_filters: See :meth:`can.BusABC.set_filters`. - :param bool receive_own_messages: + :param receive_own_messages: Enable self-reception of sent messages. - :param int unique_hardware_id: + :param unique_hardware_id: unique_hardware_id to connect (optional, will use the first found if not supplied) - :param int extended: + :param extended: Default True, enables the capability to use extended IDs. - :param int rx_fifo_size: + :param rx_fifo_size: Receive fifo size (default 16) - :param int tx_fifo_size: + :param tx_fifo_size: Transmit fifo size (default 16) - :param int bitrate: + :param bitrate: Channel bitrate in bit/s """ if _canlib is None: diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 9ab90ad1c..3ac24a834 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -442,52 +442,52 @@ def __init__( ssp_dbr: int = None, ): """ - :param int channel: + :param channel: The Channel id to create this bus with. - :param list can_filters: + :param can_filters: See :meth:`can.BusABC.set_filters`. - :param bool receive_own_messages: + :param receive_own_messages: Enable self-reception of sent messages. - :param int unique_hardware_id: + :param unique_hardware_id: unique_hardware_id to connect (optional, will use the first found if not supplied) - :param int extended: + :param extended: Default True, enables the capability to use extended IDs. - :param int rx_fifo_size: + :param rx_fifo_size: Receive fifo size (default 1024) - :param int tx_fifo_size: + :param tx_fifo_size: Transmit fifo size (default 128) - :param int bitrate: + :param bitrate: Channel bitrate in bit/s - :param int data_bitrate: + :param data_bitrate: Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled). - :param int sjw_abr: + :param sjw_abr: Bus timing value sample jump width (arbitration). - :param int tseg1_abr: + :param tseg1_abr: Bus timing value tseg1 (arbitration) - :param int tseg2_abr: + :param tseg2_abr: Bus timing value tseg2 (arbitration) - :param int sjw_dbr: + :param sjw_dbr: Bus timing value sample jump width (data) - :param int tseg1_dbr: + :param tseg1_dbr: Bus timing value tseg1 (data). Only takes effect with fd and bitrate switch enabled. - :param int tseg2_dbr: + :param tseg2_dbr: Bus timing value tseg2 (data). Only takes effect with fd and bitrate switch enabled. - :param int ssp_dbr: + :param ssp_dbr: Secondary sample point (data). Only takes effect with fd and bitrate switch enabled. """ From 6147e3fd7389eded7791ac2230fc557061cc8b04 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 6 Dec 2021 13:57:11 +0100 Subject: [PATCH 21/22] Changed: Updated Ixxat tests to use new argument names. --- test/test_interface_ixxat.py | 8 ++++---- test/test_interface_ixxat_fd.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/test_interface_ixxat.py b/test/test_interface_ixxat.py index 55618c769..ccd985051 100644 --- a/test/test_interface_ixxat.py +++ b/test/test_interface_ixxat.py @@ -26,13 +26,13 @@ def test_bus_creation(self): with self.assertRaises(ValueError): can.Bus(interface="ixxat", channel=-1) - # rxFifoSize must be > 0 + # rx_fifo_size must be > 0 with self.assertRaises(ValueError): - can.Bus(interface="ixxat", channel=0, rxFifoSize=0) + can.Bus(interface="ixxat", channel=0, rx_fifo_size=0) - # txFifoSize must be > 0 + # tx_fifo_size must be > 0 with self.assertRaises(ValueError): - can.Bus(interface="ixxat", channel=0, txFifoSize=0) + can.Bus(interface="ixxat", channel=0, tx_fifo_size=0) class HardwareTestCase(unittest.TestCase): diff --git a/test/test_interface_ixxat_fd.py b/test/test_interface_ixxat_fd.py index 23452b9d7..0aa999a21 100644 --- a/test/test_interface_ixxat_fd.py +++ b/test/test_interface_ixxat_fd.py @@ -26,13 +26,13 @@ def test_bus_creation(self): with self.assertRaises(ValueError): can.Bus(interface="ixxat", fd=True, channel=-1) - # rxFifoSize must be > 0 + # rx_fifo_size must be > 0 with self.assertRaises(ValueError): - can.Bus(interface="ixxat", fd=True, channel=0, rxFifoSize=0) + can.Bus(interface="ixxat", fd=True, channel=0, rx_fifo_size=0) - # txFifoSize must be > 0 + # tx_fifo_size must be > 0 with self.assertRaises(ValueError): - can.Bus(interface="ixxat", fd=True, channel=0, txFifoSize=0) + can.Bus(interface="ixxat", fd=True, channel=0, tx_fifo_size=0) class HardwareTestCase(unittest.TestCase): From f9161cb226f68b8295e123439be50c03abc609e9 Mon Sep 17 00:00:00 2001 From: fjburgos Date: Mon, 6 Dec 2021 15:43:47 +0100 Subject: [PATCH 22/22] Fixed: Missing **kwargs parameter in vcinpl and vcinpl2. --- can/interfaces/ixxat/canlib.py | 2 ++ can/interfaces/ixxat/canlib_vcinpl.py | 1 + can/interfaces/ixxat/canlib_vcinpl2.py | 1 + 3 files changed, 4 insertions(+) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index bf52e7ed5..4dc0d3e6e 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -110,6 +110,7 @@ def __init__( tseg1_dbr=tseg1_dbr, tseg2_dbr=tseg2_dbr, ssp_dbr=ssp_dbr, + **kwargs ) else: if rx_fifo_size is None: @@ -125,6 +126,7 @@ def __init__( rx_fifo_size=rx_fifo_size, tx_fifo_size=tx_fifo_size, bitrate=bitrate, + **kwargs ) def flush_tx_buffer(self): diff --git a/can/interfaces/ixxat/canlib_vcinpl.py b/can/interfaces/ixxat/canlib_vcinpl.py index edf940862..cb0447b49 100644 --- a/can/interfaces/ixxat/canlib_vcinpl.py +++ b/can/interfaces/ixxat/canlib_vcinpl.py @@ -426,6 +426,7 @@ def __init__( rx_fifo_size: int = 16, tx_fifo_size: int = 16, bitrate: int = 500000, + **kwargs, ): """ :param channel: diff --git a/can/interfaces/ixxat/canlib_vcinpl2.py b/can/interfaces/ixxat/canlib_vcinpl2.py index 3ac24a834..37085d74a 100644 --- a/can/interfaces/ixxat/canlib_vcinpl2.py +++ b/can/interfaces/ixxat/canlib_vcinpl2.py @@ -440,6 +440,7 @@ def __init__( tseg1_dbr: int = None, tseg2_dbr: int = None, ssp_dbr: int = None, + **kwargs, ): """ :param channel: