python-can-2.0.0/0000755000175000017500000000000013223535610014001 5ustar brianbrian00000000000000python-can-2.0.0/can/0000755000175000017500000000000013223535610014542 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/0000755000175000017500000000000013223535610016665 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/ixxat/0000755000175000017500000000000013223535610020022 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/ixxat/__init__.py0000644000175000017500000000034413223535415022137 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems Copyright (C) 2016 Giuseppe Corbelli """ from can.interfaces.ixxat.canlib import IXXATBus python-can-2.0.0/can/interfaces/ixxat/canlib.py0000644000175000017500000006334713223535415021644 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems Copyright (C) 2016 Giuseppe Corbelli """ import ctypes import functools import logging import sys import time from can import CanError, BusABC from can import Message from can.interfaces.ixxat import constants, structures from can.broadcastmanager import (LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC) from can.ctypesutil import CLibrary, HANDLE, PHANDLE from .constants import VCI_MAX_ERRSTRLEN from .exceptions import * __all__ = ["VCITimeout", "VCIError", "VCIDeviceNotFoundError", "IXXATBus", "vciFormatError"] log = logging.getLogger('can.ixxat') if ((sys.version_info.major == 3) and (sys.version_info.minor >= 3)): _timer_function = time.perf_counter else: _timer_function = time.clock # Hack to have vciFormatError as a free function, see below vciFormatError = None # main ctypes instance _canlib = None if sys.platform == "win32": try: _canlib = CLibrary("vcinpl") 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(VCI_MAX_ERRSTRLEN) ctypes.memset(buf, 0, VCI_MAX_ERRSTRLEN) library_instance.vciFormatError(HRESULT, buf, 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_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", } #---------------------------------------------------------------------------- class IXXATBus(BusABC): """The CAN Bus implemented for the IXXAT interface. """ 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, 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, 800000: constants.CAN_BT1_800KB, 1000000: constants.CAN_BT1_1000KB } } 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, 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 << 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__() 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(self, timeout=None): """ 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 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_TIMEOVR: pass else: log.warn("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 # The _message.dwTime is a 32bit tick value and will overrun, # so expect to see the value restarting from 0 rx_msg = Message( self._message.dwTime / self._tick_resolution, # Relative time in s True if self._message.uMsgInfo.Bits.rtr else False, True if self._message.uMsgInfo.Bits.ext else False, False, self._message.dwMsgId, self._message.uMsgInfo.Bits.dlc, self._message.abData[:self._message.uMsgInfo.Bits.dlc], self.channel ) log.debug('Recv()ed message %s', rx_msg) return rx_msg 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 * 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(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, msg, period, duration, resolution): super(CyclicSendTask, self).__init__(msg, period, duration) 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 = msg.arbitration_id self._msg.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA self._msg.uMsgInfo.Bits.ext = 1 if msg.id_type else 0 self._msg.uMsgInfo.Bits.rtr = 1 if msg.is_remote_frame else 0 self._msg.uMsgInfo.Bits.dlc = msg.dlc for i, b in enumerate(msg.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 python-can-2.0.0/can/interfaces/ixxat/constants.py0000644000175000017500000001047113223535415022416 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ 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_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 python-can-2.0.0/can/interfaces/ixxat/exceptions.py0000644000175000017500000000133413223535415022561 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems Copyright (C) 2016 Giuseppe Corbelli """ from can import CanError __all__ = ['VCITimeout', 'VCIError', 'VCIRxQueueEmptyError', 'VCIDeviceNotFoundError'] class VCITimeout(CanError): """ Wraps the VCI_E_TIMEOUT error """ pass class VCIError(CanError): """ Try to display errors that occur within the wrapped C library nicely. """ pass class VCIRxQueueEmptyError(VCIError): """ Wraps the VCI_E_RXQUEUE_EMPTY error """ def __init__(self): super(VCIRxQueueEmptyError, self).__init__("Receive queue is empty") class VCIDeviceNotFoundError(CanError): pass python-can-2.0.0/can/interfaces/ixxat/structures.py0000644000175000017500000001073513223535415022630 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ 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_ulong), ("HighPart", ctypes.c_long), ] 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_long), ("Data2", ctypes.c_short), ("Data3", ctypes.c_short), ("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) python-can-2.0.0/can/interfaces/kvaser/0000755000175000017500000000000013223535610020160 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/kvaser/__init__.py0000644000175000017500000000005313113515201022257 0ustar brianbrian00000000000000from can.interfaces.kvaser.canlib import * python-can-2.0.0/can/interfaces/kvaser/argument_parser.py0000644000175000017500000000265213113515201023725 0ustar brianbrian00000000000000 def add_to_parser(parser): parser.add_argument("-c", "--channel", type=str, dest="channel", help=""" If the CAN interface supports multiple channels, select which one you are after here. For example on linux this might be 1 """, default='0') parser.add_argument("-b", "--bitrate", type=int, dest="bitrate", help="CAN bus bitrate", default=1000000) parser.add_argument("--tseg1", type=int, dest="tseg1", help="CAN bus tseg1", default=4) parser.add_argument("--tseg2", type=int, dest="tseg2", help="CAN bus tseg2", default=3) parser.add_argument("--sjw", type=int, dest="sjw", help="Synchronisation Jump Width decides the maximum number of time quanta that the controller can resynchronise every bit.", default=1) parser.add_argument("-n", "--num_samples", type=int, dest="no_samp", help="""Some CAN controllers can also sample each bit three times. In this case, the bit will be sampled three quanta in a row, with the last sample being taken in the edge between TSEG1 and TSEG2. Three samples should only be used for relatively slow baudrates.""", default=1) python-can-2.0.0/can/interfaces/kvaser/canlib.py0000644000175000017500000005367413223535415022004 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ Contains Python equivalents of the function and constant definitions in CANLIB's canlib.h, with some supporting functionality specific to Python. Copyright (C) 2010 Dynamic Controls """ import sys import time import logging import ctypes from can import CanError, BusABC from can import Message from can.interfaces.kvaser import constants as canstat log = logging.getLogger('can.kvaser') # Resolution in us TIMESTAMP_RESOLUTION = 10 TIMESTAMP_FACTOR = TIMESTAMP_RESOLUTION / 1000000.0 try: if sys.platform == "win32": __canlib = ctypes.windll.LoadLibrary("canlib32") else: __canlib = ctypes.cdll.LoadLibrary("libcanlib.so") log.info("loaded kvaser's CAN library") except OSError: log.warning("Kvaser canlib is unavailable.") __canlib = None def _unimplemented_function(*args): raise NotImplementedError('This function is not implemented in canlib') def __get_canlib_function(func_name, argtypes=[], restype=None, errcheck=None): #log.debug('Wrapping function "%s"' % func_name) try: # e.g. canlib.canBusOn retval = getattr(__canlib, func_name) #log.debug('"%s" found in library', func_name) except AttributeError: log.warning('"%s" was not found in library', func_name) return _unimplemented_function else: #log.debug('Result type is: %s' % type(restype)) #log.debug('Error check function is: %s' % errcheck) retval.argtypes = argtypes retval.restype = restype if errcheck: retval.errcheck = errcheck return retval class CANLIBError(CanError): """ Try to display errors that occur within the wrapped C library nicely. """ def __init__(self, function, error_code, arguments): super(CANLIBError, self).__init__() self.error_code = error_code self.function = function self.arguments = arguments def __str__(self): return "Function %s failed - %s" % (self.function.__name__, self.__get_error_message()) def __get_error_message(self): errmsg = ctypes.create_string_buffer(128) canGetErrorText(self.error_code, errmsg, len(errmsg)) return errmsg.value.decode("ascii") def __convert_can_status_to_int(result): #log.debug("converting can status to int {} ({})".format(result, type(result))) if isinstance(result, int): return result else: return result.value def __check_status(result, function, arguments): result = __convert_can_status_to_int(result) if not canstat.CANSTATUS_SUCCESS(result): #log.debug('Detected error while checking CAN status') raise CANLIBError(function, result, arguments) return result def __check_status_read(result, function, arguments): result = __convert_can_status_to_int(result) if not canstat.CANSTATUS_SUCCESS(result) and result != canstat.canERR_NOMSG: #log.debug('Detected error in which checking status read') raise CANLIBError(function, result, arguments) return result class c_canHandle(ctypes.c_int): pass canINVALID_HANDLE = -1 def __handle_is_valid(handle): return (handle.value > canINVALID_HANDLE) def __check_bus_handle_validity(handle, function, arguments): if not __handle_is_valid(handle): result = __convert_can_status_to_int(handle) raise CANLIBError(function, result, arguments) else: return handle if __canlib is not None: canInitializeLibrary = __get_canlib_function("canInitializeLibrary") canGetErrorText = __get_canlib_function("canGetErrorText", argtypes=[canstat.c_canStatus, ctypes.c_char_p, ctypes.c_uint], restype=canstat.c_canStatus, errcheck=__check_status) # TODO wrap this type of function to provide a more Pythonic API canGetNumberOfChannels = __get_canlib_function("canGetNumberOfChannels", argtypes=[ctypes.c_void_p], restype=canstat.c_canStatus, errcheck=__check_status) kvReadTimer = __get_canlib_function("kvReadTimer", argtypes=[c_canHandle, ctypes.POINTER(ctypes.c_uint)], restype=canstat.c_canStatus, errcheck=__check_status) canBusOff = __get_canlib_function("canBusOff", argtypes=[c_canHandle], restype=canstat.c_canStatus, errcheck=__check_status) canBusOn = __get_canlib_function("canBusOn", argtypes=[c_canHandle], restype=canstat.c_canStatus, errcheck=__check_status) canClose = __get_canlib_function("canClose", argtypes=[c_canHandle], restype=canstat.c_canStatus, errcheck=__check_status) canOpenChannel = __get_canlib_function("canOpenChannel", argtypes=[ctypes.c_int, ctypes.c_int], restype=c_canHandle, errcheck=__check_bus_handle_validity) canSetBusParams = __get_canlib_function("canSetBusParams", argtypes=[c_canHandle, ctypes.c_long, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint], restype=canstat.c_canStatus, errcheck=__check_status) canSetBusOutputControl = __get_canlib_function("canSetBusOutputControl", argtypes=[c_canHandle, ctypes.c_uint], restype=canstat.c_canStatus, errcheck=__check_status) canSetAcceptanceFilter = __get_canlib_function("canSetAcceptanceFilter", argtypes=[ c_canHandle, ctypes.c_uint, ctypes.c_uint, ctypes.c_int ], restype=canstat.c_canStatus, errcheck=__check_status) canReadWait = __get_canlib_function("canReadWait", argtypes=[c_canHandle, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long], restype=canstat.c_canStatus, errcheck=__check_status_read) canWrite = __get_canlib_function("canWrite", argtypes=[ c_canHandle, ctypes.c_long, ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint], restype=canstat.c_canStatus, errcheck=__check_status) canWriteSync = __get_canlib_function("canWriteSync", argtypes=[c_canHandle, ctypes.c_ulong], restype=canstat.c_canStatus, errcheck=__check_status) canIoCtl = __get_canlib_function("canIoCtl", argtypes=[c_canHandle, ctypes.c_uint, ctypes.c_void_p, ctypes.c_uint], restype=canstat.c_canStatus, errcheck=__check_status) canGetVersion = __get_canlib_function("canGetVersion", restype=ctypes.c_short, errcheck=__check_status) kvFlashLeds = __get_canlib_function("kvFlashLeds", argtypes=[c_canHandle, ctypes.c_int, ctypes.c_int], restype=ctypes.c_short, errcheck=__check_status) if sys.platform == "win32": canGetVersionEx = __get_canlib_function("canGetVersionEx", argtypes=[ctypes.c_uint], restype=ctypes.c_uint, errcheck=__check_status) canGetChannelData = __get_canlib_function("canGetChannelData", argtypes=[ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t], restype=canstat.c_canStatus, errcheck=__check_status) def init_kvaser_library(): if __canlib is not None: try: log.debug("Initializing Kvaser CAN library") canInitializeLibrary() log.debug("CAN library initialized") except: log.warning("Kvaser canlib could not be initialized.") DRIVER_MODE_SILENT = False DRIVER_MODE_NORMAL = True BITRATE_OBJS = {1000000 : canstat.canBITRATE_1M, 500000 : canstat.canBITRATE_500K, 250000 : canstat.canBITRATE_250K, 125000 : canstat.canBITRATE_125K, 100000 : canstat.canBITRATE_100K, 83000 : canstat.canBITRATE_83K, 62000 : canstat.canBITRATE_62K, 50000 : canstat.canBITRATE_50K, 10000 : canstat.canBITRATE_10K} class KvaserBus(BusABC): """ The CAN Bus implemented for the Kvaser interface. """ 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}] Backend Configuration :param int bitrate: Bitrate of channel in bit/s :param int tseg1: Time segment 1, that is, the number of quanta from (but not including) the Sync Segment to the sampling point. If this parameter is not given, the Kvaser driver will try to choose all bit timing parameters from a set of defaults. :param int tseg2: Time segment 2, that is, the number of quanta from the sampling point to the end of the bit. :param int sjw: The Synchronisation Jump Width. Decides the maximum number of time quanta that the controller can resynchronise every bit. :param int no_samp: Either 1 or 3. Some CAN controllers can also sample each bit three times. In this case, the bit will be sampled three quanta in a row, with the last sample being taken in the edge between TSEG1 and TSEG2. Three samples should only be used for relatively slow baudrates. :param bool driver_mode: Silent or normal. :param bool single_handle: Use one Kvaser CANLIB bus handle for both reading and writing. This can be set if reading and/or writing is done from one thread. :param bool receive_own_messages: If messages transmitted should also be received back. Only works if single_handle is also False. If you want to receive messages from other applications on the same computer, set this to True or set single_handle to True. """ log.info("CAN Filters: {}".format(can_filters)) log.info("Got configuration of: {}".format(config)) bitrate = config.get('bitrate', 500000) tseg1 = config.get('tseg1', 0) tseg2 = config.get('tseg2', 0) sjw = config.get('sjw', 0) no_samp = config.get('no_samp', 0) driver_mode = config.get('driver_mode', DRIVER_MODE_NORMAL) single_handle = config.get('single_handle', False) receive_own_messages = config.get('receive_own_messages', False) try: channel = int(channel) except ValueError: raise ValueError('channel must be an integer') self.channel = channel if 'tseg1' not in config and bitrate in BITRATE_OBJS: bitrate = BITRATE_OBJS[bitrate] log.debug('Initialising bus instance') self.single_handle = single_handle num_channels = ctypes.c_int(0) res = canGetNumberOfChannels(ctypes.byref(num_channels)) #log.debug("Res: {}".format(res)) num_channels = int(num_channels.value) log.info('Found %d available channels' % num_channels) for idx in range(num_channels): channel_info = get_channel_info(idx) log.info('%d: %s', idx, channel_info) if idx == channel: self.channel_info = channel_info log.debug('Creating read handle to bus channel: %s' % channel) self._read_handle = canOpenChannel(channel, canstat.canOPEN_ACCEPT_VIRTUAL) canIoCtl(self._read_handle, canstat.canIOCTL_SET_TIMER_SCALE, ctypes.byref(ctypes.c_long(TIMESTAMP_RESOLUTION)), 4) canSetBusParams(self._read_handle, bitrate, tseg1, tseg2, sjw, no_samp, 0) # By default, use local echo if single handle is used (see #160) local_echo = single_handle or receive_own_messages if receive_own_messages and single_handle: log.warning("receive_own_messages only works if single_handle is False") canIoCtl(self._read_handle, canstat.canIOCTL_SET_LOCAL_TXECHO, ctypes.byref(ctypes.c_byte(local_echo)), 1) if self.single_handle: log.debug("We don't require separate handles to the bus") self._write_handle = self._read_handle else: log.debug('Creating separate handle for TX on channel: %s' % channel) self._write_handle = canOpenChannel(channel, canstat.canOPEN_ACCEPT_VIRTUAL) canBusOn(self._read_handle) self.set_filters(can_filters) can_driver_mode = canstat.canDRIVER_SILENT if driver_mode == DRIVER_MODE_SILENT else canstat.canDRIVER_NORMAL canSetBusOutputControl(self._write_handle, can_driver_mode) log.debug('Going bus on TX handle') canBusOn(self._write_handle) timer = ctypes.c_uint(0) try: kvReadTimer(self._read_handle, ctypes.byref(timer)) except Exception as exc: # timer is usually close to 0 log.info(str(exc)) self._timestamp_offset = time.time() - (timer.value * TIMESTAMP_FACTOR) super(KvaserBus, self).__init__() def set_filters(self, can_filters=None): """Apply filtering to all messages received by this Bus. Calling without passing any filters will reset the applied filters. Since Kvaser only supports setting one filter per handle, the filtering will be disabled if more than one filter is requested. :param list can_filters: A list of dictionaries each containing a "can_id", "can_mask" and "extended". >>> [{"can_id": 0x11, "can_mask": 0x21, "extended": False}] A filter matches, when `` & can_mask == can_id & can_mask`` """ if can_filters and len(can_filters) == 1: can_id = can_filters[0]['can_id'] can_mask = can_filters[0]['can_mask'] extended = 1 if can_filters[0].get('extended') else 0 try: for handle in (self._read_handle, self._write_handle): canSetAcceptanceFilter(handle, can_id, can_mask, extended) except (NotImplementedError, CANLIBError) as e: log.error('Filtering is not supported - %s', e) else: log.info('canlib is filtering on ID 0x%X, mask 0x%X', can_id, can_mask) else: log.info('Hardware filtering has been disabled') try: for handle in (self._read_handle, self._write_handle): for extended in (0, 1): canSetAcceptanceFilter(handle, 0, 0, extended) except (NotImplementedError, CANLIBError): pass def flush_tx_buffer(self): """ Wipeout the transmit buffer on the Kvaser. """ canIoCtl(self._write_handle, canstat.canIOCTL_FLUSH_TX_BUFFER, 0, 0) def recv(self, timeout=None): """ Read a message from kvaser device. """ arb_id = ctypes.c_long(0) data = ctypes.create_string_buffer(8) dlc = ctypes.c_uint(0) flags = ctypes.c_uint(0) timestamp = ctypes.c_ulong(0) if timeout is None: # Set infinite timeout # http://www.kvaser.com/canlib-webhelp/group___c_a_n.html#ga2edd785a87cc16b49ece8969cad71e5b timeout = 0xFFFFFFFF else: timeout = int(timeout * 1000) log.log(9, 'Reading for %d ms on handle: %s' % (timeout, self._read_handle)) status = canReadWait( self._read_handle, ctypes.byref(arb_id), ctypes.byref(data), ctypes.byref(dlc), ctypes.byref(flags), ctypes.byref(timestamp), timeout # This is an X ms blocking read ) if status == canstat.canOK: #log.debug('read complete -> status OK') data_array = data.raw flags = flags.value is_extended = bool(flags & canstat.canMSG_EXT) is_remote_frame = bool(flags & canstat.canMSG_RTR) is_error_frame = bool(flags & canstat.canMSG_ERROR_FRAME) msg_timestamp = timestamp.value * TIMESTAMP_FACTOR rx_msg = Message(arbitration_id=arb_id.value, data=data_array[:dlc.value], dlc=dlc.value, extended_id=is_extended, is_error_frame=is_error_frame, is_remote_frame=is_remote_frame, channel=self.channel, timestamp=msg_timestamp + self._timestamp_offset) rx_msg.flags = flags rx_msg.raw_timestamp = msg_timestamp #log.debug('Got message: %s' % rx_msg) return rx_msg else: #log.debug('read complete -> status not okay') return None def send(self, msg, timeout=None): #log.debug("Writing a message: {}".format(msg)) flags = canstat.canMSG_EXT if msg.id_type else canstat.canMSG_STD if msg.is_remote_frame: flags |= canstat.canMSG_RTR if msg.is_error_frame: flags |= canstat.canMSG_ERROR_FRAME ArrayConstructor = ctypes.c_byte * msg.dlc buf = ArrayConstructor(*msg.data) canWrite(self._write_handle, msg.arbitration_id, ctypes.byref(buf), msg.dlc, flags) if timeout: canWriteSync(self._write_handle, int(timeout * 1000)) def flash(self, flash=True): """ Turn on or off flashing of the device's LED for physical identification purposes. """ if flash: action = canstat.kvLED_ACTION_ALL_LEDS_ON else: action = canstat.kvLED_ACTION_ALL_LEDS_OFF try: kvFlashLeds(self._read_handle, action, 30000) except (CANLIBError, NotImplementedError) as e: log.error('Could not flash LEDs (%s)', e) def shutdown(self): # Wait for transmit queue to be cleared try: canWriteSync(self._write_handle, 100) except CANLIBError as e: log.warning("There may be messages in the transmit queue that could " "not be transmitted before going bus off (%s)", e) if not self.single_handle: canBusOff(self._read_handle) canClose(self._read_handle) canBusOff(self._write_handle) canClose(self._write_handle) def get_channel_info(channel): name = ctypes.create_string_buffer(80) serial = ctypes.c_uint64() number = ctypes.c_uint() canGetChannelData(channel, canstat.canCHANNELDATA_DEVDESCR_ASCII, ctypes.byref(name), ctypes.sizeof(name)) canGetChannelData(channel, canstat.canCHANNELDATA_CARD_SERIAL_NO, ctypes.byref(serial), ctypes.sizeof(serial)) canGetChannelData(channel, canstat.canCHANNELDATA_CHAN_NO_ON_CARD, ctypes.byref(number), ctypes.sizeof(number)) return '%s, S/N %d (#%d)' % ( name.value.decode("ascii"), serial.value, number.value + 1) init_kvaser_library() python-can-2.0.0/can/interfaces/kvaser/constants.py0000644000175000017500000001471013223535415022554 0ustar brianbrian00000000000000""" Contains Python equivalents of the function and constant definitions in CANLIB's canstat.h, with some supporting functionality specific to Python. Copyright (C) 2010 Dynamic Controls """ import ctypes class c_canStatus(ctypes.c_int): pass canOK = 0 canERR_PARAM = -1 canERR_NOMSG = -2 canERR_NOTFOUND = -3 canERR_NOMEM = -4 canERR_NOCHANNELS = -5 canERR_RESERVED_3 = -6 canERR_TIMEOUT = -7 canERR_NOTINITIALIZED = -8 canERR_NOHANDLES = -9 canERR_INVHANDLE = -10 canERR_INIFILE = -11 canERR_DRIVER = -12 canERR_TXBUFOFL = -13 canERR_RESERVED_1 = -14 canERR_HARDWARE = -15 canERR_DYNALOAD = -16 canERR_DYNALIB = -17 canERR_DYNAINIT = -18 canERR_NOT_SUPPORTED = -19 canERR_RESERVED_5 = -20 canERR_RESERVED_6 = -21 canERR_RESERVED_2 = -22 canERR_DRIVERLOAD = -23 canERR_DRIVERFAILED = -24 canERR_NOCONFIGMGR = -25 canERR_NOCARD = -26 canERR_RESERVED_7 = -27 canERR_REGISTRY = -28 canERR_LICENSE = -29 canERR_INTERNAL = -30 canERR_NO_ACCESS = -31 canERR_NOT_IMPLEMENTED = -32 canERR__RESERVED = -33 def CANSTATUS_SUCCESS(status): return status >= canOK canMSG_MASK = 0x00ff canMSG_RTR = 0x0001 canMSG_STD = 0x0002 canMSG_EXT = 0x0004 canMSG_WAKEUP = 0x0008 canMSG_NERR = 0x0010 canMSG_ERROR_FRAME = 0x0020 canMSG_TXACK = 0x0040 canMSG_TXRQ = 0x0080 canMSGERR_MASK = 0xff00 canMSGERR_HW_OVERRUN = 0x0200 canMSGERR_SW_OVERRUN = 0x0400 canMSGERR_STUFF = 0x0800 canMSGERR_FORM = 0x1000 canMSGERR_CRC = 0x2000 canMSGERR_BIT0 = 0x4000 canMSGERR_BIT1 = 0x8000 canMSGERR_OVERRUN = 0x0600 canMSGERR_BIT = 0xC000 canMSGERR_BUSERR = 0xF800 canTRANSCEIVER_LINEMODE_NA = 0 canTRANSCEIVER_LINEMODE_SWC_SLEEP = 4 canTRANSCEIVER_LINEMODE_SWC_NORMAL = 5 canTRANSCEIVER_LINEMODE_SWC_FAST = 6 canTRANSCEIVER_LINEMODE_SWC_WAKEUP = 7 canTRANSCEIVER_LINEMODE_SLEEP = 8 canTRANSCEIVER_LINEMODE_NORMAL = 9 canTRANSCEIVER_LINEMODE_STDBY = 10 canTRANSCEIVER_LINEMODE_TT_CAN_H = 11 canTRANSCEIVER_LINEMODE_TT_CAN_L = 12 canTRANSCEIVER_LINEMODE_OEM1 = 13 canTRANSCEIVER_LINEMODE_OEM2 = 14 canTRANSCEIVER_LINEMODE_OEM3 = 15 canTRANSCEIVER_LINEMODE_OEM4 = 16 canTRANSCEIVER_RESNET_NA = 0 canTRANSCEIVER_RESNET_MASTER = 1 canTRANSCEIVER_RESNET_MASTER_STBY = 2 canTRANSCEIVER_RESNET_SLAVE = 3 canTRANSCEIVER_TYPE_UNKNOWN = 0 canTRANSCEIVER_TYPE_251 = 1 canTRANSCEIVER_TYPE_252 = 2 canTRANSCEIVER_TYPE_DNOPTO = 3 canTRANSCEIVER_TYPE_W210 = 4 canTRANSCEIVER_TYPE_SWC_PROTO = 5 canTRANSCEIVER_TYPE_SWC = 6 canTRANSCEIVER_TYPE_EVA = 7 canTRANSCEIVER_TYPE_FIBER = 8 canTRANSCEIVER_TYPE_K251 = 9 canTRANSCEIVER_TYPE_K = 10 canTRANSCEIVER_TYPE_1054_OPTO = 11 canTRANSCEIVER_TYPE_SWC_OPTO = 12 canTRANSCEIVER_TYPE_TT = 13 canTRANSCEIVER_TYPE_1050 = 14 canTRANSCEIVER_TYPE_1050_OPTO = 15 canTRANSCEIVER_TYPE_1041 = 16 canTRANSCEIVER_TYPE_1041_OPTO = 17 canTRANSCEIVER_TYPE_RS485 = 18 canTRANSCEIVER_TYPE_LIN = 19 canTRANSCEIVER_TYPE_KONE = 20 canTRANSCEIVER_TYPE_LINX_LIN = 64 canTRANSCEIVER_TYPE_LINX_J1708 = 66 canTRANSCEIVER_TYPE_LINX_K = 68 canTRANSCEIVER_TYPE_LINX_SWC = 70 canTRANSCEIVER_TYPE_LINX_LS = 72 canTransceiverTypeStrings = { canTRANSCEIVER_TYPE_UNKNOWN: "unknown", canTRANSCEIVER_TYPE_251: "82C251", canTRANSCEIVER_TYPE_252: "82C252/TJA1053/TJA1054", canTRANSCEIVER_TYPE_DNOPTO: "Optoisolated 82C251", canTRANSCEIVER_TYPE_W210: "W210", canTRANSCEIVER_TYPE_SWC_PROTO: "AU5790 prototype", canTRANSCEIVER_TYPE_SWC: "AU5790", canTRANSCEIVER_TYPE_EVA: "EVA", canTRANSCEIVER_TYPE_FIBER: "82C251 with fibre extension", canTRANSCEIVER_TYPE_K251: "K251", canTRANSCEIVER_TYPE_K: "K", canTRANSCEIVER_TYPE_1054_OPTO: "TJA1054 optical isolation", canTRANSCEIVER_TYPE_SWC_OPTO: "AU5790 optical isolation", canTRANSCEIVER_TYPE_TT: "B10011S Truck-And-Trailer", canTRANSCEIVER_TYPE_1050: "TJA1050", canTRANSCEIVER_TYPE_1050_OPTO: "TJA1050 optical isolation", canTRANSCEIVER_TYPE_1041: "TJA1041", canTRANSCEIVER_TYPE_1041_OPTO: "TJA1041 optical isolation", canTRANSCEIVER_TYPE_RS485: "RS485", canTRANSCEIVER_TYPE_LIN: "LIN", canTRANSCEIVER_TYPE_KONE: "KONE", canTRANSCEIVER_TYPE_LINX_LIN: "LINX_LIN", canTRANSCEIVER_TYPE_LINX_J1708: "LINX_J1708", canTRANSCEIVER_TYPE_LINX_K: "LINX_K", canTRANSCEIVER_TYPE_LINX_SWC: "LINX_SWC", canTRANSCEIVER_TYPE_LINX_LS: "LINX_LS" } canDRIVER_NORMAL = 4 canDRIVER_SILENT = 1 canDRIVER_SELFRECEPTION = 8 canDRIVER_OFF = 0 canOPEN_ACCEPT_VIRTUAL = 0x0020 canOPEN_OVERRIDE_EXCLUSIVE = 0x0040 canOPEN_REQUIRE_INIT_ACCESS = 0x0080 canOPEN_NO_INIT_ACCESS = 0x0100 canOPEN_ACCEPT_LARGE_DLC = 0x0200 canIOCTL_GET_RX_BUFFER_LEVEL = 8 canIOCTL_GET_TX_BUFFER_LEVEL = 9 canIOCTL_FLUSH_RX_BUFFER = 10 canIOCTL_FLUSH_TX_BUFFER = 11 canIOCTL_GET_TIMER_SCALE = 12 canIOCTL_SET_TXRQ = 13 canIOCTL_GET_EVENTHANDLE = 14 canIOCTL_SET_BYPASS_MODE = 15 canIOCTL_SET_WAKEUP = 16 canIOCTL_GET_DRIVERHANDLE = 17 canIOCTL_MAP_RXQUEUE = 18 canIOCTL_GET_WAKEUP = 19 canIOCTL_SET_REPORT_ACCESS_ERRORS = 20 canIOCTL_GET_REPORT_ACCESS_ERRORS = 21 canIOCTL_CONNECT_TO_VIRTUAL_BUS = 22 canIOCTL_DISCONNECT_FROM_VIRTUAL_BUS = 23 canIOCTL_SET_USER_IOPORT = 24 canIOCTL_GET_USER_IOPORT = 25 canIOCTL_SET_BUFFER_WRAPAROUND_MODE = 26 canIOCTL_SET_RX_QUEUE_SIZE = 27 canIOCTL_SET_USB_THROTTLE = 28 canIOCTL_GET_USB_THROTTLE = 29 canIOCTL_SET_BUSON_TIME_AUTO_RESET = 30 canIOCTL_SET_LOCAL_TXECHO = 32 canIOCTL_PREFER_EXT = 1 canIOCTL_PREFER_STD = 2 canIOCTL_CLEAR_ERROR_COUNTERS = 5 canIOCTL_SET_TIMER_SCALE = 6 canIOCTL_SET_TXACK = 7 canCHANNELDATA_CHANNEL_CAP = 1 canCHANNELDATA_TRANS_CAP = 2 canCHANNELDATA_CHANNEL_FLAGS = 3 canCHANNELDATA_CARD_TYPE = 4 canCHANNELDATA_CARD_NUMBER = 5 canCHANNELDATA_CHAN_NO_ON_CARD = 6 canCHANNELDATA_CARD_SERIAL_NO = 7 canCHANNELDATA_TRANS_SERIAL_NO = 8 canCHANNELDATA_CARD_FIRMWARE_REV = 9 canCHANNELDATA_CARD_HARDWARE_REV = 10 canCHANNELDATA_CARD_UPC_NO = 11 canCHANNELDATA_TRANS_UPC_NO = 12 canCHANNELDATA_CHANNEL_NAME = 13 canCHANNELDATA_DLL_FILE_VERSION = 14 canCHANNELDATA_DLL_PRODUCT_VERSION = 15 canCHANNELDATA_DLL_FILETYPE = 16 canCHANNELDATA_TRANS_TYPE = 17 canCHANNELDATA_DEVICE_PHYSICAL_POSITION = 18 canCHANNELDATA_UI_NUMBER = 19 canCHANNELDATA_TIMESYNC_ENABLED = 20 canCHANNELDATA_DRIVER_FILE_VERSION = 21 canCHANNELDATA_DRIVER_PRODUCT_VERSION = 22 canCHANNELDATA_MFGNAME_UNICODE = 23 canCHANNELDATA_MFGNAME_ASCII = 24 canCHANNELDATA_DEVDESCR_UNICODE = 25 canCHANNELDATA_DEVDESCR_ASCII = 26 canCHANNELDATA_DRIVER_NAME = 27 kvLED_ACTION_ALL_LEDS_ON = 0 kvLED_ACTION_ALL_LEDS_OFF = 1 canBITRATE_1M = -1 canBITRATE_500K = -2 canBITRATE_250K = -3 canBITRATE_125K = -4 canBITRATE_100K = -5 canBITRATE_62K = -6 canBITRATE_50K = -7 canBITRATE_83K = -8 canBITRATE_10K = -9 python-can-2.0.0/can/interfaces/neovi_api/0000755000175000017500000000000013223535610020636 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/neovi_api/__init__.py0000644000175000017500000000007013223535415022747 0ustar brianbrian00000000000000from can.interfaces.neovi_api.neovi_api import NeoVIBus python-can-2.0.0/can/interfaces/neovi_api/neovi_api.py0000644000175000017500000000712213223535415023166 0ustar brianbrian00000000000000""" pyneovi interface module. pyneovi is a Python wrapper around the API provided by Intrepid Control Systems for communicating with their NeoVI range of devices. Implementation references: * http://pyneovi.readthedocs.io/en/latest/ * https://bitbucket.org/Kemp_J/pyneovi """ import logging logger = logging.getLogger(__name__) try: import queue except ImportError: import Queue as queue try: from neovi import neodevice from neovi import neovi from neovi.structures import icsSpyMessage except ImportError as e: logger.warning("Cannot load pyneovi: %s", e) from can import Message from can.bus import BusABC SPY_STATUS_XTD_FRAME = 0x04 SPY_STATUS_REMOTE_FRAME = 0x08 def neo_device_name(device_type): names = { neovi.NEODEVICE_BLUE: 'neoVI BLUE', neovi.NEODEVICE_DW_VCAN: 'ValueCAN', neovi.NEODEVICE_FIRE: 'neoVI FIRE', neovi.NEODEVICE_VCAN3: 'ValueCAN3', neovi.NEODEVICE_YELLOW: 'neoVI YELLOW', neovi.NEODEVICE_RED: 'neoVI RED', neovi.NEODEVICE_ECU: 'neoECU', # neovi.NEODEVICE_IEVB: '' } return names.get(device_type, 'Unknown neoVI') class NeoVIBus(BusABC): """ The CAN Bus implemented for the pyneovi interface. """ def __init__(self, channel=None, can_filters=None, **config): """ :param int channel: The Channel id to create this bus with. """ type_filter = config.get('type_filter', neovi.NEODEVICE_ALL) neodevice.init_api() self.device = neodevice.find_devices(type_filter)[0] self.device.open() self.channel_info = '%s %s on channel %s' % ( neo_device_name(self.device.get_type()), self.device.device.SerialNumber, channel ) self.rx_buffer = queue.Queue() self.network = int(channel) if channel is not None else None self.device.subscribe_to(self._rx_buffer, network=self.network) def __del__(self): self.shutdown() def shutdown(self): self.device.pump_messages = False if self.device.msg_queue_thread is not None: self.device.msg_queue_thread.join() def _rx_buffer(self, msg, user_data): self.rx_buffer.put_nowait(msg) def _ics_msg_to_message(self, ics_msg): return Message( timestamp=neovi.GetTimeStampForMsg(self.device.handle, ics_msg)[1], arbitration_id=ics_msg.ArbIDOrHeader, data=ics_msg.Data[:ics_msg.NumberBytesData], dlc=ics_msg.NumberBytesData, extended_id=bool(ics_msg.StatusBitField & SPY_STATUS_XTD_FRAME), is_remote_frame=bool(ics_msg.StatusBitField & SPY_STATUS_REMOTE_FRAME), ) def recv(self, timeout=None): try: ics_msg = self.rx_buffer.get(block=True, timeout=timeout) except queue.Empty: pass else: if ics_msg.NetworkID == self.network: return self._ics_msg_to_message(ics_msg) def send(self, msg, timeout=None): data = tuple(msg.data) flags = SPY_STATUS_XTD_FRAME if msg.is_extended_id else 0 if msg.is_remote_frame: flags |= SPY_STATUS_REMOTE_FRAME ics_msg = icsSpyMessage() ics_msg.ArbIDOrHeader = msg.arbitration_id ics_msg.NumberBytesData = len(data) ics_msg.Data = data ics_msg.StatusBitField = flags ics_msg.StatusBitField2 = 0 ics_msg.DescriptionID = self.device.tx_id self.device.tx_id += 1 self.device.tx_raw_message(ics_msg, self.network) python-can-2.0.0/can/interfaces/pcan/0000755000175000017500000000000013223535610017606 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/pcan/PCANBasic.py0000644000175000017500000010126213223535415021650 0ustar brianbrian00000000000000# PCANBasic.py # # ~~~~~~~~~~~~ # # PCAN-Basic API # # ~~~~~~~~~~~~ # # ------------------------------------------------------------------ # Author : Keneth Wagner # Last change: 18.05.2016 Wagner # # Language: Python 2.7 # ------------------------------------------------------------------ # # Copyright (C) 1999-2016 PEAK-System Technik GmbH, Darmstadt # more Info at http://www.peak-system.com # # Module Imports # from ctypes import * import platform import logging # Patched for python-can: use logger instead of print() logger = logging.getLogger('can.pcan') #/////////////////////////////////////////////////////////// # Type definitions #/////////////////////////////////////////////////////////// TPCANHandle = c_ushort # Represents a PCAN hardware channel handle TPCANStatus = int # Represents a PCAN status/error code TPCANParameter = c_ubyte # Represents a PCAN parameter to be read or set TPCANDevice = c_ubyte # Represents a PCAN device TPCANMessageType = c_ubyte # Represents the type of a PCAN message TPCANType = c_ubyte # Represents the type of PCAN hardware to be initialized TPCANMode = c_ubyte # Represents a PCAN filter mode TPCANBaudrate = c_ushort # Represents a PCAN Baud rate register value TPCANBitrateFD = c_char_p # Represents a PCAN-FD bit rate string TPCANTimestampFD = c_ulonglong # Represents a timestamp of a received PCAN FD message #/////////////////////////////////////////////////////////// # Value definitions #/////////////////////////////////////////////////////////// # Currently defined and supported PCAN channels # PCAN_NONEBUS = TPCANHandle(0x00) # Undefined/default value for a PCAN bus PCAN_ISABUS1 = TPCANHandle(0x21) # PCAN-ISA interface, channel 1 PCAN_ISABUS2 = TPCANHandle(0x22) # PCAN-ISA interface, channel 2 PCAN_ISABUS3 = TPCANHandle(0x23) # PCAN-ISA interface, channel 3 PCAN_ISABUS4 = TPCANHandle(0x24) # PCAN-ISA interface, channel 4 PCAN_ISABUS5 = TPCANHandle(0x25) # PCAN-ISA interface, channel 5 PCAN_ISABUS6 = TPCANHandle(0x26) # PCAN-ISA interface, channel 6 PCAN_ISABUS7 = TPCANHandle(0x27) # PCAN-ISA interface, channel 7 PCAN_ISABUS8 = TPCANHandle(0x28) # PCAN-ISA interface, channel 8 PCAN_DNGBUS1 = TPCANHandle(0x31) # PCAN-Dongle/LPT interface, channel 1 PCAN_PCIBUS1 = TPCANHandle(0x41) # PCAN-PCI interface, channel 1 PCAN_PCIBUS2 = TPCANHandle(0x42) # PCAN-PCI interface, channel 2 PCAN_PCIBUS3 = TPCANHandle(0x43) # PCAN-PCI interface, channel 3 PCAN_PCIBUS4 = TPCANHandle(0x44) # PCAN-PCI interface, channel 4 PCAN_PCIBUS5 = TPCANHandle(0x45) # PCAN-PCI interface, channel 5 PCAN_PCIBUS6 = TPCANHandle(0x46) # PCAN-PCI interface, channel 6 PCAN_PCIBUS7 = TPCANHandle(0x47) # PCAN-PCI interface, channel 7 PCAN_PCIBUS8 = TPCANHandle(0x48) # PCAN-PCI interface, channel 8 PCAN_PCIBUS9 = TPCANHandle(0x409) # PCAN-PCI interface, channel 9 PCAN_PCIBUS10 = TPCANHandle(0x40A) # PCAN-PCI interface, channel 10 PCAN_PCIBUS11 = TPCANHandle(0x40B) # PCAN-PCI interface, channel 11 PCAN_PCIBUS12 = TPCANHandle(0x40C) # PCAN-PCI interface, channel 12 PCAN_PCIBUS13 = TPCANHandle(0x40D) # PCAN-PCI interface, channel 13 PCAN_PCIBUS14 = TPCANHandle(0x40E) # PCAN-PCI interface, channel 14 PCAN_PCIBUS15 = TPCANHandle(0x40F) # PCAN-PCI interface, channel 15 PCAN_PCIBUS16 = TPCANHandle(0x410) # PCAN-PCI interface, channel 16 PCAN_USBBUS1 = TPCANHandle(0x51) # PCAN-USB interface, channel 1 PCAN_USBBUS2 = TPCANHandle(0x52) # PCAN-USB interface, channel 2 PCAN_USBBUS3 = TPCANHandle(0x53) # PCAN-USB interface, channel 3 PCAN_USBBUS4 = TPCANHandle(0x54) # PCAN-USB interface, channel 4 PCAN_USBBUS5 = TPCANHandle(0x55) # PCAN-USB interface, channel 5 PCAN_USBBUS6 = TPCANHandle(0x56) # PCAN-USB interface, channel 6 PCAN_USBBUS7 = TPCANHandle(0x57) # PCAN-USB interface, channel 7 PCAN_USBBUS8 = TPCANHandle(0x58) # PCAN-USB interface, channel 8 PCAN_USBBUS9 = TPCANHandle(0x509) # PCAN-USB interface, channel 9 PCAN_USBBUS10 = TPCANHandle(0x50A) # PCAN-USB interface, channel 10 PCAN_USBBUS11 = TPCANHandle(0x50B) # PCAN-USB interface, channel 11 PCAN_USBBUS12 = TPCANHandle(0x50C) # PCAN-USB interface, channel 12 PCAN_USBBUS13 = TPCANHandle(0x50D) # PCAN-USB interface, channel 13 PCAN_USBBUS14 = TPCANHandle(0x50E) # PCAN-USB interface, channel 14 PCAN_USBBUS15 = TPCANHandle(0x50F) # PCAN-USB interface, channel 15 PCAN_USBBUS16 = TPCANHandle(0x510) # PCAN-USB interface, channel 16 PCAN_PCCBUS1 = TPCANHandle(0x61) # PCAN-PC Card interface, channel 1 PCAN_PCCBUS2 = TPCANHandle(0x62) # PCAN-PC Card interface, channel 2 PCAN_LANBUS1 = TPCANHandle(0x801) # PCAN-LAN interface, channel 1 PCAN_LANBUS2 = TPCANHandle(0x802) # PCAN-LAN interface, channel 2 PCAN_LANBUS3 = TPCANHandle(0x803) # PCAN-LAN interface, channel 3 PCAN_LANBUS4 = TPCANHandle(0x804) # PCAN-LAN interface, channel 4 PCAN_LANBUS5 = TPCANHandle(0x805) # PCAN-LAN interface, channel 5 PCAN_LANBUS6 = TPCANHandle(0x806) # PCAN-LAN interface, channel 6 PCAN_LANBUS7 = TPCANHandle(0x807) # PCAN-LAN interface, channel 7 PCAN_LANBUS8 = TPCANHandle(0x808) # PCAN-LAN interface, channel 8 PCAN_LANBUS9 = TPCANHandle(0x809) # PCAN-LAN interface, channel 9 PCAN_LANBUS10 = TPCANHandle(0x80A) # PCAN-LAN interface, channel 10 PCAN_LANBUS11 = TPCANHandle(0x80B) # PCAN-LAN interface, channel 11 PCAN_LANBUS12 = TPCANHandle(0x80C) # PCAN-LAN interface, channel 12 PCAN_LANBUS13 = TPCANHandle(0x80D) # PCAN-LAN interface, channel 13 PCAN_LANBUS14 = TPCANHandle(0x80E) # PCAN-LAN interface, channel 14 PCAN_LANBUS15 = TPCANHandle(0x80F) # PCAN-LAN interface, channel 15 PCAN_LANBUS16 = TPCANHandle(0x810) # PCAN-LAN interface, channel 16 # Represent the PCAN error and status codes # PCAN_ERROR_OK = TPCANStatus(0x00000) # No error PCAN_ERROR_XMTFULL = TPCANStatus(0x00001) # Transmit buffer in CAN controller is full PCAN_ERROR_OVERRUN = TPCANStatus(0x00002) # CAN controller was read too late PCAN_ERROR_BUSLIGHT = TPCANStatus(0x00004) # Bus error: an error counter reached the 'light' limit PCAN_ERROR_BUSHEAVY = TPCANStatus(0x00008) # Bus error: an error counter reached the 'heavy' limit PCAN_ERROR_BUSWARNING = TPCANStatus(PCAN_ERROR_BUSHEAVY) # Bus error: an error counter reached the 'warning' limit PCAN_ERROR_BUSPASSIVE = TPCANStatus(0x40000) # Bus error: the CAN controller is error passive PCAN_ERROR_BUSOFF = TPCANStatus(0x00010) # Bus error: the CAN controller is in bus-off state PCAN_ERROR_ANYBUSERR = TPCANStatus(PCAN_ERROR_BUSWARNING | PCAN_ERROR_BUSLIGHT | PCAN_ERROR_BUSHEAVY | PCAN_ERROR_BUSOFF | PCAN_ERROR_BUSPASSIVE) # Mask for all bus errors PCAN_ERROR_QRCVEMPTY = TPCANStatus(0x00020) # Receive queue is empty PCAN_ERROR_QOVERRUN = TPCANStatus(0x00040) # Receive queue was read too late PCAN_ERROR_QXMTFULL = TPCANStatus(0x00080) # Transmit queue is full PCAN_ERROR_REGTEST = TPCANStatus(0x00100) # Test of the CAN controller hardware registers failed (no hardware found) PCAN_ERROR_NODRIVER = TPCANStatus(0x00200) # Driver not loaded PCAN_ERROR_HWINUSE = TPCANStatus(0x00400) # Hardware already in use by a Net PCAN_ERROR_NETINUSE = TPCANStatus(0x00800) # A Client is already connected to the Net PCAN_ERROR_ILLHW = TPCANStatus(0x01400) # Hardware handle is invalid PCAN_ERROR_ILLNET = TPCANStatus(0x01800) # Net handle is invalid PCAN_ERROR_ILLCLIENT = TPCANStatus(0x01C00) # Client handle is invalid PCAN_ERROR_ILLHANDLE = TPCANStatus(PCAN_ERROR_ILLHW | PCAN_ERROR_ILLNET | PCAN_ERROR_ILLCLIENT) # Mask for all handle errors PCAN_ERROR_RESOURCE = TPCANStatus(0x02000) # Resource (FIFO, Client, timeout) cannot be created PCAN_ERROR_ILLPARAMTYPE = TPCANStatus(0x04000) # Invalid parameter PCAN_ERROR_ILLPARAMVAL = TPCANStatus(0x08000) # Invalid parameter value PCAN_ERROR_UNKNOWN = TPCANStatus(0x10000) # Unknown error PCAN_ERROR_ILLDATA = TPCANStatus(0x20000) # Invalid data, function, or action PCAN_ERROR_CAUTION = TPCANStatus(0x2000000)# An operation was successfully carried out, however, irregularities were registered PCAN_ERROR_INITIALIZE = TPCANStatus(0x4000000)# Channel is not initialized [Value was changed from 0x40000 to 0x4000000] PCAN_ERROR_ILLOPERATION = TPCANStatus(0x8000000)# Invalid operation [Value was changed from 0x80000 to 0x8000000] # PCAN devices # PCAN_NONE = TPCANDevice(0x00) # Undefined, unknown or not selected PCAN device value PCAN_PEAKCAN = TPCANDevice(0x01) # PCAN Non-Plug&Play devices. NOT USED WITHIN PCAN-Basic API PCAN_ISA = TPCANDevice(0x02) # PCAN-ISA, PCAN-PC/104, and PCAN-PC/104-Plus PCAN_DNG = TPCANDevice(0x03) # PCAN-Dongle PCAN_PCI = TPCANDevice(0x04) # PCAN-PCI, PCAN-cPCI, PCAN-miniPCI, and PCAN-PCI Express PCAN_USB = TPCANDevice(0x05) # PCAN-USB and PCAN-USB Pro PCAN_PCC = TPCANDevice(0x06) # PCAN-PC Card PCAN_VIRTUAL = TPCANDevice(0x07) # PCAN Virtual hardware. NOT USED WITHIN PCAN-Basic API PCAN_LAN = TPCANDevice(0x08) # PCAN Gateway devices # PCAN parameters # PCAN_DEVICE_NUMBER = TPCANParameter(0x01) # PCAN-USB device number parameter PCAN_5VOLTS_POWER = TPCANParameter(0x02) # PCAN-PC Card 5-Volt power parameter PCAN_RECEIVE_EVENT = TPCANParameter(0x03) # PCAN receive event handler parameter PCAN_MESSAGE_FILTER = TPCANParameter(0x04) # PCAN message filter parameter PCAN_API_VERSION = TPCANParameter(0x05) # PCAN-Basic API version parameter PCAN_CHANNEL_VERSION = TPCANParameter(0x06) # PCAN device channel version parameter PCAN_BUSOFF_AUTORESET = TPCANParameter(0x07) # PCAN Reset-On-Busoff parameter PCAN_LISTEN_ONLY = TPCANParameter(0x08) # PCAN Listen-Only parameter PCAN_LOG_LOCATION = TPCANParameter(0x09) # Directory path for log files PCAN_LOG_STATUS = TPCANParameter(0x0A) # Debug-Log activation status PCAN_LOG_CONFIGURE = TPCANParameter(0x0B) # Configuration of the debugged information (LOG_FUNCTION_***) PCAN_LOG_TEXT = TPCANParameter(0x0C) # Custom insertion of text into the log file PCAN_CHANNEL_CONDITION = TPCANParameter(0x0D) # Availability status of a PCAN-Channel PCAN_HARDWARE_NAME = TPCANParameter(0x0E) # PCAN hardware name parameter PCAN_RECEIVE_STATUS = TPCANParameter(0x0F) # Message reception status of a PCAN-Channel PCAN_CONTROLLER_NUMBER = TPCANParameter(0x10) # CAN-Controller number of a PCAN-Channel PCAN_TRACE_LOCATION = TPCANParameter(0x11) # Directory path for PCAN trace files PCAN_TRACE_STATUS = TPCANParameter(0x12) # CAN tracing activation status PCAN_TRACE_SIZE = TPCANParameter(0x13) # Configuration of the maximum file size of a CAN trace PCAN_TRACE_CONFIGURE = TPCANParameter(0x14) # Configuration of the trace file storing mode (TRACE_FILE_***) PCAN_CHANNEL_IDENTIFYING = TPCANParameter(0x15) # Physical identification of a USB based PCAN-Channel by blinking its associated LED PCAN_CHANNEL_FEATURES = TPCANParameter(0x16) # Capabilities of a PCAN device (FEATURE_***) PCAN_BITRATE_ADAPTING = TPCANParameter(0x17) # Using of an existing bit rate (PCAN-View connected to a channel) PCAN_BITRATE_INFO = TPCANParameter(0x18) # Configured bit rate as Btr0Btr1 value PCAN_BITRATE_INFO_FD = TPCANParameter(0x19) # Configured bit rate as TPCANBitrateFD string PCAN_BUSSPEED_NOMINAL = TPCANParameter(0x1A) # Configured nominal CAN Bus speed as Bits per seconds PCAN_BUSSPEED_DATA = TPCANParameter(0x1B) # Configured CAN data speed as Bits per seconds PCAN_IP_ADDRESS = TPCANParameter(0x1C) # Remote address of a LAN channel as string in IPv4 format PCAN_LAN_SERVICE_STATUS = TPCANParameter(0x1D) # Status of the Virtual PCAN-Gateway Service # PCAN parameter values # PCAN_PARAMETER_OFF = int(0x00) # The PCAN parameter is not set (inactive) PCAN_PARAMETER_ON = int(0x01) # The PCAN parameter is set (active) PCAN_FILTER_CLOSE = int(0x00) # The PCAN filter is closed. No messages will be received PCAN_FILTER_OPEN = int(0x01) # The PCAN filter is fully opened. All messages will be received PCAN_FILTER_CUSTOM = int(0x02) # The PCAN filter is custom configured. Only registered messages will be received PCAN_CHANNEL_UNAVAILABLE = int(0x00) # The PCAN-Channel handle is illegal, or its associated hardware is not available PCAN_CHANNEL_AVAILABLE = int(0x01) # The PCAN-Channel handle is available to be connected (Plug&Play Hardware: it means furthermore that the hardware is plugged-in) PCAN_CHANNEL_OCCUPIED = int(0x02) # The PCAN-Channel handle is valid, and is already being used PCAN_CHANNEL_PCANVIEW = PCAN_CHANNEL_AVAILABLE | PCAN_CHANNEL_OCCUPIED # The PCAN-Channel handle is already being used by a PCAN-View application, but is available to connect LOG_FUNCTION_DEFAULT = int(0x00) # Logs system exceptions / errors LOG_FUNCTION_ENTRY = int(0x01) # Logs the entries to the PCAN-Basic API functions LOG_FUNCTION_PARAMETERS = int(0x02) # Logs the parameters passed to the PCAN-Basic API functions LOG_FUNCTION_LEAVE = int(0x04) # Logs the exits from the PCAN-Basic API functions LOG_FUNCTION_WRITE = int(0x08) # Logs the CAN messages passed to the CAN_Write function LOG_FUNCTION_READ = int(0x10) # Logs the CAN messages received within the CAN_Read function LOG_FUNCTION_ALL = int(0xFFFF) # Logs all possible information within the PCAN-Basic API functions TRACE_FILE_SINGLE = int(0x00) # A single file is written until it size reaches PAN_TRACE_SIZE TRACE_FILE_SEGMENTED = int(0x01) # Traced data is distributed in several files with size PAN_TRACE_SIZE TRACE_FILE_DATE = int(0x02) # Includes the date into the name of the trace file TRACE_FILE_TIME = int(0x04) # Includes the start time into the name of the trace file TRACE_FILE_OVERWRITE = int(0x80) # Causes the overwriting of available traces (same name) FEATURE_FD_CAPABLE = int(0x01) # Device supports flexible data-rate (CAN-FD) SERVICE_STATUS_STOPPED = int(0x01) # The service is not running SERVICE_STATUS_RUNNING = int(0x04) # The service is running # PCAN message types # PCAN_MESSAGE_STANDARD = TPCANMessageType(0x00) # The PCAN message is a CAN Standard Frame (11-bit identifier) PCAN_MESSAGE_RTR = TPCANMessageType(0x01) # The PCAN message is a CAN Remote-Transfer-Request Frame PCAN_MESSAGE_EXTENDED = TPCANMessageType(0x02) # The PCAN message is a CAN Extended Frame (29-bit identifier) PCAN_MESSAGE_FD = TPCANMessageType(0x04) # The PCAN message represents a FD frame in terms of CiA Specs PCAN_MESSAGE_BRS = TPCANMessageType(0x08) # The PCAN message represents a FD bit rate switch (CAN data at a higher bit rate) PCAN_MESSAGE_ESI = TPCANMessageType(0x10) # The PCAN message represents a FD error state indicator(CAN FD transmitter was error active) PCAN_MESSAGE_STATUS = TPCANMessageType(0x80) # The PCAN message represents a PCAN status message # Frame Type / Initialization Mode # PCAN_MODE_STANDARD = PCAN_MESSAGE_STANDARD PCAN_MODE_EXTENDED = PCAN_MESSAGE_EXTENDED # Baud rate codes = BTR0/BTR1 register values for the CAN controller. # You can define your own Baud rate with the BTROBTR1 register. # Take a look at www.peak-system.com for our free software "BAUDTOOL" # to calculate the BTROBTR1 register for every bit rate and sample point. # PCAN_BAUD_1M = TPCANBaudrate(0x0014) # 1 MBit/s PCAN_BAUD_800K = TPCANBaudrate(0x0016) # 800 kBit/s PCAN_BAUD_500K = TPCANBaudrate(0x001C) # 500 kBit/s PCAN_BAUD_250K = TPCANBaudrate(0x011C) # 250 kBit/s PCAN_BAUD_125K = TPCANBaudrate(0x031C) # 125 kBit/s PCAN_BAUD_100K = TPCANBaudrate(0x432F) # 100 kBit/s PCAN_BAUD_95K = TPCANBaudrate(0xC34E) # 95,238 kBit/s PCAN_BAUD_83K = TPCANBaudrate(0x852B) # 83,333 kBit/s PCAN_BAUD_50K = TPCANBaudrate(0x472F) # 50 kBit/s PCAN_BAUD_47K = TPCANBaudrate(0x1414) # 47,619 kBit/s PCAN_BAUD_33K = TPCANBaudrate(0x8B2F) # 33,333 kBit/s PCAN_BAUD_20K = TPCANBaudrate(0x532F) # 20 kBit/s PCAN_BAUD_10K = TPCANBaudrate(0x672F) # 10 kBit/s PCAN_BAUD_5K = TPCANBaudrate(0x7F7F) # 5 kBit/s # Represents the configuration for a CAN bit rate # Note: # * Each parameter and its value must be separated with a '='. # * Each pair of parameter/value must be separated using ','. # # Example: # f_clock=80000000,nom_brp=0,nom_tseg1=13,nom_tseg2=0,nom_sjw=0,data_brp=0,data_tseg1=13,data_tseg2=0,data_sjw=0 # # Patched for python-can: use bytes to fix incompatibility with Python 3 PCAN_BR_CLOCK = TPCANBitrateFD(b"f_clock") PCAN_BR_CLOCK_MHZ = TPCANBitrateFD(b"f_clock_mhz") PCAN_BR_NOM_BRP = TPCANBitrateFD(b"nom_brp") PCAN_BR_NOM_TSEG1 = TPCANBitrateFD(b"nom_tseg1") PCAN_BR_NOM_TSEG2 = TPCANBitrateFD(b"nom_tseg2") PCAN_BR_NOM_SJW = TPCANBitrateFD(b"nom_sjw") PCAN_BR_NOM_SAMPLE = TPCANBitrateFD(b"nom_sam") PCAN_BR_DATA_BRP = TPCANBitrateFD(b"data_brp") PCAN_BR_DATA_TSEG1 = TPCANBitrateFD(b"data_tseg1") PCAN_BR_DATA_TSEG2 = TPCANBitrateFD(b"data_tseg2") PCAN_BR_DATA_SJW = TPCANBitrateFD(b"data_sjw") PCAN_BR_DATA_SAMPLE = TPCANBitrateFD(b"data_ssp_offset") # Supported No-Plug-And-Play Hardware types # PCAN_TYPE_ISA = TPCANType(0x01) # PCAN-ISA 82C200 PCAN_TYPE_ISA_SJA = TPCANType(0x09) # PCAN-ISA SJA1000 PCAN_TYPE_ISA_PHYTEC = TPCANType(0x04) # PHYTEC ISA PCAN_TYPE_DNG = TPCANType(0x02) # PCAN-Dongle 82C200 PCAN_TYPE_DNG_EPP = TPCANType(0x03) # PCAN-Dongle EPP 82C200 PCAN_TYPE_DNG_SJA = TPCANType(0x05) # PCAN-Dongle SJA1000 PCAN_TYPE_DNG_SJA_EPP = TPCANType(0x06) # PCAN-Dongle EPP SJA1000 # Represents a PCAN message # class TPCANMsg (Structure): """ Represents a PCAN message """ _fields_ = [ ("ID", c_uint), # 11/29-bit message identifier ("MSGTYPE", TPCANMessageType), # Type of the message ("LEN", c_ubyte), # Data Length Code of the message (0..8) ("DATA", c_ubyte * 8) ] # Data of the message (DATA[0]..DATA[7]) # Represents a timestamp of a received PCAN message # Total Microseconds = micros + 1000 * millis + 0x100000000 * 1000 * millis_overflow # class TPCANTimestamp (Structure): """ Represents a timestamp of a received PCAN message Total Microseconds = micros + 1000 * millis + 0x100000000 * 1000 * millis_overflow """ _fields_ = [ ("millis", c_uint), # Base-value: milliseconds: 0.. 2^32-1 ("millis_overflow", c_ushort), # Roll-arounds of millis ("micros", c_ushort) ] # Microseconds: 0..999 # Represents a PCAN message from a FD capable hardware # class TPCANMsgFD (Structure): """ Represents a PCAN message """ _fields_ = [ ("ID", c_uint), # 11/29-bit message identifier ("MSGTYPE", TPCANMessageType), # Type of the message ("DLC", c_ubyte), # Data Length Code of the message (0..15) ("DATA", c_ubyte * 64) ] # Data of the message (DATA[0]..DATA[63]) #/////////////////////////////////////////////////////////// # PCAN-Basic API function declarations #/////////////////////////////////////////////////////////// # PCAN-Basic API class implementation # class PCANBasic: """ PCAN-Basic API class implementation """ def __init__(self): # Loads the PCANBasic.dll # if platform.system() == 'Windows': self.__m_dllBasic = windll.LoadLibrary("PCANBasic") else: self.__m_dllBasic = cdll.LoadLibrary("libpcanbasic.so") if self.__m_dllBasic == None: logger.error("Exception: The PCAN-Basic DLL couldn't be loaded!") # Initializes a PCAN Channel # def Initialize( self, Channel, Btr0Btr1, HwType = TPCANType(0), IOPort = c_uint(0), Interrupt = c_ushort(0)): """ Initializes a PCAN Channel Parameters: Channel : A TPCANHandle representing a PCAN Channel Btr0Btr1 : The speed for the communication (BTR0BTR1 code) HwType : NON PLUG&PLAY: The type of hardware and operation mode IOPort : NON PLUG&PLAY: The I/O address for the parallel port Interrupt: NON PLUG&PLAY: Interrupt number of the parallel port Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_Initialize(Channel,Btr0Btr1,HwType,IOPort,Interrupt) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.Initialize") raise # Initializes a FD capable PCAN Channel # def InitializeFD( self, Channel, BitrateFD): """ Initializes a FD capable PCAN Channel Parameters: Channel : The handle of a FD capable PCAN Channel BitrateFD : The speed for the communication (FD bit rate string) Remarks: See PCAN_BR_* values. * parameter and values must be separated by '=' * Couples of Parameter/value must be separated by ',' * Following Parameter must be filled out: f_clock, data_brp, data_sjw, data_tseg1, data_tseg2, nom_brp, nom_sjw, nom_tseg1, nom_tseg2. * Following Parameters are optional (not used yet): data_ssp_offset, nom_samp Example: f_clock_mhz=80,nom_brp=0,nom_tseg1=13,nom_tseg2=0,nom_sjw=0,data_brp=0,data_tseg1=13,data_tseg2=0,data_sjw=0 Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_InitializeFD(Channel,BitrateFD) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.InitializeFD") raise # Uninitializes one or all PCAN Channels initialized by CAN_Initialize # def Uninitialize( self, Channel): """ Uninitializes one or all PCAN Channels initialized by CAN_Initialize Remarks: Giving the TPCANHandle value "PCAN_NONEBUS", uninitialize all initialized channels Parameters: Channel : A TPCANHandle representing a PCAN Channel Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_Uninitialize(Channel) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.Uninitialize") raise # Resets the receive and transmit queues of the PCAN Channel # def Reset( self, Channel): """ Resets the receive and transmit queues of the PCAN Channel Remarks: A reset of the CAN controller is not performed Parameters: Channel : A TPCANHandle representing a PCAN Channel Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_Reset(Channel) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.Reset") raise # Gets the current status of a PCAN Channel # def GetStatus( self, Channel): """ Gets the current status of a PCAN Channel Parameters: Channel : A TPCANHandle representing a PCAN Channel Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_GetStatus(Channel) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.GetStatus") raise # Reads a CAN message from the receive queue of a PCAN Channel # def Read( self, Channel): """ Reads a CAN message from the receive queue of a PCAN Channel Remarks: The return value of this method is a 3-touple, where the first value is the result (TPCANStatus) of the method. The order of the values are: [0]: A TPCANStatus error code [1]: A TPCANMsg structure with the CAN message read [2]: A TPCANTimestamp structure with the time when a message was read Parameters: Channel : A TPCANHandle representing a PCAN Channel Returns: A touple with three values """ try: msg = TPCANMsg() timestamp = TPCANTimestamp() res = self.__m_dllBasic.CAN_Read(Channel,byref(msg),byref(timestamp)) return TPCANStatus(res),msg,timestamp except: logger.error("Exception on PCANBasic.Read") raise # Reads a CAN message from the receive queue of a FD capable PCAN Channel # def ReadFD( self, Channel): """ Reads a CAN message from the receive queue of a FD capable PCAN Channel Remarks: The return value of this method is a 3-touple, where the first value is the result (TPCANStatus) of the method. The order of the values are: [0]: A TPCANStatus error code [1]: A TPCANMsgFD structure with the CAN message read [2]: A TPCANTimestampFD that is the time when a message was read Parameters: Channel : The handle of a FD capable PCAN Channel Returns: A touple with three values """ try: msg = TPCANMsgFD() timestamp = TPCANTimestampFD() res = self.__m_dllBasic.CAN_ReadFD(Channel,byref(msg),byref(timestamp)) return TPCANStatus(res),msg,timestamp except: logger.error("Exception on PCANBasic.ReadFD") raise # Transmits a CAN message # def Write( self, Channel, MessageBuffer): """ Transmits a CAN message Parameters: Channel : A TPCANHandle representing a PCAN Channel MessageBuffer: A TPCANMsg representing the CAN message to be sent Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_Write(Channel,byref(MessageBuffer)) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.Write") raise # Transmits a CAN message over a FD capable PCAN Channel # def WriteFD( self, Channel, MessageBuffer): """ Transmits a CAN message over a FD capable PCAN Channel Parameters: Channel : The handle of a FD capable PCAN Channel MessageBuffer: A TPCANMsgFD buffer with the message to be sent Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_WriteFD(Channel,byref(MessageBuffer)) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.WriteFD") raise # Configures the reception filter # def FilterMessages( self, Channel, FromID, ToID, Mode): """ Configures the reception filter Remarks: The message filter will be expanded with every call to this function. If it is desired to reset the filter, please use the 'SetValue' function. Parameters: Channel : A TPCANHandle representing a PCAN Channel FromID : A c_uint value with the lowest CAN ID to be received ToID : A c_uint value with the highest CAN ID to be received Mode : A TPCANMode representing the message type (Standard, 11-bit identifier, or Extended, 29-bit identifier) Returns: A TPCANStatus error code """ try: res = self.__m_dllBasic.CAN_FilterMessages(Channel,FromID,ToID,Mode) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.FilterMessages") raise # Retrieves a PCAN Channel value # def GetValue( self, Channel, Parameter): """ Retrieves a PCAN Channel value Remarks: Parameters can be present or not according with the kind of Hardware (PCAN Channel) being used. If a parameter is not available, a PCAN_ERROR_ILLPARAMTYPE error will be returned. The return value of this method is a 2-touple, where the first value is the result (TPCANStatus) of the method and the second one, the asked value Parameters: Channel : A TPCANHandle representing a PCAN Channel Parameter : The TPCANParameter parameter to get Returns: A touple with 2 values """ try: if Parameter == PCAN_API_VERSION or Parameter == PCAN_HARDWARE_NAME or Parameter == PCAN_CHANNEL_VERSION or Parameter == PCAN_LOG_LOCATION or Parameter == PCAN_TRACE_LOCATION or Parameter == PCAN_BITRATE_INFO_FD or Parameter == PCAN_IP_ADDRESS: mybuffer = create_string_buffer(256) else: mybuffer = c_int(0) res = self.__m_dllBasic.CAN_GetValue(Channel,Parameter,byref(mybuffer),sizeof(mybuffer)) return TPCANStatus(res),mybuffer.value except: logger.error("Exception on PCANBasic.GetValue") raise # Returns a descriptive text of a given TPCANStatus # error code, in any desired language # def SetValue( self, Channel, Parameter, Buffer): """ Returns a descriptive text of a given TPCANStatus error code, in any desired language Remarks: Parameters can be present or not according with the kind of Hardware (PCAN Channel) being used. If a parameter is not available, a PCAN_ERROR_ILLPARAMTYPE error will be returned. Parameters: Channel : A TPCANHandle representing a PCAN Channel Parameter : The TPCANParameter parameter to set Buffer : Buffer with the value to be set BufferLength : Size in bytes of the buffer Returns: A TPCANStatus error code """ try: if Parameter == PCAN_LOG_LOCATION or Parameter == PCAN_LOG_TEXT or Parameter == PCAN_TRACE_LOCATION: mybuffer = create_string_buffer(256) else: mybuffer = c_int(0) mybuffer.value = Buffer res = self.__m_dllBasic.CAN_SetValue(Channel,Parameter,byref(mybuffer),sizeof(mybuffer)) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.SetValue") raise def GetErrorText( self, Error, Language = 0): """ Configures or sets a PCAN Channel value Remarks: The current languages available for translation are: Neutral (0x00), German (0x07), English (0x09), Spanish (0x0A), Italian (0x10) and French (0x0C) The return value of this method is a 2-touple, where the first value is the result (TPCANStatus) of the method and the second one, the error text Parameters: Error : A TPCANStatus error code Language : Indicates a 'Primary language ID' (Default is Neutral(0)) Returns: A touple with 2 values """ try: mybuffer = create_string_buffer(256) res = self.__m_dllBasic.CAN_GetErrorText(Error,Language,byref(mybuffer)) return TPCANStatus(res),mybuffer.value except: logger.error("Exception on PCANBasic.GetErrorText") raise python-can-2.0.0/can/interfaces/pcan/__init__.py0000644000175000017500000000005513223535415021722 0ustar brianbrian00000000000000from can.interfaces.pcan.pcan import PcanBus python-can-2.0.0/can/interfaces/pcan/pcan.py0000644000175000017500000002047713223535415021116 0ustar brianbrian00000000000000""" Enable basic can over a PCAN USB device. """ import logging import sys from can.interfaces.pcan.PCANBasic import * from can.bus import BusABC from can.message import Message from can import CanError import time boottimeEpoch = 0 try: import uptime import datetime boottimeEpoch = (uptime.boottime() - datetime.datetime.utcfromtimestamp(0)).total_seconds() except: boottimeEpoch = 0 try: # Try builtin Python 3 Windows API from _overlapped import CreateEvent from _winapi import WaitForSingleObject, WAIT_OBJECT_0, INFINITE HAS_EVENTS = True except ImportError: try: # Try pywin32 package from win32event import CreateEvent from win32event import WaitForSingleObject, WAIT_OBJECT_0, INFINITE HAS_EVENTS = True except ImportError: # Use polling instead HAS_EVENTS = False if sys.version_info >= (3, 3): # new in 3.3 timeout_clock = time.perf_counter else: # deprecated in 3.3 timeout_clock = time.clock # Set up logging log = logging.getLogger('can.pcan') pcan_bitrate_objs = {1000000 : PCAN_BAUD_1M, 800000 : PCAN_BAUD_800K, 500000 : PCAN_BAUD_500K, 250000 : PCAN_BAUD_250K, 125000 : PCAN_BAUD_125K, 100000 : PCAN_BAUD_100K, 95000 : PCAN_BAUD_95K, 83000 : PCAN_BAUD_83K, 50000 : PCAN_BAUD_50K, 47000 : PCAN_BAUD_47K, 33000 : PCAN_BAUD_33K, 20000 : PCAN_BAUD_20K, 10000 : PCAN_BAUD_10K, 5000 : PCAN_BAUD_5K} class PcanBus(BusABC): def __init__(self, channel, *args, **kwargs): """A PCAN USB interface to CAN. On top of the usual :class:`~can.Bus` methods provided, the PCAN interface includes the `flash()` and `status()` methods. :param str channel: The can interface name. An example would be PCAN_USBBUS1 :param int bitrate: Bitrate of channel in bit/s. Default is 500 Kbs """ if channel is None or channel == '': raise ArgumentError("Must specify a PCAN channel") else: self.channel_info = channel bitrate = kwargs.get('bitrate', 500000) pcan_bitrate = pcan_bitrate_objs.get(bitrate, PCAN_BAUD_500K) hwtype = PCAN_TYPE_ISA ioport = 0x02A0 interrupt = 11 self.m_objPCANBasic = PCANBasic() self.m_PcanHandle = globals()[channel] result = self.m_objPCANBasic.Initialize(self.m_PcanHandle, pcan_bitrate, hwtype, ioport, interrupt) if result != PCAN_ERROR_OK: raise PcanError(self._get_formatted_error(result)) if HAS_EVENTS: self._recv_event = CreateEvent(None, 0, 0, None) result = self.m_objPCANBasic.SetValue( self.m_PcanHandle, PCAN_RECEIVE_EVENT, self._recv_event) if result != PCAN_ERROR_OK: raise PcanError(self._get_formatted_error(result)) super(PcanBus, self).__init__(*args, **kwargs) def _get_formatted_error(self, error): """ Gets the text using the GetErrorText API function If the function succeeds, the translated error is returned. If it fails, a text describing the current error is returned. Multiple errors may be present in which case their individual messages are included in the return string, one line per error. """ def bits(n): while n: b = n & (~n+1) yield b n ^= b stsReturn = self.m_objPCANBasic.GetErrorText(error, 0) if stsReturn[0] != PCAN_ERROR_OK: strings = [] for b in bits(error): stsReturn = self.m_objPCANBasic.GetErrorText(b, 0) if stsReturn[0] != PCAN_ERROR_OK: text = "An error occurred. Error-code's text ({0:X}h) couldn't be retrieved".format(error) else: text = stsReturn[1].decode('utf-8') strings.append(text) complete_text = '\n'.join(strings) else: complete_text = stsReturn[1].decode('utf-8') return complete_text def status(self): """ Query the PCAN bus status. :return: The status code. See values in pcan_constants.py """ return self.m_objPCANBasic.GetStatus(self.m_PcanHandle) def status_is_ok(self): """ Convenience method to check that the bus status is OK """ status = self.status() return status == PCAN_ERROR_OK def reset(self): # Command the PCAN driver to reset the bus after an error. status = self.m_objPCANBasic.Reset(self.m_PcanHandle) return status == PCAN_ERROR_OK def recv(self, timeout=None): if HAS_EVENTS: # We will utilize events for the timeout handling timeout_ms = int(timeout * 1000) if timeout is not None else INFINITE elif timeout is not None: # Calculate max time end_time = timeout_clock() + timeout log.debug("Trying to read a msg") result = None while result is None: result = self.m_objPCANBasic.Read(self.m_PcanHandle) if result[0] == PCAN_ERROR_QRCVEMPTY: if HAS_EVENTS: result = None val = WaitForSingleObject(self._recv_event, timeout_ms) if val != WAIT_OBJECT_0: return None elif timeout is not None and timeout_clock() >= end_time: return None else: result = None time.sleep(0.001) elif result[0] & (PCAN_ERROR_BUSLIGHT | PCAN_ERROR_BUSHEAVY): log.warning(self._get_formatted_error(result[0])) return None elif result[0] != PCAN_ERROR_OK: raise PcanError(self._get_formatted_error(result[0])) theMsg = result[1] itsTimeStamp = result[2] log.debug("Received a message") bIsRTR = (theMsg.MSGTYPE & PCAN_MESSAGE_RTR.value) == PCAN_MESSAGE_RTR.value bIsExt = (theMsg.MSGTYPE & PCAN_MESSAGE_EXTENDED.value) == PCAN_MESSAGE_EXTENDED.value if bIsExt: #rx_msg.id_type = ID_TYPE_EXTENDED log.debug("CAN: Extended") else: #rx_msg.id_type = ID_TYPE_STANDARD log.debug("CAN: Standard") dlc = theMsg.LEN timestamp = boottimeEpoch + ((itsTimeStamp.micros + (1000 * itsTimeStamp.millis)) / (1000.0 * 1000.0)) rx_msg = Message(timestamp=timestamp, arbitration_id=theMsg.ID, extended_id=bIsExt, is_remote_frame=bIsRTR, dlc=dlc, data=theMsg.DATA[:dlc]) return rx_msg def send(self, msg, timeout=None): if msg.id_type: msgType = PCAN_MESSAGE_EXTENDED else: msgType = PCAN_MESSAGE_STANDARD # create a TPCANMsg message structure CANMsg = TPCANMsg() # configure the message. ID, Length of data, message type and data CANMsg.ID = msg.arbitration_id CANMsg.LEN = msg.dlc CANMsg.MSGTYPE = msgType # if a remote frame will be sent, data bytes are not important. if msg.is_remote_frame: CANMsg.MSGTYPE = msgType.value | PCAN_MESSAGE_RTR.value else: # copy data for i in range(CANMsg.LEN): CANMsg.DATA[i] = msg.data[i] log.debug("Data: %s", msg.data) log.debug("Type: %s", type(msg.data)) result = self.m_objPCANBasic.Write(self.m_PcanHandle, CANMsg) if result != PCAN_ERROR_OK: raise PcanError("Failed to send: " + self._get_formatted_error(result)) def flash(self, flash): """ Turn on or off flashing of the device's LED for physical identification purposes. """ self.m_objPCANBasic.SetValue(self.m_PcanHandle, PCAN_CHANNEL_IDENTIFYING, bool(flash)) def shutdown(self): self.m_objPCANBasic.Uninitialize(self.m_PcanHandle) class PcanError(CanError): pass python-can-2.0.0/can/interfaces/serial/0000755000175000017500000000000013223535610020144 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/serial/__init__.py0000644000175000017500000000007613113515201022250 0ustar brianbrian00000000000000from can.interfaces.serial.serial_can import SerialBus as Bus python-can-2.0.0/can/interfaces/serial/serial_can.py0000644000175000017500000001063113223535415022622 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ A text based interface. For example use over serial ports like "/dev/ttyS1" or "/dev/ttyUSB0" on Linux machines or "COM1" on Windows. The interface is a simple implementation that has been used for recording CAN traces. """ import logging import struct from can.bus import BusABC from can.message import Message logger = logging.getLogger('can.serial') try: import serial except ImportError: logger.warning("You won't be able to use the serial can backend without " "the serial module installed!") serial = None class SerialBus(BusABC): """ Enable basic can communication over a serial device. """ def __init__(self, channel, *args, **kwargs): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or "/dev/ttyUSB0" on Linux or "COM1" on Windows systems. :param int baudrate: Baud rate of the serial device in bit/s (default 115200). .. note:: Some serial port implementations don't care about the baud rate. :param float timeout: Timeout for the serial device in seconds (default 0.1). """ if channel == '': raise ValueError("Must specify a serial port.") else: self.channel_info = "Serial interface: " + channel baudrate = kwargs.get('baudrate', 115200) timeout = kwargs.get('timeout', 0.1) self.ser = serial.Serial(channel, baudrate=baudrate, timeout=timeout) super(SerialBus, self).__init__(*args, **kwargs) def shutdown(self): """ Close the serial interface. """ self.ser.close() def send(self, msg, timeout=None): """ Send a message over the serial device. :param can.Message msg: Message to send. .. note:: Flags like extended_id, is_remote_frame and is_error_frame will be ignored. .. note:: If the timestamp a float value it will be convert to an integer. :param timeout: This parameter will be ignored. The timeout value of the channel is used. """ try: timestamp = struct.pack(' 0: log.debug("Creating a filtered can bus") self.set_filters(kwargs['can_filters']) error = bindSocket(self.socket, channel) if error < 0: m = u'bindSocket failed for channel {} with error {}'.format( channel, error) raise can.CanError(m) if receive_own_messages: error1 = recv_own_msgs(self.socket) super(SocketcanCtypes_Bus, self).__init__(*args, **kwargs) def set_filters(self, can_filters=None): """Apply filtering to all messages received by this Bus. Calling without passing any filters will reset the applied filters. :param list can_filters: A list of dictionaries each containing a "can_id" and a "can_mask". >>> [{"can_id": 0x11, "can_mask": 0x21}] A filter matches, when `` & can_mask == can_id & can_mask`` """ filter_struct = pack_filters(can_filters) res = libc.setsockopt(self.socket, SOL_CAN_RAW, CAN_RAW_FILTER, filter_struct, len(filter_struct) ) # TODO Is this serious enough to raise a CanError exception? if res != 0: log.error('Setting filters failed: ' + str(res)) def recv(self, timeout=None): log.debug("Trying to read a msg") if timeout is None or len(select.select([self.socket], [], [], timeout)[0]) > 0: packet = capturePacket(self.socket) else: # socket wasn't readable or timeout occurred return None log.debug("Receiving a message") arbitration_id = packet['CAN ID'] & MSK_ARBID # Flags: EXT, RTR, ERR flags = (packet['CAN ID'] & MSK_FLAGS) >> 29 rx_msg = Message( timestamp=packet['Timestamp'], is_remote_frame=bool(flags & SKT_RTRFLG), extended_id=bool(flags & EXTFLG), is_error_frame=bool(flags & SKT_ERRFLG), arbitration_id=arbitration_id, dlc=packet['DLC'], data=packet['Data'] ) return rx_msg def send(self, msg, timeout=None): frame = _build_can_frame(msg) if timeout: # Wait for write availability. write will fail below on timeout select.select([], [self.socket], [], timeout) bytes_sent = libc.write(self.socket, ctypes.byref(frame), ctypes.sizeof(frame)) if bytes_sent == -1: log.debug("Error sending frame :-/") raise can.CanError("can.socketcan.ctypes failed to transmit") elif bytes_sent == 0: raise can.CanError("Transmit buffer overflow") log.debug("Frame transmitted with %s bytes", bytes_sent) def send_periodic(self, msg, period, duration=None): task = CyclicSendTask(self.channel, msg, period) if duration is not None: threading.Timer(duration, task.stop).start() return task class SOCKADDR(ctypes.Structure): # See /usr/include/i386-linux-gnu/bits/socket.h for original struct _fields_ = [("sa_family", ctypes.c_uint16), ("sa_data", (ctypes.c_char)*14)] class TP(ctypes.Structure): # This struct is only used within the SOCKADDR_CAN struct _fields_ = [("rx_id", ctypes.c_uint32), ("tx_id", ctypes.c_uint32)] class ADDR_INFO(ctypes.Union): # This struct is only used within the SOCKADDR_CAN struct # This union is to future proof for future can address information _fields_ = [("tp", TP)] class SOCKADDR_CAN(ctypes.Structure): # See /usr/include/linux/can.h for original struct _fields_ = [("can_family", ctypes.c_uint16), ("can_ifindex", ctypes.c_int), ("can_addr", ADDR_INFO)] class IFREQ(ctypes.Structure): # The two fields in this struct were originally unions. # See /usr/include/net/if.h for original struct _fields_ = [("ifr_name", ctypes.c_char*16), ("ifr_ifindex", ctypes.c_int)] class CAN_FRAME(ctypes.Structure): # See /usr/include/linux/can.h for original struct # The 32bit can id is directly followed by the 8bit data link count # The data field is aligned on an 8 byte boundary, hence the padding. # Aligns the data field to an 8 byte boundary _fields_ = [("can_id", ctypes.c_uint32), ("can_dlc", ctypes.c_uint8), ("padding", ctypes.c_ubyte * 3), ("data", ctypes.c_uint8 * 8) ] class TIME_VALUE(ctypes.Structure): # See usr/include/linux/time.h for original struct _fields_ = [("tv_sec", ctypes.c_ulong), ("tv_usec", ctypes.c_ulong)] class BCM_HEADER(ctypes.Structure): # See usr/include/linux/can/bcm.h for original struct _fields_ = [ ("opcode", ctypes.c_uint32), ("flags", ctypes.c_uint32), ("count", ctypes.c_uint32), ("ival1", TIME_VALUE), ("ival2", TIME_VALUE), ("can_id", ctypes.c_uint32), ("nframes", ctypes.c_uint32), ("frames", CAN_FRAME) ] def createSocket(protocol=CAN_RAW): """ This function creates a RAW CAN socket. The socket returned needs to be bound to an interface by calling :func:`bindSocket`. :param int protocol: The type of the socket to be bound. Valid values include CAN_RAW and CAN_BCM :return: +-----------+----------------------------+ | 0 |protocol invalid | +-----------+----------------------------+ | -1 |socket creation unsuccessful| +-----------+----------------------------+ | socketID | successful creation | +-----------+----------------------------+ """ if protocol == CAN_RAW: socketID = libc.socket(PF_CAN, SOCK_RAW, CAN_RAW) elif protocol == CAN_BCM: socketID = libc.socket(PF_CAN, SOCK_DGRAM, CAN_BCM) else: socketID = -1 return socketID def bindSocket(socketID, channel_name): """ Binds the given socket to the given interface. :param int socketID: The ID of the socket to be bound :param str channel_name: The interface name to find and bind. :return: The error code from the bind call. +----+----------------------------+ | 0 |protocol invalid | +----+----------------------------+ | -1 |socket creation unsuccessful| +----+----------------------------+ """ log.debug('Binding socket with id %d to channel %s', socketID, channel_name) socketID = ctypes.c_int(socketID) ifr = IFREQ() ifr.ifr_name = channel_name.encode('ascii') log.debug('calling ioctl SIOCGIFINDEX') # ifr.ifr_ifindex gets filled with that device's index ret = libc.ioctl(socketID, SIOCGIFINDEX, ctypes.byref(ifr)) if ret < 0: m = u'Failure while getting "{}" interface index.'.format(channel_name) raise can.CanError(m) log.info('ifr.ifr_ifindex: %d', ifr.ifr_ifindex) # select the CAN interface and bind the socket to it addr = SOCKADDR_CAN(AF_CAN, ifr.ifr_ifindex) error = libc.bind(socketID, ctypes.byref(addr), ctypes.sizeof(addr)) if error < 0: log.error("Couldn't bind socket") log.debug('bind returned: %d', error) return error def connectSocket(socketID, channel_name): """Connects the given socket to the given interface. :param int socketID: The ID of the socket to be bound :param str channel_name: The interface name to find and bind. :return: The error code from the bind call. """ log.debug('Connecting socket with id %d to channel %s', socketID, channel_name) socketID = ctypes.c_int(socketID) ifr = IFREQ() ifr.ifr_name = channel_name.encode('ascii') log.debug('calling ioctl SIOCGIFINDEX') # ifr.ifr_ifindex gets filled with that device's index libc.ioctl(socketID, SIOCGIFINDEX, ctypes.byref(ifr)) log.info('ifr.ifr_ifindex: %d', ifr.ifr_ifindex) # select the CAN interface and bind the socket to it addr = SOCKADDR_CAN(AF_CAN, ifr.ifr_ifindex) error = libc.connect(socketID, ctypes.byref(addr), ctypes.sizeof(addr)) if error < 0: log.error("Couldn't connect socket") log.debug('connect returned: %d', error) return error def recv_own_msgs(socket_id): setting = ctypes.c_int(1) error = libc.setsockopt(socket_id, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, ctypes.byref(setting), ctypes.sizeof(setting)) if error < 0: log.error("Couldn't set recv own msgs") return error def _build_can_frame(message): log.debug("Packing a can frame") arbitration_id = message.arbitration_id if message.id_type: log.debug("sending an extended id type message") arbitration_id |= 0x80000000 if message.is_remote_frame: log.debug("requesting a remote frame") arbitration_id |= 0x40000000 if message.is_error_frame: log.debug("sending error frame") arbitration_id |= 0x20000000 log.debug("Data: %s", message.data) log.debug("Type: %s", type(message.data)) # TODO need to understand the extended frame format frame = CAN_FRAME() frame.can_id = arbitration_id frame.can_dlc = len(message.data) frame.data[0:frame.can_dlc] = message.data log.debug("sizeof frame: %d", ctypes.sizeof(frame)) return frame def capturePacket(socketID): """ Captures a packet of data from the given socket. :param int socketID: The socket to read from :return: A dictionary with the following keys: - `"CAN ID"` (int) - `"DLC"` (int) - `"Data"` (list) - `"Timestamp"` (float) """ packet = {} frame = CAN_FRAME() time = TIME_VALUE() # Fetching the Arb ID, DLC and Data bytes_read = libc.read(socketID, ctypes.byref(frame), ctypes.sizeof(frame)) # Fetching the timestamp error = libc.ioctl(socketID, SIOCGSTAMP, ctypes.byref(time)) packet['CAN ID'] = frame.can_id packet['DLC'] = frame.can_dlc packet["Data"] = [frame.data[i] for i in range(frame.can_dlc)] timestamp = time.tv_sec + (time.tv_usec / 1000000.0) packet['Timestamp'] = timestamp return packet def _create_bcm_frame(opcode, flags, count, ival1_seconds, ival1_usec, ival2_seconds, ival2_usec, can_id, nframes, msg_frame): frame = BCM_HEADER() frame.opcode = opcode frame.flags = flags frame.count = count frame.ival1.tv_sec = ival1_seconds frame.ival1.tv_usec = ival1_usec frame.ival2.tv_sec = ival2_seconds frame.ival2.tv_usec = ival2_usec frame.can_id = can_id frame.nframes = nframes frame.frames = msg_frame return frame class SocketCanCtypesBCMBase(object): """Mixin to add a BCM socket""" def __init__(self, channel, *args, **kwargs): log.debug("Creating bcm socket on channel '%s'", channel) # Set up the bcm socket using ctypes self.bcm_socket = createSocket(protocol=CAN_BCM) log.debug("Created bcm socket (un-connected fd=%d)", self.bcm_socket) connectSocket(self.bcm_socket, channel) log.debug("Connected bcm socket") super(SocketCanCtypesBCMBase, self).__init__(*args, **kwargs) class CyclicSendTask(SocketCanCtypesBCMBase, RestartableCyclicTaskABC, ModifiableCyclicTaskABC): def __init__(self, channel, message, period): """ :param channel: The name of the CAN channel to connect to. :param message: The message to be sent periodically. :param period: The rate in seconds at which to send the message. """ super(CyclicSendTask, self).__init__(channel, message, period) self.message = message # Send the bcm message with opcode TX_SETUP to start the cyclic transmit self._tx_setup() def _tx_setup(self): message = self.message # Create a low level packed frame to pass to the kernel msg_frame = _build_can_frame(message) frame = _create_bcm_frame(opcode=CAN_BCM_TX_SETUP, flags=SETTIMER | STARTTIMER, count=0, ival1_seconds=0, ival1_usec=0, ival2_seconds=int(self.period), ival2_usec=int(1e6 * (self.period - int(self.period))), can_id=message.arbitration_id, nframes=1, msg_frame=msg_frame) log.info("Sending BCM TX_SETUP command") bytes_sent = libc.send(self.bcm_socket, ctypes.byref(frame), ctypes.sizeof(frame)) if bytes_sent == -1: log.debug("Error sending frame :-/") def start(self): self._tx_setup() def stop(self): """Send a TX_DELETE message to cancel this task. This will delete the entry for the transmission of the CAN-message with the specified can_id CAN identifier. The message length for the command TX_DELETE is {[bcm_msg_head]} (only the header). """ frame = _create_bcm_frame( opcode=CAN_BCM_TX_DELETE, flags=0, count=0, ival1_seconds=0, ival1_usec=0, ival2_seconds=0, ival2_usec=0, can_id=self.can_id, nframes=0, msg_frame=CAN_FRAME() ) bytes_sent = libc.send(self.bcm_socket, ctypes.byref(frame), ctypes.sizeof(frame)) if bytes_sent == -1: log.debug("Error sending frame to stop cyclic message:-/") def modify_data(self, message): """Update the contents of this periodically sent message. """ assert message.arbitration_id == self.can_id, "You cannot modify the can identifier" self.message = message self._tx_setup() class MultiRateCyclicSendTask(CyclicSendTask): """Exposes more of the full power of the TX_SETUP opcode. Transmits a message `count` times at `initial_period` then continues to transmit message at `subsequent_period`. """ def __init__(self, channel, message, count, initial_period, subsequent_period): super(MultiRateCyclicSendTask, self).__init__(channel, message, subsequent_period) msg_frame = _build_can_frame(message) frame = _create_bcm_frame(opcode=CAN_BCM_TX_SETUP, flags=SETTIMER | STARTTIMER, count=count, ival1_seconds=int(initial_period), ival1_usec=int(1e6 * (initial_period - int(initial_period))), ival2_seconds=int(subsequent_period), ival2_usec=int(1e6 * (subsequent_period - int(subsequent_period))), can_id=message.arbitration_id, nframes=1, msg_frame=msg_frame) log.info("Sending BCM TX_SETUP command") bytes_sent = libc.send(self.bcm_socket, ctypes.byref(frame), ctypes.sizeof(frame)) if bytes_sent == -1: log.debug("Error sending frame :-/") python-can-2.0.0/can/interfaces/socketcan/socketcan_native.py0000644000175000017500000003730713223535415024546 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ This implementation is for versions of Python that have native can socket and can bcm socket support: >3.4 """ import logging import select import threading import socket import struct import errno import os log = logging.getLogger('can.socketcan.native') log_tx = log.getChild("tx") log_rx = log.getChild("rx") log.info("Loading socketcan native backend") try: import fcntl except ImportError: log.warning("fcntl not available on this platform") try: socket.CAN_RAW except: log.debug("CAN_* properties not found in socket module. These are required to use native socketcan") import can from can.interfaces.socketcan.socketcan_constants import * # CAN_RAW, CAN_*_FLAG from can.interfaces.socketcan.socketcan_common import * # parseCanFilters from can import Message, BusABC from can.broadcastmanager import ModifiableCyclicTaskABC, RestartableCyclicTaskABC, LimitedDurationCyclicSendTaskABC # struct module defines a binary packing format: # https://docs.python.org/3/library/struct.html#struct-format-strings # The 32bit can id is directly followed by the 8bit data link count # The data field is aligned on an 8 byte boundary, hence we add padding # which aligns the data field to an 8 byte boundary. can_frame_fmt = "=IB3x8s" can_frame_size = struct.calcsize(can_frame_fmt) def build_can_frame(can_id, data): """ CAN frame packing/unpacking (see 'struct can_frame' in ) /** * struct can_frame - basic CAN frame structure * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above. * @can_dlc: the data length field of the CAN frame * @data: the CAN frame payload. */ struct can_frame { canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ __u8 can_dlc; /* data length code: 0 .. 8 */ __u8 data[8] __attribute__((aligned(8))); }; """ can_dlc = len(data) data = data.ljust(8, b'\x00') return struct.pack(can_frame_fmt, can_id, can_dlc, data) def build_bcm_header(opcode, flags, count, ival1_seconds, ival1_usec, ival2_seconds, ival2_usec, can_id, nframes): # == Must use native not standard types for packing == # struct bcm_msg_head { # __u32 opcode; -> I # __u32 flags; -> I # __u32 count; -> I # struct timeval ival1, ival2; -> llll ... # canid_t can_id; -> I # __u32 nframes; -> I bcm_cmd_msg_fmt = "@3I4l2I0q" return struct.pack(bcm_cmd_msg_fmt, opcode, flags, count, ival1_seconds, ival1_usec, ival2_seconds, ival2_usec, can_id, nframes) def build_bcm_tx_delete_header(can_id): opcode = CAN_BCM_TX_DELETE return build_bcm_header(opcode, 0, 0, 0, 0, 0, 0, can_id, 1) def build_bcm_transmit_header(can_id, count, initial_period, subsequent_period): opcode = CAN_BCM_TX_SETUP flags = SETTIMER | STARTTIMER if initial_period > 0: # Note `TX_COUNTEVT` creates the message TX_EXPIRED when count expires flags |= TX_COUNTEVT def split_time(value): """Given seconds as a float, return whole seconds and microseconds""" seconds = int(value) microseconds = int(1e6 * (value - seconds)) return seconds, microseconds ival1_seconds, ival1_usec = split_time(initial_period) ival2_seconds, ival2_usec = split_time(subsequent_period) nframes = 1 return build_bcm_header(opcode, flags, count, ival1_seconds, ival1_usec, ival2_seconds, ival2_usec, can_id, nframes) def dissect_can_frame(frame): can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame) return can_id, can_dlc, data[:can_dlc] def create_bcm_socket(channel): """create a broadcast manager socket and connect to the given interface""" try: s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM) except AttributeError: raise SystemExit("To use BCM sockets you need Python3.4 or higher") try: s.connect((channel,)) except OSError as e: log.error("Couldn't connect a broadcast manager socket") raise e return s def send_bcm(socket, data): """ Send raw frame to a BCM socket and handle errors. :param socket: :param data: :return: """ try: return socket.send(data) except OSError as e: base = "Couldn't send CAN BCM frame. OS Error {}: {}\n".format(e.errno, os.strerror(e.errno)) if e.errno == errno.EINVAL: raise can.CanError( base + "You are probably referring to a non-existing frame.") elif e.errno == errno.ENETDOWN: raise can.CanError( base + "The CAN interface appears to be down." ) elif e.errno == errno.EBADF: raise can.CanError(base + "The CAN socket appears to be closed.") else: raise def _add_flags_to_can_id(message): can_id = message.arbitration_id if message.is_extended_id: log.debug("sending an extended id type message") can_id |= CAN_EFF_FLAG if message.is_remote_frame: log.debug("requesting a remote frame") can_id |= CAN_RTR_FLAG if message.is_error_frame: log.debug("sending error frame") can_id |= CAN_ERR_FLAG return can_id class SocketCanBCMBase(object): """Mixin to add a BCM socket""" def __init__(self, channel, *args, **kwargs): self.bcm_socket = create_bcm_socket(channel) super(SocketCanBCMBase, self).__init__(*args, **kwargs) class CyclicSendTask(SocketCanBCMBase, LimitedDurationCyclicSendTaskABC, ModifiableCyclicTaskABC, RestartableCyclicTaskABC): """ A socketcan cyclic send task supports: - setting of a task duration - modifying the data - stopping then subsequent restarting of the task """ def __init__(self, channel, message, period): """ :param channel: The name of the CAN channel to connect to. :param message: The message to be sent periodically. :param period: The rate in seconds at which to send the message. """ super(CyclicSendTask, self).__init__(channel, message, period, duration=None) self._tx_setup(message) self.message = message def _tx_setup(self, message): # Create a low level packed frame to pass to the kernel self.can_id_with_flags = _add_flags_to_can_id(message) header = build_bcm_transmit_header(self.can_id_with_flags, 0, 0.0, self.period) frame = build_can_frame(self.can_id_with_flags, message.data) log.debug("Sending BCM command") send_bcm(self.bcm_socket, header + frame) def stop(self): """Send a TX_DELETE message to cancel this task. This will delete the entry for the transmission of the CAN-message with the specified can_id CAN identifier. The message length for the command TX_DELETE is {[bcm_msg_head]} (only the header). """ log.debug("Stopping periodic task") stopframe = build_bcm_tx_delete_header(self.can_id_with_flags) send_bcm(self.bcm_socket, stopframe) def modify_data(self, message): """Update the contents of this periodically sent message. Note the Message must have the same :attr:`~can.Message.arbitration_id`. """ assert message.arbitration_id == self.can_id, "You cannot modify the can identifier" self._tx_setup(message) def start(self): self._tx_setup(self.message) class MultiRateCyclicSendTask(CyclicSendTask): """Exposes more of the full power of the TX_SETUP opcode. Transmits a message `count` times at `initial_period` then continues to transmit message at `subsequent_period`. """ def __init__(self, channel, message, count, initial_period, subsequent_period): super(MultiRateCyclicSendTask, self).__init__(channel, message, subsequent_period) # Create a low level packed frame to pass to the kernel frame = build_can_frame(self.can_id, message.data) header = build_bcm_transmit_header( self.can_id, count, initial_period, subsequent_period) log.info("Sending BCM TX_SETUP command") send_bcm(self.bcm_socket, header + frame) def createSocket(can_protocol=None): """Creates a CAN socket. The socket can be BCM or RAW. The socket will be returned unbound to any interface. :param int can_protocol: The protocol to use for the CAN socket, either: * socket.CAN_RAW * socket.CAN_BCM. :return: * -1 if socket creation unsuccessful * socketID - successful creation """ if can_protocol is None or can_protocol == socket.CAN_RAW: can_protocol = socket.CAN_RAW socket_type = socket.SOCK_RAW elif can_protocol == socket.CAN_BCM: can_protocol = socket.CAN_BCM socket_type = socket.SOCK_DGRAM sock = socket.socket(socket.PF_CAN, socket_type, can_protocol) log.info('Created a socket') return sock def bindSocket(sock, channel='can0'): """ Binds the given socket to the given interface. :param Socket socketID: The ID of the socket to be bound :raise: :class:`OSError` if the specified interface isn't found. """ log.debug('Binding socket to channel=%s', channel) sock.bind((channel,)) log.debug('Bound socket.') def captureMessage(sock): """ Captures a message from given socket. :param socket sock: The socket to read a message from. :return: The received message, or None on failure. """ # Fetching the Arb ID, DLC and Data try: cf, addr = sock.recvfrom(can_frame_size) except BlockingIOError: log.debug('Captured no data, socket in non-blocking mode.') return None except socket.timeout: log.debug('Captured no data, socket read timed out.') return None except OSError: # something bad happened (e.g. the interface went down) log.exception("Captured no data.") return None can_id, can_dlc, data = dissect_can_frame(cf) # Fetching the timestamp binary_structure = "@LL" res = fcntl.ioctl(sock, SIOCGSTAMP, struct.pack(binary_structure, 0, 0)) seconds, microseconds = struct.unpack(binary_structure, res) timestamp = seconds + microseconds / 1000000 # EXT, RTR, ERR flags -> boolean attributes # /* special address description flags for the CAN_ID */ # #define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ # #define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ # #define CAN_ERR_FLAG 0x20000000U /* error frame */ is_extended_frame_format = bool(can_id & 0x80000000) is_remote_transmission_request = bool(can_id & 0x40000000) is_error_frame = bool(can_id & 0x20000000) if is_extended_frame_format: log.debug("CAN: Extended") # TODO does this depend on SFF or EFF? arbitration_id = can_id & 0x1FFFFFFF else: log.debug("CAN: Standard") arbitration_id = can_id & 0x000007FF msg = Message(timestamp=timestamp, arbitration_id=arbitration_id, extended_id=is_extended_frame_format, is_remote_frame=is_remote_transmission_request, is_error_frame=is_error_frame, dlc=can_dlc, data=data) log_rx.debug('Received: %s', msg) return msg class SocketcanNative_Bus(BusABC): channel_info = "native socketcan channel" def __init__(self, channel, receive_own_messages=False, **kwargs): """ :param str channel: The can interface name with which to create this bus. An example channel would be 'vcan0'. :param bool receive_own_messages: If messages transmitted should also be received back. :param list can_filters: A list of dictionaries, each containing a "can_id" and a "can_mask". """ self.socket = createSocket(CAN_RAW) self.channel = channel # Add any socket options such as can frame filters if 'can_filters' in kwargs and len(kwargs['can_filters']) > 0: log.debug("Creating a filtered can bus") self.set_filters(kwargs['can_filters']) try: self.socket.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_RECV_OWN_MSGS, struct.pack('i', receive_own_messages)) except Exception as e: log.error("Could not receive own messages (%s)", e) bindSocket(self.socket, channel) super(SocketcanNative_Bus, self).__init__() def shutdown(self): self.socket.close() def recv(self, timeout=None): data_ready = True try: if timeout is not None: data_ready = len(select.select([self.socket], [], [], timeout)[0]) > 0 except OSError: # something bad happened (e.g. the interface went down) log.exception("Error while waiting for timeout") return None if data_ready: return captureMessage(self.socket) else: # socket wasn't readable or timeout occurred return None def send(self, msg, timeout=None): log.debug("We've been asked to write a message to the bus") arbitration_id = msg.arbitration_id if msg.id_type: log.debug("sending an extended id type message") arbitration_id |= 0x80000000 if msg.is_remote_frame: log.debug("requesting a remote frame") arbitration_id |= 0x40000000 if msg.is_error_frame: log.warning("Trying to send an error frame - this won't work") arbitration_id |= 0x20000000 log_tx.debug("Sending: %s", msg) if timeout: # Wait for write availability. send will fail below on timeout select.select([], [self.socket], [], timeout) try: bytes_sent = self.socket.send(build_can_frame(arbitration_id, msg.data)) except OSError as exc: raise can.CanError("Transmit failed (%s)" % exc) if bytes_sent == 0: raise can.CanError("Transmit buffer overflow") def send_periodic(self, msg, period, duration=None): task = CyclicSendTask(self.channel, msg, period) if duration is not None: stop_timer = threading.Timer(duration, task.stop) stop_timer.start() return task def set_filters(self, can_filters=None): filter_struct = pack_filters(can_filters) self.socket.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FILTER, filter_struct ) if __name__ == "__main__": # Create two sockets on vcan0 to test send and receive # # If you want to try it out you can do the following: # # modprobe vcan # ip link add dev vcan0 type vcan # ifconfig vcan0 up log.setLevel(logging.DEBUG) def receiver(e): receiver_socket = createSocket() bindSocket(receiver_socket, 'vcan0') print("Receiver is waiting for a message...") e.set() print("Receiver got: ", captureMessage(receiver_socket)) def sender(e): e.wait() sender_socket = createSocket() bindSocket(sender_socket, 'vcan0') sender_socket.send(build_can_frame(0x01, b'\x01\x02\x03')) print("Sender sent a message.") import threading e = threading.Event() threading.Thread(target=receiver, args=(e,)).start() threading.Thread(target=sender, args=(e,)).start() python-can-2.0.0/can/interfaces/usb2can/0000755000175000017500000000000013223535610020222 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/usb2can/__init__.py0000644000175000017500000000022213223535415022332 0ustar brianbrian00000000000000from can.interfaces.usb2can.usb2canInterface import Usb2canBus from can.interfaces.usb2can.usb2canabstractionlayer import Usb2CanAbstractionLayer python-can-2.0.0/can/interfaces/usb2can/serial_selector.py0000644000175000017500000000233113223535415023755 0ustar brianbrian00000000000000import logging try: import win32com.client except ImportError: logging.warning("win32com.client module required for usb2can") raise def WMIDateStringToDate(dtmDate): if (dtmDate[4] == 0): strDateTime = dtmDate[5] + '/' else: strDateTime = dtmDate[4] + dtmDate[5] + '/' if (dtmDate[6] == 0): strDateTime = strDateTime + dtmDate[7] + '/' else: strDateTime = strDateTime + dtmDate[6] + dtmDate[7] + '/' strDateTime = strDateTime + dtmDate[0] + dtmDate[1] + dtmDate[2] + dtmDate[3] + " " + dtmDate[8] + dtmDate[ 9] + ":" + dtmDate[10] + dtmDate[11] + ':' + dtmDate[12] + dtmDate[13] return strDateTime def serial(): strComputer = "." objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator") objSWbemServices = objWMIService.ConnectServer(strComputer, "root\cimv2") colItems = objSWbemServices.ExecQuery("SELECT * FROM Win32_USBControllerDevice") for objItem in colItems: string = objItem.Dependent # find based on beginning of serial if "ED" in string: # print "Dependent:" + ` objItem.Dependent` string = string[len(string) - 9:len(string) - 1] return string python-can-2.0.0/can/interfaces/usb2can/usb2canInterface.py0000644000175000017500000001051113223535415023753 0ustar brianbrian00000000000000# this interface is for windows only, otherwise use socketCAN import logging from can import BusABC, Message from can.interfaces.usb2can.usb2canabstractionlayer import * bootTimeEpoch = 0 try: import uptime import datetime bootTimeEpoch = (uptime.boottime() - datetime.datetime.utcfromtimestamp(0)).total_seconds() except: bootTimeEpoch = 0 # Set up logging log = logging.getLogger('can.usb2can') def format_connection_string(deviceID, baudrate='500'): """setup the string for the device config = deviceID + '; ' + baudrate """ return "%s; %s" % (deviceID, baudrate) # TODO: Issue 36 with data being zeros or anything other than 8 must be fixed def message_convert_tx(msg): messagetx = CanalMsg() length = len(msg.data) messagetx.sizeData = length messagetx.id = msg.arbitration_id for i in range(length): messagetx.data[i] = msg.data[i] messagetx.flags = 80000000 if msg.is_error_frame: messagetx.flags |= IS_ERROR_FRAME if msg.is_remote_frame: messagetx.flags |= IS_REMOTE_FRAME if msg.id_type: messagetx.flags |= IS_ID_TYPE return messagetx def message_convert_rx(messagerx): """convert the message from the CANAL type to pythoncan type""" ID_TYPE = bool(messagerx.flags & IS_ID_TYPE) REMOTE_FRAME = bool(messagerx.flags & IS_REMOTE_FRAME) ERROR_FRAME = bool(messagerx.flags & IS_ERROR_FRAME) msgrx = Message(timestamp=messagerx.timestamp, is_remote_frame=REMOTE_FRAME, extended_id=ID_TYPE, is_error_frame=ERROR_FRAME, arbitration_id=messagerx.id, dlc=messagerx.sizeData, data=messagerx.data[:messagerx.sizeData] ) return msgrx class Usb2canBus(BusABC): """Interface to a USB2CAN Bus. Note the USB2CAN interface doesn't implement set_filters, or flush_tx_buffer methods. :param str channel: The device's serial number. If not provided, Windows Management Instrumentation will be used to identify the first such device. The *kwarg* `serial` may also be used. :param int bitrate: Bitrate of channel in bit/s. Values will be limited to a maximum of 1000 Kb/s. Default is 500 Kbs :param int flags: Flags to directly pass to open function of the usb2can abstraction layer. """ def __init__(self, channel, *args, **kwargs): self.can = Usb2CanAbstractionLayer() # set flags on the connection if 'flags' in kwargs: enable_flags = kwargs["flags"] else: enable_flags = 0x00000008 # code to get the serial number of the device if 'serial' in kwargs: deviceID = kwargs["serial"] elif channel is not None: deviceID = channel else: from can.interfaces.usb2can.serial_selector import serial deviceID = serial() # set baudrate in kb/s from bitrate # (eg:500000 bitrate must be 500) if 'bitrate' in kwargs: br = kwargs["bitrate"] # max rate is 1000 kbps baudrate = max(1000, int(br/1000)) # set default value else: baudrate = 500 connector = format_connection_string(deviceID, baudrate) self.handle = self.can.open(connector, enable_flags) def send(self, msg, timeout=None): tx = message_convert_tx(msg) if timeout: self.can.blocking_send(self.handle, byref(tx), int(timeout * 1000)) else: self.can.send(self.handle, byref(tx)) def recv(self, timeout=None): messagerx = CanalMsg() if timeout == 0: status = self.can.receive(self.handle, byref(messagerx)) else: time = 0 if timeout is None else int(timeout * 1000) status = self.can.blocking_receive(self.handle, byref(messagerx), time) if status == 0: rx = message_convert_rx(messagerx) elif status == 19 or status == 32: # CANAL_ERROR_RCV_EMPTY or CANAL_ERROR_TIMEOUT rx = None else: log.error('Canal Error %s', status) rx = None return rx def shutdown(self): """Shut down the device safely""" status = self.can.close(self.handle) python-can-2.0.0/can/interfaces/usb2can/usb2canabstractionlayer.py0000644000175000017500000001003113223535415025416 0ustar brianbrian00000000000000# This wrapper is for windows or direct access via CANAL API. Socket CAN is recommended under Unix/Linux systems import can from ctypes import * from struct import * import logging log = logging.getLogger('can.usb2can') # type definitions flags = c_ulong pConfigureStr = c_char_p handle = c_long timeout = c_ulong filter = c_ulong # flags mappings IS_ERROR_FRAME = 4 IS_REMOTE_FRAME = 2 IS_ID_TYPE = 1 class CanalStatistics(Structure): _fields_ = [('ReceiveFrams', c_ulong), ('TransmistFrams', c_ulong), ('ReceiveData', c_ulong), ('TransmitData', c_ulong), ('Overruns', c_ulong), ('BusWarnings', c_ulong), ('BusOff', c_ulong)] stat = CanalStatistics class CanalStatus(Structure): _fields_ = [('channel_status', c_ulong), ('lasterrorcode', c_ulong), ('lasterrorsubcode', c_ulong), ('lasterrorstr', c_byte * 80)] # data type for the CAN Message class CanalMsg(Structure): _fields_ = [('flags', c_ulong), ('obid', c_ulong), ('id', c_ulong), ('sizeData', c_ubyte), ('data', c_ubyte * 8), ('timestamp', c_ulong)] class Usb2CanAbstractionLayer: """A low level wrapper around the usb2can library. Documentation: http://www.8devices.com/media/products/usb2can/downloads/CANAL_API.pdf """ def __init__(self): self.__m_dllBasic = windll.LoadLibrary("usb2can.dll") if self.__m_dllBasic is None: log.warning('DLL failed to load') def open(self, pConfigureStr, flags): try: res = self.__m_dllBasic.CanalOpen(pConfigureStr, flags) return res except: log.warning('Failed to open') raise def close(self, handle): try: res = self.__m_dllBasic.CanalClose(handle) return res except: log.warning('Failed to close') raise def send(self, handle, msg): try: res = self.__m_dllBasic.CanalSend(handle, msg) return res except: log.warning('Sending error') raise can.CanError("Failed to transmit frame") def receive(self, handle, msg): try: res = self.__m_dllBasic.CanalReceive(handle, msg) return res except: log.warning('Receive error') raise def blocking_send(self, handle, msg, timeout): try: res = self.__m_dllBasic.CanalBlockingSend(handle, msg, timeout) return res except: log.warning('Blocking send error') raise def blocking_receive(self, handle, msg, timeout): try: res = self.__m_dllBasic.CanalBlockingReceive(handle, msg, timeout) return res except: log.warning('Blocking Receive Failed') raise def get_status(self, handle, CanalStatus): try: res = self.__m_dllBasic.CanalGetStatus(handle, CanalStatus) return res except: log.warning('Get status failed') raise def get_statistics(self, handle, CanalStatistics): try: res = self.__m_dllBasic.CanalGetStatistics(handle, CanalStatistics) return res except: log.warning('Get Statistics failed') raise def get_version(self): try: res = self.__m_dllBasic.CanalGetVersion() return res except: log.warning('Failed to get version info') raise def get_library_version(self): try: res = self.__m_dllBasic.CanalGetDllVersion() return res except: log.warning('Failed to get DLL version') raise def get_vendor_string(self): try: res = self.__m_dllBasic.CanalGetVendorString() return res except: log.warning('Failed to get vendor string') raise python-can-2.0.0/can/interfaces/vector/0000755000175000017500000000000013223535610020167 5ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/vector/__init__.py0000644000175000017500000000010213223535415022274 0ustar brianbrian00000000000000from .canlib import VectorBus from .exceptions import VectorError python-can-2.0.0/can/interfaces/vector/canlib.py0000644000175000017500000001621513223535415022001 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ Ctypes wrapper module for Vector CAN Interface on win32/win64 systems Authors: Julien Grave , Christian Sandberg """ # Import Standard Python Modules # ============================== import ctypes import logging import sys import time # Import Modules # ============== from can import BusABC, Message from .exceptions import VectorError # Define Module Logger # ==================== LOG = logging.getLogger(__name__) # Import safely Vector API module for Travis tests vxlapi = None try: from . import vxlapi except Exception as exc: LOG.warning('Could not import vxlapi: %s', exc) class VectorBus(BusABC): """The CAN Bus implemented for the Vector interface.""" def __init__(self, channel, can_filters=None, poll_interval=0.01, bitrate=None, rx_queue_size=256, app_name="CANalyzer", **config): """ :param list channel: The channel indexes to create this bus with. Can also be a single integer or a comma separated string. :param float poll_interval: Poll interval in seconds. :param int bitrate: Bitrate in bits/s. :param int rx_queue_size: Number of messages in receive queue. :param str app_name: Name of application in Hardware Config. """ if vxlapi is None: raise ImportError("The Vector API has not been loaded") self.poll_interval = poll_interval if isinstance(channel, (list, tuple)): self.channels = channel elif isinstance(channel, int): self.channels = [channel] else: # Assume comma separated string of channels self.channels = [int(ch.strip()) for ch in channel.split(',')] self._app_name = app_name.encode() self.channel_info = 'Application %s: %s' % ( app_name, ', '.join('CAN %d' % (ch + 1) for ch in self.channels)) vxlapi.xlOpenDriver() self.port_handle = vxlapi.XLportHandle(vxlapi.XL_INVALID_PORTHANDLE) self.mask = 0 # Get channels masks for channel in self.channels: hw_type = ctypes.c_uint(0) hw_index = ctypes.c_uint(0) hw_channel = ctypes.c_uint(0) vxlapi.xlGetApplConfig(self._app_name, channel, hw_type, hw_index, hw_channel, vxlapi.XL_BUS_TYPE_CAN) LOG.debug('Channel index %d found', channel) mask = vxlapi.xlGetChannelMask(hw_type.value, hw_index.value, hw_channel.value) LOG.debug('Channel %d, Type: %d, Mask: %d', hw_channel.value, hw_type.value, mask) self.mask |= mask permission_mask = vxlapi.XLaccess() # Set mask to request channel init permission if needed if bitrate: permission_mask.value = self.mask vxlapi.xlOpenPort(self.port_handle, self._app_name, self.mask, permission_mask, rx_queue_size, vxlapi.XL_INTERFACE_VERSION, vxlapi.XL_BUS_TYPE_CAN) LOG.debug( 'Open Port: PortHandle: %d, PermissionMask: 0x%X', self.port_handle.value, permission_mask.value) if bitrate: if permission_mask.value != self.mask: LOG.warning('Can not set bitrate since no init access') vxlapi.xlCanSetChannelBitrate(self.port_handle, permission_mask, bitrate) self.set_filters(can_filters) try: vxlapi.xlActivateChannel(self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0) except VectorError: self.shutdown() raise # Calculate time offset for absolute timestamps offset = vxlapi.XLuint64() vxlapi.xlGetSyncTime(self.port_handle, offset) self._time_offset = time.time() - offset.value / 1000000000.0 super(VectorBus, self).__init__() def set_filters(self, can_filters=None): if can_filters: # Only one filter per ID type allowed if len(can_filters) == 1 or ( len(can_filters) == 2 and can_filters[0].get("extended") != can_filters[1].get("extended")): for can_filter in can_filters: try: vxlapi.xlCanSetChannelAcceptance( self.port_handle, self.mask, can_filter["can_id"], can_filter["can_mask"], vxlapi.XL_CAN_EXT if can_filter.get("extended") else vxlapi.XL_CAN_STD) except VectorError as exc: LOG.warning("Could not set filters: %s", exc) else: LOG.warning("Only one filter per extended or standard ID allowed") def recv(self, timeout=None): end_time = time.time() + timeout if timeout is not None else None event = vxlapi.XLevent(0) while True: event_count = ctypes.c_uint(1) try: vxlapi.xlReceive(self.port_handle, event_count, event) except VectorError as exc: if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY: raise else: if event.tag == vxlapi.XL_RECEIVE_MSG: msg_id = event.tagData.msg.id dlc = event.tagData.msg.dlc flags = event.tagData.msg.flags timestamp = event.timeStamp / 1000000000.0 msg = Message( timestamp=timestamp + self._time_offset, arbitration_id=msg_id & 0x1FFFFFFF, extended_id=bool(msg_id & vxlapi.XL_CAN_EXT_MSG_ID), is_remote_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME), is_error_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_ERROR_FRAME), dlc=dlc, data=event.tagData.msg.data[:dlc], channel=event.chanIndex) return msg if end_time is not None and time.time() > end_time: return None time.sleep(self.poll_interval) def send(self, msg, timeout=None): message_count = ctypes.c_uint(1) msg_id = msg.arbitration_id if msg.id_type: msg_id |= vxlapi.XL_CAN_EXT_MSG_ID flags = 0 if msg.is_remote_frame: flags |= vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME xl_event = vxlapi.XLevent() xl_event.tag = vxlapi.XL_TRANSMIT_MSG xl_event.tagData.msg.id = msg_id xl_event.tagData.msg.dlc = msg.dlc xl_event.tagData.msg.flags = flags for idx, value in enumerate(msg.data): xl_event.tagData.msg.data[idx] = value vxlapi.xlCanTransmit(self.port_handle, self.mask, message_count, xl_event) def flush_tx_buffer(self): vxlapi.xlCanFlushTransmitQueue(self.port_handle, self.mask) def shutdown(self): vxlapi.xlDeactivateChannel(self.port_handle, self.mask) vxlapi.xlClosePort(self.port_handle) vxlapi.xlCloseDriver() python-can-2.0.0/can/interfaces/vector/exceptions.py0000644000175000017500000000031013223535415022717 0ustar brianbrian00000000000000from can import CanError class VectorError(CanError): def __init__(self, error_code, error_string): self.error_code = error_code super(VectorError, self).__init__(error_string) python-can-2.0.0/can/interfaces/vector/vxlapi.py0000644000175000017500000001232013223535415022045 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ Ctypes wrapper module for Vector CAN Interface on win32/win64 systems Authors: Julien Grave , Christian Sandberg """ # Import Standard Python Modules # ============================== import ctypes import logging import platform from .exceptions import VectorError # Define Module Logger # ==================== LOG = logging.getLogger(__name__) # Vector XL API Definitions # ========================= # Load Windows DLL DLL_NAME = 'vxlapi64' if platform.architecture()[0] == '64bit' else 'vxlapi' _xlapi_dll = ctypes.windll.LoadLibrary(DLL_NAME) XL_BUS_TYPE_CAN = 0x00000001 XL_ERR_QUEUE_IS_EMPTY = 10 XL_RECEIVE_MSG = 1 XL_TRANSMIT_MSG = 10 XL_CAN_EXT_MSG_ID = 0x80000000 XL_CAN_MSG_FLAG_ERROR_FRAME = 0x01 XL_CAN_MSG_FLAG_REMOTE_FRAME = 0x10 XL_CAN_STD = 1 XL_CAN_EXT = 2 XLuint64 = ctypes.c_ulonglong XLaccess = XLuint64 MAX_MSG_LEN = 8 # current version XL_INTERFACE_VERSION = 3 # structure for XL_RECEIVE_MSG, XL_TRANSMIT_MSG class s_xl_can_msg(ctypes.Structure): _fields_ = [('id', ctypes.c_ulong), ('flags', ctypes.c_ushort), ('dlc', ctypes.c_ushort), ('res1', XLuint64), ('data', ctypes.c_ubyte * MAX_MSG_LEN), ('res2', XLuint64)] # BASIC bus message structure class s_xl_tag_data(ctypes.Union): _fields_ = [('msg', s_xl_can_msg)] XLeventTag = ctypes.c_ubyte class XLevent(ctypes.Structure): _fields_ = [('tag', XLeventTag), ('chanIndex', ctypes.c_ubyte), ('transId', ctypes.c_ushort), ('portHandle', ctypes.c_ushort), ('flags', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte), ('timeStamp', XLuint64), ('tagData', s_xl_tag_data)] # driver status XLstatus = ctypes.c_short # porthandle XL_INVALID_PORTHANDLE = (-1) XLportHandle = ctypes.c_long def check_status(result, function, arguments): if result > 0: raise VectorError(result, xlGetErrorString(result).decode()) return result xlOpenDriver = _xlapi_dll.xlOpenDriver xlOpenDriver.argtypes = [] xlOpenDriver.restype = XLstatus xlOpenDriver.errcheck = check_status xlCloseDriver = _xlapi_dll.xlCloseDriver xlCloseDriver.argtypes = [] xlCloseDriver.restype = XLstatus xlCloseDriver.errcheck = check_status xlGetApplConfig = _xlapi_dll.xlGetApplConfig xlGetApplConfig.argtypes = [ ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.c_uint ] xlGetApplConfig.restype = XLstatus xlGetApplConfig.errcheck = check_status xlGetChannelIndex = _xlapi_dll.xlGetChannelIndex xlGetChannelIndex.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int] xlGetChannelIndex.restype = ctypes.c_int xlGetChannelMask = _xlapi_dll.xlGetChannelMask xlGetChannelMask.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int] xlGetChannelMask.restype = XLaccess xlOpenPort = _xlapi_dll.xlOpenPort xlOpenPort.argtypes = [ ctypes.POINTER(XLportHandle), ctypes.c_char_p, XLaccess, ctypes.POINTER(XLaccess), ctypes.c_uint, ctypes.c_uint, ctypes.c_uint ] xlOpenPort.restype = XLstatus xlOpenPort.errcheck = check_status xlGetSyncTime = _xlapi_dll.xlGetSyncTime xlGetSyncTime.argtypes = [XLportHandle, ctypes.POINTER(XLuint64)] xlGetSyncTime.restype = XLstatus xlGetSyncTime.errcheck = check_status xlClosePort = _xlapi_dll.xlClosePort xlClosePort.argtypes = [XLportHandle] xlClosePort.restype = XLstatus xlClosePort.errcheck = check_status xlActivateChannel = _xlapi_dll.xlActivateChannel xlActivateChannel.argtypes = [ XLportHandle, XLaccess, ctypes.c_uint, ctypes.c_uint ] xlActivateChannel.restype = XLstatus xlActivateChannel.errcheck = check_status xlDeactivateChannel = _xlapi_dll.xlDeactivateChannel xlDeactivateChannel.argtypes = [XLportHandle, XLaccess] xlDeactivateChannel.restype = XLstatus xlDeactivateChannel.errcheck = check_status xlReceive = _xlapi_dll.xlReceive xlReceive.argtypes = [ XLportHandle, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLevent) ] xlReceive.restype = XLstatus xlReceive.errcheck = check_status xlGetErrorString = _xlapi_dll.xlGetErrorString xlGetErrorString.argtypes = [XLstatus] xlGetErrorString.restype = ctypes.c_char_p xlCanSetChannelBitrate = _xlapi_dll.xlCanSetChannelBitrate xlCanSetChannelBitrate.argtypes = [XLportHandle, XLaccess, ctypes.c_ulong] xlCanSetChannelBitrate.restype = XLstatus xlCanSetChannelBitrate.errcheck = check_status xlCanTransmit = _xlapi_dll.xlCanTransmit xlCanTransmit.argtypes = [ XLportHandle, XLaccess, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLevent) ] xlCanTransmit.restype = XLstatus xlCanTransmit.errcheck = check_status xlCanFlushTransmitQueue = _xlapi_dll.xlCanFlushTransmitQueue xlCanFlushTransmitQueue.argtypes = [XLportHandle, XLaccess] xlCanFlushTransmitQueue.restype = XLstatus xlCanFlushTransmitQueue.errcheck = check_status xlCanSetChannelAcceptance = _xlapi_dll.xlCanSetChannelAcceptance xlCanSetChannelAcceptance.argtypes = [ XLportHandle, XLaccess, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_uint] xlCanSetChannelAcceptance.restype = XLstatus xlCanSetChannelAcceptance.errcheck = check_status xlCanResetAcceptance = _xlapi_dll.xlCanResetAcceptance xlCanResetAcceptance.argtypes = [XLportHandle, XLaccess, ctypes.c_uint] xlCanResetAcceptance.restype = XLstatus xlCanResetAcceptance.errcheck = check_status python-can-2.0.0/can/interfaces/__init__.py0000644000175000017500000000052513223535415021003 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ Interfaces contain low level implementations that interact with CAN hardware. """ VALID_INTERFACES = set(['kvaser', 'serial', 'pcan', 'socketcan_native', 'socketcan_ctypes', 'socketcan', 'usb2can', 'ixxat', 'nican', 'iscan', 'vector', 'virtual', 'neovi','slcan']) python-can-2.0.0/can/interfaces/interface.py0000644000175000017500000000000013223535415021170 0ustar brianbrian00000000000000python-can-2.0.0/can/interfaces/iscan.py0000644000175000017500000001252213223535415020341 0ustar brianbrian00000000000000""" Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH. """ import ctypes import time import logging from can import CanError, BusABC, Message logger = logging.getLogger(__name__) CanData = ctypes.c_ubyte * 8 class MessageExStruct(ctypes.Structure): _fields_ = [ ("message_id", ctypes.c_ulong), ("is_extended", ctypes.c_ubyte), ("remote_req", ctypes.c_ubyte), ("data_len", ctypes.c_ubyte), ("data", CanData), ] def check_status(result, function, arguments): if result > 0: raise IscanError(function, result, arguments) return result try: iscan = ctypes.cdll.LoadLibrary("iscandrv") except Exception as e: iscan = None logger.warning("Failed to load IS-CAN driver: %s", e) else: iscan.isCAN_DeviceInitEx.argtypes = [ctypes.c_ubyte, ctypes.c_ubyte] iscan.isCAN_DeviceInitEx.errcheck = check_status iscan.isCAN_DeviceInitEx.restype = ctypes.c_ubyte iscan.isCAN_ReceiveMessageEx.errcheck = check_status iscan.isCAN_ReceiveMessageEx.restype = ctypes.c_ubyte iscan.isCAN_TransmitMessageEx.errcheck = check_status iscan.isCAN_TransmitMessageEx.restype = ctypes.c_ubyte iscan.isCAN_CloseDevice.errcheck = check_status iscan.isCAN_CloseDevice.restype = ctypes.c_ubyte class IscanBus(BusABC): """isCAN interface""" BAUDRATES = { 5000: 0, 10000: 1, 20000: 2, 50000: 3, 100000: 4, 125000: 5, 250000: 6, 500000: 7, 800000: 8, 1000000: 9 } def __init__(self, channel, bitrate=500000, poll_interval=0.01, **kwargs): """ :param int channel: Device number :param int bitrate: Bitrate in bits/s :param float poll_interval: Poll interval in seconds when reading messages """ if iscan is None: raise ImportError("Could not load isCAN driver") self.channel = ctypes.c_ubyte(int(channel)) self.channel_info = "IS-CAN: %s" % channel if bitrate not in self.BAUDRATES: valid_bitrates = ", ".join(str(bitrate) for bitrate in self.BAUDRATES) raise ValueError("Invalid bitrate, choose one of " + valid_bitrates) self.poll_interval = poll_interval iscan.isCAN_DeviceInitEx(self.channel, self.BAUDRATES[bitrate]) super(IscanBus, self).__init__(channel, **kwargs) def recv(self, timeout=None): raw_msg = MessageExStruct() end_time = time.time() + timeout if timeout is not None else None while True: try: iscan.isCAN_ReceiveMessageEx(self.channel, ctypes.byref(raw_msg)) except IscanError as e: if e.error_code != 8: # An error occurred raise if end_time is not None and time.time() > end_time: # No message within timeout return None # Sleep a short time to avoid hammering time.sleep(self.poll_interval) else: # A message was received break return Message(arbitration_id=raw_msg.message_id, extended_id=bool(raw_msg.is_extended), timestamp=time.time(), # Better than nothing... is_remote_frame=bool(raw_msg.remote_req), dlc=raw_msg.data_len, data=raw_msg.data[:raw_msg.data_len], channel=self.channel.value) def send(self, msg, timeout=None): raw_msg = MessageExStruct(msg.arbitration_id, bool(msg.is_extended_id), bool(msg.is_remote_frame), msg.dlc, CanData(*msg.data)) iscan.isCAN_TransmitMessageEx(self.channel, ctypes.byref(raw_msg)) def shutdown(self): iscan.isCAN_CloseDevice(self.channel) class IscanError(CanError): ERROR_CODES = { 1: "No access to device", 2: "Device with ID not found", 3: "Driver operation failed", 4: "Invalid parameter", 5: "Operation allowed only in online state", 6: "Device timeout", 7: "Device is transmitting a message", 8: "No message received", 9: "Thread not started", 10: "Thread already started", 11: "Buffer overrun", 12: "Device not initialized", 16: "Bus error", 17: "Bus off", 18: "Error passive", 19: "Data overrun", 20: "Error warning", 30: "Send error", 31: "Transmission not acknowledged on bus", 32: "Error critical bus", 35: "Callbackthread is blocked, stopping thread failed", 40: "Need a licence number under NT4" } def __init__(self, function, error_code, arguments): super(IscanError, self).__init__() #: Status code self.error_code = error_code #: Function that failed self.function = function #: Arguments passed to function self.arguments = arguments def __str__(self): description = self.ERROR_CODES.get(self.error_code, "Error code %d" % self.error_code) return "Function %s failed: %s" % (self.function.__name__, description) python-can-2.0.0/can/interfaces/nican.py0000644000175000017500000002241313223535415020334 0ustar brianbrian00000000000000""" NI-CAN interface module. Implementation references: * http://www.ni.com/pdf/manuals/370289c.pdf * https://github.com/buendiya/NicanPython """ import ctypes import logging import sys from can import CanError, BusABC, Message logger = logging.getLogger(__name__) NC_SUCCESS = 0 NC_ERR_TIMEOUT = 1 TIMEOUT_ERROR_CODE = -1074388991 NC_DURATION_INFINITE = 0xFFFFFFFF NC_OP_START = 0x80000001 NC_OP_STOP = 0x80000002 NC_OP_RESET = 0x80000003 NC_FRMTYPE_REMOTE = 1 NC_FRMTYPE_COMM_ERR = 2 NC_ST_READ_AVAIL = 0x00000001 NC_ST_WRITE_SUCCESS = 0x00000002 NC_ST_ERROR = 0x00000010 NC_ST_WARNING = 0x00000020 NC_ATTR_BAUD_RATE = 0x80000007 NC_ATTR_START_ON_OPEN = 0x80000006 NC_ATTR_READ_Q_LEN = 0x80000013 NC_ATTR_WRITE_Q_LEN = 0x80000014 NC_ATTR_CAN_COMP_STD = 0x80010001 NC_ATTR_CAN_MASK_STD = 0x80010002 NC_ATTR_CAN_COMP_XTD = 0x80010003 NC_ATTR_CAN_MASK_XTD = 0x80010004 NC_ATTR_LOG_COMM_ERRS = 0x8001000A NC_FL_CAN_ARBID_XTD = 0x20000000 CanData = ctypes.c_ubyte * 8 class RxMessageStruct(ctypes.Structure): _pack_ = 1 _fields_ = [ ("timestamp", ctypes.c_ulonglong), ("arb_id", ctypes.c_ulong), ("frame_type", ctypes.c_ubyte), ("dlc", ctypes.c_ubyte), ("data", CanData), ] class TxMessageStruct(ctypes.Structure): _fields_ = [ ("arb_id", ctypes.c_ulong), ("is_remote", ctypes.c_ubyte), ("dlc", ctypes.c_ubyte), ("data", CanData), ] def check_status(result, function, arguments): if result > 0: logger.warning(get_error_message(result)) elif result < 0: raise NicanError(function, result, arguments) return result def get_error_message(status_code): """Convert status code to descriptive string.""" errmsg = ctypes.create_string_buffer(1024) nican.ncStatusToString(status_code, len(errmsg), errmsg) return errmsg.value.decode("ascii") if sys.platform == "win32": try: nican = ctypes.windll.LoadLibrary("nican") except Exception as e: nican = None logger.error("Failed to load NI-CAN driver: %s", e) else: nican.ncConfig.argtypes = [ ctypes.c_char_p, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_void_p] nican.ncConfig.errcheck = check_status nican.ncOpenObject.argtypes = [ctypes.c_char_p, ctypes.c_void_p] nican.ncOpenObject.errcheck = check_status nican.ncCloseObject.errcheck = check_status nican.ncAction.argtypes = [ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong] nican.ncAction.errcheck = check_status nican.ncRead.errcheck = check_status nican.ncWrite.errcheck = check_status nican.ncWaitForState.argtypes = [ ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_void_p] nican.ncWaitForState.errcheck = check_status nican.ncStatusToString.argtypes = [ ctypes.c_int, ctypes.c_uint, ctypes.c_char_p] else: nican = None logger.warning("NI-CAN interface is only available on Windows systems") class NicanBus(BusABC): """ The CAN Bus implemented for the NI-CAN interface. """ def __init__(self, channel, can_filters=None, bitrate=None, log_errors=True, **kwargs): """ :param str channel: Name of the object to open (e.g. 'CAN0') :param int bitrate: Bitrate in bits/s :param list can_filters: A list of dictionaries each containing a "can_id" and a "can_mask". >>> [{"can_id": 0x11, "can_mask": 0x21}] :param bool log_errors: If True, communication errors will appear as CAN messages with ``is_error_frame`` set to True and ``arbitration_id`` will identify the error (default True) :raises can.interfaces.nican.NicanError: If starting communication fails """ if nican is None: raise ImportError("The NI-CAN driver could not be loaded. " "Check that you are using 32-bit Python on Windows.") self.channel_info = "NI-CAN: " + channel if not isinstance(channel, bytes): channel = channel.encode() config = [ (NC_ATTR_START_ON_OPEN, True), (NC_ATTR_LOG_COMM_ERRS, log_errors) ] if not can_filters: logger.info("Filtering has been disabled") config.extend([ (NC_ATTR_CAN_COMP_STD, 0), (NC_ATTR_CAN_MASK_STD, 0), (NC_ATTR_CAN_COMP_XTD, 0), (NC_ATTR_CAN_MASK_XTD, 0) ]) else: for can_filter in can_filters: can_id = can_filter["can_id"] can_mask = can_filter["can_mask"] logger.info("Filtering on ID 0x%X, mask 0x%X", can_id, can_mask) if can_filter.get("extended"): config.extend([ (NC_ATTR_CAN_COMP_XTD, can_id | NC_FL_CAN_ARBID_XTD), (NC_ATTR_CAN_MASK_XTD, can_mask) ]) else: config.extend([ (NC_ATTR_CAN_COMP_STD, can_id), (NC_ATTR_CAN_MASK_STD, can_mask), ]) if bitrate: config.append((NC_ATTR_BAUD_RATE, bitrate)) AttrList = ctypes.c_ulong * len(config) attr_id_list = AttrList(*(row[0] for row in config)) attr_value_list = AttrList(*(row[1] for row in config)) nican.ncConfig(channel, len(config), ctypes.byref(attr_id_list), ctypes.byref(attr_value_list)) self.handle = ctypes.c_ulong() nican.ncOpenObject(channel, ctypes.byref(self.handle)) def recv(self, timeout=None): """ Read a message from NI-CAN. :param float timeout: Max time to wait in seconds or None if infinite :returns: The CAN message or None if timeout :rtype: can.Message :raises can.interfaces.nican.NicanError: If reception fails """ if timeout is None: timeout = NC_DURATION_INFINITE else: timeout = int(timeout * 1000) state = ctypes.c_ulong() try: nican.ncWaitForState( self.handle, NC_ST_READ_AVAIL, timeout, ctypes.byref(state)) except NicanError as e: if e.error_code == TIMEOUT_ERROR_CODE: return None else: raise raw_msg = RxMessageStruct() nican.ncRead(self.handle, ctypes.sizeof(raw_msg), ctypes.byref(raw_msg)) # http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux timestamp = raw_msg.timestamp / 10000000.0 - 11644473600 is_remote_frame = raw_msg.frame_type == NC_FRMTYPE_REMOTE is_error_frame = raw_msg.frame_type == NC_FRMTYPE_COMM_ERR is_extended = bool(raw_msg.arb_id & NC_FL_CAN_ARBID_XTD) arb_id = raw_msg.arb_id if not is_error_frame: arb_id &= 0x1FFFFFFF dlc = raw_msg.dlc msg = Message(timestamp=timestamp, is_remote_frame=is_remote_frame, is_error_frame=is_error_frame, extended_id=is_extended, arbitration_id=arb_id, dlc=dlc, data=raw_msg.data[:dlc]) return msg def send(self, msg, timeout=None): """ Send a message to NI-CAN. :param can.Message msg: Message to send :raises can.interfaces.nican.NicanError: If writing to transmit buffer fails. It does not wait for message to be ACKed currently. """ arb_id = msg.arbitration_id if msg.id_type: arb_id |= NC_FL_CAN_ARBID_XTD raw_msg = TxMessageStruct(arb_id, bool(msg.is_remote_frame), msg.dlc, CanData(*msg.data)) nican.ncWrite( self.handle, ctypes.sizeof(raw_msg), ctypes.byref(raw_msg)) # ncWaitForState can not be called here if the recv() method is called # from a different thread, which is a very common use case. # Maybe it is possible to use ncCreateNotification instead but seems a # bit overkill at the moment. #state = ctypes.c_ulong() #nican.ncWaitForState( # self.handle, NC_ST_WRITE_SUCCESS, int(timeout * 1000), ctypes.byref(state)) def flush_tx_buffer(self): """ Resets the CAN chip which includes clearing receive and transmit queues. """ nican.ncAction(self.handle, NC_OP_RESET, 0) def shutdown(self): """Close object.""" nican.ncCloseObject(self.handle) class NicanError(CanError): """Error from NI-CAN driver.""" def __init__(self, function, error_code, arguments): super(NicanError, self).__init__() #: Status code self.error_code = error_code #: Function that failed self.function = function #: Arguments passed to function self.arguments = arguments def __str__(self): return "Function %s failed:\n%s" % ( self.function.__name__, get_error_message(self.error_code)) python-can-2.0.0/can/interfaces/slcan.py0000755000175000017500000001124013223535415020343 0ustar brianbrian00000000000000""" Interface for slcan compatible interfaces (win32/linux). (Linux could use slcand/socketcan also). """ from __future__ import absolute_import import serial import io import time import logging from can import CanError, BusABC, Message logger = logging.getLogger(__name__) class slcanBus(BusABC): """slcan interface""" def write(self, str): if not str.endswith("\r"): str += "\r" self.serialPort.write(str.decode()) self.serialPort.flush() def open(self): self.write("O") def close(self): self.write("C") def __init__(self, channel, ttyBaudrate=115200, timeout=1, bitrate=None , **kwargs): """ :param string channel: port of underlying serial or usb device (e.g. /dev/ttyUSB0, COM8, ...) :param int ttyBaudrate: baudrate of underlying serial or usb device :param int bitrate: Bitrate in bits/s :param float poll_interval: Poll interval in seconds when reading messages :param float timeout timeout in seconds when reading message """ if channel == '': raise TypeError("Must specify a serial port.") if '@' in channel: (channel, ttyBaudrate) = channel.split('@') self.serialPortOrig = serial.Serial(channel, baudrate=ttyBaudrate, timeout=timeout) self.serialPort = io.TextIOWrapper(io.BufferedRWPair(self.serialPortOrig, self.serialPortOrig, 1), newline='\r', line_buffering=True) time.sleep(2) if bitrate is not None: self.close() if bitrate == 10000: self.write('S0') elif bitrate == 20000: self.write('S1') elif bitrate == 50000: self.write('S2') elif bitrate == 100000: self.write('S3') elif bitrate == 125000: self.write('S4') elif bitrate == 250000: self.write('S5') elif bitrate == 500000: self.write('S6') elif bitrate == 750000: self.write('S7') elif bitrate == 1000000: self.write('S8') elif bitrate == 83300: self.write('S9') else: raise ValueError("Invalid bitrate, choose one of 10000 20000 50000 100000 125000 250000 500000 750000 1000000 83300") self.open() super(slcanBus, self).__init__(channel, **kwargs) def recv(self, timeout=None): if timeout is not None: self.serialPortOrig.timeout = timeout canId = None remote = False frame = [] readStr = self.serialPort.readline() if readStr is None or len(readStr) == 0: return None else: if readStr[0] == 'T': # entended frame canId = int(readStr[1:9], 16) dlc = int(readStr[9]) extended = True for i in range(0, dlc): frame.append(int(readStr[10 + i * 2:12 + i * 2], 16)) elif readStr[0] == 't': # normal frame canId = int(readStr[1:4], 16) dlc = int(readStr[4]) for i in range(0, dlc): frame.append(int(readStr[5 + i * 2:7 + i * 2], 16)) extended = False elif readStr[0] == 'r': # remote frame canId = int(readStr[1:4], 16) remote = True elif readStr[0] == 'R': # remote extended frame canId = int(readStr[1:9], 16) extended = True remote = True if canId is not None: return Message(arbitration_id=canId, extended_id=extended, timestamp=time.time(), # Better than nothing... is_remote_frame=remote, dlc=dlc, data=frame) else: return None def send(self, msg, timeout=None): if msg.is_remote_frame: if msg.is_extended_id: sendStr = "R%08X0" % (msg.arbitration_id) else: sendStr = "r%03X0" % (msg.arbitration_id) else: if msg.is_extended_id: sendStr = "T%08X%d" % (msg.arbitration_id, msg.dlc) else: sendStr = "t%03X%d" % (msg.arbitration_id, msg.dlc) for i in range(0, msg.dlc): sendStr += "%02X" % msg.data[i] self.write(sendStr) def shutdown(self): self.close()python-can-2.0.0/can/interfaces/virtual.py0000644000175000017500000000316413223535415020734 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- """ This module implements an OS and hardware independent virtual CAN interface for testing purposes. Any VirtualBus instances connecting to the same channel will get the same messages. """ import logging import time try: import queue except ImportError: import Queue as queue from can.bus import BusABC logger = logging.getLogger(__name__) #logger.setLevel(logging.DEBUG) # Channels are lists of queues, one for each connection channels = {} class VirtualBus(BusABC): """Virtual CAN bus using an internal message queue for testing.""" def __init__(self, channel=None, receive_own_messages=False, **config): self.channel_info = 'Virtual bus channel %s' % channel self.receive_own_messages = receive_own_messages # Create a new channel if one does not exist if channel not in channels: channels[channel] = [] self.queue = queue.Queue() self.channel = channels[channel] self.channel.append(self.queue) def recv(self, timeout=None): try: msg = self.queue.get(block=True, timeout=timeout) except queue.Empty: return None logger.log(9, 'Received message:\n%s', msg) return msg def send(self, msg, timeout=None): msg.timestamp = time.time() # Add message to all listening on this channel for bus_queue in self.channel: if bus_queue is not self.queue or self.receive_own_messages: bus_queue.put(msg) logger.log(9, 'Transmitted message:\n%s', msg) def shutdown(self): self.channel.remove(self.queue) python-can-2.0.0/can/io/0000755000175000017500000000000013223535610015151 5ustar brianbrian00000000000000python-can-2.0.0/can/io/__init__.py0000644000175000017500000000061213223535415017264 0ustar brianbrian00000000000000""" Read and Write CAN bus messages using a range of Readers and Writers based off the file extension. """ from .logger import Logger from .player import LogReader from .log import CanutilsLogReader, CanutilsLogWriter from .asc import ASCWriter, ASCReader from .blf import BLFReader, BLFWriter from .csv import CSVWriter from .sqlite import SqlReader, SqliteWriter from .stdout import Printer python-can-2.0.0/can/io/asc.py0000644000175000017500000001061713223535415016301 0ustar brianbrian00000000000000from can.listener import Listener from can.message import Message from datetime import datetime import time CAN_MSG_EXT = 0x80000000 CAN_ID_MASK = 0x1FFFFFFF class ASCReader(object): """ Iterator of CAN messages from a ASC Logging File. """ def __init__(self, filename): self.fp = open(filename, "r") def __iter__(self): def extractCanId(strCanId): if strCanId[-1:].lower() == "x": isExtended = True can_id = int(strCanId[0:-1], 16) else: isExtended = False can_id = int(strCanId, 16) return (can_id, isExtended) for line in self.fp: temp = line.strip() if len(temp) == 0 or not temp[0].isdigit(): continue (time, channel, dummy) = temp.split(None,2) # , frameType, dlc, frameData time = float(time) if dummy.strip()[0:10] == "ErrorFrame": time = float(time) msg = Message(timestamp=time, is_error_frame=True) yield msg continue if not channel.isdigit() or dummy.strip()[0:10] == "Statistic:": continue if dummy[-1:].lower() == "r": (canId, _) = dummy.split(None, 1) msg = Message(timestamp=time, arbitration_id=extractCanId(canId)[0] & CAN_ID_MASK, extended_id=extractCanId(canId)[1], is_remote_frame=True) yield msg else: (canId, direction,_,dlc,data) = dummy.split(None,4) dlc = int(dlc) frame = bytearray() data = data.split() for byte in data[0:dlc]: frame.append(int(byte,16)) msg = Message(timestamp=time, arbitration_id=extractCanId(canId)[0] & CAN_ID_MASK, extended_id=extractCanId(canId)[1], is_remote_frame=False, dlc=dlc, data=frame) yield msg class ASCWriter(Listener): """Logs CAN data to an ASCII log file (.asc)""" LOG_STRING = "{time: 9.4f} {channel} {id:<15} Rx {dtype} {data}\n" EVENT_STRING = "{time: 9.4f} {message}\n" def __init__(self, filename, channel=1): now = datetime.now().strftime("%a %b %m %I:%M:%S %p %Y") self.channel = channel self.started = time.time() self.log_file = open(filename, "w") self.log_file.write("date %s\n" % now) self.log_file.write("base hex timestamps absolute\n") self.log_file.write("internal events logged\n") self.log_file.write("Begin Triggerblock %s\n" % now) self.log_event("Start of measurement") def stop(self): """Stops logging and closes the file.""" if not self.log_file.closed: self.log_file.write("End TriggerBlock\n") self.log_file.close() def log_event(self, message, timestamp=None): """Add an arbitrary message to the log file.""" timestamp = (timestamp or time.time()) if timestamp >= self.started: timestamp -= self.started line = self.EVENT_STRING.format(time=timestamp, message=message) if not self.log_file.closed: self.log_file.write(line) def on_message_received(self, msg): if msg.is_error_frame: self.log_event("{} ErrorFrame".format(self.channel), msg.timestamp) return if msg.is_remote_frame: dtype = "r" data = [] else: dtype = "d {}".format(msg.dlc) data = ["{:02X}".format(byte) for byte in msg.data] arb_id = "{:X}".format(msg.arbitration_id) if msg.id_type: arb_id = arb_id + "x" timestamp = msg.timestamp if timestamp >= self.started: timestamp -= self.started channel = msg.channel if isinstance(msg.channel, int) else self.channel line = self.LOG_STRING.format(time=timestamp, channel=channel, id=arb_id, dtype=dtype, data=" ".join(data)) if not self.log_file.closed: self.log_file.write(line) python-can-2.0.0/can/io/blf.py0000644000175000017500000002505713223535415016302 0ustar brianbrian00000000000000""" Implements support for BLF (Binary Logging Format) which is a proprietary CAN log format from Vector Informatik GmbH. No official specification of the binary logging format is available. This implementation is based on Toby Lorenz' C++ library "Vector BLF" which is licenced under GPLv3. https://bitbucket.org/tobylorenz/vector_blf. The file starts with a header. The rest is one or more "log containers" which consists of a header and some zlib compressed data, usually up to 128 kB of uncompressed data each. This data contains the actual CAN messages and other objects types. """ import struct import zlib import datetime import time from can.message import Message from can.listener import Listener # 0 = unknown, 2 = CANoe APPLICATION_ID = 5 # Header must be 144 bytes in total # signature ("LOGG"), header size, # application ID, application major, application minor, application build, # bin log major, bin log minor, bin log build, bin log patch, # file size, uncompressed size, count of objects, count of objects read, # time start (SYSTEMTIME), time stop (SYSTEMTIME) FILE_HEADER_STRUCT = struct.Struct("<4sLBBBBBBBBQQLL8H8H72x") # signature ("LOBJ"), header size, header version (1), object size, object type, # flags, object version, size uncompressed or timestamp OBJ_HEADER_STRUCT = struct.Struct("<4sHHLLL2xHQ") # channel, flags, dlc, arbitration id, data CAN_MSG_STRUCT = struct.Struct(" len(data): # Object continues in next log container break obj_type = header[4] timestamp = header[7] / 1000000000.0 + self.start_timestamp if obj_type == CAN_MESSAGE: assert obj_size == OBJ_HEADER_STRUCT.size + CAN_MSG_STRUCT.size (channel, flags, dlc, can_id, can_data) = CAN_MSG_STRUCT.unpack_from( data, pos + OBJ_HEADER_STRUCT.size) msg = Message(timestamp=timestamp, arbitration_id=can_id & 0x1FFFFFFF, extended_id=bool(can_id & CAN_MSG_EXT), is_remote_frame=bool(flags & REMOTE_FLAG), dlc=dlc, data=can_data[:dlc], channel=channel) yield msg elif obj_type == CAN_ERROR: assert obj_size == OBJ_HEADER_STRUCT.size + CAN_ERROR_STRUCT.size channel, length = CAN_ERROR_STRUCT.unpack_from( data, pos + OBJ_HEADER_STRUCT.size) msg = Message(timestamp=timestamp, is_error_frame=True, channel=channel) yield msg pos += obj_size # Add padding bytes pos += obj_size % 4 # Save remaing data that could not be processed tail = data[pos:] self.fp.close() class BLFWriter(Listener): """ Logs CAN data to a Binary Logging File compatible with Vector's tools. """ #: Max log container size of uncompressed data MAX_CACHE_SIZE = 0x20000 #: ZLIB compression level COMPRESSION_LEVEL = 9 def __init__(self, filename, channel=1): self.fp = open(filename, "wb") self.channel = channel # Header will be written after log is done self.fp.write(b"\x00" * FILE_HEADER_STRUCT.size) self.cache = [] self.cache_size = 0 self.count_of_objects = 0 self.uncompressed_size = FILE_HEADER_STRUCT.size self.start_timestamp = None self.stop_timestamp = None def on_message_received(self, msg): channel = msg.channel if isinstance(msg.channel, int) else self.channel if not msg.is_error_frame: flags = REMOTE_FLAG if msg.is_remote_frame else 0 arb_id = msg.arbitration_id if msg.id_type: arb_id |= CAN_MSG_EXT data = CAN_MSG_STRUCT.pack(channel, flags, msg.dlc, arb_id, bytes(msg.data)) self._add_object(CAN_MESSAGE, data, msg.timestamp) else: data = CAN_ERROR_STRUCT.pack(channel, 0) self._add_object(CAN_ERROR, data, msg.timestamp) def log_event(self, text, timestamp=None): """Add an arbitrary message to the log file as a global marker. :param str text: The group name of the marker. :param float timestamp: Absolute timestamp in Unix timestamp format. If not given, the marker will be placed along the last message. """ try: # Only works on Windows text = text.encode("mbcs") except LookupError: text = text.encode("ascii") comment = b"Added by python-can" marker = b"python-can" data = GLOBAL_MARKER_STRUCT.pack( 0, 0xFFFFFF, 0xFF3300, 0, len(text), len(marker), len(comment)) self._add_object(GLOBAL_MARKER, data + text + marker + comment, timestamp) def _add_object(self, obj_type, data, timestamp=None): if timestamp is None: timestamp = self.stop_timestamp or time.time() if self.start_timestamp is None: self.start_timestamp = timestamp self.stop_timestamp = timestamp timestamp = int((timestamp - self.start_timestamp) * 1000000000) obj_size = OBJ_HEADER_STRUCT.size + len(data) header = OBJ_HEADER_STRUCT.pack( b"LOBJ", OBJ_HEADER_STRUCT.size, 1, obj_size, obj_type, 2, 0, max(timestamp, 0)) self.cache.append(header) self.cache.append(data) padding_size = len(data) % 4 if padding_size: self.cache.append(b"\x00" * padding_size) self.cache_size += obj_size + padding_size self.count_of_objects += 1 if self.cache_size >= self.MAX_CACHE_SIZE: self._flush() def _flush(self): """Compresses and writes data in the cache to file.""" if self.fp.closed: return cache = b"".join(self.cache) if not cache: # Nothing to write return uncompressed_data = cache[:self.MAX_CACHE_SIZE] # Save data that comes after max size to next round tail = cache[self.MAX_CACHE_SIZE:] self.cache = [tail] self.cache_size = len(tail) compressed_data = zlib.compress(uncompressed_data, self.COMPRESSION_LEVEL) obj_size = OBJ_HEADER_STRUCT.size + len(compressed_data) header = OBJ_HEADER_STRUCT.pack( b"LOBJ", 16, 1, obj_size, LOG_CONTAINER, 2, 0, len(uncompressed_data)) self.fp.write(header) self.fp.write(compressed_data) # Write padding bytes self.fp.write(b"\x00" * (obj_size % 4)) self.uncompressed_size += len(uncompressed_data) + OBJ_HEADER_STRUCT.size def stop(self): """Stops logging and closes the file.""" if self.fp.closed: return self._flush() filesize = self.fp.tell() self.fp.close() # Write header in the beginning of the file header = [b"LOGG", FILE_HEADER_STRUCT.size, APPLICATION_ID, 0, 0, 0, 2, 6, 8, 1] # The meaning of "count of objects read" is unknown header.extend([filesize, self.uncompressed_size, self.count_of_objects, 0]) header.extend(timestamp_to_systemtime(self.start_timestamp)) header.extend(timestamp_to_systemtime(self.stop_timestamp)) with open(self.fp.name, "r+b") as f: f.write(FILE_HEADER_STRUCT.pack(*header)) python-can-2.0.0/can/io/csv.py0000644000175000017500000000161213223535415016321 0ustar brianbrian00000000000000from can.listener import Listener import base64 class CSVWriter(Listener): """Writes a comma separated text file of timestamp, arbitration id, flags, dlc, data for each messages received. """ def __init__(self, filename): self.csv_file = open(filename, 'wt') # Write a header row self.csv_file.write("timestamp, arbitration id, extended, remote, error, dlc, data\n") def on_message_received(self, msg): row = ','.join([ str(msg.timestamp), hex(msg.arbitration_id), '1' if msg.id_type else '0', '1' if msg.is_remote_frame else '0', '1' if msg.is_error_frame else '0', str(msg.dlc), base64.b64encode(msg.data).decode('utf8') ]) self.csv_file.write(row + '\n') def stop(self): self.csv_file.flush() self.csv_file.close() python-can-2.0.0/can/io/log.py0000644000175000017500000000634413223535415016316 0ustar brianbrian00000000000000from can.listener import Listener import datetime import time from can.message import Message CAN_MSG_EXT = 0x80000000 CAN_ERR_FLAG = 0x20000000 CAN_ERR_BUSERROR = 0x00000080 CAN_ERR_DLC = 8 class CanutilsLogReader(object): """ Iterator of CAN messages from a .log Logging File (candump -L). .log-format looks like this: (0.0) vcan0 001#8d00100100820100 """ def __init__(self, filename): self.fp = open(filename, "r") def __iter__(self): for line in self.fp: temp = line.strip() if len(temp) > 0: (timestamp, bus, frame) = temp.split() timestamp = float(timestamp[1:-1]) (canId, data) = frame.split("#") if len(canId) > 3: isExtended = True else: isExtended = False canId = int(canId, 16) if len(data) > 0 and data[0].lower() == "r": isRemoteFrame = True if len(data) > 1: dlc = int(data[1:]) else: dlc = 0 else: isRemoteFrame = False dlc = int(len(data) / 2) dataBin = bytearray() for i in range(0, 2 * dlc, 2): dataBin.append(int(data[i:(i + 2)], 16)) if canId & CAN_ERR_FLAG and canId & CAN_ERR_BUSERROR: msg = Message(timestamp=timestamp, is_error_frame=True) else: msg = Message(timestamp=timestamp, arbitration_id=canId & 0x1FFFFFFF, extended_id=isExtended, is_remote_frame=isRemoteFrame, dlc=dlc, data=dataBin) yield msg class CanutilsLogWriter(Listener): """Logs CAN data to an ASCII log file (.log) compatible to candump -L """ def __init__(self, filename, channel="vcan0"): self.channel = channel self.started = time.time() self.log_file = open(filename, "w") def stop(self): """Stops logging and closes the file.""" if self.log_file is not None: self.log_file.close() self.log_file = None def on_message_received(self, msg): if self.log_file is None: return if msg.is_error_frame: self.log_file.write("(%f) vcan0 %08X#0000000000000000\n" % (msg.timestamp, CAN_ERR_FLAG | CAN_ERR_BUSERROR, )) return timestamp = msg.timestamp if timestamp >= self.started: timestamp -= self.started if msg.is_remote_frame: data = [] if msg.is_extended_id: self.log_file.write("(%f) vcan0 %08X#R\n" % (msg.timestamp, msg.arbitration_id )) else: self.log_file.write("(%f) vcan0 %03X#R\n" % (msg.timestamp, msg.arbitration_id )) else: data = ["{:02X}".format(byte) for byte in msg.data] if msg.is_extended_id: self.log_file.write("(%f) vcan0 %08X#%s\n" % (msg.timestamp, msg.arbitration_id, "".join(data))) else: self.log_file.write("(%f) vcan0 %03X#%s\n" % (msg.timestamp, msg.arbitration_id, "".join(data))) python-can-2.0.0/can/io/logger.py0000755000175000017500000000230013223535415017003 0ustar brianbrian00000000000000from .asc import ASCWriter from .blf import BLFWriter from .csv import CSVWriter from .sqlite import SqliteWriter from .stdout import Printer from .log import CanutilsLogWriter class Logger(object): """ Logs CAN messages to a file. The format is determined from the file format which can be one of: * .asc: :class:`can.ASCWriter` * .blf :class:`can.BLFWriter` * .csv: :class:`can.CSVWriter` * .db: :class:`can.SqliteWriter` * other: :class:`can.Printer` Note this class itself is just a dispatcher, an object that inherits from Listener will be created when instantiating this class. """ @classmethod def __new__(cls, other, filename): if not filename: return Printer() elif filename.endswith(".asc"): return ASCWriter(filename) elif filename.endswith(".log"): return CanutilsLogWriter(filename) elif filename.endswith(".blf"): return BLFWriter(filename) elif filename.endswith(".csv"): return CSVWriter(filename) elif filename.endswith(".db"): return SqliteWriter(filename) else: return Printer(filename) python-can-2.0.0/can/io/player.py0000755000175000017500000000466513223535415017040 0ustar brianbrian00000000000000from __future__ import print_function import time import logging from .asc import ASCReader from .log import CanutilsLogReader from .blf import BLFReader from .sqlite import SqlReader log = logging.getLogger('can.io.player') class LogReader(object): """ Replay logged CAN messages from a file. The format is determined from the file format which can be one of: * .asc * .blf * .csv * .db Exposes a simple iterator interface, to use simply: >>> for m in LogReader(my_file): ... print(m) Note there are no time delays, if you want to reproduce the measured delays between messages look at the :class:`can.util.MessageSync` class. """ @classmethod def __new__(cls, other, filename): if filename.endswith(".blf"): return BLFReader(filename) if filename.endswith(".db"): return SqlReader(filename) if filename.endswith(".asc"): return ASCReader(filename) if filename.endswith(".log"): return CanutilsLogReader(filename) raise NotImplementedError("No read support for this log format") class MessageSync(object): def __init__(self, messages, timestamps=True, gap=0.0001, skip=60): """ :param messages: An iterable of :class:`can.Message` instances. :param timestamps: Use the messages' timestamps. :param gap: Minimum time between sent messages :param skip: Skip periods of inactivity greater than this. """ self.raw_messages = messages self.timestamps = timestamps self.gap = gap self.skip = skip def __iter__(self): log.debug("Iterating over messages at real speed") playback_start_time = time.time() recorded_start_time = None for m in self.raw_messages: if recorded_start_time is None: recorded_start_time = m.timestamp if self.timestamps: # Work out the correct wait time now = time.time() current_offset = now - playback_start_time recorded_offset_from_start = m.timestamp - recorded_start_time remaining_gap = recorded_offset_from_start - current_offset sleep_period = max(self.gap, min(self.skip, remaining_gap)) else: sleep_period = self.gap time.sleep(sleep_period) yield m python-can-2.0.0/can/io/sqlite.py0000644000175000017500000001056413223535415017035 0ustar brianbrian00000000000000from can.listener import BufferedReader from can.message import Message import sys import time import threading import sqlite3 import logging log = logging.getLogger('can.io.sql') if sys.version_info > (3,): buffer = memoryview class SqlReader: def __init__(self, filename): log.debug("Starting sqlreader with {}".format(filename)) conn = sqlite3.connect(filename) self.c = conn.cursor() @staticmethod def create_frame_from_db_tuple(frame_data): ts, id, is_extended, is_remote, is_error, dlc, data = frame_data return Message( ts, is_remote, is_extended, is_error, id, dlc, data ) def __iter__(self): log.debug("Iterating through messages from sql db") for frame_data in self.c.execute("SELECT * FROM messages"): yield SqlReader.create_frame_from_db_tuple(frame_data) class SqliteWriter(BufferedReader): """Logs received CAN data to a simple SQL database. The sqlite database may already exist, otherwise it will be created when the first message arrives. Messages are internally buffered and written to the SQL file in a background thread. .. note:: When the listener's :meth:`~SqliteWriter.stop` method is called the thread writing to the sql file will continue to receive and internally buffer messages if they continue to arrive before the :attr:`~SqliteWriter.GET_MESSAGE_TIMEOUT`. If the :attr:`~SqliteWriter.GET_MESSAGE_TIMEOUT` expires before a message is received, the internal buffer is written out to the sql file. However if the bus is still saturated with messages, the Listener will continue receiving until the :attr:`~SqliteWriter.MAX_TIME_BETWEEN_WRITES` timeout is reached. """ insert_msg_template = ''' INSERT INTO messages VALUES (?, ?, ?, ?, ?, ?, ?) ''' GET_MESSAGE_TIMEOUT = 0.25 """Number of seconds to wait for messages from internal queue""" MAX_TIME_BETWEEN_WRITES = 5 """Maximum number of seconds to wait between writes to the database""" def __init__(self, filename): super(SqliteWriter, self).__init__() self.db_fn = filename self.stop_running_event = threading.Event() self.writer_thread = threading.Thread(target=self.db_writer_thread) self.writer_thread.start() def _create_db(self): # Note you can't share sqlite3 connections between threads # hence we setup the db here. log.info("Creating sqlite db") self.conn = sqlite3.connect(self.db_fn) c = self.conn.cursor() # create table structure c.execute(''' CREATE TABLE IF NOT EXISTS messages ( ts REAL, arbitration_id INTEGER, extended INTEGER, remote INTEGER, error INTEGER, dlc INTEGER, data BLOB ) ''') self.conn.commit() self.db_setup = True def db_writer_thread(self): num_frames = 0 last_write = time.time() self._create_db() while not self.stop_running_event.is_set(): messages = [] m = self.get_message(self.GET_MESSAGE_TIMEOUT) while m is not None: log.debug("sqlitewriter buffering message") messages.append(( m.timestamp, m.arbitration_id, m.id_type, m.is_remote_frame, m.is_error_frame, m.dlc, buffer(m.data) )) if time.time() - last_write > self.MAX_TIME_BETWEEN_WRITES: log.debug("Max timeout between writes reached") break m = self.get_message(self.GET_MESSAGE_TIMEOUT) if len(messages) > 0: with self.conn: log.debug("Writing %s frames to db", len(messages)) self.conn.executemany(SqliteWriter.insert_msg_template, messages) num_frames += len(messages) last_write = time.time() self.conn.close() log.info("Stopped sqlite writer after writing %s messages", num_frames) def stop(self): self.stop_running_event.set() log.debug("Stopping sqlite writer") self.writer_thread.join() python-can-2.0.0/can/io/stdout.py0000644000175000017500000000152713223535415017055 0ustar brianbrian00000000000000from can.listener import Listener import logging log = logging.getLogger('can.io.stdout') class Printer(Listener): """ The Printer class is a subclass of :class:`~can.Listener` which simply prints any messages it receives to the terminal. :param output_file: An optional file to "print" to. """ def __init__(self, output_file=None): if output_file is not None: log.info("Creating log file '{}' ".format(output_file)) output_file = open(output_file, 'wt') self.output_file = output_file def on_message_received(self, msg): if self.output_file is not None: self.output_file.write(str(msg) + "\n") else: print(msg) def stop(self): if self.output_file: self.output_file.write("\n") self.output_file.close() python-can-2.0.0/can/CAN.py0000644000175000017500000000103113223535415015513 0ustar brianbrian00000000000000""" This module was once the core of python-can, containing implementations of all the major classes in the library, now however all functionality has been refactored out. This api is left intact for version 2.0 to aide with migration. """ from __future__ import absolute_import from can.message import Message from can.listener import Listener, BufferedReader, RedirectReader from can.util import set_logging_level from can.io import * import logging log = logging.getLogger('can') log.info("Loading python-can via the old CAN api") python-can-2.0.0/can/__init__.py0000644000175000017500000000174213223535415016662 0ustar brianbrian00000000000000""" can is an object-orient Controller Area Network interface module. """ from __future__ import absolute_import import logging __version__ = "2.0.0" log = logging.getLogger('can') rc = dict() class CanError(IOError): pass from can.listener import Listener, BufferedReader, RedirectReader from can.io import Logger, Printer, LogReader from can.io import ASCWriter, ASCReader from can.io import BLFReader, BLFWriter from can.io import CanutilsLogReader, CanutilsLogWriter from can.io import CSVWriter from can.io import SqliteWriter, SqlReader from can.util import set_logging_level from can.message import Message from can.bus import BusABC from can.notifier import Notifier from can.interfaces import VALID_INTERFACES from . import interface from .interface import Bus from can.broadcastmanager import send_periodic, \ CyclicSendTaskABC, \ LimitedDurationCyclicSendTaskABC, \ ModifiableCyclicTaskABC, \ MultiRateCyclicSendTaskABC, \ RestartableCyclicTaskABC python-can-2.0.0/can/broadcastmanager.py0000644000175000017500000001023713223535415020417 0ustar brianbrian00000000000000#!/usr/bin/env python3 """ Exposes several methods for transmitting cyclic messages. 20/09/13 """ import can import abc import logging import sched import threading import time log = logging.getLogger('can.bcm') log.debug("Loading base broadcast manager functionality") class CyclicTask(object): """ Abstract Base for all Cyclic Tasks """ @abc.abstractmethod def stop(self): """Cancel this periodic task. """ class CyclicSendTaskABC(CyclicTask): """ Message send task with defined period """ def __init__(self, message, period): """ :param message: The :class:`can.Message` to be sent periodically. :param float period: The rate in seconds at which to send the message. """ self.message = message self.can_id = message.arbitration_id self.period = period super(CyclicSendTaskABC, self).__init__() class LimitedDurationCyclicSendTaskABC(CyclicSendTaskABC): def __init__(self, message, period, duration): """Message send task with a defined duration and period. :param message: The :class:`can.Message` to be sent periodically. :param float period: The rate in seconds at which to send the message. :param float duration: The duration to keep sending this message at given rate. """ super(LimitedDurationCyclicSendTaskABC, self).__init__(message, period) self.duration = duration class RestartableCyclicTaskABC(CyclicSendTaskABC): """Adds support for restarting a stopped cyclic task""" @abc.abstractmethod def start(self): """Restart a stopped periodic task. """ class ModifiableCyclicTaskABC(CyclicSendTaskABC): """Adds support for modifying a periodic message""" def modify_data(self, message): """Update the contents of this periodically sent message without altering the timing. :param message: The :class:`~can.Message` with new :attr:`Message.data`. """ self.message = message class MultiRateCyclicSendTaskABC(CyclicSendTaskABC): """Exposes more of the full power of the TX_SETUP opcode. Transmits a message `count` times at `initial_period` then continues to transmit message at `subsequent_period`. """ def __init__(self, channel, message, count, initial_period, subsequent_period): super(MultiRateCyclicSendTaskABC, self).__init__(channel, message, subsequent_period) class ThreadBasedCyclicSendTask(ModifiableCyclicTaskABC, LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC): """Fallback cyclic send task using thread.""" def __init__(self, bus, lock, message, period, duration=None): super(ThreadBasedCyclicSendTask, self).__init__(message, period, duration) self.bus = bus self.lock = lock self.stopped = True self.thread = None self.end_time = time.time() + duration if duration else None self.start() def stop(self): self.stopped = True def start(self): self.stopped = False if self.thread is None or not self.thread.is_alive(): name = "Cyclic send task for 0x%X" % (self.message.arbitration_id) self.thread = threading.Thread(target=self._run, name=name) self.thread.daemon = True self.thread.start() def _run(self): while not self.stopped: # Prevent calling bus.send from multiple threads with self.lock: started = time.time() try: self.bus.send(self.message) except Exception as exc: log.exception(exc) break if self.end_time is not None and time.time() >= self.end_time: break # Compensate for the time it takes to send the message delay = self.period - (time.time() - started) time.sleep(max(0.0, delay)) def send_periodic(bus, message, period): """ Send a message every `period` seconds on the given channel. """ return can.interface.CyclicSendTask(bus, message, period) python-can-2.0.0/can/bus.py0000644000175000017500000001063113223535415015711 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- from __future__ import print_function, absolute_import import abc import logging import threading from can.broadcastmanager import ThreadBasedCyclicSendTask logger = logging.getLogger(__name__) class BusABC(object): """CAN Bus Abstract Base Class Concrete implementations must implement the following methods: * send * recv As well as setting the `channel_info` attribute to a string describing the interface. """ #: a string describing the underlying bus channel channel_info = 'unknown' @abc.abstractmethod def __init__(self, channel=None, can_filters=None, **config): """ :param channel: The can interface identifier. Expected type is backend dependent. :param list can_filters: A list of dictionaries each containing a "can_id", a "can_mask", and an "extended" key. >>> [{"can_id": 0x11, "can_mask": 0x21, "extended": False}] A filter matches, when `` & can_mask == can_id & can_mask`` :param dict config: Any backend dependent configurations are passed in this dictionary """ @abc.abstractmethod def recv(self, timeout=None): """Block waiting for a message from the Bus. :param float timeout: Seconds to wait for a message. :return: None on timeout or a :class:`can.Message` object. """ raise NotImplementedError("Trying to read from a write only bus?") @abc.abstractmethod def send(self, msg, timeout=None): """Transmit a message to CAN bus. Override this method to enable the transmit path. :param can.Message msg: A message object. :param float timeout: If > 0, wait up to this many seconds for message to be ACK:ed or for transmit queue to be ready depending on driver implementation. If timeout is exceeded, an exception will be raised. Might not be supported by all interfaces. :raise: :class:`can.CanError` if the message could not be written. """ raise NotImplementedError("Trying to write to a readonly bus?") def send_periodic(self, msg, period, duration=None): """Start sending a message at a given period on this bus. :param can.Message msg: Message to transmit :param float period: Period in seconds between each message :param float duration: The duration to keep sending this message at given rate. If no duration is provided, the task will continue indefinitely. :return: A started task instance :rtype: can.CyclicSendTaskABC Note the duration before the message stops being sent may not be exactly the same as the duration specified by the user. In general the message will be sent at the given rate until at least *duration* seconds. """ if not hasattr(self, "_lock"): # Create send lock for this bus self._lock = threading.Lock() return ThreadBasedCyclicSendTask(self, self._lock, msg, period, duration) def __iter__(self): """Allow iteration on messages as they are received. >>> for msg in bus: ... print(msg) :yields: :class:`can.Message` msg objects. """ while True: m = self.recv(timeout=1.0) if m is not None: yield m logger.debug("done iterating over bus messages") def set_filters(self, can_filters=None): """Apply filtering to all messages received by this Bus. Calling without passing any filters will reset the applied filters. :param list can_filters: A list of dictionaries each containing a "can_id" and a "can_mask". >>> [{"can_id": 0x11, "can_mask": 0x21}] A filter matches, when `` & can_mask == can_id & can_mask`` """ raise NotImplementedError("Trying to set_filters on unsupported bus") def flush_tx_buffer(self): """Discard every message that may be queued in the output buffer(s). """ pass def shutdown(self): """ Called to carry out any interface specific cleanup required in shutting down a bus. """ self.flush_tx_buffer() __metaclass__ = abc.ABCMeta python-can-2.0.0/can/ctypesutil.py0000644000175000017500000000525613113515201017321 0ustar brianbrian00000000000000# -*- coding: utf-8 -*- " Common ctypes utils " import binascii import ctypes import logging import sys log = logging.getLogger('can.ctypesutil') __all__ = ['CLibrary', 'HANDLE', 'PHANDLE'] class LibraryMixin: def map_symbol(self, func_name, restype=None, argtypes=(), errcheck=None): """ Map and return a symbol (function) from a C library. A reference to the mapped symbol is also held in the instance :param str func_name: symbol_name :param ctypes.c_* restype: function result type (i.e. ctypes.c_ulong...), defaults to void :param tuple(ctypes.c_* ... ) argtypes: argument types, defaults to no args :param callable errcheck: optional error checking function, see ctypes docs for _FuncPtr """ if (argtypes): prototype = self.function_type(restype, *argtypes) else: prototype = self.function_type(restype) try: symbol = prototype((func_name, self)) except AttributeError: raise ImportError("Could not map function '{}' from library {}".format(func_name, self._name)) setattr(symbol, "_name", func_name) log.debug('Wrapped function "{}", result type: {}, error_check {}'.format(func_name, type(restype), errcheck)) if (errcheck): symbol.errcheck = errcheck setattr(self, func_name, symbol) return symbol try: _LibBase = ctypes.WinDLL except AttributeError: _LibBase = ctypes.CDLL class CLibrary_Win32(_LibBase, LibraryMixin): " Basic ctypes.WinDLL derived class + LibraryMixin " def __init__(self, library_or_path): if (isinstance(library_or_path, str)): super(CLibrary_Win32, self).__init__(library_or_path) else: super(CLibrary_Win32, self).__init__(library_or_path._name, library_or_path._handle) @property def function_type(self): return ctypes.WINFUNCTYPE class CLibrary_Unix(ctypes.CDLL, LibraryMixin): " Basic ctypes.CDLL derived class + LibraryMixin " def __init__(self, library_or_path): if (isinstance(library_or_path, str)): super(CLibrary_Unix, self).__init__(library_or_path) else: super(CLibrary_Unix, self).__init__(library_or_path._name, library_or_path._handle) @property def function_type(self): return ctypes.CFUNCTYPE if sys.platform == "win32": CLibrary = CLibrary_Win32 else: CLibrary = CLibrary_Unix # Common win32 definitions class HANDLE(ctypes.c_void_p): pass PHANDLE = ctypes.POINTER(HANDLE) python-can-2.0.0/can/interface.py0000644000175000017500000001075113223535415017063 0ustar brianbrian00000000000000from __future__ import absolute_import import can import importlib from can.broadcastmanager import CyclicSendTaskABC, MultiRateCyclicSendTaskABC from can.util import load_config # interface_name => (module, classname) BACKENDS = { 'kvaser': ('can.interfaces.kvaser', 'KvaserBus'), 'socketcan_ctypes': ('can.interfaces.socketcan', 'SocketcanCtypes_Bus'), 'socketcan_native': ('can.interfaces.socketcan', 'SocketcanNative_Bus'), 'serial': ('can.interfaces.serial.serial_can', 'SerialBus'), 'pcan': ('can.interfaces.pcan', 'PcanBus'), 'usb2can': ('can.interfaces.usb2can', 'Usb2canBus'), 'ixxat': ('can.interfaces.ixxat', 'IXXATBus'), 'nican': ('can.interfaces.nican', 'NicanBus'), 'iscan': ('can.interfaces.iscan', 'IscanBus'), 'virtual': ('can.interfaces.virtual', 'VirtualBus'), 'neovi': ('can.interfaces.neovi_api', 'NeoVIBus'), 'vector': ('can.interfaces.vector', 'VectorBus'), 'slcan': ('can.interfaces.slcan', 'slcanBus') } class Bus(object): """ Instantiates a CAN Bus of the given `bustype`, falls back to reading a configuration file from default locations. """ @classmethod def __new__(cls, other, channel=None, *args, **kwargs): """ Takes the same arguments as :class:`can.BusABC` with the addition of: :param kwargs: Should contain a bustype key with a valid interface name. :raises: NotImplementedError if the bustype isn't recognized :raises: ValueError if the bustype or channel isn't either passed as an argument or set in the can.rc config. """ config = load_config(config={ 'interface': kwargs.get('bustype'), 'channel': channel }) if 'bustype' in kwargs: # remove the bustype so it doesn't get passed to the backend del kwargs['bustype'] interface = config['interface'] channel = config['channel'] # Import the correct Bus backend try: (module_name, class_name) = BACKENDS[interface] except KeyError: raise NotImplementedError("CAN interface '{}' not supported".format(interface)) try: module = importlib.import_module(module_name) except Exception as e: raise ImportError( "Cannot import module {} for CAN interface '{}': {}".format(module_name, interface, e) ) try: cls = getattr(module, class_name) except Exception as e: raise ImportError( "Cannot import class {} from module {} for CAN interface '{}': {}".format( class_name, module_name, interface, e ) ) return cls(channel, **kwargs) class CyclicSendTask(CyclicSendTaskABC): @classmethod def __new__(cls, other, channel, *args, **kwargs): config = load_config(config={'channel': channel}) # Import the correct implementation of CyclicSendTask if config['interface'] == 'socketcan_ctypes': from can.interfaces.socketcan.socketcan_ctypes import CyclicSendTask as _ctypesCyclicSendTask cls = _ctypesCyclicSendTask elif config['interface'] == 'socketcan_native': from can.interfaces.socketcan.socketcan_native import CyclicSendTask as _nativeCyclicSendTask cls = _nativeCyclicSendTask else: raise can.CanError("Current CAN interface doesn't support CyclicSendTask") return cls(config['channel'], *args, **kwargs) class MultiRateCyclicSendTask(MultiRateCyclicSendTaskABC): @classmethod def __new__(cls, other, channel, *args, **kwargs): config = load_config(config={'channel': channel}) # Import the correct implementation of CyclicSendTask if config['interface'] == 'socketcan_ctypes': from can.interfaces.socketcan.socketcan_ctypes import MultiRateCyclicSendTask as _ctypesMultiRateCyclicSendTask cls = _ctypesMultiRateCyclicSendTask elif config['interface'] == 'socketcan_native': from can.interfaces.socketcan.socketcan_native import MultiRateCyclicSendTask as _nativeMultiRateCyclicSendTask cls = _nativeMultiRateCyclicSendTask else: can.log.info("Current CAN interface doesn't support CyclicSendTask") return cls(config['channel'], *args, **kwargs) python-can-2.0.0/can/listener.py0000644000175000017500000000323613223535415016750 0ustar brianbrian00000000000000try: import queue except ImportError: import Queue as queue class Listener(object): def on_message_received(self, msg): raise NotImplementedError( "{} has not implemented on_message_received".format( self.__class__.__name__) ) def __call__(self, msg): return self.on_message_received(msg) def stop(self): """ Override to cleanup any open resources. """ class RedirectReader(Listener): """ A RedirectReader sends all received messages to another Bus. """ def __init__(self, bus): self.bus = bus def on_message_received(self, msg): self.bus.send(msg) class BufferedReader(Listener): """ A BufferedReader is a subclass of :class:`~can.Listener` which implements a **message buffer**: that is, when the :class:`can.BufferedReader` instance is notified of a new message it pushes it into a queue of messages waiting to be serviced. """ def __init__(self): self.buffer = queue.Queue(0) def on_message_received(self, msg): self.buffer.put(msg) def get_message(self, timeout=0.5): """ Attempts to retrieve the latest message received by the instance. If no message is available it blocks for given timeout or until a message is received (whichever is shorter), :param float timeout: The number of seconds to wait for a new message. :return: the :class:`~can.Message` if there is one, or None if there is not. """ try: return self.buffer.get(block=True, timeout=timeout) except queue.Empty: return None python-can-2.0.0/can/logger.py0000644000175000017500000000715013223535415016401 0ustar brianbrian00000000000000#!/usr/bin/env python """ logger.py logs CAN traffic to the terminal and to a file on disk. logger.py can0 See candump in the can-utils package for a C implementation. Efficient filtering has been implemented for the socketcan backend. For example the command logger.py can0 F03000:FFF000 Will filter for can frames with a can_id containing XXF03XXX. Dynamic Controls 2010 """ from __future__ import print_function import datetime import argparse import socket import can from can.io.logger import Logger def main(): parser = argparse.ArgumentParser( "python -m can.logger", description="Log CAN traffic, printing messages to stdout or to a given file") parser.add_argument("-f", "--file_name", dest="log_file", help="""Path and base log filename, extension can be .txt, .asc, .csv, .db, .npz""", default=None) parser.add_argument("-v", action="count", dest="verbosity", help='''How much information do you want to see at the command line? You can add several of these e.g., -vv is DEBUG''', default=2) parser.add_argument('-c', '--channel', help='''Most backend interfaces require some sort of channel. For example with the serial interface the channel might be a rfcomm device: "/dev/rfcomm0" With the socketcan interfaces valid channel examples include: "can0", "vcan0"''') parser.add_argument('-i', '--interface', dest="interface", help='''Specify the backend CAN interface to use. If left blank, fall back to reading from configuration files.''', choices=can.VALID_INTERFACES) parser.add_argument('--filter', help='''Comma separated filters can be specified for the given CAN interface: : (matches when & mask == can_id & mask) ~ (matches when & mask != can_id & mask) ''', nargs=argparse.REMAINDER, default='') parser.add_argument('-b', '--bitrate', type=int, help='''Bitrate to use for the CAN bus.''') results = parser.parse_args() verbosity = results.verbosity logging_level_name = ['critical', 'error', 'warning', 'info', 'debug', 'subdebug'][min(5, verbosity)] can.set_logging_level(logging_level_name) can_filters = [] if len(results.filter) > 0: print('Adding filter/s', results.filter) for filt in results.filter: if ':' in filt: _ = filt.split(":") can_id, can_mask = int(_[0], base=16), int(_[1], base=16) elif "~" in filt: can_id, can_mask = filt.split("~") can_id = int(can_id, base=16) | 0x20000000 # CAN_INV_FILTER can_mask = int(can_mask, base=16) & socket.CAN_ERR_FLAG can_filters.append({"can_id": can_id, "can_mask": can_mask}) config = {"can_filters": can_filters, "single_handle": True} if results.interface: config["bustype"] = results.interface if results.bitrate: config["bitrate"] = results.bitrate bus = can.interface.Bus(results.channel, **config) print('Connected to {}: {}'.format(bus.__class__.__name__, bus.channel_info)) print('Can Logger (Started on {})\n'.format(datetime.datetime.now())) logger = Logger(results.log_file) try: while True: msg = bus.recv(1) if msg is not None: logger(msg) except KeyboardInterrupt: pass finally: bus.shutdown() logger.stop() if __name__ == "__main__": main() python-can-2.0.0/can/message.py0000644000175000017500000000754213223535415016553 0ustar brianbrian00000000000000import logging logger = logging.getLogger(__name__) class Message(object): """ The :class:`~can.Message` object is used to represent CAN messages for both sending and receiving. Messages can use extended identifiers, be remote or error frames, and contain data. """ def __init__(self, timestamp=0.0, is_remote_frame=False, extended_id=True, is_error_frame=False, arbitration_id=0, dlc=None, data=None, channel=None): self.timestamp = timestamp self.id_type = extended_id self.is_extended_id = extended_id self.is_remote_frame = is_remote_frame self.is_error_frame = is_error_frame self.arbitration_id = arbitration_id self.channel = channel if data is None or is_remote_frame: self.data = bytearray() elif isinstance(data, bytearray): self.data = data else: try: self.data = bytearray(data) except TypeError: err = "Couldn't create message from {} ({})".format(data, type(data)) raise TypeError(err) if dlc is None: self.dlc = len(self.data) else: self.dlc = dlc assert self.dlc <= 8, "data link count was {} but it must be less than or equal to 8".format(self.dlc) def __str__(self): field_strings = ["Timestamp: {0:15.6f}".format(self.timestamp)] if self.id_type: # Extended arbitrationID arbitration_id_string = "ID: {0:08x}".format(self.arbitration_id) else: arbitration_id_string = "ID: {0:04x}".format(self.arbitration_id) field_strings.append(arbitration_id_string.rjust(12, " ")) flag_string = " ".join([ "X" if self.id_type else "S", "E" if self.is_error_frame else " ", "R" if self.is_remote_frame else " ", ]) field_strings.append(flag_string) field_strings.append("DLC: {0:d}".format(self.dlc)) data_strings = [] if self.data is not None: for index in range(0, min(self.dlc, len(self.data))): data_strings.append("{0:02x}".format(self.data[index])) if len(data_strings) > 0: field_strings.append(" ".join(data_strings).ljust(24, " ")) else: field_strings.append(" " * 24) if (self.data is not None) and (self.data.isalnum()): try: field_strings.append("'{}'".format(self.data.decode('utf-8'))) except UnicodeError as e: pass return " ".join(field_strings).strip() def __len__(self): return len(self.data) def __bool__(self): return True def __nonzero__(self): return self.__bool__() def __repr__(self): data = ["{:#02x}".format(byte) for byte in self.data] args = ["timestamp={}".format(self.timestamp), "is_remote_frame={}".format(self.is_remote_frame), "extended_id={}".format(self.id_type), "is_error_frame={}".format(self.is_error_frame), "arbitration_id={:#x}".format(self.arbitration_id), "dlc={}".format(self.dlc), "data=[{}]".format(", ".join(data))] if self.channel is not None: args.append("channel={}".format(self.channel)) return "can.Message({})".format(", ".join(args)) def __eq__(self, other): return (isinstance(other, self.__class__) and self.arbitration_id == other.arbitration_id and #self.timestamp == other.timestamp and self.id_type == other.id_type and self.dlc == other.dlc and self.data == other.data and self.is_remote_frame == other.is_remote_frame and self.is_error_frame == other.is_error_frame) python-can-2.0.0/can/notifier.py0000644000175000017500000000271713223535415016745 0ustar brianbrian00000000000000import threading class Notifier(object): def __init__(self, bus, listeners, timeout=None): """Manages the distribution of **Messages** from a given bus to a list of listeners. :param bus: The :ref:`bus` to listen too. :param listeners: An iterable of :class:`~can.Listeners` :param timeout: An optional maximum number of seconds to wait for any message. """ self.listeners = listeners self.bus = bus self.timeout = timeout #: Exception raised in thread self.exception = None self.running = threading.Event() self.running.set() self._reader = threading.Thread(target=self.rx_thread) self._reader.daemon = True self._reader.start() def stop(self): """Stop notifying Listeners when new :class:`~can.Message` objects arrive and call :meth:`~can.Listener.stop` on each Listener.""" self.running.clear() if self.timeout is not None: self._reader.join(self.timeout + 0.1) def rx_thread(self): try: while self.running.is_set(): msg = self.bus.recv(self.timeout) if msg is not None: for callback in self.listeners: callback(msg) except Exception as exc: self.exception = exc raise finally: for listener in self.listeners: listener.stop() python-can-2.0.0/can/player.py0000644000175000017500000000610113223535415016411 0ustar brianbrian00000000000000#!/usr/bin/env python """ Replays CAN traffic saved with can.logger back to a CAN bus. Similar to canplayer in the can-utils package. """ from __future__ import print_function import argparse import datetime import can from can.io.player import LogReader, MessageSync def main(): parser = argparse.ArgumentParser( "python -m can.player", description="Replay CAN traffic") parser.add_argument("-f", "--file_name", dest="log_file", help="""Path and base log filename, extension can be .txt, .asc, .csv, .db, .npz""", default=None) parser.add_argument("-v", action="count", dest="verbosity", help='''Also print can frames to stdout. You can add several of these to enable debugging''', default=2) parser.add_argument('-c', '--channel', help='''Most backend interfaces require some sort of channel. For example with the serial interface the channel might be a rfcomm device: "/dev/rfcomm0" With the socketcan interfaces valid channel examples include: "can0", "vcan0"''') parser.add_argument('-i', '--interface', dest="interface", help='''Specify the backend CAN interface to use. If left blank, fall back to reading from configuration files.''', choices=can.VALID_INTERFACES) parser.add_argument('-b', '--bitrate', type=int, help='''Bitrate to use for the CAN bus.''') parser.add_argument('--ignore-timestamps', dest='timestamps', help='''Ignore timestamps (send all frames immediately with minimum gap between frames)''', action='store_false') parser.add_argument('-g', '--gap', type=float, help=''' minimum time between replayed frames''') parser.add_argument('-s', '--skip', type=float, default=60*60*24, help=''' skip gaps greater than 's' seconds''') parser.add_argument('infile', metavar='input-file', type=str, help='The file to replay. Supported types: .db, .blf') results = parser.parse_args() verbosity = results.verbosity gap = 0.0001 if results.gap is None else results.gap logging_level_name = ['critical', 'error', 'warning', 'info', 'debug', 'subdebug'][min(5, verbosity)] can.set_logging_level(logging_level_name) config = {"single_handle": True} if results.interface: config["bustype"] = results.interface if results.bitrate: config["bitrate"] = results.bitrate bus = can.interface.Bus(results.channel, **config) player = LogReader(results.infile) in_sync = MessageSync(player, timestamps=results.timestamps, gap=gap, skip=results.skip) print('Can LogReader (Started on {})'.format( datetime.datetime.now())) try: for m in in_sync: if verbosity >= 3: print(m) bus.send(m) except KeyboardInterrupt: pass finally: bus.shutdown() if __name__ == "__main__": main() python-can-2.0.0/can/util.py0000644000175000017500000001335113223535415016077 0ustar brianbrian00000000000000#!/usr/bin/env python3 """ Utilities and configuration file parsing. """ from __future__ import absolute_import import can from can.interfaces import VALID_INTERFACES try: from configparser import ConfigParser except ImportError: from ConfigParser import SafeConfigParser as ConfigParser import os import os.path import sys import platform import re import logging log = logging.getLogger('can.util') REQUIRED_KEYS = [ 'interface', 'channel', ] CONFIG_FILES = ['~/can.conf'] if platform.system() == "Linux": CONFIG_FILES.extend( [ '/etc/can.conf', '~/.can', '~/.canrc' ] ) elif platform.system() == "Windows" or platform.python_implementation() == "IronPython": CONFIG_FILES.extend( [ 'can.ini', os.path.join(os.getenv('APPDATA', ''), 'can.ini') ] ) def load_file_config(path=None): """ Loads configuration from file with following content:: [default] interface = socketcan channel = can0 :param path: path to config file. If not specified, several sensible default locations are tried depending on platform. """ config = ConfigParser() if path is None: config.read([os.path.expanduser(path) for path in CONFIG_FILES]) else: config.read(path) if not config.has_section('default'): return {} return dict( (key, val) for key, val in config.items('default') ) def load_environment_config(): """ Loads config dict from environmental variables (if set): * CAN_INTERFACE * CAN_CHANNEL * CAN_BITRATE """ mapper = { 'interface': 'CAN_INTERFACE', 'channel': 'CAN_CHANNEL', 'bitrate': 'CAN_BITRATE', } return dict( (key, os.environ.get(val)) for key, val in mapper.items() if val in os.environ ) def load_config(path=None, config=None): """ Returns a dict with configuration details which is loaded from (in this order): - config - can.rc - Environment variables CAN_INTERFACE, CAN_CHANNEL, CAN_BITRATE - Config files ``/etc/can.conf`` or ``~/.can`` or ``~/.canrc`` where the latter may add or replace values of the former. Interface can be any of the strings from ``can.VALID_INTERFACES`` for example: kvaser, socketcan, pcan, usb2can, ixxat, nican, virtual. .. note:: If you pass ``"socketcan"`` this automatically selects between the native and ctypes version. :param path: Optional path to config file. :param config: A dict which may set the 'interface', and/or the 'channel', or neither. :return: A config dictionary that should contain 'interface' & 'channel':: { 'interface': 'python-can backend interface to use', 'channel': 'default channel to use', } Note ``None`` will be used if all the options are exhausted without finding a value. """ if config is None: config = {} system_config = {} configs = [ config, can.rc, load_environment_config, lambda: load_file_config(path) ] # Slightly complex here to only search for the file config if required for cfg in configs: if callable(cfg): cfg = cfg() for key in cfg: if key not in system_config and cfg[key] is not None: system_config[key] = cfg[key] # substitute None for all values not found for key in REQUIRED_KEYS: if key not in system_config: system_config[key] = None if system_config['interface'] == 'socketcan': system_config['interface'] = choose_socketcan_implementation() if system_config['interface'] not in VALID_INTERFACES: raise NotImplementedError('Invalid CAN Bus Type - {}'.format(can.rc['interface'])) if 'bitrate' in system_config: system_config['bitrate'] = int(system_config['bitrate']) can.log.debug("can config: {}".format(system_config)) return system_config def choose_socketcan_implementation(): """Set the best version of SocketCAN for this system. :param config: The can.rc configuration dictionary :raises Exception: If the system doesn't support SocketCAN """ # Check OS: SocketCAN is available only under Linux if not sys.platform.startswith('linux'): msg = 'SocketCAN not available under {}'.format( sys.platform) raise Exception(msg) else: # Check release: SocketCAN was added to Linux 2.6.25 rel_string = platform.release() m = re.match(r'\d+\.\d+\.\d', rel_string) if m is None: msg = 'Bad linux release {}'.format(rel_string) raise Exception(msg) rel_num = [int(i) for i in rel_string[:m.end()].split('.')] if (rel_num >= [2, 6, 25]): # Check Python version: SocketCAN was added in 3.3 return 'socketcan_native' if sys.version_info >= (3, 3) else 'socketcan_ctypes' else: msg = 'SocketCAN not available under Linux {}'.format( rel_string) raise Exception(msg) def set_logging_level(level_name=None): """Set the logging level for the "can" logger. Expects one of: 'critical', 'error', 'warning', 'info', 'debug', 'subdebug' """ can_logger = logging.getLogger('can') try: can_logger.setLevel(getattr(logging, level_name.upper())) except AttributeError: can_logger.setLevel(logging.DEBUG) log.debug("Logging set to {}".format(level_name)) if __name__ == "__main__": print("Searching for configuration named:") print("\n".join(CONFIG_FILES)) print("Settings:") print(load_config()) python-can-2.0.0/doc/0000755000175000017500000000000013223535610014546 5ustar brianbrian00000000000000python-can-2.0.0/doc/interfaces/0000755000175000017500000000000013223535610016671 5ustar brianbrian00000000000000python-can-2.0.0/doc/interfaces/iscan.rst0000644000175000017500000000050313223535415020521 0ustar brianbrian00000000000000isCAN ===== Interface for isCAN from `Thorsis Technologies GmbH`_, former ifak system GmbH. Bus --- .. autoclass:: can.interfaces.iscan.IscanBus .. autoexception:: can.interfaces.iscan.IscanError .. _Thorsis Technologies GmbH: https://www.thorsis.com/en/industrial-automation/usb-interfaces/can/iscan-usb-interface/ python-can-2.0.0/doc/interfaces/ixxat.rst0000644000175000017500000000463113223535415020567 0ustar brianbrian00000000000000.. _ixxatdoc: IXXAT Virtual CAN Interface =========================== Interface to `IXXAT `__ Virtual CAN Interface V3 SDK. Works on Windows. .. note:: The Linux ECI SDK is currently unsupported, however on Linux some devices are supported with :doc:`socketcan`. The :meth:`~can.interfaces.ixxat.IXXATBus.send_periodic` method is supported natively through the on-board cyclic transmit list. Modifying cyclic messages is not possible. You will need to stop it, then start a new periodic message. Bus --- .. autoclass:: can.interfaces.ixxat.IXXATBus .. autoclass:: can.interfaces.ixxat.canlib.CyclicSendTask Configuration file ------------------ The simplest configuration file would be:: [default] interface = ixxat channel = 0 Python-can will search for the first IXXAT device available and open the first channel. ``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 * ``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 * ``extended`` (default False) Allow usage of extended IDs Internals --------- The IXXAT :class:`~can.BusABC` object is a farly straightforward interface to the IXXAT VCI library. It can open a specific device ID or use the first one found. The frame exchange *do not involve threads* in the background but is 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`` options, defaulting at 16 for both. The CAN filters act as a "whitelist" in IXXAT implementation, that is if you supply a non-empty filter list you must explicitly state EVERY frame you want to receive (including RTR field). The can_id/mask must be specified according to IXXAT behaviour, that is bit 0 of can_id/mask parameters represents the RTR field in CAN frame. See IXXAT VCI documentation, section "Message filters" for more info. .. hint:: Module uses ``can.ixxat`` logger and at DEBUG level logs every frame sent or received. It may be too verbose for your purposes. python-can-2.0.0/doc/interfaces/kvaser.rst0000644000175000017500000000214713113515201020712 0ustar brianbrian00000000000000.. _kvaserdoc: Kvaser's CANLIB =============== `Kvaser `__'s CANLib SDK for Windows (also available on Linux). Bus --- .. autoclass:: can.interfaces.kvaser.canlib.KvaserBus Internals --------- The Kvaser :class:`~can.Bus` object with a physical CAN Bus can be operated in two modes; ``single_handle`` mode with one shared bus handle used for both reading and writing to the CAN bus, or with two separate bus handles. Two separate handles are needed if receiving and sending messages are done in different threads (see `Kvaser documentation `_). .. warning:: Any objects inheriting from `Bus`_ should *not* directly use the interface handle(/s). Message filtering ~~~~~~~~~~~~~~~~~ The Kvaser driver and hardware only supports setting one filter per handle. If one filter is requested, this is will be handled by the Kvaser driver. If more than one filter is needed, these will be handled in Python code in the ``recv`` method. If a message does not match any of the filters, ``recv()`` will return None. python-can-2.0.0/doc/interfaces/neovi.rst0000644000175000017500000000235613223535415020554 0ustar brianbrian00000000000000neoVI Interface =============== .. warning:: This ``neoVI`` documentation is a work in progress. Feedback and revisions are most welcome! Interface to `Intrepid Control Systems `__ neoVI API range of devices via `pyneovi `__ wrapper on Windows. .. note:: This interface is not supported on Linux, however on Linux neoVI devices are supported via :doc:`socketcan` with ICS `Kernel-mode SocketCAN module for Intrepid devices `__ and `icsscand `__ Installation ------------ This neoVI interface requires the installation of the ICS neoVI DLL and pyneovi package. - Download and install the Intrepid Product Drivers `Intrepid Product Drivers `__ - Install pyneovi using pip and the pyneovi bitbucket repo: .. code-block:: bash pip install https://bitbucket.org/Kemp_J/pyneovi/get/default.zip Configuration ------------- An example `can.ini` file for windows 7: :: [default] interface = neovi channel = 1 Bus --- .. autoclass:: can.interfaces.neovi_api.NeoVIBus python-can-2.0.0/doc/interfaces/nican.rst0000644000175000017500000000077313223535415020525 0ustar brianbrian00000000000000NI-CAN ====== This interface adds support for CAN controllers by `National Instruments`_. .. warning:: NI-CAN only seems to support 32-bit architectures so if the driver can't be loaded on a 64-bit Python, try using a 32-bit version instead. .. warning:: CAN filtering has not been tested throughly and may not work as expected. Bus --- .. autoclass:: can.interfaces.nican.NicanBus .. autoexception:: can.interfaces.nican.NicanError .. _National Instruments: http://www.ni.com/can/ python-can-2.0.0/doc/interfaces/pcan.rst0000644000175000017500000000065313223535415020353 0ustar brianbrian00000000000000.. _pcandoc: PCAN Basic API ============== .. warning:: This ``PCAN`` documentation is a work in progress. Feedback and revisions are most welcome! Interface to `Peak-System `__'s PCAN-Basic API. Configuration ------------- An example `can.ini` file for windows 7: :: [default] interface = pcan channel = PCAN_USBBUS1 Bus --- .. autoclass:: can.interfaces.pcan.PcanBus python-can-2.0.0/doc/interfaces/serial.rst0000644000175000017500000001366513223535415020720 0ustar brianbrian00000000000000.. _serial: CAN over Serial =============== A text based interface. For example use over serial ports like ``/dev/ttyS1`` or ``/dev/ttyUSB0`` on Linux machines or ``COM1`` on Windows. The interface is a simple implementation that has been used for recording CAN traces. .. note:: The properties extended_id, is_remote_frame and is_error_frame from the class can.Message are not in use. These interface will not send or receive flags for this properties. Bus --- .. autoclass:: can.interfaces.serial.serial_can.SerialBus Internals --------- The frames that will be sent and received over the serial interface consist of six parts. The start and the stop byte for the frame, the timestamp, DLC, arbitration ID and the payload. The payload has a variable length of between 0 and 8 bytes, the other parts are fixed. Both, the timestamp and the arbitration ID will be interpreted as 4 byte unsigned integers. The DLC is also an unsigned integer with a length of 1 byte. Serial frame format ^^^^^^^^^^^^^^^^^^^ +-------------------+----------------+-----------------------------------------------+-------------------------------+-------------------------+---------+--------------+ | | Start of frame | Timestamp | DLC | Arbitration ID | Payload | End of frame | +===================+================+==============================+================+===============================+=========================+=========+==============+ | **Length (Byte)** | 1 | 4 | 1 | 4 | 0 - 8 | 1 | +-------------------+----------------+-----------------------------------------------+-------------------------------+-------------------------+---------+--------------+ | **Data type** | Byte | Unsigned 4 byte integer | Unsigned 1 byte integer | Unsigned 4 byte integer | Byte | Byte | +-------------------+----------------+-----------------------------------------------+-------------------------------+-------------------------+---------+--------------+ | **Byte order** | \- | Little-Endian | Little-Endian | Little-Endian | \- | \- | +-------------------+----------------+-----------------------------------------------+-------------------------------+-------------------------+---------+--------------+ | **Description** | Must be 0xAA | Usually s, ms or µs since start of the device | Length in byte of the payload | \- | \- | Must be 0xBB | +-------------------+----------------+-----------------------------------------------+-------------------------------+-------------------------+---------+--------------+ Examples of serial frames ^^^^^^^^^^^^^^^^^^^^^^^^^ .. rubric:: CAN message with 8 byte payload +----------------+-----------------------------------------+ | CAN message | +----------------+-----------------------------------------+ | Arbitration ID | Payload | +================+=========================================+ | 1 | 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 | +----------------+-----------------------------------------+ +----------------+---------------------+------+---------------------+-----------------------------------------+--------------+ | Serial frame | +----------------+---------------------+------+---------------------+-----------------------------------------+--------------+ | Start of frame | Timestamp | DLC | Arbitration ID | Payload | End of frame | +================+=====================+======+=====================+=========================================+==============+ | 0xAA | 0x66 0x73 0x00 0x00 | 0x08 | 0x01 0x00 0x00 0x00 | 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 | 0xBB | +----------------+---------------------+------+---------------------+-----------------------------------------+--------------+ .. rubric:: CAN message with 1 byte payload +----------------+---------+ | CAN message | +----------------+---------+ | Arbitration ID | Payload | +================+=========+ | 1 | 0x11 | +----------------+---------+ +----------------+---------------------+------+---------------------+---------+--------------+ | Serial frame | +----------------+---------------------+------+---------------------+---------+--------------+ | Start of frame | Timestamp | DLC | Arbitration ID | Payload | End of frame | +================+=====================+======+=====================+=========+==============+ | 0xAA | 0x66 0x73 0x00 0x00 | 0x01 | 0x01 0x00 0x00 0x00 | 0x11 | 0xBB | +----------------+---------------------+------+---------------------+---------+--------------+ .. rubric:: CAN message with 0 byte payload +----------------+---------+ | CAN message | +----------------+---------+ | Arbitration ID | Payload | +================+=========+ | 1 | None | +----------------+---------+ +----------------+---------------------+------+---------------------+--------------+ | Serial frame | +----------------+---------------------+------+---------------------+--------------+ | Start of frame | Timestamp | DLC | Arbitration ID | End of frame | +================+=====================+======+=====================+==============+ | 0xAA | 0x66 0x73 0x00 0x00 | 0x00 | 0x01 0x00 0x00 0x00 | 0xBBS | +----------------+---------------------+------+---------------------+--------------+python-can-2.0.0/doc/interfaces/slcan.rst0000755000175000017500000000125713223535415020536 0ustar brianbrian00000000000000.. _slcan: CAN over Serial / SLCAN ======================== A text based interface: compatible to slcan-interfaces (slcan ASCII protocol) should also support LAWICEL direct. These interfaces can also be used with socketcan and slcand with Linux. This driver directly uses the serial port, it makes slcan-compatible interfaces usable with Windows also. Hint: Arduino-Interface could easyly be build https://github.com/latonita/arduino-canbus-monitor Usage: use ``port[@baurate]`` to open the device. For example use ``/dev/ttyUSB0@115200`` or ``COM4@9600`` Bus --- .. autoclass:: can.interfaces.slcan.slcanBus Internals --------- .. TODO:: Implement and document slcan interface. python-can-2.0.0/doc/interfaces/socketcan.rst0000644000175000017500000001136213223535415021403 0ustar brianbrian00000000000000Socketcan ========= There are two implementations of socketcan backends. One written with :mod:`ctypes` to be compatible with Python 2 and 3, and one written for future versions of Python3 which feature native support. .. toctree:: :maxdepth: 2 socketcan_ctypes socketcan_native Unless you're running Python3.3 or lower the recommended backend is :doc:`socketcan_native `. For Python2.7 and Python3 <3.4, the available backend is :doc:`socketcan_ctypes `. Socketcan Quickstart -------------------- The full documentation for socketcan can be found in the kernel docs at `networking/can.txt `_. The CAN network driver provides a generic interface to setup, configure and monitor CAN devices. To configure bit-timing parameters use the program ``ip``. The virtual CAN driver (vcan) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The virtual CAN interfaces allow the transmission and reception of CAN frames without real CAN controller hardware. Virtual CAN network devices are usually named 'vcanX', like vcan0 vcan1 vcan2. To create a virtual can interface using socketcan run the following: .. code-block:: bash sudo modprobe vcan # Create a vcan network interface with a specific name sudo ip link add dev vcan0 type vcan sudo ip link set vcan0 up Real Device ~~~~~~~~~~~ ``vcan`` should be substituted for ``can`` and ``vcan0`` should be substituted for ``can0`` if you are using real hardware. Setting the bitrate can also be done at the same time, for example to enable an existing ``can0`` interface with a bitrate of 1MB: .. code-block:: bash sudo ip link set can0 up type can bitrate 1000000 Send Test Message ^^^^^^^^^^^^^^^^^ The `can-utils `_ library for linux includes a script `cansend` which is useful to send known payloads. For example to send a message on `vcan0`: .. code-block:: bash cansend vcan0 123#DEADBEEF CAN Errors ^^^^^^^^^^ A device may enter the "bus-off" state if too many errors occurred on the CAN bus. Then no more messages are received or sent. An automatic bus-off recovery can be enabled by setting the "restart-ms" to a non-zero value, e.g.: .. code-block:: bash sudo ip link set canX type can restart-ms 100 Alternatively, the application may realize the "bus-off" condition by monitoring CAN error frames and do a restart when appropriate with the command: .. code-block:: bash ip link set canX type can restart Note that a restart will also create a CAN error frame. List network interfaces ~~~~~~~~~~~~~~~~~~~~~~~ To reveal the newly created ``can0`` or a ``vcan0`` interface: .. code-block:: bash ifconfig Display CAN statistics ~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: bash ip -details -statistics link show vcan0 Network Interface Removal ~~~~~~~~~~~~~~~~~~~~~~~~~ To remove the network interface: .. code-block:: bash sudo ip link del vcan0 Wireshark --------- Wireshark supports socketcan and can be used to debug *python-can* messages. Fire it up and watch your new interface. To spam a bus: .. code-block:: python import time import can bustype = 'socketcan_native' channel = 'vcan0' def producer(id): """:param id: Spam the bus with messages including the data id.""" bus = can.interface.Bus(channel=channel, bustype=bustype) for i in range(10): msg = can.Message(arbitration_id=0xc0ffee, data=[id, i, 0, 1, 3, 1, 4, 1], extended_id=False) bus.send(msg) # Issue #3: Need to keep running to ensure the writing threads stay alive. ? time.sleep(1) producer(10) With debugging turned right up this looks something like this: .. image:: ../images/wireshark.png :width: 100% The process to follow bus traffic is even easier: .. code-block:: python for message in Bus(can_interface): print(message) Reading and Timeouts -------------------- Reading a single CAN message off of the bus is simple with the ``bus.recv()`` function: .. code-block:: python import can can_interface = 'vcan0' bus = can.interface.Bus(can_interface, bustype='socketcan_native') message = bus.recv() By default, this performs a blocking read, which means ``bus.recv()`` won't return until a CAN message shows up on the socket. You can optionally perform a blocking read with a timeout like this: .. code-block:: python message = bus.recv(1.0) # Timeout in seconds. if message is None: print('Timeout occurred, no message.') If you set the timeout to ``0.0``, the read will be executed as non-blocking, which means ``bus.recv(0.0)`` will return immediately, either with a ``Message`` object or ``None``, depending on whether data was available on the socket. python-can-2.0.0/doc/interfaces/socketcan_ctypes.rst0000644000175000017500000000260513223535415022772 0ustar brianbrian00000000000000SocketCAN (ctypes) ================== `socketcan_ctypes.py` is a ctypes wrapper class around libc. It contains replications of constants and structures found in various linux header files. With Python 3.3, much of the functionality of this library is likely to be available natively in the Python socket module. Bus ---- .. autoclass:: can.interfaces.socketcan.SocketcanCtypes_Bus Broadcast-Manager ----------------- The ``socketcan_ctypes`` interface implements thin wrappers to the linux `broadcast manager` socket api. This allows the cyclic transmission of CAN messages at given intervals. The overhead for periodic message sending is extremely low as all the heavy lifting occurs within the linux kernel. send_periodic() ~~~~~~~~~~~~~~~ An example that uses the send_periodic is included in ``python-can/examples/cyclic.py`` The object returned can be used to halt, alter or cancel the periodic message task. .. autoclass:: can.interfaces.socketcan.socketcan_ctypes.CyclicSendTask Internals --------- createSocket ~~~~~~~~~~~~ .. autofunction:: can.interfaces.socketcan.socketcan_ctypes.createSocket bindSocket ~~~~~~~~~~ .. autofunction:: can.interfaces.socketcan.socketcan_ctypes.bindSocket connectSocket .. autofunction:: can.interfaces.socketcan.socketcan_ctypes.connectSocket capturePacket ~~~~~~~~~~~~~ .. autofunction:: can.interfaces.socketcan.socketcan_ctypes.capturePacket python-can-2.0.0/doc/interfaces/socketcan_native.rst0000644000175000017500000000212213223535415022743 0ustar brianbrian00000000000000SocketCAN (python) ================== Python 3.3 added support for socketcan for linux systems. The socketcan_native interface directly uses Python's socket module to access SocketCAN on linux. This is the most direct route to the kernel and should provide the most responsive. The implementation features efficient filtering of can_id's, this filtering occurs in the kernel and is much much more efficient than filtering messages in Python. Python 3.4 added support for the Broadcast Connection Manager (BCM) protocol, which if enabled should be used for queueing periodic tasks. Documentation for the socket can backend file can be found: https://www.kernel.org/doc/Documentation/networking/can.txt Bus --- .. autoclass:: can.interfaces.socketcan.SocketcanNative_Bus Internals --------- createSocket ~~~~~~~~~~~~ .. autofunction:: can.interfaces.socketcan.socketcan_native.createSocket bindSocket ~~~~~~~~~~ .. autofunction:: can.interfaces.socketcan.socketcan_native.bindSocket captureMessage ~~~~~~~~~~~~~~ .. autofunction:: can.interfaces.socketcan.socketcan_native.captureMessage python-can-2.0.0/doc/interfaces/usb2can.rst0000644000175000017500000001167513223535415020775 0ustar brianbrian00000000000000USB2CAN Interface ================= OVERVIEW -------- The `USB2CAN `_ is a cheap CAN interface based on an ARM7 chip (STR750FV2). There is support for this device on Linux through the :doc:`socketcan` interface and for Windows using this ``usb2can`` interface. WINDOWS SUPPORT --------------- Support though windows is achieved through a DLL very similar to the way the PCAN functions. The API is called CANAL (CAN Abstraction Layer) which is a separate project designed to be used with VSCP which is a socket like messaging system that is not only cross platform but also supports other types of devices. This device can be used through one of three ways 1)Through python-can 2)CANAL API either using the DLL and C/C++ or through the python wrapper that has been added to this project 3)VSCP Using python-can is strongly suggested as with little extra work the same interface can be used on both Windows and Linux. WINDOWS INSTALL --------------- 1. To install on Windows download the USB2CAN Windows driver. It is compatible with XP, Vista, Win7, Win8/8.1. (Written against driver version v1.0.2.1) 2. Install the appropriate version of `pywin32 `_ (win32com) 3. Download the USB2CAN CANAL DLL from the USB2CAN website. Place this in either the same directory you are running usb2can.py from or your DLL folder in your python install. (Written against CANAL DLL version v1.0.6) Interface Layout ---------------- - ``usb2canabstractionlayer.py`` This file is only a wrapper for the CANAL API that the interface expects. There are also a couple of constants here to try and make dealing with the bitwise operations for flag setting a little easier. Other than that this is only the CANAL API. If a programmer wanted to work with the API directly this is the file that allows you to do this. The CANAL project does not provide this wrapper and normally must be accessed with C. - ``usb2canInterface.py`` This file provides the translation to and from the python-can library to the CANAL API. This is where all the logic is and setup code is. Most issues if they are found will be either found here or within the DLL that is provided - ``serial_selector.py`` See the section below for the reason for adding this as it is a little odd. What program does is if a serial number is not provided to the usb2canInterface file this program does WMI (Windows Management Instrumentation) calls to try and figure out what device to connect to. It then returns the serial number of the device. Currently it is not really smart enough to figure out what to do if there are multiple devices. This needs to be changed if people are using more than one interface. Interface Specific Items ------------------------ There are a few things that are kinda strange about this device and are not overly obvious about the code or things that are not done being implemented in the DLL. 1. You need the Serial Number to connect to the device under Windows. This is part of the "setup string" that configures the device. There are a few options for how to get this. 1. Use usb2canWin.py to find the serial number 2. Look on the device and enter it either through a prompt/barcode scanner/hardcode it.(Not recommended) 3. Reprogram the device serial number to something and do that for all the devices you own. (Really Not Recommended, can no longer use multiple devices on one computer) 2. In usb2canabstractionlayer.py there is a structure called CanalMsg which has a unsigned byte array of size 8. In the usb2canInterface file it passes in an unsigned byte array of size 8 also which if you pass less than 8 bytes in it stuffs it with extra zeros. So if the data "01020304" is sent the message would look like "0102030400000000". There is also a part of this structure called sizeData which is the actual length of the data that was sent not the stuffed message (in this case would be 4). What then happens is although a message of size 8 is sent to the device only the length of information so the first 4 bytes of information would be sent. This is done because the DLL expects a length of 8 and nothing else. So to make it compatible that has to be sent through the wrapper. If usb2canInterface sent an array of length 4 with sizeData of 4 as well the array would throw an incompatible data type error. There is a Wireshark file posted in Issue #36 that demonstrates that the bus is only sending the data and not the extra zeros. 3. The masking features have not been implemented currently in the CANAL interface in the version currently on the USB2CAN website. .. warning:: Currently message filtering is not implemented. Contributions are most welcome! Bus --- .. autoclass:: can.interfaces.usb2can.Usb2canBus Internals --------- .. autoclass:: can.interfaces.usb2can.Usb2CanAbstractionLayer :members: :undoc-members: python-can-2.0.0/doc/interfaces/vector.rst0000644000175000017500000000143613223535415020734 0ustar brianbrian00000000000000Vector ====== This interface adds support for CAN controllers by `Vector`_. By default this library uses the channel configuration for CANalyzer. To use a different application, open Vector Hardware Config program and create a new application and assign the channels you may want to use. Specify the application name as ``app_name='Your app name'`` when constructing the bus or in a config file. Channel should be given as a list of channels starting at 0. Here is an example configuration file connecting to CAN 1 and CAN 2 for an application named "python-can":: [default] interface = vector channel = 0, 1 app_name = python-can Bus --- .. autoclass:: can.interfaces.vector.VectorBus .. autoexception:: can.interfaces.vector.VectorError .. _Vector: https://vector.com/ python-can-2.0.0/doc/interfaces/virtual.rst0000644000175000017500000000105413113515201021101 0ustar brianbrian00000000000000Virtual ======= The virtual interface can be used as a way to write OS and driver independent tests. A virtual CAN bus that can be used for automatic tests. Any Bus instances connecting to the same channel (in the same python program) will get each others messages. .. code-block:: python import can bus1 = can.interface.Bus('test', bustype='virtual') bus2 = can.interface.Bus('test', bustype='virtual') msg1 = can.Message(arbitration_id=0xabcde, data=[1,2,3]) bus1.send(msg1) msg2 = bus2.recv() assert msg1 == msg2 python-can-2.0.0/doc/api.rst0000644000175000017500000000101513113515201016036 0ustar brianbrian00000000000000Library API =========== The main objects are the :class:`~can.BusABC` and the :class:`~can.Message`. A form of CAN interface is also required. .. hint:: Check the backend specific documentation for any implementation specific details. .. toctree:: :maxdepth: 1 bus message listeners bcm Utilities --------- .. automodule:: can.util :members: .. _notifier: Notifier -------- The Notifier object is used as a message distributor for a bus. .. autoclass:: can.Notifier :members: python-can-2.0.0/doc/bcm.rst0000644000175000017500000000126313223535415016046 0ustar brianbrian00000000000000 Broadcast Manager ================= The broadcast manager isn't yet supported by all interfaces. Currently SockerCAN and IXXAT are supported at least partially. It allows the user to setup periodic message jobs. If periodic transmission is not supported natively, a software thread based scheduler is used as a fallback. This example shows the ctypes socketcan using the broadcast manager: .. literalinclude:: ../examples/cyclic.py :language: python :linenos: Functional API -------------- .. autofunction:: can.send_periodic Class based API --------------- .. autoclass:: can.CyclicSendTaskABC :members: .. autoclass:: can.MultiRateCyclicSendTaskABC :members: python-can-2.0.0/doc/bin.rst0000644000175000017500000001060613223535415016056 0ustar brianbrian00000000000000Scripts ======= The following modules are callable from python-can. can.logger ---------- Command line help (``python -m can.logger --help``):: usage: python -m can.logger [-h] [-f LOG_FILE] [-v] [-c CHANNEL] [-i {iscan,slcan,virtual,socketcan_ctypes,usb2can,ixxat,socketcan_native,kvaser,neovi,vector,nican,pcan,serial,remote,socketcan}] [--filter ...] [-b BITRATE] Log CAN traffic, printing messages to stdout or to a given file optional arguments: -h, --help show this help message and exit -f LOG_FILE, --file_name LOG_FILE Path and base log filename, extension can be .txt, .asc, .csv, .db, .npz -v How much information do you want to see at the command line? You can add several of these e.g., -vv is DEBUG -c CHANNEL, --channel CHANNEL Most backend interfaces require some sort of channel. For example with the serial interface the channel might be a rfcomm device: "/dev/rfcomm0" With the socketcan interfaces valid channel examples include: "can0", "vcan0" -i {iscan,slcan,virtual,socketcan_ctypes,usb2can,ixxat,socketcan_native,kvaser,neovi,vector,nican,pcan,serial,remote,socketcan}, --interface {iscan,slcan,virtual,socketcan_ctypes,usb2can,ixxat,socketcan_native,kvaser,neovi,vector,nican,pcan,serial,remote,socketcan} Specify the backend CAN interface to use. If left blank, fall back to reading from configuration files. --filter ... Comma separated filters can be specified for the given CAN interface: : (matches when & mask == can_id & mask) ~ (matches when & mask != can_id & mask) -b BITRATE, --bitrate BITRATE Bitrate to use for the CAN bus. can.player ---------- Command line help (``python -m can.player --help``):: usage: python -m can.player [-h] [-f LOG_FILE] [-v] [-c CHANNEL] [-i {kvaser,virtual,slcan,nican,neovi,ixxat,serial,usb2can,socketcan_ctypes,remote,socketcan_native,iscan,vector,pcan,socketcan}] [-b BITRATE] [--ignore-timestamps] [-g GAP] [-s SKIP] input-file Replay CAN traffic positional arguments: input-file The file to replay. Supported types: .db, .blf optional arguments: -h, --help show this help message and exit -f LOG_FILE, --file_name LOG_FILE Path and base log filename, extension can be .txt, .asc, .csv, .db, .npz -v Also print can frames to stdout. You can add several of these to enable debugging -c CHANNEL, --channel CHANNEL Most backend interfaces require some sort of channel. For example with the serial interface the channel might be a rfcomm device: "/dev/rfcomm0" With the socketcan interfaces valid channel examples include: "can0", "vcan0" -i {kvaser,virtual,slcan,nican,neovi,ixxat,serial,usb2can,socketcan_ctypes,remote,socketcan_native,iscan,vector,pcan,socketcan}, --interface {kvaser,virtual,slcan,nican,neovi,ixxat,serial,usb2can,socketcan_ctypes,remote,socketcan_native,iscan,vector,pcan,socketcan} Specify the backend CAN interface to use. If left blank, fall back to reading from configuration files. -b BITRATE, --bitrate BITRATE Bitrate to use for the CAN bus. --ignore-timestamps Ignore timestamps (send all frames immediately with minimum gap between frames) -g GAP, --gap GAP minimum time between replayed frames -s SKIP, --skip SKIP skip gaps greater than 's' seconds python-can-2.0.0/doc/bus.rst0000644000175000017500000000176713223535415016107 0ustar brianbrian00000000000000.. _bus: Bus --- The :class:`~can.Bus` class, as the name suggests, provides an abstraction of a CAN bus. The bus provides a wrapper around a physical or virtual CAN Bus. Filtering ''''''''' Message filtering can be set up for each bus. Where the interface supports it, this is carried out in the hardware or kernel layer - not in Python. API '''' .. autoclass:: can.BusABC :members: :special-members: __iter__ .. autoclass:: can.interface.Bus :members: :special-members: __iter__ Transmitting '''''''''''' Writing to the bus is done by calling the :meth:`~can.BusABC.send()` method and passing a :class:`~can.Message` object. Receiving ''''''''' Reading from the bus is achieved by either calling the :meth:`~can.BusABC.recv` method or by directly iterating over the bus:: for msg in bus: print(msg.data) Alternatively the :class:`~can.Listener` api can be used, which is a list of :class:`~can.Listener` subclasses that receive notifications when new messages arrive. python-can-2.0.0/doc/configuration.rst0000644000175000017500000000626013223535415020156 0ustar brianbrian00000000000000Configuration ============= Usually this library is used with a particular CAN interface, this can be specified in code, read from configuration files or environment variables. See :func:`can.util.load_config` for implementation. In Code ------- The ``can`` object exposes an ``rc`` dictionary which can be used to set the **interface** and **channel** before importing from ``can.interfaces``. :: import can can.rc['interface'] = 'socketcan' can.rc['channel'] = 'vcan0' can.rc['bitrate'] = 500000 from can.interfaces.interface import Bus bus = Bus() You can also specify the interface and channel for each Bus instance:: import can bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=500000) Configuration File ------------------ On Linux systems the config file is searched in the following paths: 1. ``~/can.conf`` 2. ``/etc/can.conf`` 3. ``$HOME/.can`` 4. ``$HOME/.canrc`` On Windows systems the config file is searched in the following paths: 1. ``~/can.conf`` 1. ``can.ini`` (current working directory) 2. ``$APPDATA/can.ini`` The configuration file sets the default interface and channel: :: [default] interface = channel = bitrate = Environment Variables --------------------- Configuration can be pulled from these environmental variables: * CAN_INTERFACE * CAN_CHANNEL * CAN_BITRATE Interface Names --------------- Lookup table of interface names: +---------------------+-------------------------------------+ | Name | Documentation | +=====================+=====================================+ | ``"socketcan"`` | :doc:`interfaces/socketcan` | +---------------------+-------------------------------------+ | ``"kvaser"`` | :doc:`interfaces/kvaser` | +---------------------+-------------------------------------+ | ``"serial"`` | :doc:`interfaces/serial` | +---------------------+-------------------------------------+ | ``"slcan"`` | :doc:`interfaces/slcan` | +---------------------+-------------------------------------+ | ``"ixxat"`` | :doc:`interfaces/ixxat` | +---------------------+-------------------------------------+ | ``"pcan"`` | :doc:`interfaces/pcan` | +---------------------+-------------------------------------+ | ``"usb2can"`` | :doc:`interfaces/usb2can` | +---------------------+-------------------------------------+ | ``"nican"`` | :doc:`interfaces/nican` | +---------------------+-------------------------------------+ | ``"iscan"`` | :doc:`interfaces/iscan` | +---------------------+-------------------------------------+ | ``"neovi"`` | :doc:`interfaces/neovi` | +---------------------+-------------------------------------+ | ``"vector"`` | :doc:`interfaces/vector` | +---------------------+-------------------------------------+ | ``"virtual"`` | :doc:`interfaces/virtual` | +---------------------+-------------------------------------+ python-can-2.0.0/doc/development.rst0000644000175000017500000000523013223535415017625 0ustar brianbrian00000000000000Developer's Overview ==================== Contributing ------------ Contribute to source code, documentation, examples and report issues: https://github.com/hardbyte/python-can Creating a Release ------------------ - Release from the ``master`` branch. - Update the library version in ``__init__.py`` using `semantic versioning `__. - Run all tests and examples against available hardware. - Update `CONTRIBUTORS.txt` with any new contributors. - Sanity check that documentation has stayed inline with code. For large changes update ``doc/history.rst`` - Create a temporary virtual environment. Run ``python setup.py install`` and ``python setup.py test`` - Create and upload the distribution: ``python setup.py sdist bdist_wheel`` - Sign the packages with gpg ``gpg --detach-sign -a dist/python_can-X.Y.Z-py3-none-any.whl`` - Upload with twine ``twine upload dist/python-can-X.Y.Z*`` - In a new virtual env check that the package can be installed with pip: ``pip install python-can==X.Y.Z`` - Create a new tag in the repository. - Check the release on PyPi and github. Code Structure -------------- The modules in ``python-can`` are: +---------------------------------+------------------------------------------------------+ |Module | Description | +=================================+======================================================+ |:doc:`interfaces ` | Contains interface dependent code. | +---------------------------------+------------------------------------------------------+ |:doc:`bus ` | Contains the interface independent Bus object. | +---------------------------------+------------------------------------------------------+ |:doc:`CAN ` | Contains modules to emulate a CAN system, such as a | | | time stamps, read/write streams and listeners. | +---------------------------------+------------------------------------------------------+ |:doc:`message ` | Contains the interface independent Message object. | +---------------------------------+------------------------------------------------------+ |:doc:`notifier ` | An object which can be used to notify listeners. | +---------------------------------+------------------------------------------------------+ |:doc:`broadcastmanager ` | Contains interface independent broadcast manager | | | code. | +---------------------------------+------------------------------------------------------+ python-can-2.0.0/doc/history.rst0000644000175000017500000000252613223535415017011 0ustar brianbrian00000000000000History and Roadmap =================== Background ---------- Originally written at `Dynamic Controls `__ for internal use testing and prototyping wheelchair components. Maintenance was taken over and the project was open sourced by Brian Thorne in 2010. Acknowledgements ---------------- Originally written by Ben Powell as a thin wrapper around the Kvaser SDK to support the leaf device. Support for linux socketcan was added by Rose Lu as a summer coding project in 2011. The socketcan interface was helped immensely by Phil Dixon who wrote a leaf-socketcan driver for Linux. The pcan interface was contributed by Albert Bloomfield in 2013. The usb2can interface was contributed by Joshua Villyard in 2015 The IXXAT VCI interface was contributed by Giuseppe Corbelli and funded by `Weightpack `__ in 2016 The NI-CAN and virtual interfaces plus the ASCII and BLF loggers were contributed by Christian Sandberg in 2016 and 2017. The BLF format is based on a C++ library by Toby Lorenz. The slcan interface, ASCII listener and log logger and listener were contributed by Eduard Bröcker in 2017. Support for CAN within Python ----------------------------- The 'socket' module contains support for SocketCAN from Python 3.3. From Python 3.4 broadcast management commands are natively supported. python-can-2.0.0/doc/index.rst0000644000175000017500000000334513223535415016417 0ustar brianbrian00000000000000python-can ========== The **python-can** library provides Controller Area Network support for `Python `__, providing common abstractions to different hardware devices, and a suite of utilities for sending and receiving messages on a CAN bus. **python-can** runs any where Python runs; from high powered computers with commercial `CAN to usb` devices right down to low powered devices running linux such as a BeagleBone or RaspberryPi. More concretely, some example uses of the library: - Passively logging what occurs on a CAN bus. For example monitoring a commercial vehicle using its **OBD-II** port. - Testing of hardware that interacts via CAN. Modules found in modern cars, motocycles, boats, and even wheelchairs have had components tested from Python using this library. - Prototyping new hardware modules or software algorithms in-the-loop. Easily interact with an existing bus. - Creating virtual modules to prototype CAN bus communication. Brief example of the library in action: connecting to a CAN bus, creating and sending a message: .. literalinclude:: ../examples/send_one.py :language: python :linenos: Contents: .. toctree:: :maxdepth: 2 installation configuration api interfaces bin development history Known Bugs ~~~~~~~~~~ See the project `bug tracker`_ on github. Patches and pull requests very welcome! .. admonition:: Documentation generated |today| .. _Python: http://www.python.org .. _Setuptools: http://pypi.python.org/pypi/setuptools .. _Pip: http://pip.openplans.org/ .. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall .. _IPython: http://ipython.scipy.org .. _bug tracker: https://github.com/hardbyte/python-can/issues python-can-2.0.0/doc/installation.rst0000644000175000017500000000523713223535415020013 0ustar brianbrian00000000000000Installation ============ Install ``can`` with ``pip``: :: $ pip install python-can As most likely you will want to interface with some hardware, you may also have to install platform dependencies. Be sure to check any other specifics for your hardware in :doc:`interfaces`. GNU/Linux dependencies ---------------------- Reasonably modern Linux Kernels (2.6.25 or newer) have an implementation of ``socketcan``. This version of python-can will directly use socketcan if called with Python 3.3 or greater, otherwise that interface is used via ctypes. Windows dependencies -------------------- Kvaser ~~~~~~ To install ``python-can`` using the Kvaser CANLib SDK as the backend: 1. Install the `latest stable release of Python `__. 2. Install `Kvaser's latest Windows CANLib drivers `__. 3. Test that Kvaser's own tools work to ensure the driver is properly installed and that the hardware is working. PCAN ~~~~ Download and install the latest driver for your interface from `PEAK-System's download page `__. Note that PCANBasic API timestamps count seconds from system startup. To convert these to epoch times, the uptime library is used. If it is not available, the times are returned as number of seconds from system startup. To install the uptime library, run ``pip install uptime``. This library can take advantage of the `Python for Windows Extensions `__ library if installed. It will be used to get notified of new messages instead of the CPU intensive polling that will otherwise have be used. IXXAT ~~~~~ To install ``python-can`` using the IXXAT VCI V3 SDK as the backend: 1. Install `IXXAT's latest Windows VCI V3 SDK drivers `__. 2. Test that IXXAT's own tools (i.e. MiniMon) work to ensure the driver is properly installed and that the hardware is working. NI-CAN ~~~~~~ Download and install the NI-CAN drivers from `National Instruments `__. Currently the driver only supports 32-bit Python on Windows. neoVI ~~~~~ See :doc:`interfaces/neovi`. Installing python-can in development mode ----------------------------------------- A "development" install of this package allows you to make changes locally or pull updates from the Mercurial repository and use them without having to reinstall. Download or clone the source repository then: :: python setup.py develop python-can-2.0.0/doc/interfaces.rst0000644000175000017500000000122413223535415017425 0ustar brianbrian00000000000000CAN Interface Modules --------------------- **python-can** hides the low-level, device-specific interfaces to controller area network adapters in interface dependant modules. However as each hardware device is different, you should carefully go through your interface's documentation. The available interfaces are: .. toctree:: :maxdepth: 1 interfaces/socketcan interfaces/kvaser interfaces/serial interfaces/slcan interfaces/ixxat interfaces/pcan interfaces/usb2can interfaces/nican interfaces/iscan interfaces/neovi interfaces/vector interfaces/virtual The *Interface Names* are listed in :doc:`configuration`. python-can-2.0.0/doc/listeners.rst0000644000175000017500000000560213223535415017316 0ustar brianbrian00000000000000Listeners ========= Listener -------- The Listener class is an "abstract" base class for any objects which wish to register to receive notifications of new messages on the bus. A Listener can be used in two ways; the default is to **call** the Listener with a new message, or by calling the method **on_message_received**. Listeners are registered with :ref:`notifier` object(s) which ensure they are notified whenever a new message is received. Subclasses of Listener that do not override **on_message_received** will cause `NotImplementedError` to be thrown when a message is received on the CAN bus. .. autoclass:: can.Listener :members: BufferedReader -------------- .. autoclass:: can.BufferedReader :members: Logger ------ The :class:`can.Logger` uses the following :class:`can.Listener` types to create *.asc*, *.csv* and *.db* files with the messages received. .. autoclass:: can.Logger :members: Printer ------- .. autoclass:: can.Printer :members: CSVWriter --------- .. autoclass:: can.CSVWriter :members: SqliteWriter ------------ .. autoclass:: can.SqliteWriter :members: ASC (.asc Logging format) ------------------------- ASCWriter logs CAN data to an ASCII log file compatible with other CAN tools such as Vector CANalyzer/CANoe and other. Since no official specification exists for the format, it has been reverse- engineered from existing log files. One description of the format can be found `here `_. .. autoclass:: can.ASCWriter :members: ASCReader reads CAN data from ASCII log files .asc as further references can-utils can be used: `asc2log `_, `log2asc `_. .. autoclass:: can.ASCReader :members: Log (.log can-utils Logging format) ----------------------------------- CanutilsLogWriter logs CAN data to an ASCII log file compatible with `can-utils ` As specification following references can-utils can be used: `asc2log `_, `log2asc `_. .. autoclass:: can.io.CanutilsLogWriter :members: CanutilsLogReader reads CAN data from ASCII log files .log .. autoclass:: can.io.CanutilsLogReader :members: BLF (Binary Logging Format) --------------------------- Implements support for BLF (Binary Logging Format) which is a proprietary CAN log format from Vector Informatik GmbH. The data is stored in a compressed format which makes it very compact. .. autoclass:: can.BLFWriter :members: .. autoclass:: can.BLFReader :members: python-can-2.0.0/doc/message.rst0000644000175000017500000001174613223535415016740 0ustar brianbrian00000000000000Message ======= .. module:: can .. autoclass:: Message One can instantiate a :class:`~can.Message` defining data, and optional arguments for all attributes such as arbitration ID, flags, and timestamp. >>> from can import Message >>> test = Message(data=[1, 2, 3, 4, 5]) >>> test.data bytearray(b'\x01\x02\x03\x04\x05') >>> test.dlc 5 >>> print(test) Timestamp: 0.000000 ID: 00000000 010 DLC: 5 01 02 03 04 05 The :attr:`~can.Message.arbitration_id` field in a CAN message may be either 11 bits (standard addressing, CAN 2.0A) or 29 bits (extended addressing, CAN 2.0B) in length, and ``python-can`` exposes this difference with the :attr:`~can.Message.is_extended_id` attribute. .. attribute:: arbitration_id :type: int The frame identifier used for arbitration on the bus. The arbitration ID can take an int between 0 and the maximum value allowed depending on the is_extended_id flag (either 2\ :sup:`11` - 1 for 11-bit IDs, or 2\ :sup:`29` - 1 for 29-bit identifiers). >>> print(Message(extended_id=False, arbitration_id=100)) Timestamp: 0.000000 ID: 0064 S DLC: 0 .. attribute:: data :type: bytearray The data parameter of a CAN message is exposed as a **bytearray** with length between 0 and 8. >>> example_data = bytearray([1, 2, 3]) >>> print(Message(data=example_data)) Timestamp: 0.000000 ID: 00000000 X DLC: 3 01 02 03 A :class:`~can.Message` can also be created with bytes, or lists of ints: >>> m1 = Message(data=[0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]) >>> print(m1.data) bytearray(b'deadbeef') >>> m2 = Message(data=b'deadbeef') >>> m2.data bytearray(b'deadbeef') .. attribute:: dlc :type: int The :abbr:`DLC (Data Link Count)` parameter of a CAN message is an integer between 0 and 8 representing the frame payload length. >>> m = Message(data=[1, 2, 3]) >>> m.dlc 3 .. note:: The DLC value does not necessarily define the number of bytes of data in a message. Its purpose varies depending on the frame type - for data frames it represents the amount of data contained in the message, in remote frames it represents the amount of data being requested. .. attribute:: is_extended_id :type: bool This flag controls the size of the :attr:`~can.Message.arbitration_id` field. >>> print(Message(extended_id=False)) Timestamp: 0.000000 ID: 0000 S DLC: 0 >>> print(Message(extended_id=True)) Timestamp: 0.000000 ID: 00000000 X DLC: 0 Previously this was exposed as `id_type`. .. attribute:: is_error_frame :type: bool This boolean parameter indicates if the message is an error frame or not. >>> print(Message(is_error_frame=True)) Timestamp: 0.000000 ID: 00000000 X E DLC: 0 .. attribute:: is_remote_frame :type: boolean This boolean attribute indicates if the message is a remote frame or a data frame, and modifies the bit in the CAN message's flags field indicating this. >>> print(Message(is_remote_frame=True)) Timestamp: 0.000000 ID: 00000000 X R DLC: 0 .. attribute:: timestamp :type: float The timestamp field in a CAN message is a floating point number representing when the message was received since the epoch in seconds. Where possible this will be timestamped in hardware. .. method:: __str__ A string representation of a CAN message: >>> from can import Message >>> test = Message() >>> print(test) Timestamp: 0.000000 ID: 00000000 X DLC: 0 >>> test2 = Message(data=[1, 2, 3, 4, 5]) >>> print(test2) Timestamp: 0.000000 ID: 00000000 X DLC: 5 01 02 03 04 05 The fields in the printed message are (in order): - timestamp, - arbitration ID, - flags, - dlc, - and data. The flags field is represented as one, two or three letters: - X if the :attr:`~can.Message.is_extended_id` attribute is set, otherwise S, - E if the :attr:`~can.Message.is_error_frame` attribute is set, - R if the :attr:`~can.Message.is_remote_frame` attribute is set. The arbitration ID field is represented as either a four or eight digit hexadecimal number depending on the length of the arbitration ID (11-bit or 29-bit). Each of the bytes in the data field (when present) are represented as two-digit hexadecimal numbers. python-can-2.0.0/python_can.egg-info/0000755000175000017500000000000013223535610017635 5ustar brianbrian00000000000000python-can-2.0.0/python_can.egg-info/PKG-INFO0000644000175000017500000000463513223535610020742 0ustar brianbrian00000000000000Metadata-Version: 1.0 Name: python-can Version: 2.0.0 Summary: Controller Area Network interface module for Python Home-page: https://github.com/hardbyte/python-can Author: Brian Thorne Author-email: brian@thorne.link License: LGPL v3 Description: python-can ========== |release| |docs| |build| .. |release| image:: https://img.shields.io/pypi/v/python-can.svg :target: https://pypi.python.org/pypi/python-can/ :alt: Latest Version .. |docs| image:: https://readthedocs.org/projects/python-can/badge/?version=stable :target: https://python-can.readthedocs.io/en/stable/ :alt: Documentation Status .. |build| image:: https://travis-ci.org/hardbyte/python-can.svg?branch=develop :target: https://travis-ci.org/hardbyte/python-can :alt: CI Server for develop branch The **C**\ ontroller **A**\ rea **N**\ etwork is a bus standard designed to allow microcontrollers and devices to communicate with each other. It has priority based bus arbitration, reliable deterministic communication. It is used in cars, trucks, boats, wheelchairs and more. The ``can`` package provides controller area network support for Python developers; providing `common abstractions to different hardware devices`, and a suite of utilities for sending and receiving messages on a can bus. The library supports Python 2.7, Python 3.3+ and runs on Mac, Linux and Windows. You can find more information in the documentation, online at `python-can.readthedocs.org `__. Discussion ---------- If you run into bugs, you can file them in our `issue tracker `__. There is also a `python-can `__ mailing list for development discussion. `Stackoverflow `__ has several questions and answers tagged with ``python+can``. Wherever we interact, we strive to follow the `Python Community Code of Conduct `__. Platform: UNKNOWN python-can-2.0.0/python_can.egg-info/SOURCES.txt0000644000175000017500000000454313223535610021527 0ustar brianbrian00000000000000CONTRIBUTORS.txt LICENSE.txt MANIFEST.in README.rst requirements.txt setup.py test.txt can/CAN.py can/__init__.py can/broadcastmanager.py can/bus.py can/ctypesutil.py can/interface.py can/listener.py can/logger.py can/message.py can/notifier.py can/player.py can/util.py can/interfaces/__init__.py can/interfaces/interface.py can/interfaces/iscan.py can/interfaces/nican.py can/interfaces/slcan.py can/interfaces/virtual.py can/interfaces/ixxat/__init__.py can/interfaces/ixxat/canlib.py can/interfaces/ixxat/constants.py can/interfaces/ixxat/exceptions.py can/interfaces/ixxat/structures.py can/interfaces/kvaser/__init__.py can/interfaces/kvaser/argument_parser.py can/interfaces/kvaser/canlib.py can/interfaces/kvaser/constants.py can/interfaces/neovi_api/__init__.py can/interfaces/neovi_api/neovi_api.py can/interfaces/pcan/PCANBasic.py can/interfaces/pcan/__init__.py can/interfaces/pcan/pcan.py can/interfaces/serial/__init__.py can/interfaces/serial/serial_can.py can/interfaces/socketcan/__init__.py can/interfaces/socketcan/socketcan_common.py can/interfaces/socketcan/socketcan_constants.py can/interfaces/socketcan/socketcan_ctypes.py can/interfaces/socketcan/socketcan_native.py can/interfaces/usb2can/__init__.py can/interfaces/usb2can/serial_selector.py can/interfaces/usb2can/usb2canInterface.py can/interfaces/usb2can/usb2canabstractionlayer.py can/interfaces/vector/__init__.py can/interfaces/vector/canlib.py can/interfaces/vector/exceptions.py can/interfaces/vector/vxlapi.py can/io/__init__.py can/io/asc.py can/io/blf.py can/io/csv.py can/io/log.py can/io/logger.py can/io/player.py can/io/sqlite.py can/io/stdout.py doc/api.rst doc/bcm.rst doc/bin.rst doc/bus.rst doc/configuration.rst doc/development.rst doc/history.rst doc/index.rst doc/installation.rst doc/interfaces.rst doc/listeners.rst doc/message.rst doc/interfaces/iscan.rst doc/interfaces/ixxat.rst doc/interfaces/kvaser.rst doc/interfaces/neovi.rst doc/interfaces/nican.rst doc/interfaces/pcan.rst doc/interfaces/serial.rst doc/interfaces/slcan.rst doc/interfaces/socketcan.rst doc/interfaces/socketcan_ctypes.rst doc/interfaces/socketcan_native.rst doc/interfaces/usb2can.rst doc/interfaces/vector.rst doc/interfaces/virtual.rst python_can.egg-info/PKG-INFO python_can.egg-info/SOURCES.txt python_can.egg-info/dependency_links.txt python_can.egg-info/requires.txt python_can.egg-info/top_level.txt test/test_kvaser.pypython-can-2.0.0/python_can.egg-info/dependency_links.txt0000644000175000017500000000000113223535610023703 0ustar brianbrian00000000000000 python-can-2.0.0/python_can.egg-info/requires.txt0000644000175000017500000000002313223535610022230 0ustar brianbrian00000000000000 [serial] pyserial python-can-2.0.0/python_can.egg-info/top_level.txt0000644000175000017500000000000413223535610022361 0ustar brianbrian00000000000000can python-can-2.0.0/test/0000755000175000017500000000000013223535610014760 5ustar brianbrian00000000000000python-can-2.0.0/test/test_kvaser.py0000644000175000017500000001410113223535415017664 0ustar brianbrian00000000000000#!/usr/bin/env python3 import ctypes import unittest import time import logging logging.basicConfig(level=logging.DEBUG) import can from can.interfaces.kvaser import canlib from can.interfaces.kvaser import constants try: from unittest.mock import Mock, patch except ImportError: from mock import patch, Mock class KvaserTest(unittest.TestCase): def setUp(self): canlib.canGetNumberOfChannels = Mock(return_value=1) canlib.canOpenChannel = Mock(return_value=0) canlib.canIoCtl = Mock(return_value=0) canlib.kvReadTimer = Mock() canlib.canSetBusParams = Mock() canlib.canBusOn = Mock() canlib.canBusOff = Mock() canlib.canClose = Mock() canlib.canSetBusOutputControl = Mock() canlib.canGetChannelData = Mock() canlib.canSetAcceptanceFilter = Mock() canlib.canWriteSync = Mock() canlib.canWrite = self.canWrite canlib.canReadWait = self.canReadWait self.msg = {} self.msg_in_cue = None self.bus = can.interface.Bus(channel=0, bustype='kvaser') def tearDown(self): if self.bus: self.bus.shutdown() self.bus = None def test_bus_creation(self): self.assertIsInstance(self.bus, canlib.KvaserBus) self.assertTrue(canlib.canGetNumberOfChannels.called) self.assertTrue(canlib.canOpenChannel.called) self.assertTrue(canlib.canBusOn.called) def test_bus_shutdown(self): self.bus.shutdown() self.assertTrue(canlib.canBusOff.called) self.assertTrue(canlib.canClose.called) def test_filter_setup(self): # No filter in constructor expected_args = [ ((0, 0, 0, 0),), # Disable filtering STD on read handle ((0, 0, 0, 1),), # Disable filtering EXT on read handle ((0, 0, 0, 0),), # Disable filtering STD on write handle ((0, 0, 0, 1),), # Disable filtering EXT on write handle ] self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list, expected_args) # One filter, will be handled by canlib canlib.canSetAcceptanceFilter.reset_mock() self.bus.set_filters([ {'can_id': 0x8, 'can_mask': 0xff, 'extended': True} ]) expected_args = [ ((0, 0x8, 0xff, 1),), # Enable filtering EXT on read handle ((0, 0x8, 0xff, 1),), # Enable filtering EXT on write handle ] self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list, expected_args) # Multiple filters, will be handled in Python canlib.canSetAcceptanceFilter.reset_mock() multiple_filters = [ {'can_id': 0x8, 'can_mask': 0xff}, {'can_id': 0x9, 'can_mask': 0xff} ] self.bus.set_filters(multiple_filters) expected_args = [ ((0, 0, 0, 0),), # Disable filtering STD on read handle ((0, 0, 0, 1),), # Disable filtering EXT on read handle ((0, 0, 0, 0),), # Disable filtering STD on write handle ((0, 0, 0, 1),), # Disable filtering EXT on write handle ] self.assertEqual(canlib.canSetAcceptanceFilter.call_args_list, expected_args) def test_send_extended(self): msg = can.Message( arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4], extended_id=True) self.bus.send(msg) self.assertEqual(self.msg['arb_id'], 0xc0ffee) self.assertEqual(self.msg['dlc'], 7) self.assertEqual(self.msg['flags'], constants.canMSG_EXT) self.assertSequenceEqual(self.msg['data'], [0, 25, 0, 1, 3, 1, 4]) def test_send_standard(self): msg = can.Message( arbitration_id=0x321, data=[50, 51], extended_id=False) self.bus.send(msg) self.assertEqual(self.msg['arb_id'], 0x321) self.assertEqual(self.msg['dlc'], 2) self.assertEqual(self.msg['flags'], constants.canMSG_STD) self.assertSequenceEqual(self.msg['data'], [50, 51]) def test_recv_no_message(self): self.assertEqual(self.bus.recv(), None) def test_recv_extended(self): self.msg_in_cue = can.Message( arbitration_id=0xc0ffef, data=[1, 2, 3, 4, 5, 6, 7, 8], extended_id=True) now = time.time() msg = self.bus.recv() self.assertEqual(msg.arbitration_id, 0xc0ffef) self.assertEqual(msg.dlc, 8) self.assertEqual(msg.id_type, True) self.assertSequenceEqual(msg.data, self.msg_in_cue.data) self.assertTrue(now - 1 < msg.timestamp < now + 1) def test_recv_standard(self): self.msg_in_cue = can.Message( arbitration_id=0x123, data=[100, 101], extended_id=False) msg = self.bus.recv() self.assertEqual(msg.arbitration_id, 0x123) self.assertEqual(msg.dlc, 2) self.assertEqual(msg.id_type, False) self.assertSequenceEqual(msg.data, [100, 101]) def canWrite(self, handle, arb_id, buf, dlc, flags): self.msg['arb_id'] = arb_id self.msg['dlc'] = dlc self.msg['flags'] = flags self.msg['data'] = bytearray(buf._obj) def canReadWait(self, handle, arb_id, data, dlc, flags, timestamp, timeout): if not self.msg_in_cue: return constants.canERR_NOMSG arb_id._obj.value = self.msg_in_cue.arbitration_id dlc._obj.value = self.msg_in_cue.dlc data._obj.raw = self.msg_in_cue.data flags_temp = 0 if self.msg_in_cue.id_type: flags_temp |= constants.canMSG_EXT else: flags_temp |= constants.canMSG_STD if self.msg_in_cue.is_remote_frame: flags_temp |= constants.canMSG_RTR if self.msg_in_cue.is_error_frame: flags_temp |= constants.canMSG_ERROR_FRAME flags._obj.value = flags_temp timestamp._obj.value = 0 return constants.canOK if __name__ == '__main__': unittest.main() python-can-2.0.0/CONTRIBUTORS.txt0000644000175000017500000000072013223535415016501 0ustar brianbrian00000000000000Ben Powell Brian Thorne Geert Linders Mark Catley Phillip Dixon Rose Lu Karl van Workum Albert Bloomfield Sam Bristow Ethan Zonca Robert Kaye Andrew Beal Jonas Frid Tynan McAuley Bruno Pennati Jack Jester-Weinstein Joshua Villyard Giuseppe Corbelli Christian Sandberg Eduard Bröcker Boris Wenzlaff python-can-2.0.0/LICENSE.txt0000644000175000017500000001721013113515201015615 0ustar brianbrian00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. python-can-2.0.0/MANIFEST.in0000644000175000017500000000005113113515201015523 0ustar brianbrian00000000000000include *.txt recursive-include doc *.rstpython-can-2.0.0/README.rst0000644000175000017500000000342213223535415015474 0ustar brianbrian00000000000000python-can ========== |release| |docs| |build| .. |release| image:: https://img.shields.io/pypi/v/python-can.svg :target: https://pypi.python.org/pypi/python-can/ :alt: Latest Version .. |docs| image:: https://readthedocs.org/projects/python-can/badge/?version=stable :target: https://python-can.readthedocs.io/en/stable/ :alt: Documentation Status .. |build| image:: https://travis-ci.org/hardbyte/python-can.svg?branch=develop :target: https://travis-ci.org/hardbyte/python-can :alt: CI Server for develop branch The **C**\ ontroller **A**\ rea **N**\ etwork is a bus standard designed to allow microcontrollers and devices to communicate with each other. It has priority based bus arbitration, reliable deterministic communication. It is used in cars, trucks, boats, wheelchairs and more. The ``can`` package provides controller area network support for Python developers; providing `common abstractions to different hardware devices`, and a suite of utilities for sending and receiving messages on a can bus. The library supports Python 2.7, Python 3.3+ and runs on Mac, Linux and Windows. You can find more information in the documentation, online at `python-can.readthedocs.org `__. Discussion ---------- If you run into bugs, you can file them in our `issue tracker `__. There is also a `python-can `__ mailing list for development discussion. `Stackoverflow `__ has several questions and answers tagged with ``python+can``. Wherever we interact, we strive to follow the `Python Community Code of Conduct `__. python-can-2.0.0/requirements.txt0000644000175000017500000000001113223535415017260 0ustar brianbrian00000000000000pyserial python-can-2.0.0/setup.py0000644000175000017500000000172313223535415015521 0ustar brianbrian00000000000000""" python-can requires the setuptools package to be installed. """ import re import logging from setuptools import setup, find_packages with open('can/__init__.py', 'r') as fd: version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) logging.basicConfig(level=logging.WARNING) setup( name="python-can", url="https://github.com/hardbyte/python-can", version=version, packages=find_packages(), author="Brian Thorne", author_email="brian@thorne.link", description="Controller Area Network interface module for Python", long_description=open('README.rst').read(), license="LGPL v3", package_data={ "": ["CONTRIBUTORS.txt", "LICENSE.txt"], "doc": ["*.*"] }, # Tests can be run using `python setup.py test` test_suite="nose.collector", tests_require=['mock', 'nose', 'pyserial'], extras_require={ 'serial': ['pyserial'] } ) python-can-2.0.0/test.txt0000644000175000017500000000000113150273751015514 0ustar brianbrian00000000000000 python-can-2.0.0/PKG-INFO0000644000175000017500000000463513223535610015106 0ustar brianbrian00000000000000Metadata-Version: 1.0 Name: python-can Version: 2.0.0 Summary: Controller Area Network interface module for Python Home-page: https://github.com/hardbyte/python-can Author: Brian Thorne Author-email: brian@thorne.link License: LGPL v3 Description: python-can ========== |release| |docs| |build| .. |release| image:: https://img.shields.io/pypi/v/python-can.svg :target: https://pypi.python.org/pypi/python-can/ :alt: Latest Version .. |docs| image:: https://readthedocs.org/projects/python-can/badge/?version=stable :target: https://python-can.readthedocs.io/en/stable/ :alt: Documentation Status .. |build| image:: https://travis-ci.org/hardbyte/python-can.svg?branch=develop :target: https://travis-ci.org/hardbyte/python-can :alt: CI Server for develop branch The **C**\ ontroller **A**\ rea **N**\ etwork is a bus standard designed to allow microcontrollers and devices to communicate with each other. It has priority based bus arbitration, reliable deterministic communication. It is used in cars, trucks, boats, wheelchairs and more. The ``can`` package provides controller area network support for Python developers; providing `common abstractions to different hardware devices`, and a suite of utilities for sending and receiving messages on a can bus. The library supports Python 2.7, Python 3.3+ and runs on Mac, Linux and Windows. You can find more information in the documentation, online at `python-can.readthedocs.org `__. Discussion ---------- If you run into bugs, you can file them in our `issue tracker `__. There is also a `python-can `__ mailing list for development discussion. `Stackoverflow `__ has several questions and answers tagged with ``python+can``. Wherever we interact, we strive to follow the `Python Community Code of Conduct `__. Platform: UNKNOWN python-can-2.0.0/setup.cfg0000644000175000017500000000004613223535610015622 0ustar brianbrian00000000000000[egg_info] tag_build = tag_date = 0