def send(self, msg, timeout=None): log.debug("Sending message: %s", msg) # 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.id_type else 0 message.dwMsgId = msg.arbitration_id if msg.dlc: message.uMsgInfo.Bits.dlc = msg.dlc adapter = (ctypes.c_uint8 * msg.dlc).from_buffer(msg.data) ctypes.memmove(message.abData, adapter, msg.dlc) if timeout: _canlib.canChannelSendMessage(self._channel_handle, int(timeout * 1000), message) else: _canlib.canChannelPostMessage(self._channel_handle, message)
def __init__(self, channel, can_filters=None, **config): """ :param int channel: The Channel id to create this bus with. :param list can_filters: A list of dictionaries each containing a "can_id" and a "can_mask". >>> [{"can_id": 0x11, "can_mask": 0x21}] :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) # 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 _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 if extended else constants.CAN_OPMODE_STANDARD | 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 is not None and len(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, mask) rtr = (code & 0x01) and (mask & 0x01) log.info("Accepting ID:%d MASK:%d RTR:%s", code >> 1, mask >> 1, "YES" if rtr else "NO") # Start the CAN controller. Messages will be forwarded to the channel _canlib.canControlStart(self._control_handle, constants.TRUE) # Usually you get back 3 messages like "CAN initialized" ecc... # Filter them out with low timeout while True: try: _canlib.canChannelWaitRxEvent(self._channel_handle, 0) except VCITimeout: break else: _canlib.canChannelReadMessage(self._channel_handle, 0, ctypes.byref(self._message)) super(IXXATBus, self).__init__()