From 516b971bb3276a57c9667425fc7f2602d11901ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20N=C3=B8rgaard?= Date: Tue, 9 May 2017 11:53:40 +0200 Subject: [PATCH 01/10] Introduce CANAL interface * almost just a copy of the usb2can module which is badly named * the usb2can module is for now left unchanged but should be changed to use the new CANAL interface * TODO serial_selector update... --- can/interface.py | 3 +- can/interfaces/__init__.py | 5 +- can/interfaces/canal/__init__.py | 2 + can/interfaces/canal/canalInterface.py | 166 ++++++++++++++++++++++++ can/interfaces/canal/canal_wrapper.py | 147 +++++++++++++++++++++ can/interfaces/canal/serial_selector.py | 37 ++++++ 6 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 can/interfaces/canal/__init__.py create mode 100644 can/interfaces/canal/canalInterface.py create mode 100644 can/interfaces/canal/canal_wrapper.py create mode 100644 can/interfaces/canal/serial_selector.py diff --git a/can/interface.py b/can/interface.py index 174f03b58..f60061cfa 100644 --- a/can/interface.py +++ b/can/interface.py @@ -21,7 +21,8 @@ 'virtual': ('can.interfaces.virtual', 'VirtualBus'), 'neovi': ('can.interfaces.ics_neovi', 'NeoViBus'), 'vector': ('can.interfaces.vector', 'VectorBus'), - 'slcan': ('can.interfaces.slcan', 'slcanBus') + 'slcan': ('can.interfaces.slcan', 'slcanBus'), + 'canal': ('can.interfaces.canal', 'CanalBus'), } diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 1bd132731..56c7bec39 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- + """ Interfaces contain low level implementations that interact with CAN hardware. """ + from pkg_resources import iter_entry_points VALID_INTERFACES = set(['kvaser', 'serial', 'pcan', 'socketcan_native', 'socketcan_ctypes', 'socketcan', 'usb2can', 'ixxat', 'nican', 'iscan', 'vector', 'virtual', 'neovi', - 'slcan']) - + 'slcan', 'canal']) VALID_INTERFACES.update(set([ interface.name for interface in iter_entry_points('python_can.interface') diff --git a/can/interfaces/canal/__init__.py b/can/interfaces/canal/__init__.py new file mode 100644 index 000000000..2e8cb8ad0 --- /dev/null +++ b/can/interfaces/canal/__init__.py @@ -0,0 +1,2 @@ +from can.interfaces.canal.canalInterface import CanalBus +from can.interfaces.canal.canal_wrapper import CanalWrapper diff --git a/can/interfaces/canal/canalInterface.py b/can/interfaces/canal/canalInterface.py new file mode 100644 index 000000000..68af0af20 --- /dev/null +++ b/can/interfaces/canal/canalInterface.py @@ -0,0 +1,166 @@ +# this interface is for windows only, otherwise use socketCAN + +import logging + +from can import BusABC, Message +from can.interfaces.canal.canal_wrapper 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.canal') + + +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 CanalBus(BusABC): + """Interface to a CANAL Bus. + + Note the 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 str dll: + dll with CANAL API to load + + :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 CANAL abstraction layer. + """ + + def __init__(self, channel, *args, **kwargs): + + # TODO force specifying dll + if 'dll' in kwargs: + dll = kwargs["dll"] + else: + raise Exception("please specify a CANAL dll to load, e.g. 'usb2can.dll'") + + self.can = CanalWrapper(dll) + + # 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.canal.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) diff --git a/can/interfaces/canal/canal_wrapper.py b/can/interfaces/canal/canal_wrapper.py new file mode 100644 index 000000000..4fea0dd24 --- /dev/null +++ b/can/interfaces/canal/canal_wrapper.py @@ -0,0 +1,147 @@ +# 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.canal') + +# 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 CanalWrapper: + """A low level wrapper around the CANAL library. + """ + def __init__(self, dll): + self.__m_dllBasic = windll.LoadLibrary(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 diff --git a/can/interfaces/canal/serial_selector.py b/can/interfaces/canal/serial_selector.py new file mode 100644 index 000000000..46808852a --- /dev/null +++ b/can/interfaces/canal/serial_selector.py @@ -0,0 +1,37 @@ +import 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 From 3c6888e1f8f777f5fbe2d59f9b5bea1ef4ac84c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20N=C3=B8rgaard?= Date: Tue, 9 May 2017 13:22:00 +0200 Subject: [PATCH 02/10] make the serial device autodetect work --- can/interfaces/canal/canalInterface.py | 12 +++++++++++- can/interfaces/canal/canal_wrapper.py | 2 ++ can/interfaces/canal/serial_selector.py | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/can/interfaces/canal/canalInterface.py b/can/interfaces/canal/canalInterface.py index 68af0af20..228123423 100644 --- a/can/interfaces/canal/canalInterface.py +++ b/can/interfaces/canal/canalInterface.py @@ -87,6 +87,12 @@ class CanalBus(BusABC): Bitrate of channel in bit/s. Values will be limited to a maximum of 1000 Kb/s. Default is 500 Kbs + :param str serial (optional) + device serial to use for the CANAL open call + + :param str serialMatcher (optional) + search string for automatic detection of the device serial + :param int flags: Flags to directly pass to open function of the CANAL abstraction layer. """ @@ -114,8 +120,12 @@ def __init__(self, channel, *args, **kwargs): elif channel is not None: deviceID = channel else: + # autodetect device from can.interfaces.canal.serial_selector import serial - deviceID = serial() + if 'serialMatcher' in kwargs: + deviceID = serial(kwargs["serialMatcher"]) + else: + deviceID = serial() # set baudrate in kb/s from bitrate # (eg:500000 bitrate must be 500) diff --git a/can/interfaces/canal/canal_wrapper.py b/can/interfaces/canal/canal_wrapper.py index 4fea0dd24..d8bf993c9 100644 --- a/can/interfaces/canal/canal_wrapper.py +++ b/can/interfaces/canal/canal_wrapper.py @@ -60,6 +60,8 @@ def __init__(self, dll): def open(self, pConfigureStr, flags): try: + # unicode is not good + pConfigureStr = pConfigureStr.encode('ascii', 'ignore') res = self.__m_dllBasic.CanalOpen(pConfigureStr, flags) return res except: diff --git a/can/interfaces/canal/serial_selector.py b/can/interfaces/canal/serial_selector.py index 46808852a..1cf8a796d 100644 --- a/can/interfaces/canal/serial_selector.py +++ b/can/interfaces/canal/serial_selector.py @@ -21,7 +21,7 @@ def WMIDateStringToDate(dtmDate): return strDateTime -def serial(): +def serial(serialMatcher = "PID_6001"): strComputer = "." objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator") objSWbemServices = objWMIService.ConnectServer(strComputer, "root\cimv2") @@ -30,7 +30,7 @@ def serial(): for objItem in colItems: string = objItem.Dependent # find based on beginning of serial - if "ED" in string: + if serialMatcher in string: # print "Dependent:" + ` objItem.Dependent` string = string[len(string) - 9:len(string) - 1] From 10b973db5f4793e9ab1a2a6ed63ab9b1215ad17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20N=C3=B8rgaard?= Date: Tue, 9 May 2017 13:46:10 +0200 Subject: [PATCH 03/10] Canal interface: Raise CanError on various errors: * autodetect device ID failed * CanalOpen failed --- can/interfaces/canal/canalInterface.py | 5 +++++ can/interfaces/canal/canal_wrapper.py | 2 ++ can/interfaces/canal/serial_selector.py | 2 ++ 3 files changed, 9 insertions(+) diff --git a/can/interfaces/canal/canalInterface.py b/can/interfaces/canal/canalInterface.py index 228123423..93ae5b07d 100644 --- a/can/interfaces/canal/canalInterface.py +++ b/can/interfaces/canal/canalInterface.py @@ -127,6 +127,9 @@ def __init__(self, channel, *args, **kwargs): else: deviceID = serial() + if not deviceID: + raise can.CanError("Device ID could not be autodetected") + # set baudrate in kb/s from bitrate # (eg:500000 bitrate must be 500) if 'bitrate' in kwargs: @@ -141,6 +144,8 @@ def __init__(self, channel, *args, **kwargs): connector = format_connection_string(deviceID, baudrate) self.handle = self.can.open(connector, enable_flags) + # print "ostemad" + def send(self, msg, timeout=None): tx = message_convert_tx(msg) diff --git a/can/interfaces/canal/canal_wrapper.py b/can/interfaces/canal/canal_wrapper.py index d8bf993c9..24f8d1e3b 100644 --- a/can/interfaces/canal/canal_wrapper.py +++ b/can/interfaces/canal/canal_wrapper.py @@ -63,6 +63,8 @@ def open(self, pConfigureStr, flags): # unicode is not good pConfigureStr = pConfigureStr.encode('ascii', 'ignore') res = self.__m_dllBasic.CanalOpen(pConfigureStr, flags) + if res == 0: + raise can.CanError("CanalOpen failed, configure string: " + pConfigureStr) return res except: log.warning('Failed to open') diff --git a/can/interfaces/canal/serial_selector.py b/can/interfaces/canal/serial_selector.py index 1cf8a796d..49941838c 100644 --- a/can/interfaces/canal/serial_selector.py +++ b/can/interfaces/canal/serial_selector.py @@ -35,3 +35,5 @@ def serial(serialMatcher = "PID_6001"): string = string[len(string) - 9:len(string) - 1] return string + + return None \ No newline at end of file From b1c524690990040553d07dc521edc3c36d4468d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20N=C3=B8rgaard?= Date: Tue, 9 May 2017 14:08:58 +0200 Subject: [PATCH 04/10] Add an example file --- can/interfaces/canal/canalInterface.py | 2 ++ examples/canal_demo.py | 50 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 examples/canal_demo.py diff --git a/can/interfaces/canal/canalInterface.py b/can/interfaces/canal/canalInterface.py index 93ae5b07d..630b3dc50 100644 --- a/can/interfaces/canal/canalInterface.py +++ b/can/interfaces/canal/canalInterface.py @@ -130,6 +130,8 @@ def __init__(self, channel, *args, **kwargs): if not deviceID: raise can.CanError("Device ID could not be autodetected") + self.channel_info = "CANAL device " + deviceID + # set baudrate in kb/s from bitrate # (eg:500000 bitrate must be 500) if 'bitrate' in kwargs: diff --git a/examples/canal_demo.py b/examples/canal_demo.py new file mode 100644 index 000000000..0b8a945e8 --- /dev/null +++ b/examples/canal_demo.py @@ -0,0 +1,50 @@ +""" +This demo shows a usage of an Lawicel CANUSB device. +""" + +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import can + +# import logging +# logging.basicConfig(level=logging.DEBUG) + +serialMatcher="PID_6001" +bus = can.interface.Bus(bustype="canal", dll="canusbdrv64.dll", serialMatcher=serialMatcher, bitrate=100000, flags=4) + +# alternative: specify device serial: +# bus = can.interface.Bus(bustype="canal", dll="canusbdrv64.dll", serial = "LW19ZSBR", bitrate=100000, flags=4) + +def send_one(): + msg = can.Message(arbitration_id=0x00c0ffee, + data=[0, 25, 0, 1, 3, 1, 4, 1], + extended_id=True) + try: + bus.send(msg) + print ("Message sent:") + print (msg) + except can.CanError: + print ("ERROR: Message send failure") + +def receive_one(): + print ("Wait for CAN message...") + try: + # blocking receive + msg = bus.recv(timeout=0) + if msg: + print ("Message received:") + print (msg) + else: + print ("ERROR: Unexpected bus.recv reply") + + except can.CanError: + print ("ERROR: Message not received") + +def demo(): + print ("===== CANAL interface demo =====") + print ("Device: {}".format(bus.channel_info)) + send_one() + receive_one() + +if __name__ == '__main__': + demo() From 4f5a9f52db3ca96ea870578602c7f118a27203a4 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 00:48:07 +0100 Subject: [PATCH 05/10] add changes that were requested in #161 --- can/interfaces/canal/canalInterface.py | 39 +++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/can/interfaces/canal/canalInterface.py b/can/interfaces/canal/canalInterface.py index 630b3dc50..c805fc4a4 100644 --- a/can/interfaces/canal/canalInterface.py +++ b/can/interfaces/canal/canalInterface.py @@ -26,7 +26,6 @@ def format_connection_string(deviceID, baudrate='500'): 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() @@ -38,7 +37,7 @@ def message_convert_tx(msg): for i in range(length): messagetx.data[i] = msg.data[i] - messagetx.flags = 80000000 + messagetx.flags = 0x80000000 if msg.is_error_frame: messagetx.flags |= IS_ERROR_FRAME @@ -58,7 +57,7 @@ def message_convert_rx(messagerx): REMOTE_FRAME = bool(messagerx.flags & IS_REMOTE_FRAME) ERROR_FRAME = bool(messagerx.flags & IS_ERROR_FRAME) - msgrx = Message(timestamp=messagerx.timestamp, + msgrx = Message(timestamp=messagerx.timestamp / 1000.0, is_remote_frame=REMOTE_FRAME, extended_id=ID_TYPE, is_error_frame=ERROR_FRAME, @@ -151,10 +150,14 @@ def __init__(self, channel, *args, **kwargs): def send(self, msg, timeout=None): tx = message_convert_tx(msg) + if timeout: - self.can.blocking_send(self.handle, byref(tx), int(timeout * 1000)) + status = self.can.blocking_send(self.handle, byref(tx), int(timeout * 1000)) else: - self.can.send(self.handle, byref(tx)) + status = self.can.send(self.handle, byref(tx)) + + if status != 0: + raise can.CanError("could not send message: status == {}".format(status)) def recv(self, timeout=None): @@ -168,16 +171,26 @@ def recv(self, timeout=None): 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 + # success + return message_convert_rx(messagerx) + + elif status == 19: + # CANAL_ERROR_RCV_EMPTY + log.warn("recv: status: CANAL_ERROR_RCV_EMPTY") + return None - return rx + elif status == 32: + # CANAL_ERROR_TIMEOUT + log.warn("recv: status: CANAL_ERROR_TIMEOUT") + return None + + else: + # another error + raise can.CanError("could not receive message: status == {}".format(status)) def shutdown(self): """Shut down the device safely""" status = self.can.close(self.handle) + + if status != 0: + raise can.CanError("could not shut down bus: status == {}".format(status)) From bd26d99e0472db82938de5d3b58ea52673a2f827 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 01:06:49 +0100 Subject: [PATCH 06/10] fix #192 --- can/interfaces/pcan/pcan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 8ff8b162c..8647222d9 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -9,6 +9,7 @@ from can.bus import BusABC from can.message import Message from can import CanError +import can import time boottimeEpoch = 0 @@ -82,7 +83,7 @@ def __init__(self, channel, *args, **kwargs): else: self.channel_info = channel - bitrate = kwargs.get('bitrate', 500000) + bitrate = kwargs.get('bitrate', can.rc.get('bitrate', 500000)) pcan_bitrate = pcan_bitrate_objs.get(bitrate, PCAN_BAUD_500K) hwtype = PCAN_TYPE_ISA From e17207c35159d7d7d0ba58891e748558c557670f Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 01:39:48 +0100 Subject: [PATCH 07/10] fix 218 --- can/io/asc.py | 18 +++++++++++------- test/data/example_data.py | 10 ++++------ test/logformats_test.py | 4 ++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index d69436cf5..c8732ab71 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -120,15 +120,19 @@ def log_event(self, message, timestamp=None): logger.debug("ASCWriter: ignoring empty message") return - timestamp = (timestamp or time.time()) + if timestamp is None: + timestamp = 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 @@ -139,18 +143,18 @@ def on_message_received(self, msg): 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 + if msg.is_extended_id: + arb_id += "x" channel = msg.channel if isinstance(msg.channel, int) else self.channel - line = self.LOG_STRING.format(time=timestamp, + + line = self.LOG_STRING.format(time=msg.timestamp, channel=channel, id=arb_id, dtype=dtype, data=" ".join(data)) + if not self.log_file.closed: self.log_file.write(line) diff --git a/test/data/example_data.py b/test/data/example_data.py index bc097f755..6d54d683f 100644 --- a/test/data/example_data.py +++ b/test/data/example_data.py @@ -9,7 +9,6 @@ from can import Message - # make tests more reproducible random.seed(13339115) @@ -73,12 +72,12 @@ TEST_MESSAGES_REMOTE_FRAMES = [ Message( - arbitration_id=0xDADADA, extended_id=True, is_remote_frame=False, + arbitration_id=0xDADADA, extended_id=True, is_remote_frame=True, timestamp=TEST_TIME + .165, data=[1, 2, 3, 4, 5, 6, 7, 8] ), Message( - arbitration_id=0x123, extended_id=False, is_remote_frame=False, + arbitration_id=0x123, extended_id=False, is_remote_frame=True, timestamp=TEST_TIME + .365, data=[254, 255] ), @@ -124,6 +123,5 @@ def generate_message(arbitration_id): Generates a new message with the given ID, some random data and a non-extended ID. """ - data = [random.randrange(0, 2 ** 8 - 1) for _ in range(8)] - msg = Message(arbitration_id=arbitration_id, data=data, extended_id=False) - return msg + data = bytes([random.randrange(0, 2 ** 8 - 1) for _ in range(8)]) + return Message(arbitration_id=arbitration_id, data=data, extended_id=False) diff --git a/test/logformats_test.py b/test/logformats_test.py index 3cc494adb..82c472243 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -112,7 +112,7 @@ def _test_writer_and_reader(test_case, writer_constructor, reader_constructor, s for i, (read, original) in enumerate(zip(read_messages, original_messages)): try: test_case.assertEqual(read, original) - test_case.assertAlmostEqual(read.timestamp, original.timestamp) + test_case.assertAlmostEqual(read.timestamp, original.timestamp, places=6) except Exception as exception: # attach the index exception.args += ("test failed at index #{}".format(i), ) @@ -142,7 +142,7 @@ class TestAscFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.ASCWriter, can.ASCReader, - check_error_frames=False, # TODO this should get fixed, see Issue #218 + check_error_frames=True, check_comments=True) From 61f27e12acb3bd671ac8900fa3a25833b84724c6 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 01:51:59 +0100 Subject: [PATCH 08/10] fix 217 --- test/logformats_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/logformats_test.py b/test/logformats_test.py index 82c472243..64f27cc2f 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -133,7 +133,6 @@ class TestCanutilsLog(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.CanutilsLogWriter, can.CanutilsLogReader, - check_error_frames=False, # TODO this should get fixed, see Issue #217 check_comments=False) @@ -142,7 +141,6 @@ class TestAscFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.ASCWriter, can.ASCReader, - check_error_frames=True, check_comments=True) @@ -192,7 +190,6 @@ class TestBlfFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.BLFWriter, can.BLFReader, - sleep_time=None, check_comments=False) def test_reader(self): From c492b3b09fb2891f46dbd5926dd1ff08bd3ec0a8 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 16:06:44 +0100 Subject: [PATCH 09/10] small fix in socketcan_native --- can/interfaces/socketcan/socketcan_native.py | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/can/interfaces/socketcan/socketcan_native.py b/can/interfaces/socketcan/socketcan_native.py index 10933d79e..b985262a2 100644 --- a/can/interfaces/socketcan/socketcan_native.py +++ b/can/interfaces/socketcan/socketcan_native.py @@ -229,16 +229,16 @@ def __init__(self, channel, message, period): :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 + self._tx_setup() - def _tx_setup(self, message): + def _tx_setup(self): # Create a low level packed frame to pass to the kernel - self.can_id_with_flags = _add_flags_to_can_id(message) - self.flags = CAN_FD_FRAME if message.is_fd else 0 + self.can_id_with_flags = _add_flags_to_can_id(self.message) + self.flags = CAN_FD_FRAME if self.message.is_fd else 0 header = build_bcm_transmit_header(self.can_id_with_flags, 0, 0.0, self.period, self.flags) - frame = build_can_frame(message) + frame = build_can_frame(self.message) log.debug("Sending BCM command") send_bcm(self.bcm_socket, header + frame) @@ -257,13 +257,14 @@ def stop(self): 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`. + Note that 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) + assert message.arbitration_id == self.can_id, "You cannot modify the CAN identifier" + self.message = message + self._tx_setup() def start(self): - self._tx_setup(self.message) + self._tx_setup() class MultiRateCyclicSendTask(CyclicSendTask): @@ -515,7 +516,8 @@ def sender(event): event.wait() sender_socket = create_socket() bind_socket(sender_socket, 'vcan0') - sender_socket.send(build_can_frame(0x01, b'\x01\x02\x03')) + message = Message(arbitration_id=0x01, data=b'\x01\x02\x03') + sender_socket.send(build_can_frame(message)) print("Sender sent a message.") import threading From 0c588b3241bd7fc52b25c7b5da7240559ab0d6d7 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 16:20:41 +0100 Subject: [PATCH 10/10] fix #219 --- can/interfaces/usb2can/usb2canInterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 9d9a68fc6..57c2b330c 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -114,14 +114,14 @@ def __init__(self, channel, *args, **kwargs): br = kwargs["bitrate"] # max rate is 1000 kbps - baudrate = max(1000, int(br/1000)) + baudrate = min(1000, int(br/1000)) # set default value else: baudrate = 500 connector = format_connection_string(deviceID, baudrate) - self.handle = self.can.open(connector, enable_flags) + self.handle = self.can.open(connector.encode('utf-8'), enable_flags) def send(self, msg, timeout=None): tx = message_convert_tx(msg)