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 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
def __init__(self, channel, can_filters=None, **config): """ :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 ImportError("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", config) # Configuration options bitrate = config.get('bitrate', 500000) UniqueHardwareId = config.get('UniqueHardwareId', None) rxFifoSize = config.get('rxFifoSize', 16) txFifoSize = config.get('txFifoSize', 16) self._receive_own_messages = config.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)) 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) _canlib.vciDeviceOpen(ctypes.byref(self._device_info.VciObjectId), ctypes.byref(self._device_handle)) 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) _canlib.canChannelOpen(self._device_handle, channel, constants.FALSE, ctypes.byref(self._channel_handle)) # 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: # Whitelist 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 i in range(rxFifoSize): try: _canlib.canChannelReadMessage(self._channel_handle, 0, ctypes.byref(self._message)) except (VCITimeout, VCIRxQueueEmptyError): break super(IXXATBus, self).__init__(channel=channel, can_filters=None, **config)
def __init__( self, channel: int, can_filters=None, receive_own_messages: bool = False, unique_hardware_id: Optional[int] = None, extended: bool = True, rx_fifo_size: int = 16, tx_fifo_size: int = 16, bitrate: int = 500000, **kwargs, ): """ :param channel: The Channel id to create this bus with. :param can_filters: See :meth:`can.BusABC.set_filters`. :param receive_own_messages: Enable self-reception of sent messages. :param unique_hardware_id: unique_hardware_id to connect (optional, will use the first found if not supplied) :param extended: Default True, enables the capability to use extended IDs. :param rx_fifo_size: Receive fifo size (default 16) :param tx_fifo_size: Transmit fifo size (default 16) :param 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) # Configuration options self._receive_own_messages = receive_own_messages # 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 rx_fifo_size <= 0: raise ValueError("rx_fifo_size 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") 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 unique_hardware_id is None: log.info("Searching for first available device") else: log.info("Searching for unique HW ID %s", unique_hardware_id) _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle)) while True: try: _canlib.vciEnumDeviceNext( self._device_handle, ctypes.byref(self._device_info) ) except StopIteration: 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( unique_hardware_id ) ) else: if (unique_hardware_id is None) or ( self._device_info.UniqueHardwareId.AsChar == bytes(unique_hardware_id, "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, rx_fifo_size, tx_fifo_size, ) 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, rx_fifo_size, 1, tx_fifo_size, 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) ) # 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, opmode, 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 self._tick_resolution = ( 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(rx_fifo_size): try: _canlib.canChannelReadMessage( self._channel_handle, 0, ctypes.byref(self._message) ) except (VCITimeout, VCIRxQueueEmptyError): break super().__init__(channel=channel, can_filters=None, **kwargs)
def __init__( self, channel: int, can_filters=None, receive_own_messages: int = False, unique_hardware_id: Optional[int] = None, extended: bool = True, 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, sjw_dbr: int = None, tseg1_dbr: int = None, tseg2_dbr: int = None, ssp_dbr: int = None, **kwargs, ): """ :param channel: The Channel id to create this bus with. :param can_filters: See :meth:`can.BusABC.set_filters`. :param receive_own_messages: Enable self-reception of sent messages. :param unique_hardware_id: unique_hardware_id to connect (optional, will use the first found if not supplied) :param extended: Default True, enables the capability to use extended IDs. :param rx_fifo_size: Receive fifo size (default 1024) :param tx_fifo_size: Transmit fifo size (default 128) :param bitrate: Channel bitrate in bit/s :param data_bitrate: Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled). :param sjw_abr: Bus timing value sample jump width (arbitration). :param tseg1_abr: Bus timing value tseg1 (arbitration) :param tseg2_abr: Bus timing value tseg2 (arbitration) :param sjw_dbr: Bus timing value sample jump width (data) :param tseg1_dbr: Bus timing value tseg1 (data). Only takes effect with fd and bitrate switch enabled. :param tseg2_dbr: Bus timing value tseg2 (data). Only takes effect with fd and bitrate switch enabled. :param ssp_dbr: Secondary sample point (data). Only takes effect with fd and bitrate switch enabled. """ 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) # Configuration options self._receive_own_messages = receive_own_messages # Usually comes as a string from the config file channel = int(channel) 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 rx_fifo_size <= 0: raise ValueError("rx_fifo_size 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") self._device_handle = HANDLE() self._device_info = structures.VCIDEVICEINFO() self._control_handle = HANDLE() self._channel_handle = HANDLE() self._channel_capabilities = structures.CANCAPABILITIES2() self._message = structures.CANMSG2() self._payload = (ctypes.c_byte * 64)() # Search for supplied device if unique_hardware_id is None: log.info("Searching for first available device") else: log.info("Searching for unique HW ID %s", unique_hardware_id) _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle)) while True: try: _canlib.vciEnumDeviceNext(self._device_handle, ctypes.byref(self._device_info)) except StopIteration: 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(unique_hardware_id)) else: if (unique_hardware_id is None) or (self._device_info.UniqueHardwareId.AsChar == bytes(unique_hardware_id, "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, rx_fifo_size, tx_fifo_size, ) 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, rx_fifo_size, 1, tx_fifo_size, 1, 0, constants.CAN_FILTER_PASS, ) _canlib.canChannelActivate(self._channel_handle, constants.TRUE) 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, pBtpSDR, pBtpFDR, ) _canlib.canControlOpen(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, bOpMode, bExMode, constants.CAN_FILTER_PASS, constants.CAN_FILTER_PASS, 0, 0, ctypes.byref(pBtpSDR), ctypes.byref(pBtpFDR), ) # 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 self._tick_resolution = (self._channel_capabilities.dwTscClkFreq / 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(rx_fifo_size): try: _canlib.canChannelReadMessage(self._channel_handle, 0, ctypes.byref(self._message)) except (VCITimeout, VCIRxQueueEmptyError): break super().__init__(channel=channel, can_filters=None, **kwargs)