From 14c5d1401dee636190453ecc625622ece3382b59 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sat, 2 Feb 2019 20:01:31 +0100 Subject: [PATCH 001/252] add read to slcanBus --- can/interfaces/slcan.py | 87 ++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 7b276a078..ba65cd5de 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -47,6 +47,9 @@ class slcanBus(BusABC): _SLEEP_AFTER_SERIAL_OPEN = 2 # in seconds + _OK = b'\r' + _ERROR = b'\a' + LINE_TERMINATOR = b'\r' def __init__(self, channel, ttyBaudrate=115200, bitrate=None, @@ -97,6 +100,43 @@ def write(self, string): self.serialPortOrig.write(string.encode() + self.LINE_TERMINATOR) self.serialPortOrig.flush() + def read(self, timeout): + + # first read what is already in receive buffer + while self.serialPortOrig.in_waiting: + self._buffer += self.serialPortOrig.read(1) + + # if we still don't have a complete message, do a blocking read + start = time.time() + time_left = timeout + while not (self._OK in self._buffer or self._ERROR in self._buffer): + self.serialPortOrig.timeout = time_left + byte = self.serialPortOrig.read(1) + if byte: + self._buffer += byte + + # if timeout is None, try indefinitely + if timeout is None: + continue + # try next one only if there still is time, and with + # reduced timeout + else: + time_left = timeout - (time.time() - start) + if time_left > 0: + continue + else: + return None + + # return first message + for i in xrange(len(self._buffer)): + if ( chr(self._buffer[i]) == self._OK or + chr(self._buffer[i]) == self._ERROR ): + string = self._buffer[:i+1].decode() + del self._buffer[:i+1] + break + + return string + def open(self): self.write('O') @@ -104,53 +144,38 @@ def close(self): self.write('C') def _recv_internal(self, timeout): - if timeout != self.serialPortOrig.timeout: - self.serialPortOrig.timeout = timeout canId = None remote = False extended = False frame = [] - # First read what is already in the receive buffer - while (self.serialPortOrig.in_waiting and - self.LINE_TERMINATOR not in self._buffer): - self._buffer += self.serialPortOrig.read(1) - - # If we still don't have a complete message, do a blocking read - if self.LINE_TERMINATOR not in self._buffer: - self._buffer += self.serialPortOrig.read_until(self.LINE_TERMINATOR) - - if self.LINE_TERMINATOR not in self._buffer: - # Timed out - return None, False + string = self.read(timeout) - readStr = self._buffer.decode() - del self._buffer[:] - if not readStr: + if not string: pass - elif readStr[0] == 'T': + elif string[0] == 'T': # extended frame - canId = int(readStr[1:9], 16) - dlc = int(readStr[9]) + canId = int(string[1:9], 16) + dlc = int(string[9]) extended = True for i in range(0, dlc): - frame.append(int(readStr[10 + i * 2:12 + i * 2], 16)) - elif readStr[0] == 't': + frame.append(int(string[10 + i * 2:12 + i * 2], 16)) + elif string[0] == 't': # normal frame - canId = int(readStr[1:4], 16) - dlc = int(readStr[4]) + canId = int(string[1:4], 16) + dlc = int(string[4]) for i in range(0, dlc): - frame.append(int(readStr[5 + i * 2:7 + i * 2], 16)) - elif readStr[0] == 'r': + frame.append(int(string[5 + i * 2:7 + i * 2], 16)) + elif string[0] == 'r': # remote frame - canId = int(readStr[1:4], 16) - dlc = int(readStr[4]) + canId = int(string[1:4], 16) + dlc = int(string[4]) remote = True - elif readStr[0] == 'R': + elif string[0] == 'R': # remote extended frame - canId = int(readStr[1:9], 16) - dlc = int(readStr[9]) + canId = int(string[1:9], 16) + dlc = int(string[9]) extended = True remote = True From d39eb2c73215346f5114c6d9081f384c8351f613 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sat, 2 Feb 2019 20:20:34 +0100 Subject: [PATCH 002/252] add flush to slcanBus --- can/interfaces/slcan.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index ba65cd5de..49e4f8424 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -137,6 +137,11 @@ def read(self, timeout): return string + def flush(self): + del self._buffer[:] + while self.serialPortOrig.in_waiting: + self.serialPortOrig.read(1) + def open(self): self.write('O') From c20c93b3ff32b835fff3949f094cc705c3c70153 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sat, 2 Feb 2019 20:31:39 +0100 Subject: [PATCH 003/252] add get_version and get_serial to slcanBus --- can/interfaces/slcan.py | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 49e4f8424..f90dc7cb5 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -221,3 +221,59 @@ def fileno(self): return self.serialPortOrig.fileno() # Return an invalid file descriptor on Windows return -1 + + def get_version(self, timeout): + cmd = "V" + self.write(cmd) + + start = time.time() + time_left = timeout + while True: + string = self.read(time_left) + + if not string: + pass + elif string[0] == cmd and len(string) == 6: + # convert ASCII coded version + hw_version = int(string[1:3]) + sw_version = int(string[3:5]) + return hw_version, sw_version + + # if timeout is None, try indefinitely + if timeout is None: + continue + # try next one only if there still is time, and with + # reduced timeout + else: + time_left = timeout - (time.time() - start) + if time_left > 0: + continue + else: + return None, None + + def get_serial(self, timeout): + cmd = "N" + self.write(cmd) + + start = time.time() + time_left = timeout + while True: + string = self.read(time_left) + + if not string: + pass + elif string[0] == cmd and len(string) == 6: + serial = string[1:-1] + return serial + + # if timeout is None, try indefinitely + if timeout is None: + continue + # try next one only if there still is time, and with + # reduced timeout + else: + time_left = timeout - (time.time() - start) + if time_left > 0: + continue + else: + return None From 89a75036f5194271bb5a2230921bcdb035cb83e5 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sat, 2 Feb 2019 21:53:13 +0100 Subject: [PATCH 004/252] add tests for get_version and get_serial --- test/test_slcan.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/test_slcan.py b/test/test_slcan.py index 29869cb1c..519c71418 100644 --- a/test/test_slcan.py +++ b/test/test_slcan.py @@ -107,6 +107,24 @@ def test_partial_recv(self): msg = self.bus.recv(0) self.assertIsNotNone(msg) + def test_version(self): + self.serial.write(b'V1013\r') + hw_ver, sw_ver = self.bus.get_version(0) + self.assertEqual(hw_ver, 10) + self.assertEqual(sw_ver, 13) + + hw_ver, sw_ver = self.bus.get_version(0) + self.assertIsNone(hw_ver) + self.assertIsNone(sw_ver) + + def test_serial(self): + self.serial.write(b'NA123\r') + sn = self.bus.get_serial(0) + self.assertEqual(sn, "A123") + + sn = self.bus.get_serial(0) + self.assertIsNone(sn) + if __name__ == '__main__': unittest.main() From 55480cc024d291805ee73aa1e63297119b8b1a43 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sat, 2 Feb 2019 22:14:09 +0100 Subject: [PATCH 005/252] add set_bitrate to slcanBus --- can/interfaces/slcan.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index f90dc7cb5..4b1fed6c9 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -85,17 +85,21 @@ def __init__(self, channel, ttyBaudrate=115200, bitrate=None, time.sleep(sleep_after_open) if bitrate is not None: - self.close() - if bitrate in self._BITRATES: - self.write(self._BITRATES[bitrate]) - else: - raise ValueError("Invalid bitrate, choose one of " + (', '.join(self._BITRATES)) + '.') + self.set_bitrate(self, bitrate) self.open() super(slcanBus, self).__init__(channel, ttyBaudrate=115200, bitrate=None, rtscts=False, **kwargs) + def set_bitrate(self, bitrate): + self.close() + if bitrate in self._BITRATES: + self.write(self._BITRATES[bitrate]) + else: + raise ValueError("Invalid bitrate, choose one of " + (', '.join(self._BITRATES)) + '.') + self.open() + def write(self, string): self.serialPortOrig.write(string.encode() + self.LINE_TERMINATOR) self.serialPortOrig.flush() From 7f8c686604756c3251a457062f48d8e3ad28a81d Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sat, 2 Feb 2019 22:48:55 +0100 Subject: [PATCH 006/252] replace xrange with range --- can/interfaces/slcan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 4b1fed6c9..27810ac48 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -132,7 +132,7 @@ def read(self, timeout): return None # return first message - for i in xrange(len(self._buffer)): + for i in range(len(self._buffer)): if ( chr(self._buffer[i]) == self._OK or chr(self._buffer[i]) == self._ERROR ): string = self._buffer[:i+1].decode() From 3ff22b2227b904933dbdb031d45d6deefa944d9a Mon Sep 17 00:00:00 2001 From: shedfly Date: Fri, 3 May 2019 03:10:19 -0700 Subject: [PATCH 007/252] adding seeedstudio usb-can-analyzer interface --- can/interfaces/__init__.py | 3 +- can/interfaces/usb_can_analyzer/__init__.py | 6 + can/interfaces/usb_can_analyzer/notes | 167 +++++++++++ .../usb_can_analyzer/usb_can_analyzer.py | 276 ++++++++++++++++++ setup.py | 3 +- 5 files changed, 453 insertions(+), 2 deletions(-) create mode 100644 can/interfaces/usb_can_analyzer/__init__.py create mode 100644 can/interfaces/usb_can_analyzer/notes create mode 100644 can/interfaces/usb_can_analyzer/usb_can_analyzer.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index ec79e51d6..864233b0f 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -23,7 +23,8 @@ 'vector': ('can.interfaces.vector', 'VectorBus'), 'slcan': ('can.interfaces.slcan', 'slcanBus'), 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus'), - 'systec': ('can.interfaces.systec', 'UcanBus') + 'systec': ('can.interfaces.systec', 'UcanBus'), + 'usb_can_analyzer': ('can.interfaces.usb_can_analyzer.usb_can_analyzer', 'CanAnalyzer') } BACKENDS.update({ diff --git a/can/interfaces/usb_can_analyzer/__init__.py b/can/interfaces/usb_can_analyzer/__init__.py new file mode 100644 index 000000000..ec07c4d68 --- /dev/null +++ b/can/interfaces/usb_can_analyzer/__init__.py @@ -0,0 +1,6 @@ +# coding: utf-8 + +""" +""" + +from can.interfaces.usb_can_analyzer.usb_can_analyzer import CanAnalyzer as Bus diff --git a/can/interfaces/usb_can_analyzer/notes b/can/interfaces/usb_can_analyzer/notes new file mode 100644 index 000000000..829c85173 --- /dev/null +++ b/can/interfaces/usb_can_analyzer/notes @@ -0,0 +1,167 @@ +USB-CAN Analyzer - USB to CAN Bus Serial Protocol Definition + +Posted by Wilfried Voss on September 19, 2018 - copperhilltech.com + +CAN Bus To USB Mini Converter + +The USB-CAN Analyzer, in combination with the corresponding Windows software, represents a very economical solution to run an effective CAN Bus Analyzer. It allows you to develop, test, manage, and maintain your own CAN Bus network, as well as receiving, sending, logging, and analyzing CAN Bus data. + +For the following, we need to point out that the device is manufactured in China, and documentation is sparse. And while this is a very affordable solution, there is naturally room for some improvement. In general, there is no application interface, or, to be more precise, the communication between the USB-CAN device and the host system, in this case a PC running Windows, is not documented. Consequently, there is currently no software support for Linux (e.g. SocketCAN), and it would be useful if users could write their own applications, regardless of the operating system they use. + +As a first step in this direction, we invested some efforts to analyze and document the USB communication protocol, which makes it easy developing an application interface. + +There has been a document, USB (Serial port) to CAN protocol defines, that was distributed online, claiming a documentation of the "Send and receive data packet format." However, after analyzing the protocol, we believe that the data frame architecture described therein represents the original intention. + +We believe that the architecture as described caused problems when it came to higher baud rates, specifically at 1 Mbps in combination with high busloads. Consequently, the firmware developers decided to optimize the data frame size. As a result, they cut the frame size from a static 20 bytes to a dynamic 6 to 16 bytes, depending on message ID length (11 or 29 bits) and number of data bytes. + +The following describes the serial protocol as we analyzed it, with a few unknowns remaining. However, the documentation includes all vital information for creating your own CAN Bus application. + +CAN Bus Data Frame + +The CAN Bus data frame format applies to both, send and receive. + +Both message ID lengths, 11-bit standard and 29-bit extended, use the first two bytes in the serial data frame. Depending on the message ID length, the remaining bytes will be filled differently. + +The data frame does not utilize a checksum check, most probably due to keeping the number of transmitted bytes to a minimum and cutting down on processing time. + +Byte Description + +0 0xAA = Packet Start + +1 CAN Bus Data Frame Information + + Bit 7 Always 1 + + Bit 6 Always 1 + + Bit 5 0=STD; 1=EXT + + Bit 4 0=Data; 1=Remote + + Bit 3…0 Data Length Code (DLC); 0…8 + +Standard CAN data frame continues: + +Byte Description + +2 Message ID LSB + +3 Message ID MSB + +x Data, length depending on DLC + +y 0x55 = Packet End + +Extended CAN data frame continues: + +Byte Description + +2 Message ID LSB + +3 Message ID 2ND + +4 Message ID 3RD + +5 Message ID MSB + +x Data, length depending on DLC + +y 0x55 = Packet End + +CAN Bus Data Frame Size + +Standard 11-bit 6 bytes with zero data bytes + + 14 bytes with eight data bytes + +Extended 29-bit 8 bytes with zero data bytes + + 16 bytes with eight data bytes + +CAN Bus Initialization Frame + +The CAN Bus initialization frame has a constant length of 20 bytes. + +Byte(s) Description + +0 0xAA = Frame Start Byte 1 + +1 0x55 = Frame Start Byte 2 + +2 0x12 = Initialization Message ID + +3 CAN Baud Rate (See table below) + +4 0x01=STD; 0x02=EXT; Applies only to transmitting + +5 – 8 Filter ID; LSB first, MSB last + +9 – 12 Mask ID; LSB first, MSB last + +13 Operation Mode (See table below) + +14 0x01; Purpose Unknown + +15 – 18 All 0x00 + +19 Checksum + +Note: The checksum is processed between bytes 2 and 18. + +CAN Bus baud rate + +0x01 1000k + +0x02 800k + +0x03 500k + +0x04 400k + +0x05 250k + +0x06 200k + +0x07 125k + +0x08 100k + +0x09 50k + +0x0A 20k + +0x0B 10k + +0x0C 5k + +Operation Mode + +0x00 Normal + +0x01 Loopback + +0x02 Silent (Listen-Only) + +0x03 Loopback + Silent + +CAN Controller Status Frame + +The format of the status request and status report frames are identical, where the request frame sends all zeroes between the frame header (frame start tokens plus frame ID) and the checksum, i.e. bytes 3 through 18 are 0x00. + +Byte(s) Description + +0 0xAA = Frame Start Byte 1 + +1 0x55 = Frame Start Byte 2 + +2 0x04 = Status Message ID + +3 Receive Error Counter + +4 Transmit Error Counter + +5 – 18 Unknown Purpose + In case of status report, they may be filled with data + +19 Checksum + diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py new file mode 100644 index 000000000..9fee2fc4d --- /dev/null +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -0,0 +1,276 @@ +# coding: utf-8 + +""" +To Support the Seeed USB-Can analyzer interface. The device will appear +as a serial port, for example "/dev/ttyS1" or "/dev/ttyUSB0" on Linux +machines or "COM1" on Windows. +https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html +SKU 114991193 +See protoocl: +https://copperhilltech.com/blog/usbcan-analyzer-usb-to-can-bus-serial-protocol-definition/ + +this file uses Crc8Darc checksums. +""" + + +from __future__ import absolute_import, division + +import logging +import struct +from crccheck.crc import Crc8Darc +from time import sleep, time +from can import BusABC, Message + +logger = logging.getLogger('can.CanAnalyzer') + +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 CanAnalyzer(BusABC): + """ + Enable basic can communication over a serial device. + + .. note:: See :meth:`can.interfaces.serial.CanAnalyzer._recv_internal` + for some special semantics. + + """ + BITRATE = { + 1000000: 0x01, + 800000: 0x02, + 500000: 0x03, + 400000: 0x04, + 250000: 0x05, + 200000: 0x06, + 125000: 0x07, + 100000: 0x08, + 50000: 0x09, + 20000: 0x0A, + 10000: 0x0B, + 5000: 0x0C + } + + FRAMETYPE = { + "STD":0x01, + "EXT":0x02 + } + + OPERATIONMODE = { + "normal":0x00, + "loopback":0x01, + "silent":0x02, + "loopback_and_silent":0x03 + } + + def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, + frame_type='STD', operation_mode='normal', bit_rate=500000, + *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). + + .. warning:: + Some serial port implementations don't care about the baudrate. + + :param float timeout: + Timeout for the serial device in seconds (default 0.1). + + :param bool rtscts: + turn hardware handshake (RTS/CTS) on and off + + """ + self.bit_rate = bit_rate + self.frame_type = frame_type + self.op_mode = operation_mode + self.filter_id = bytearray([0x00, 0x00, 0x00, 0x00]) + self.mask_id = bytearray([0x00, 0x00, 0x00, 0x00]) + if not channel: + raise ValueError("Must specify a serial port.") + + self.channel_info = "Serial interface: " + channel + self.ser = serial.Serial( + channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) + + super(CanAnalyzer, self).__init__(channel=channel, *args, **kwargs) + self.init_frame + + def shutdown(self): + """ + Close the serial interface. + """ + self.ser.close() + + def init_frame(self, timeout=None): + + byte_msg = bytearray() + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + + byte_msg.append(0x12) # Initialization Message ID + + byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate + byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) + + for i in range(0, 4): + byte_msg.append(self.filter_id[i]) + + for i in range(0, 4): + byte_msg.append(self.mask_id[i]) + + byte_msg.append(CanAnalyzer.OPERATIONMODE[self.op_mode]) + + byte_msg.append(0x01) + + for i in range(0, 4): + byte_msg.append(0x00) + + print "byte_str = " + str(byte_msg[2:]) + + crc = Crc8Darc.calc(byte_msg[2:]) + crc_byte = struct.pack('B', crc) + + print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) + + byte_msg.append(crc_byte) + self.ser.write(byte_msg) + + def flush_buffer(self): + self.ser.flushInput() + + def status_frame(self, timeout=None): + byte_msg = bytearray() + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + byte_msg.append(0x04) # Status Message ID + byte_msg.append(0x00) # In response packet - Rx error count + byte_msg.append(0x00) # In response packet - Tx error count + + for i in range(0, 14): + byte_msg.append(0x00) + + print "byte_str = " + str(byte_msg[2:]) + + crc = Crc8Darc.calc(byte_msg[2:]) + crc_byte = struct.pack('B', crc) + + print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) + + byte_msg.append(crc_byte) + self.ser.write(byte_msg) + + + 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 is a float value it will be converted + to an integer. + + :param timeout: + This parameter will be ignored. The timeout value of the channel is + used instead. + + """ + try: + timestamp = struct.pack(' Date: Mon, 6 May 2019 06:59:03 +0000 Subject: [PATCH 008/252] replacing print with log, moving crccheck to extras_require --- .../usb_can_analyzer/usb_can_analyzer.py | 39 +++++++++---------- setup.py | 6 +-- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index 9fee2fc4d..9a2ee0baa 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -12,12 +12,10 @@ this file uses Crc8Darc checksums. """ - from __future__ import absolute_import, division import logging import struct -from crccheck.crc import Crc8Darc from time import sleep, time from can import BusABC, Message @@ -30,6 +28,11 @@ "the serial module installed!") serial = None +try: + from crccheck.crc import Crc8Darc +except ImportError: + logger.warning("The interface requires the install option crccheck.") + class CanAnalyzer(BusABC): """ @@ -53,7 +56,7 @@ class CanAnalyzer(BusABC): 10000: 0x0B, 5000: 0x0C } - + FRAMETYPE = { "STD":0x01, "EXT":0x02 @@ -100,7 +103,7 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) super(CanAnalyzer, self).__init__(channel=channel, *args, **kwargs) - self.init_frame + self.init_frame() def shutdown(self): """ @@ -119,27 +122,23 @@ def init_frame(self, timeout=None): byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) - for i in range(0, 4): - byte_msg.append(self.filter_id[i]) + byte_msg.extend(self.filter_id) - for i in range(0, 4): - byte_msg.append(self.mask_id[i]) + byte_msg.extend(self.mask_id) byte_msg.append(CanAnalyzer.OPERATIONMODE[self.op_mode]) - + byte_msg.append(0x01) for i in range(0, 4): byte_msg.append(0x00) - print "byte_str = " + str(byte_msg[2:]) - crc = Crc8Darc.calc(byte_msg[2:]) crc_byte = struct.pack('B', crc) - print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) - byte_msg.append(crc_byte) + + logger.debug("init_frm:\t" + str(byte_msg)) self.ser.write(byte_msg) def flush_buffer(self): @@ -156,16 +155,13 @@ def status_frame(self, timeout=None): for i in range(0, 14): byte_msg.append(0x00) - print "byte_str = " + str(byte_msg[2:]) - crc = Crc8Darc.calc(byte_msg[2:]) crc_byte = struct.pack('B', crc) - print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) - byte_msg.append(crc_byte) - self.ser.write(byte_msg) + logger.debug("status_frm:\t" + str(byte_msg)) + self.ser.write(byte_msg) def send(self, msg, timeout=None): """ @@ -203,6 +199,8 @@ def send(self, msg, timeout=None): for i in range(0, msg.dlc): byte_msg.append(msg.data[i]) byte_msg.append(0xBB) + + logger.debug("Sending:\t" + byte_msg) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -229,7 +227,7 @@ def _recv_internal(self, timeout): # ser.read can return an empty string # or raise a SerialException rx_byte_1 = self.ser.read() - + except serial.SerialException: return None, False @@ -260,7 +258,7 @@ def _recv_internal(self, timeout): arbitration_id=arb_id, extended_id=is_extended, data=data) - + logger.debug("recv_msg:\t" + str(msg)) return msg, False else: @@ -268,7 +266,6 @@ def _recv_internal(self, timeout): return None, None - def fileno(self): if hasattr(self.ser, 'fileno'): return self.ser.fileno() diff --git a/setup.py b/setup.py index dcef0acb3..d4c6b7838 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,8 @@ # Dependencies extras_require = { 'serial': ['pyserial~=3.0'], - 'neovi': ['python-ics>=2.12'] + 'neovi': ['python-ics>=2.12'], + 'usb-can-analyzer': ['crccheck>=0.6'] } tests_require = [ @@ -36,8 +37,7 @@ 'codecov~=2.0', 'future', 'six', - 'hypothesis', - 'crccheck' + 'hypothesis' ] + extras_require['serial'] extras_require['test'] = tests_require From ea5d15cecf4476d3dbb232af26276557038efa53 Mon Sep 17 00:00:00 2001 From: shedfly Date: Mon, 6 May 2019 10:43:38 +0000 Subject: [PATCH 009/252] adding 'is_remote' to message, add binascii to debug logs --- .../usb_can_analyzer/usb_can_analyzer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index 9a2ee0baa..6f37a9066 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -16,6 +16,7 @@ import logging import struct +import binascii from time import sleep, time from can import BusABC, Message @@ -138,7 +139,7 @@ def init_frame(self, timeout=None): byte_msg.append(crc_byte) - logger.debug("init_frm:\t" + str(byte_msg)) + logger.debug("init_frm:\t" + binascii.hexlify(byte_msg)) self.ser.write(byte_msg) def flush_buffer(self): @@ -160,7 +161,7 @@ def status_frame(self, timeout=None): byte_msg.append(crc_byte) - logger.debug("status_frm:\t" + str(byte_msg)) + logger.debug("status_frm:\t" + binascii.hexlify(byte_msg)) self.ser.write(byte_msg) def send(self, msg, timeout=None): @@ -200,7 +201,7 @@ def send(self, msg, timeout=None): byte_msg.append(msg.data[i]) byte_msg.append(0xBB) - logger.debug("Sending:\t" + byte_msg) + logger.debug("Sending:\t" + binascii.hexlify(byte_msg)) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -240,8 +241,7 @@ def _recv_internal(self, timeout): else: length = int(rx_byte_2 & 0x0F) is_extended = bool(rx_byte_2 & 0x20) - is_data = bool(rx_byte_2 & 10) - + is_remote = bool(rx_byte_2 & 0x10) if is_extended: s_3_4_5_6 = bytearray(self.ser.read(4)) arb_id = (struct.unpack(' Date: Mon, 6 May 2019 12:57:35 +0000 Subject: [PATCH 010/252] more general name 'crccheck' for install option, also update Message named args --- can/interfaces/usb_can_analyzer/usb_can_analyzer.py | 3 ++- setup.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index 6f37a9066..b1fd95ffa 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -255,8 +255,9 @@ def _recv_internal(self, timeout): if end_packet == 0x55: msg = Message(timestamp=time_stamp, arbitration_id=arb_id, - extended_id=is_extended, + is_extended_id=is_extended, is_remote_frame=is_remote, + dlc=length, data=data) logger.debug("recv message: " + str(msg)) return msg, False diff --git a/setup.py b/setup.py index d4c6b7838..a4ae7b405 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ extras_require = { 'serial': ['pyserial~=3.0'], 'neovi': ['python-ics>=2.12'], - 'usb-can-analyzer': ['crccheck>=0.6'] + 'crccheck': ['crccheck>=0.6'] } tests_require = [ From 71407f99d6643e3d5044d7aa13bb3bffa16be009 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 8 May 2019 21:23:37 +0200 Subject: [PATCH 011/252] Fix warning in usb2can This PR removes [this `DeprecationWarning`](https://travis-ci.org/hardbyte/python-can/jobs/529939236#L473): > invalid escape sequence \c Is this correct to to? It now is like [this example](https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemlocator-connectserver#examples). --- can/interfaces/usb2can/serial_selector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/usb2can/serial_selector.py b/can/interfaces/usb2can/serial_selector.py index b47396876..fbfe60a6e 100644 --- a/can/interfaces/usb2can/serial_selector.py +++ b/can/interfaces/usb2can/serial_selector.py @@ -39,7 +39,7 @@ def find_serial_devices(serial_matcher="ED"): :rtype: List[str] """ objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator") - objSWbemServices = objWMIService.ConnectServer(".", "root\cimv2") + objSWbemServices = objWMIService.ConnectServer(".", "root\\cimv2") items = objSWbemServices.ExecQuery("SELECT * FROM Win32_USBControllerDevice") ids = (item.Dependent.strip('"')[-8:] for item in items) return [e for e in ids if e.startswith(serial_matcher)] From 93ad7238633bf3f36373e094a89c325b7cc1c401 Mon Sep 17 00:00:00 2001 From: shedfly Date: Fri, 10 May 2019 12:24:49 +0000 Subject: [PATCH 012/252] implementing 'send' --- .../usb_can_analyzer/usb_can_analyzer.py | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index b1fd95ffa..73def806d 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -171,35 +171,32 @@ def send(self, msg, timeout=None): :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 is a float value it will be converted - to an integer. - :param timeout: This parameter will be ignored. The timeout value of the channel is used instead. - """ - try: - timestamp = struct.pack(' Date: Fri, 10 May 2019 12:31:34 +0000 Subject: [PATCH 013/252] changing extras_require install option name to match interface name and adding pyserial --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a4ae7b405..388b96747 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ extras_require = { 'serial': ['pyserial~=3.0'], 'neovi': ['python-ics>=2.12'], - 'crccheck': ['crccheck>=0.6'] + 'usb_can_analyzer': ['crccheck>=0.6','pyserial>=3.0'] } tests_require = [ From ccc13801ca250d524adda68c37cf49b58bea8b92 Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 16 May 2019 13:36:18 -0700 Subject: [PATCH 014/252] Add additional coverage to SocketCAN interface Add additional test cases to cover code exercising the Linux Broadcast Manager as part of the SocketCAN interface. --- test/test_socketcan.py | 160 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 3 deletions(-) diff --git a/test/test_socketcan.py b/test/test_socketcan.py index f010a6372..828a67307 100644 --- a/test/test_socketcan.py +++ b/test/test_socketcan.py @@ -14,7 +14,21 @@ import ctypes -from can.interfaces.socketcan.socketcan import bcm_header_factory +from can.interfaces.socketcan.socketcan import ( + bcm_header_factory, + build_bcm_header, + build_bcm_tx_delete_header, + build_bcm_transmit_header, + build_bcm_update_header, + BcmMsgHead, +) +from can.interfaces.socketcan.constants import ( + CAN_BCM_TX_DELETE, + CAN_BCM_TX_SETUP, + SETTIMER, + STARTTIMER, + TX_COUNTEVT, +) class SocketCANTest(unittest.TestCase): @@ -93,7 +107,7 @@ def side_effect_ctypes_alignment(value): @patch("ctypes.sizeof") @patch("ctypes.alignment") - def test_bcm_header_factory_32_bit_sizeof_long_4_alignof_long_8( + def test_bcm_header_factory_32_bit_sizeof_long_4_alignof_long_long_8( self, ctypes_sizeof, ctypes_alignment ): """This tests a 32-bit platform (ex. Raspbian Stretch on armv7l), where: @@ -162,7 +176,7 @@ def side_effect_ctypes_alignment(value): @patch("ctypes.sizeof") @patch("ctypes.alignment") - def test_bcm_header_factory_64_bit_sizeof_long_4_alignof_long_4( + def test_bcm_header_factory_64_bit_sizeof_long_8_alignof_long_8( self, ctypes_sizeof, ctypes_alignment ): """This tests a 64-bit platform (ex. Ubuntu 18.04 on x86_64), where: @@ -229,6 +243,146 @@ def side_effect_ctypes_alignment(value): ] self.assertEqual(expected_fields, BcmMsgHead._fields_) + @unittest.skipIf( + not ( + ctypes.sizeof(ctypes.c_long) == 4 and ctypes.alignment(ctypes.c_long) == 4 + ), + "Should only run on platforms where sizeof(long) == 4 and alignof(long) == 4", + ) + def test_build_bcm_header_sizeof_long_4_alignof_long_4(self): + expected_result = b"" + expected_result += b"\x02\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x01\x04\x00\x00" + expected_result += b"\x01\x00\x00\x00\x00\x00\x00\x00" + + self.assertEqual( + expected_result, + build_bcm_header( + opcode=CAN_BCM_TX_DELETE, + flags=0, + count=0, + ival1_seconds=0, + ival1_usec=0, + ival2_seconds=0, + ival2_usec=0, + can_id=0x401, + nframes=1, + ), + ) + + @unittest.skipIf( + not ( + ctypes.sizeof(ctypes.c_long) == 8 and ctypes.alignment(ctypes.c_long) == 8 + ), + "Should only run on platforms where sizeof(long) == 8 and alignof(long) == 8", + ) + def test_build_bcm_header_sizeof_long_8_alignof_long_8(self): + expected_result = b"" + expected_result += b"\x02\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x00\x00\x00\x00\x00\x00\x00\x00" + expected_result += b"\x01\x04\x00\x00\x01\x00\x00\x00" + + self.assertEqual( + expected_result, + build_bcm_header( + opcode=CAN_BCM_TX_DELETE, + flags=0, + count=0, + ival1_seconds=0, + ival1_usec=0, + ival2_seconds=0, + ival2_usec=0, + can_id=0x401, + nframes=1, + ), + ) + + def test_build_bcm_tx_delete_header(self): + can_id = 0x401 + flags = 0 + bcm_buffer = build_bcm_tx_delete_header(can_id=can_id, flags=flags) + result = BcmMsgHead.from_buffer_copy(bcm_buffer) + + self.assertEqual(CAN_BCM_TX_DELETE, result.opcode) + self.assertEqual(flags, result.flags) + self.assertEqual(0, result.count) + self.assertEqual(0, result.ival1_tv_sec) + self.assertEqual(0, result.ival1_tv_usec) + self.assertEqual(0, result.ival2_tv_sec) + self.assertEqual(0, result.ival2_tv_usec) + self.assertEqual(can_id, result.can_id) + self.assertEqual(1, result.nframes) + + def test_build_bcm_transmit_header_initial_period_0(self): + can_id = 0x401 + flags = 0 + count = 42 + bcm_buffer = build_bcm_transmit_header( + can_id=can_id, + count=count, + initial_period=0, + subsequent_period=2, + msg_flags=flags, + ) + result = BcmMsgHead.from_buffer_copy(bcm_buffer) + + self.assertEqual(CAN_BCM_TX_SETUP, result.opcode) + # SETTIMER and STARTTIMER should be added to the initial flags + self.assertEqual(flags | SETTIMER | STARTTIMER, result.flags) + self.assertEqual(count, result.count) + self.assertEqual(0, result.ival1_tv_sec) + self.assertEqual(0, result.ival1_tv_usec) + self.assertEqual(2, result.ival2_tv_sec) + self.assertEqual(0, result.ival2_tv_usec) + self.assertEqual(can_id, result.can_id) + self.assertEqual(1, result.nframes) + + def test_build_bcm_transmit_header_initial_period_1_24(self): + can_id = 0x401 + flags = 0 + count = 42 + bcm_buffer = build_bcm_transmit_header( + can_id=can_id, + count=count, + initial_period=1.24, + subsequent_period=2, + msg_flags=flags, + ) + result = BcmMsgHead.from_buffer_copy(bcm_buffer) + + self.assertEqual(CAN_BCM_TX_SETUP, result.opcode) + # SETTIMER, STARTTIMER, TX_COUNTEVT should be added to the initial flags + self.assertEqual(flags | SETTIMER | STARTTIMER | TX_COUNTEVT, result.flags) + self.assertEqual(count, result.count) + self.assertEqual(1, result.ival1_tv_sec) + self.assertEqual(240000, result.ival1_tv_usec) + self.assertEqual(2, result.ival2_tv_sec) + self.assertEqual(0, result.ival2_tv_usec) + self.assertEqual(can_id, result.can_id) + self.assertEqual(1, result.nframes) + + def test_build_bcm_update_header(self): + can_id = 0x401 + flags = 0 + bcm_buffer = build_bcm_update_header(can_id=can_id, msg_flags=flags) + result = BcmMsgHead.from_buffer_copy(bcm_buffer) + + self.assertEqual(CAN_BCM_TX_SETUP, result.opcode) + self.assertEqual(flags, result.flags) + self.assertEqual(0, result.count) + self.assertEqual(0, result.ival1_tv_sec) + self.assertEqual(0, result.ival1_tv_usec) + self.assertEqual(0, result.ival2_tv_sec) + self.assertEqual(0, result.ival2_tv_usec) + self.assertEqual(can_id, result.can_id) + self.assertEqual(1, result.nframes) + if __name__ == "__main__": unittest.main() From 5103e3592bf17a40caa3a1f2af0e88d72f9ca616 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 19:00:41 +0200 Subject: [PATCH 015/252] modernize test cases --- test/back2back_test.py | 2 -- test/listener_test.py | 4 +--- test/logformats_test.py | 14 ++------------ test/message_helper.py | 4 +--- test/network_test.py | 8 ++------ test/notifier_test.py | 7 +------ test/serial_test.py | 4 +--- test/simplecyclic_test.py | 2 -- test/test_detect_available_configs.py | 6 +----- test/test_kvaser.py | 5 +---- test/test_load_file_config.py | 1 + test/test_message_filtering.py | 2 -- test/test_message_sync.py | 2 -- test/test_scripts.py | 6 +----- test/test_slcan.py | 1 + test/test_socketcan.py | 11 +++-------- test/test_socketcan_helpers.py | 4 +--- test/test_systec.py | 5 +---- test/test_viewer.py | 15 ++------------- 19 files changed, 20 insertions(+), 83 deletions(-) diff --git a/test/back2back_test.py b/test/back2back_test.py index 4062d462a..e54ad93e4 100644 --- a/test/back2back_test.py +++ b/test/back2back_test.py @@ -5,8 +5,6 @@ This module tests two virtual buses attached to each other. """ -from __future__ import absolute_import, print_function - import sys import unittest from time import sleep diff --git a/test/listener_test.py b/test/listener_test.py index c25a6fb56..840f687fb 100644 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -4,8 +4,6 @@ """ """ -from __future__ import absolute_import, print_function - from time import sleep import unittest import random @@ -117,7 +115,7 @@ def test_filetype_to_instance(extension, klass): test_filetype_to_instance(".log", can.CanutilsLogReader) # test file extensions that are not supported - with self.assertRaisesRegexp(NotImplementedError, ".xyz_42"): + with self.assertRaisesRegex(NotImplementedError, ".xyz_42"): test_filetype_to_instance(".xyz_42", can.Printer) def testLoggerTypeResolution(self): diff --git a/test/logformats_test.py b/test/logformats_test.py index d9551e5d6..809639e6d 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -14,20 +14,12 @@ TODO: implement CAN FD support testing """ -from __future__ import print_function, absolute_import, division - import logging import unittest import tempfile import os from abc import abstractmethod, ABCMeta - -try: - # Python 3 - from itertools import zip_longest -except ImportError: - # Python 2 - from itertools import izip_longest as zip_longest +from itertools import zip_longest import can @@ -39,7 +31,7 @@ logging.basicConfig(level=logging.DEBUG) -class ReaderWriterTest(unittest.TestCase, ComparingMessagesTestCase): +class ReaderWriterTest(unittest.TestCase, ComparingMessagesTestCase, metaclass=ABCMeta): """Tests a pair of writer and reader by writing all data first and then reading all data and checking if they could be reconstructed correctly. Optionally writes some comments as well. @@ -50,8 +42,6 @@ class ReaderWriterTest(unittest.TestCase, ComparingMessagesTestCase): (Source: `*Wojciech B.* on StackOverlfow `_) """ - __metaclass__ = ABCMeta - def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) self._setup_instance() diff --git a/test/message_helper.py b/test/message_helper.py index 9a4756207..8fb94f48b 100644 --- a/test/message_helper.py +++ b/test/message_helper.py @@ -5,12 +5,10 @@ This module contains a helper for writing test cases that need to compare messages. """ -from __future__ import absolute_import, print_function - from copy import copy -class ComparingMessagesTestCase(object): +class ComparingMessagesTestCase: """ Must be extended by a class also extending a unittest.TestCase. diff --git a/test/network_test.py b/test/network_test.py index f4163329d..1af102ec3 100644 --- a/test/network_test.py +++ b/test/network_test.py @@ -1,17 +1,13 @@ #!/usr/bin/env python # coding: utf-8 -from __future__ import print_function import unittest import threading -try: - import queue -except ImportError: - import Queue as queue +import queue import random - import logging + logging.getLogger(__file__).setLevel(logging.WARNING) # make a random bool: diff --git a/test/notifier_test.py b/test/notifier_test.py index 3ab257cf7..71ac7a944 100644 --- a/test/notifier_test.py +++ b/test/notifier_test.py @@ -3,10 +3,7 @@ import unittest import time -try: - import asyncio -except ImportError: - asyncio = None +import asyncio import can @@ -45,7 +42,6 @@ def test_multiple_bus(self): class AsyncNotifierTest(unittest.TestCase): - @unittest.skipIf(asyncio is None, 'Test requires asyncio') def test_asyncio_notifier(self): loop = asyncio.get_event_loop() bus = can.Bus('test', bustype='virtual', receive_own_messages=True) @@ -60,6 +56,5 @@ def test_asyncio_notifier(self): bus.shutdown() - if __name__ == '__main__': unittest.main() diff --git a/test/serial_test.py b/test/serial_test.py index 5b26ae42a..c49c15354 100644 --- a/test/serial_test.py +++ b/test/serial_test.py @@ -7,8 +7,6 @@ Copyright: 2017 Boris Wenzlaff """ -from __future__ import division - import unittest from mock import patch @@ -18,7 +16,7 @@ from .message_helper import ComparingMessagesTestCase -class SerialDummy(object): +class SerialDummy: """ Dummy to mock the serial communication """ diff --git a/test/simplecyclic_test.py b/test/simplecyclic_test.py index a10871648..06712e921 100644 --- a/test/simplecyclic_test.py +++ b/test/simplecyclic_test.py @@ -5,8 +5,6 @@ This module tests cyclic send tasks. """ -from __future__ import absolute_import - from time import sleep import unittest import gc diff --git a/test/test_detect_available_configs.py b/test/test_detect_available_configs.py index ca2d82c15..69addafb3 100644 --- a/test/test_detect_available_configs.py +++ b/test/test_detect_available_configs.py @@ -6,12 +6,8 @@ :meth:`can.BusABC.detect_available_configs`. """ -from __future__ import absolute_import - import sys import unittest -if sys.version_info.major > 2: - basestring = str from can import detect_available_configs @@ -33,7 +29,7 @@ def test_general_values(self): for config in configs: self.assertIn('interface', config) self.assertIn('channel', config) - self.assertIsInstance(config['interface'], basestring) + self.assertIsInstance(config['interface'], str) def test_content_virtual(self): configs = detect_available_configs(interfaces='virtual') diff --git a/test/test_kvaser.py b/test/test_kvaser.py index 3e0bcf396..106ce7dc5 100644 --- a/test/test_kvaser.py +++ b/test/test_kvaser.py @@ -8,10 +8,7 @@ import time import logging import unittest -try: - from unittest.mock import Mock, patch -except ImportError: - from mock import patch, Mock +from unittest.mock import Mock, patch import pytest diff --git a/test/test_load_file_config.py b/test/test_load_file_config.py index 52a45d734..dbd7673cb 100644 --- a/test/test_load_file_config.py +++ b/test/test_load_file_config.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # coding: utf-8 + import shutil import tempfile import unittest diff --git a/test/test_message_filtering.py b/test/test_message_filtering.py index 1419a4439..1b498b95e 100644 --- a/test/test_message_filtering.py +++ b/test/test_message_filtering.py @@ -5,8 +5,6 @@ This module tests :meth:`can.BusABC._matches_filters`. """ -from __future__ import absolute_import - import unittest from can import Bus, Message diff --git a/test/test_message_sync.py b/test/test_message_sync.py index ec21a0660..ba2332776 100644 --- a/test/test_message_sync.py +++ b/test/test_message_sync.py @@ -5,8 +5,6 @@ This module tests :class:`can.MessageSync`. """ -from __future__ import absolute_import - from copy import copy from time import time import gc diff --git a/test/test_scripts.py b/test/test_scripts.py index 74ae71489..11dc06991 100644 --- a/test/test_scripts.py +++ b/test/test_scripts.py @@ -5,8 +5,6 @@ This module tests that the scripts are all callable. """ -from __future__ import absolute_import - import subprocess import unittest import sys @@ -16,9 +14,7 @@ from .config import * -class CanScriptTest(unittest.TestCase): - - __metaclass__ = ABCMeta +class CanScriptTest(unittest.TestCase, metaclass=ABCMeta): @classmethod def setUpClass(cls): diff --git a/test/test_slcan.py b/test/test_slcan.py index 29869cb1c..43703b3f0 100644 --- a/test/test_slcan.py +++ b/test/test_slcan.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # coding: utf-8 + import unittest import can diff --git a/test/test_socketcan.py b/test/test_socketcan.py index 828a67307..32389ff9f 100644 --- a/test/test_socketcan.py +++ b/test/test_socketcan.py @@ -3,14 +3,9 @@ """ import unittest -try: - from unittest.mock import Mock - from unittest.mock import patch - from unittest.mock import call -except ImportError: - from mock import Mock - from mock import patch - from mock import call +from unittest.mock import Mock +from unittest.mock import patch +from unittest.mock import call import ctypes diff --git a/test/test_socketcan_helpers.py b/test/test_socketcan_helpers.py index f1462549a..cc12eb415 100644 --- a/test/test_socketcan_helpers.py +++ b/test/test_socketcan_helpers.py @@ -5,8 +5,6 @@ Tests helpers in `can.interfaces.socketcan.socketcan_common`. """ -from __future__ import absolute_import - import unittest from can.interfaces.socketcan.utils import \ @@ -36,7 +34,7 @@ def test_find_available_interfaces(self): result = list(find_available_interfaces()) self.assertGreaterEqual(len(result), 0) for entry in result: - self.assertRegexpMatches(entry, r"v?can\d+") + self.assertRegex(entry, r"v?can\d+") if TEST_INTERFACE_SOCKETCAN: self.assertGreaterEqual(len(result), 1) self.assertIn("vcan0", result) diff --git a/test/test_systec.py b/test/test_systec.py index ce5dda4a7..0cd38da0d 100644 --- a/test/test_systec.py +++ b/test/test_systec.py @@ -2,10 +2,7 @@ # coding: utf-8 import unittest -try: - from unittest.mock import Mock, patch -except ImportError: - from mock import Mock, patch +from unittest.mock import Mock, patch import can from can.interfaces.systec import ucan, ucanbus diff --git a/test/test_viewer.py b/test/test_viewer.py index c4b7aa45f..9420b6d37 100644 --- a/test/test_viewer.py +++ b/test/test_viewer.py @@ -23,8 +23,6 @@ # Web : http://www.lauszus.com # e-mail : lauszus@gmail.com -from __future__ import absolute_import - import argparse import can import curses @@ -36,15 +34,8 @@ import unittest import os import six - from typing import Dict, Tuple, Union - -try: - # noinspection PyCompatibility - from unittest.mock import Mock, patch -except ImportError: - # noinspection PyPackageRequirements - from mock import Mock, patch +from unittest.mock import Mock, patch from can.viewer import KEY_ESC, KEY_SPACE, CanViewer, parse_args @@ -337,9 +328,7 @@ def test_pack_unpack(self): parsed_data = CanViewer.unpack_data(CANOPEN_TPDO4 + 2, data_structs, raw_data) self.assertListEqual(parsed_data, [0xFFFFFF, 0xFFFFFFFF]) - # See: http://python-future.org/compatible_idioms.html#long-integers - from past.builtins import long - self.assertTrue(all(isinstance(d, (int, long)) for d in parsed_data)) + self.assertTrue(all(isinstance(d, int) for d in parsed_data)) # Make sure that the ValueError exception is raised with self.assertRaises(ValueError): From 262c209c4d17f7cac8c57106c2401d4ea3d0dc84 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:02:11 +0200 Subject: [PATCH 016/252] remove func:send_periodic() --- can/broadcastmanager.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 79d586744..85ad03776 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -142,17 +142,3 @@ def _run(self): # 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, *args, **kwargs): - """ - Send a :class:`~can.Message` every `period` seconds on the given bus. - - :param can.BusABC bus: A CAN bus which supports sending. - :param can.Message message: Message to send periodically. - :param float period: The minimum time between sending messages. - :return: A started task instance - """ - warnings.warn("The function `can.send_periodic` is deprecated and will " + - "be removed in an upcoming version. Please use `can.Bus.send_periodic` instead.", DeprecationWarning) - return bus.send_periodic(message, period, *args, **kwargs) From c1e10c1949c73769a4e1f1d3632c28144805be02 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:04:58 +0200 Subject: [PATCH 017/252] remove old CAN module --- can/CAN.py | 29 ----------------------------- doc/development.rst | 2 -- setup.cfg | 3 --- 3 files changed, 34 deletions(-) delete mode 100644 can/CAN.py diff --git a/can/CAN.py b/can/CAN.py deleted file mode 100644 index 0ed96dfb1..000000000 --- a/can/CAN.py +++ /dev/null @@ -1,29 +0,0 @@ -# coding: utf-8 - -""" -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.x to aide with migration. - -WARNING: -This module is deprecated an will get removed in version 3.x. -Please use ``import can`` instead. -""" - -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 warnings - -# See #267. -# Version 2.0 - 2.1: Log a Debug message -# Version 2.2: Log a Warning -# Version 3.x: DeprecationWarning -# Version 4.0: Remove the module -warnings.warn('Loading python-can via the old "CAN" API is deprecated since v3.0 an will get removed in v4.0 ' - 'Please use `import can` instead.', DeprecationWarning) diff --git a/doc/development.rst b/doc/development.rst index 602e4e347..8bad5c58e 100644 --- a/doc/development.rst +++ b/doc/development.rst @@ -69,8 +69,6 @@ The modules in ``python-can`` are: |:doc:`broadcastmanager ` | Contains interface independent broadcast manager | | | code. | +---------------------------------+------------------------------------------------------+ -|:doc:`CAN ` | Legacy API. Deprecated. | -+---------------------------------+------------------------------------------------------+ Creating a new Release diff --git a/setup.cfg b/setup.cfg index 49177e68e..e9a7cb985 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,9 +15,6 @@ addopts = -v --timeout=300 --cov=can --cov-config=setup.cfg branch = False # already specified by call to pytest using --cov=can #source = can -omit = - # legacy code - can/CAN.py [coverage:report] # two digits after decimal point From 682403cab9ac67c1b66eb9f2b01972b848dc0519 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:06:14 +0200 Subject: [PATCH 018/252] remove now obsolete docs for send_periodic() --- doc/bcm.rst | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/doc/bcm.rst b/doc/bcm.rst index 96e73d52d..549b06edd 100644 --- a/doc/bcm.rst +++ b/doc/bcm.rst @@ -42,14 +42,3 @@ which inherits from :class:`~can.broadcastmanager.CyclicTask`. .. autoclass:: can.RestartableCyclicTaskABC :members: - - -Functional API --------------- - -.. warning:: - The functional API in :func:`can.broadcastmanager.send_periodic` is now deprecated - and will be removed in version 4.0. - Use the object oriented API via :meth:`can.BusABC.send_periodic` instead. - -.. autofunction:: can.broadcastmanager.send_periodic From 36434c8a05841a664319773f9cb5896ec90012cd Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:07:46 +0200 Subject: [PATCH 019/252] remove deprecated access to socketcan member --- can/interface.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/can/interface.py b/can/interface.py index 6c830d0c4..cbc907f86 100644 --- a/can/interface.py +++ b/can/interface.py @@ -18,11 +18,6 @@ from .util import load_config from .interfaces import BACKENDS -if 'linux' in sys.platform: - # Deprecated and undocumented access to SocketCAN cyclic tasks - # Will be removed in version 4.0 - from can.interfaces.socketcan import CyclicSendTask, MultiRateCyclicSendTask - # Required by "detect_available_configs" for argument interpretation if sys.version_info.major > 2: basestring = str From 8ad901e84a87231951423f2f8d71bb5146afabbb Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:12:20 +0200 Subject: [PATCH 020/252] remove entry point python_can.interface --- can/interfaces/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index ec79e51d6..c4c7f52f7 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -31,10 +31,4 @@ for interface in iter_entry_points('can.interface') }) -# Old entry point name. May be removed >3.0. -for interface in iter_entry_points('python_can.interface'): - BACKENDS[interface.name] = (interface.module_name, interface.attrs[0]) - warnings.warn('{} is using the deprecated python_can.interface entry point. '.format(interface.name) + - 'Please change to can.interface instead.', DeprecationWarning) - VALID_INTERFACES = frozenset(list(BACKENDS.keys()) + ['socketcan_native', 'socketcan_ctypes']) From a688695f7da3f49596f4243705ee4412ae92e9fb Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:14:30 +0200 Subject: [PATCH 021/252] remove special case for socketcan_{native,ctypes} --- can/interfaces/socketcan/socketcan.py | 4 +--- can/util.py | 7 ------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index 633c87b22..6aebc7221 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -1,6 +1,6 @@ # coding: utf-8 -import logging +import logging import ctypes import ctypes.util import os @@ -14,8 +14,6 @@ log_tx = log.getChild("tx") log_rx = log.getChild("rx") -log.debug("Loading socketcan native backend") - try: import fcntl except ImportError: diff --git a/can/util.py b/can/util.py index af421651f..be6793152 100644 --- a/can/util.py +++ b/can/util.py @@ -186,13 +186,6 @@ def load_config(path=None, config=None, context=None): if key not in config: config[key] = None - # Handle deprecated socketcan types - if config['interface'] in ('socketcan_native', 'socketcan_ctypes'): - # DeprecationWarning in 3.x releases - # TODO: Remove completely in 4.0 - warnings.warn('{} is deprecated, use socketcan instead'.format(config['interface']), DeprecationWarning) - config['interface'] = 'socketcan' - if config['interface'] not in VALID_INTERFACES: raise NotImplementedError('Invalid CAN Bus Type - {}'.format(config['interface'])) From 6a38debd041e4448017e451517b15daaa0933dd5 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:18:10 +0200 Subject: [PATCH 022/252] remove deprecated members of can.Message --- can/message.py | 51 ++------------------------------------------------ 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/can/message.py b/can/message.py index f85218fc0..1c1a3fd9d 100644 --- a/can/message.py +++ b/can/message.py @@ -45,46 +45,13 @@ class Message(object): "is_fd", "bitrate_switch", "error_state_indicator", - "__weakref__", # support weak references to messages - "_dict" # see __getattr__ + "__weakref__" # support weak references to messages ) - def __getattr__(self, key): - # TODO keep this for a version, in order to not break old code - # this entire method (as well as the _dict attribute in __slots__ and the __setattr__ method) - # can be removed in 4.0 - # this method is only called if the attribute was not found elsewhere, like in __slots__ - try: - warnings.warn("Custom attributes of messages are deprecated and will be removed in 4.0", DeprecationWarning) - return self._dict[key] - except KeyError: - raise AttributeError("'message' object has no attribute '{}'".format(key)) - - def __setattr__(self, key, value): - # see __getattr__ - try: - super(Message, self).__setattr__(key, value) - except AttributeError: - warnings.warn("Custom attributes of messages are deprecated and will be removed in 4.0", DeprecationWarning) - self._dict[key] = value - - @property - def id_type(self): - # TODO remove in 4.0 - warnings.warn("Message.id_type is deprecated and will be removed in 4.0, use is_extended_id instead", DeprecationWarning) - return self.is_extended_id - - @id_type.setter - def id_type(self, value): - # TODO remove in 4.0 - warnings.warn("Message.id_type is deprecated and will be removed in 4.0, use is_extended_id instead", DeprecationWarning) - self.is_extended_id = value - def __init__(self, timestamp=0.0, arbitration_id=0, is_extended_id=None, is_remote_frame=False, is_error_frame=False, channel=None, dlc=None, data=None, is_fd=False, bitrate_switch=False, error_state_indicator=False, - extended_id=None, # deprecated in 3.x, TODO remove in 4.x check=False): """ To create a message object, simply provide any of the below attributes @@ -98,24 +65,12 @@ def __init__(self, timestamp=0.0, arbitration_id=0, is_extended_id=None, :raises ValueError: iff `check` is set to `True` and one or more arguments were invalid """ - self._dict = dict() # see __getattr__ - self.timestamp = timestamp self.arbitration_id = arbitration_id - - if extended_id is not None: - # TODO remove in 4.0 - warnings.warn("The extended_id parameter is deprecated and will be removed in 4.0, use is_extended_id instead", DeprecationWarning) - - if is_extended_id is not None: - self.is_extended_id = is_extended_id - else: - self.is_extended_id = True if extended_id is None else extended_id - + self.is_extended_id = is_extended_id self.is_remote_frame = is_remote_frame self.is_error_frame = is_error_frame self.channel = channel - self.is_fd = is_fd self.bitrate_switch = bitrate_switch self.error_state_indicator = error_state_indicator @@ -239,7 +194,6 @@ def __copy__(self): bitrate_switch=self.bitrate_switch, error_state_indicator=self.error_state_indicator ) - new._dict.update(self._dict) return new def __deepcopy__(self, memo): @@ -256,7 +210,6 @@ def __deepcopy__(self, memo): bitrate_switch=self.bitrate_switch, error_state_indicator=self.error_state_indicator ) - new._dict.update(self._dict) return new def _check(self): From f31a36f92382a13a18f961573e8c33cfd38db95c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:33:33 +0200 Subject: [PATCH 023/252] remove import of removed function --- can/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/__init__.py b/can/__init__.py index a612363ae..0bc8cfe62 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -45,7 +45,7 @@ class CanError(IOError): from . import interface from .interface import Bus, detect_available_configs -from .broadcastmanager import send_periodic, \ +from .broadcastmanager import \ CyclicSendTaskABC, \ LimitedDurationCyclicSendTaskABC, \ ModifiableCyclicTaskABC, \ From d8a8d811da47a7c5f9902b82e9fe22f74dac49de Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 19:04:22 +0200 Subject: [PATCH 024/252] fix small problem in __repr__() --- can/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/message.py b/can/message.py index 1c1a3fd9d..e0435867e 100644 --- a/can/message.py +++ b/can/message.py @@ -149,7 +149,7 @@ def __nonzero__(self): def __repr__(self): args = ["timestamp={}".format(self.timestamp), "arbitration_id={:#x}".format(self.arbitration_id), - "extended_id={}".format(self.is_extended_id)] + "is_extended_id={}".format(self.is_extended_id)] if self.is_remote_frame: args.append("is_remote_frame={}".format(self.is_remote_frame)) From 978d0109f01c288aacfda8fb45ab0963cd1b169c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 19:05:09 +0200 Subject: [PATCH 025/252] make is_extended_id True by default again --- can/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/message.py b/can/message.py index e0435867e..3826cede2 100644 --- a/can/message.py +++ b/can/message.py @@ -48,7 +48,7 @@ class Message(object): "__weakref__" # support weak references to messages ) - def __init__(self, timestamp=0.0, arbitration_id=0, is_extended_id=None, + def __init__(self, timestamp=0.0, arbitration_id=0, is_extended_id=True, is_remote_frame=False, is_error_frame=False, channel=None, dlc=None, data=None, is_fd=False, bitrate_switch=False, error_state_indicator=False, From 342f7400f41822560e2a24ea6f6d2e5ec9b89cdb Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 12:27:00 +0200 Subject: [PATCH 026/252] remove python <3.6 from root filea --- .appveyor.yml | 6 +----- .travis.yml | 5 +---- README.rst | 4 ++-- setup.py | 5 +---- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f65386f3e..500c71320 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,14 +3,10 @@ environment: # For Python versions available on Appveyor, see # https://www.appveyor.com/docs/windows-images-software/#python - # Python pre-2.7 and 3.0-3.4 have reached EOL + # Only Python 3.6+ is supported - - PYTHON: "C:\\Python27" - - PYTHON: "C:\\Python35" - PYTHON: "C:\\Python36" - PYTHON: "C:\\Python37" - - PYTHON: "C:\\Python27-x64" - - PYTHON: "C:\\Python35-x64" - PYTHON: "C:\\Python36-x64" - PYTHON: "C:\\Python37-x64" diff --git a/.travis.yml b/.travis.yml index b45e17d95..6e84eb22a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,15 +8,12 @@ cache: - "$HOME/.cache/pip" python: - # CPython; versions pre-2.7 and 3.0-3.5 have reached EOL - - "2.7" + # CPython; only 3.6 is supported - "3.6" - "3.7" - 3.8-dev - nightly # PyPy: - - pypy # Python 2.7 - - pypy3.5 # Python 3.5 - pypy3 env: diff --git a/README.rst b/README.rst index affcde831..5b29f46ad 100644 --- a/README.rst +++ b/README.rst @@ -37,7 +37,7 @@ 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.5+ as well as PyPy 2 & 3 and runs +The library currently supports Python 3.6+ as well as PyPy 3 and runs on Mac, Linux and Windows. ================== =========== @@ -45,7 +45,7 @@ Library Version Python ------------------ ----------- 2.x 2.6+, 3.4+ 3.x 2.7+, 3.5+ - 4.x (expected) 3.6+ + 4.x 3.6+ ================== =========== diff --git a/setup.py b/setup.py index c600b7215..a5d452370 100644 --- a/setup.py +++ b/setup.py @@ -51,8 +51,6 @@ classifiers=[ # a list of all available ones: https://pypi.org/classifiers/ "Programming Language :: Python", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", @@ -97,11 +95,10 @@ # Installation # see https://www.python.org/dev/peps/pep-0345/#version-specifiers - python_requires=">=2.7", + python_requires=">=3.6", install_requires=[ 'wrapt~=1.10', 'aenum', - 'typing;python_version<"3.5"', 'windows-curses;platform_system=="Windows"', ], setup_requires=["pytest-runner"], From 036b99fd6d5c73e92fe7b6c368d2302944fe34ca Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 12:39:08 +0200 Subject: [PATCH 027/252] remove python <3.6 from can/* --- can/__init__.py | 2 - can/broadcastmanager.py | 2 +- can/bus.py | 2 - can/interface.py | 2 - can/listener.py | 82 +++++++++++++++++++---------------------- can/logger.py | 3 +- can/message.py | 3 +- can/notifier.py | 7 +--- can/player.py | 2 - can/thread_safe_bus.py | 2 - can/util.py | 8 +--- can/viewer.py | 6 +-- 12 files changed, 45 insertions(+), 76 deletions(-) diff --git a/can/__init__.py b/can/__init__.py index 0bc8cfe62..b9b3b3e21 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -4,8 +4,6 @@ ``can`` is an object-orient Controller Area Network (CAN) interface module. """ -from __future__ import absolute_import - import logging __version__ = "3.2.0" diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 85ad03776..564d6ee49 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -16,7 +16,7 @@ log = logging.getLogger('can.bcm') -class CyclicTask(object): +class CyclicTask: """ Abstract Base for all cyclic tasks. """ diff --git a/can/bus.py b/can/bus.py index 2b36b3c57..9e46cbbf3 100644 --- a/can/bus.py +++ b/can/bus.py @@ -4,8 +4,6 @@ Contains the ABC bus implementation and its documentation. """ -from __future__ import print_function, absolute_import - from abc import ABCMeta, abstractmethod import logging import threading diff --git a/can/interface.py b/can/interface.py index cbc907f86..92c592874 100644 --- a/can/interface.py +++ b/can/interface.py @@ -6,8 +6,6 @@ CyclicSendTasks. """ -from __future__ import absolute_import, print_function - import sys import importlib import logging diff --git a/can/listener.py b/can/listener.py index a91b1dac1..a44d1c04f 100644 --- a/can/listener.py +++ b/can/listener.py @@ -10,20 +10,13 @@ # Python 3.7 from queue import SimpleQueue, Empty except ImportError: - try: - # Python 3.0 - 3.6 - from queue import Queue as SimpleQueue, Empty - except ImportError: - # Python 2 - from Queue import Queue as SimpleQueue, Empty + # Python 3.0 - 3.6 + from queue import Queue as SimpleQueue, Empty -try: - import asyncio -except ImportError: - asyncio = None +import asyncio -class Listener(object): +class Listener: """The basic listener that can be called directly to handle some CAN message:: @@ -133,42 +126,41 @@ def stop(self): self.is_stopped = True -if asyncio is not None: - class AsyncBufferedReader(Listener): - """A message buffer for use with :mod:`asyncio`. +class AsyncBufferedReader(Listener): + """A message buffer for use with :mod:`asyncio`. - See :ref:`asyncio` for how to use with :class:`can.Notifier`. - - Can also be used as an asynchronous iterator:: + See :ref:`asyncio` for how to use with :class:`can.Notifier`. + + Can also be used as an asynchronous iterator:: + + async for msg in reader: + print(msg) + """ + + def __init__(self, loop=None): + # set to "infinite" size + self.buffer = asyncio.Queue(loop=loop) - async for msg in reader: - print(msg) + def on_message_received(self, msg): + """Append a message to the buffer. + + Must only be called inside an event loop! """ + self.buffer.put_nowait(msg) - def __init__(self, loop=None): - # set to "infinite" size - self.buffer = asyncio.Queue(loop=loop) - - def on_message_received(self, msg): - """Append a message to the buffer. - - Must only be called inside an event loop! - """ - self.buffer.put_nowait(msg) - - def get_message(self): - """ - Retrieve the latest message when awaited for:: - - msg = await reader.get_message() - - :rtype: can.Message - :return: The CAN message. - """ - return self.buffer.get() - - def __aiter__(self): - return self + def get_message(self): + """ + Retrieve the latest message when awaited for:: - def __anext__(self): - return self.buffer.get() + msg = await reader.get_message() + + :rtype: can.Message + :return: The CAN message. + """ + return self.buffer.get() + + def __aiter__(self): + return self + + def __anext__(self): + return self.buffer.get() diff --git a/can/logger.py b/can/logger.py index 204eb8dfb..bc35252e9 100644 --- a/can/logger.py +++ b/can/logger.py @@ -16,8 +16,6 @@ Dynamic Controls 2010 """ -from __future__ import absolute_import, print_function - import sys import argparse import socket @@ -116,5 +114,6 @@ def main(): bus.shutdown() logger.stop() + if __name__ == "__main__": main() diff --git a/can/message.py b/can/message.py index 3826cede2..9bb864c0a 100644 --- a/can/message.py +++ b/can/message.py @@ -8,14 +8,13 @@ starting with Python 3.7. """ -from __future__ import absolute_import, division import warnings from copy import deepcopy from math import isinf, isnan -class Message(object): +class Message: """ The :class:`~can.Message` object is used to represent CAN messages for sending, receiving and other purposes like converting between different diff --git a/can/notifier.py b/can/notifier.py index 737ec978e..256085a7b 100644 --- a/can/notifier.py +++ b/can/notifier.py @@ -7,15 +7,12 @@ import threading import logging import time -try: - import asyncio -except ImportError: - asyncio = None +import asyncio logger = logging.getLogger('can.Notifier') -class Notifier(object): +class Notifier: def __init__(self, bus, listeners, timeout=1.0, loop=None): """Manages the distribution of :class:`can.Message` instances to listeners. diff --git a/can/player.py b/can/player.py index c712f1714..e744837c3 100644 --- a/can/player.py +++ b/can/player.py @@ -7,8 +7,6 @@ Similar to canplayer in the can-utils package. """ -from __future__ import absolute_import, print_function - import sys import argparse from datetime import datetime diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index d82ac6bd6..648285b05 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -1,7 +1,5 @@ # coding: utf-8 -from __future__ import print_function, absolute_import - from threading import RLock try: diff --git a/can/util.py b/can/util.py index be6793152..7e106fde4 100644 --- a/can/util.py +++ b/can/util.py @@ -4,8 +4,6 @@ Utilities and configuration file parsing. """ -from __future__ import absolute_import, print_function - import os import os.path import sys @@ -13,11 +11,7 @@ import re import logging import warnings - -try: - from configparser import ConfigParser -except ImportError: - from ConfigParser import SafeConfigParser as ConfigParser +from configparser import ConfigParser import can from can.interfaces import VALID_INTERFACES diff --git a/can/viewer.py b/can/viewer.py index 316d3e3e4..d7e734fb9 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -1,5 +1,5 @@ # coding: utf-8 -# + # Copyright (C) 2018 Kristian Sloth Lauszus. # # This program is free software; you can redistribute it and/or @@ -22,8 +22,6 @@ # Web : http://www.lauszus.com # e-mail : lauszus@gmail.com -from __future__ import absolute_import, print_function - import argparse import os import struct @@ -496,7 +494,7 @@ def main(): # pragma: no cover curses.wrapper(CanViewer, bus, data_structs) -if __name__ == '__main__': # pragma: no cover +if __name__ == '__main__': # Catch ctrl+c try: main() From 877343b8bead125bfe24754ab41403fc406447c3 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 12:42:42 +0200 Subject: [PATCH 028/252] remove python <3.6 from can/io/* --- can/io/__init__.py | 2 -- can/io/asc.py | 2 -- can/io/blf.py | 2 -- can/io/canutils.py | 2 -- can/io/csv.py | 2 -- can/io/generic.py | 2 +- can/io/logger.py | 4 +--- can/io/player.py | 4 +--- can/io/printer.py | 2 -- can/io/sqlite.py | 6 ------ 10 files changed, 3 insertions(+), 25 deletions(-) diff --git a/can/io/__init__.py b/can/io/__init__.py index a0d89f28b..3797d4b5d 100644 --- a/can/io/__init__.py +++ b/can/io/__init__.py @@ -5,8 +5,6 @@ and Writers based off the file extension. """ -from __future__ import absolute_import - # Generic from .logger import Logger from .player import LogReader, MessageSync diff --git a/can/io/asc.py b/can/io/asc.py index 3ed50f04a..6cbfeb60d 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -8,8 +8,6 @@ - under `test/data/logfile.asc` """ -from __future__ import absolute_import - from datetime import datetime import time import logging diff --git a/can/io/blf.py b/can/io/blf.py index d162fdebc..55aab0715 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -14,8 +14,6 @@ objects types. """ -from __future__ import absolute_import - import struct import zlib import datetime diff --git a/can/io/canutils.py b/can/io/canutils.py index 69c0227a4..2d1232c74 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -6,8 +6,6 @@ (https://github.com/linux-can/can-utils). """ -from __future__ import absolute_import, division - import time import datetime import logging diff --git a/can/io/csv.py b/can/io/csv.py index 92f841f8f..c85e6339f 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -11,8 +11,6 @@ of a CSV file. """ -from __future__ import absolute_import - from base64 import b64encode, b64decode from can.message import Message diff --git a/can/io/generic.py b/can/io/generic.py index a61c33a9f..26b85f8e3 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -9,7 +9,7 @@ from can import Listener -class BaseIOHandler(object): +class BaseIOHandler: """A generic file handler that can be used for reading and writing. Can be used as a context manager. diff --git a/can/io/logger.py b/can/io/logger.py index 52d2e8d83..da3edb2b5 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -4,8 +4,6 @@ See the :class:`Logger` class. """ -from __future__ import absolute_import - import logging from ..listener import Listener @@ -61,5 +59,5 @@ def __new__(cls, filename, *args, **kwargs): return CanutilsLogWriter(filename, *args, **kwargs) # else: - log.info('unknown file type "%s", falling pack to can.Printer', filename) + log.warning('unknown file type "%s", falling pack to can.Printer', filename) return Printer(filename, *args, **kwargs) diff --git a/can/io/player.py b/can/io/player.py index 229c157c3..3e856767b 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -6,8 +6,6 @@ in the recorded order an time intervals. """ -from __future__ import absolute_import - from time import time, sleep import logging @@ -65,7 +63,7 @@ def __new__(cls, filename, *args, **kwargs): raise NotImplementedError("No read support for this log format: {}".format(filename)) -class MessageSync(object): +class MessageSync: """ Used to iterate over some given messages in the recorded time. """ diff --git a/can/io/printer.py b/can/io/printer.py index 6cc01f69b..7eac86935 100644 --- a/can/io/printer.py +++ b/can/io/printer.py @@ -4,8 +4,6 @@ This Listener simply prints to stdout / the terminal or a file. """ -from __future__ import print_function, absolute_import - import logging from can.listener import Listener diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 21cd2aafc..f61117384 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -6,8 +6,6 @@ .. note:: The database schema is given in the documentation of the loggers. """ -from __future__ import absolute_import - import sys import time import threading @@ -20,10 +18,6 @@ log = logging.getLogger('can.io.sqlite') -if sys.version_info.major < 3: - # legacy fallback for Python 2 - memoryview = buffer - class SqliteReader(BaseIOHandler): """ From 71d9fa4fd7ecd00ebb50acd21fcc9876c2769fda Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 12:45:13 +0200 Subject: [PATCH 029/252] remove python <3.6 from can/interfaces/* --- can/interfaces/canalystii.py | 2 ++ can/interfaces/iscan.py | 2 -- can/interfaces/slcan.py | 2 -- can/interfaces/virtual.py | 5 +---- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 35f240a66..2d4c69fcb 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -1,3 +1,5 @@ +# coding: utf-8 + from ctypes import * import logging import platform diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index a646bd96e..bfca3fdd3 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -4,8 +4,6 @@ Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH. """ -from __future__ import absolute_import, division - import ctypes import time import logging diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 8793e2c22..70b3dc27b 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -9,8 +9,6 @@ """ -from __future__ import absolute_import - import time import logging diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index 6f24c73f2..5594674c1 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -11,10 +11,7 @@ from copy import deepcopy import logging import time -try: - import queue -except ImportError: - import Queue as queue +import queue from threading import RLock from random import randint From fb9b63fa7bf06a45d4efe931ccdddd6fada3f614 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 17:59:16 +0200 Subject: [PATCH 030/252] remove support for Python < 3.6 from can/interfaces/*/* --- can/interfaces/ixxat/canlib.py | 8 +------- can/interfaces/kvaser/canlib.py | 2 -- can/interfaces/pcan/pcan.py | 16 +++------------- can/interfaces/serial/serial_can.py | 2 -- can/interfaces/socketcan/__init__.py | 2 +- can/interfaces/systec/ucanbus.py | 1 + can/interfaces/usb2can/__init__.py | 2 -- can/interfaces/usb2can/serial_selector.py | 2 -- can/interfaces/usb2can/usb2canInterface.py | 2 -- .../usb2can/usb2canabstractionlayer.py | 2 -- 10 files changed, 6 insertions(+), 33 deletions(-) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 84c8751c1..63b422035 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -13,8 +13,6 @@ """ -from __future__ import absolute_import, division - import ctypes import functools import logging @@ -32,11 +30,7 @@ log = logging.getLogger('can.ixxat') -try: - # since Python 3.3 - from time import perf_counter as _timer_function -except ImportError: - from time import clock as _timer_function +from time import perf_counter as _timer_function # Hack to have vciFormatError as a free function, see below vciFormatError = None diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index fa3a70221..fdde4ef1e 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -8,8 +8,6 @@ Copyright (C) 2010 Dynamic Controls """ -from __future__ import absolute_import - import sys import time import logging diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 864308bab..f772ff576 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -4,8 +4,6 @@ Enable basic CAN over a PCAN USB device. """ -from __future__ import absolute_import, print_function, division - import logging import sys import time @@ -16,9 +14,8 @@ from can.util import len2dlc, dlc2len from .basic import * -boottimeEpoch = 0 try: - import uptime + import uptime # isn't this from a library? import datetime boottimeEpoch = (uptime.boottime() - datetime.datetime.utcfromtimestamp(0)).total_seconds() except: @@ -39,13 +36,6 @@ # Use polling instead HAS_EVENTS = False -try: - # new in 3.3 - timeout_clock = time.perf_counter -except AttributeError: - # deprecated in 3.3 - timeout_clock = time.clock - # Set up logging log = logging.getLogger('can.pcan') @@ -277,7 +267,7 @@ def _recv_internal(self, timeout): 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 + end_time = time.perf_counter() + timeout #log.debug("Trying to read a msg") @@ -293,7 +283,7 @@ def _recv_internal(self, timeout): val = WaitForSingleObject(self._recv_event, timeout_ms) if val != WAIT_OBJECT_0: return None, False - elif timeout is not None and timeout_clock() >= end_time: + elif timeout is not None and time.perf_counter() >= end_time: return None, False else: result = None diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index afa545734..500614868 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -7,8 +7,6 @@ recording CAN traces. """ -from __future__ import absolute_import, division - import logging import struct diff --git a/can/interfaces/socketcan/__init__.py b/can/interfaces/socketcan/__init__.py index 8a2105598..06a9d9959 100644 --- a/can/interfaces/socketcan/__init__.py +++ b/can/interfaces/socketcan/__init__.py @@ -4,4 +4,4 @@ See: https://www.kernel.org/doc/Documentation/networking/can.txt """ -from can.interfaces.socketcan.socketcan import SocketcanBus, CyclicSendTask, MultiRateCyclicSendTask +from .socketcan import SocketcanBus, CyclicSendTask, MultiRateCyclicSendTask diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index 9731398bd..d074a3028 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -16,6 +16,7 @@ class Ucan(UcanServer): """ Wrapper around UcanServer to read messages with timeout using events. """ + def __init__(self): super(Ucan, self).__init__() self._msg_received_event = Event() diff --git a/can/interfaces/usb2can/__init__.py b/can/interfaces/usb2can/__init__.py index 454942934..623af5bc3 100644 --- a/can/interfaces/usb2can/__init__.py +++ b/can/interfaces/usb2can/__init__.py @@ -3,7 +3,5 @@ """ """ -from __future__ import absolute_import - from .usb2canInterface import Usb2canBus from .usb2canabstractionlayer import Usb2CanAbstractionLayer diff --git a/can/interfaces/usb2can/serial_selector.py b/can/interfaces/usb2can/serial_selector.py index b47396876..e74430f04 100644 --- a/can/interfaces/usb2can/serial_selector.py +++ b/can/interfaces/usb2can/serial_selector.py @@ -3,8 +3,6 @@ """ """ -from __future__ import division, print_function, absolute_import - import logging try: diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index eb87ffbd7..1f2b38c32 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -4,8 +4,6 @@ This interface is for Windows only, otherwise use socketCAN. """ -from __future__ import division, print_function, absolute_import - import logging from ctypes import byref diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index a318bcd6f..0086d542d 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -5,8 +5,6 @@ Socket CAN is recommended under Unix/Linux systems. """ -from __future__ import division, print_function, absolute_import - from ctypes import * from struct import * import logging From bb0103febe28c5868b2af79106f1fd4c84266d4d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:09:25 +0200 Subject: [PATCH 031/252] remove obsolete string compatability fix --- can/interface.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/can/interface.py b/can/interface.py index 92c592874..9eba8bcc9 100644 --- a/can/interface.py +++ b/can/interface.py @@ -16,10 +16,6 @@ from .util import load_config from .interfaces import BACKENDS -# Required by "detect_available_configs" for argument interpretation -if sys.version_info.major > 2: - basestring = str - log = logging.getLogger('can.interface') log_autodetect = log.getChild('detect_available_configs') @@ -144,7 +140,7 @@ def detect_available_configs(interfaces=None): if interfaces is None: # use an iterator over the keys so we do not have to copy it interfaces = BACKENDS.keys() - elif isinstance(interfaces, basestring): + elif isinstance(interfaces, str): interfaces = [interfaces, ] # else it is supposed to be an iterable of strings From 5c1644b8ee4bb9dca853c8c740b28e69a4821275 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:19:11 +0200 Subject: [PATCH 032/252] remove obsolete __nonzero__()-method in can.Message --- can/message.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/can/message.py b/can/message.py index 9bb864c0a..cc39642bf 100644 --- a/can/message.py +++ b/can/message.py @@ -138,13 +138,8 @@ def __len__(self): return self.dlc def __bool__(self): - # For Python 3 return True - def __nonzero__(self): - # For Python 2 - return self.__bool__() - def __repr__(self): args = ["timestamp={}".format(self.timestamp), "arbitration_id={:#x}".format(self.arbitration_id), From 9f09418b3c5d075a1210f1e3ac9dd081505bfc7f Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:22:26 +0200 Subject: [PATCH 033/252] remove socketcan_ctypes leftovers --- can/interfaces/socketcan/socketcan.py | 75 ++------------------------- 1 file changed, 5 insertions(+), 70 deletions(-) diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index 6aebc7221..6979594f8 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -29,42 +29,6 @@ pack_filters, find_available_interfaces, error_code_to_str -try: - socket.CAN_BCM -except AttributeError: - HAS_NATIVE_SUPPORT = False -else: - HAS_NATIVE_SUPPORT = True - - -if not HAS_NATIVE_SUPPORT: - def check_status(result, function, arguments): - if result < 0: - raise can.CanError(error_code_to_str(ctypes.get_errno())) - return result - - try: - libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) - libc.bind.errcheck = check_status - libc.connect.errcheck = check_status - libc.sendto.errcheck = check_status - libc.recvfrom.errcheck = check_status - except: - log.warning("libc is unavailable") - libc = None - - def get_addr(sock, channel): - """Get sockaddr for a channel.""" - if channel: - data = struct.pack("16si", channel.encode(), 0) - res = fcntl.ioctl(sock, SIOCGIFINDEX, data) - idx, = struct.unpack("16xi", res) - else: - # All channels - idx = 0 - return struct.pack("HiLL", AF_CAN, idx, 0, 0) - - # Setup BCM struct def bcm_header_factory(fields, alignment=8): curr_stride = 0 @@ -262,11 +226,7 @@ def dissect_can_frame(frame): def create_bcm_socket(channel): """create a broadcast manager socket and connect to the given interface""" s = socket.socket(PF_CAN, socket.SOCK_DGRAM, CAN_BCM) - if HAS_NATIVE_SUPPORT: - s.connect((channel,)) - else: - addr = get_addr(s, channel) - libc.connect(s.fileno(), addr, len(addr)) + s.connect((channel,)) return s @@ -421,12 +381,7 @@ def bind_socket(sock, channel='can0'): If the specified interface isn't found. """ log.debug('Binding socket to channel=%s', channel) - if HAS_NATIVE_SUPPORT: - sock.bind((channel,)) - else: - # For Python 2.7 - addr = get_addr(sock, channel) - libc.bind(sock.fileno(), addr, len(addr)) + sock.bind((channel,)) log.debug('Bound socket.') @@ -444,22 +399,8 @@ def capture_message(sock, get_channel=False): # Fetching the Arb ID, DLC and Data try: if get_channel: - if HAS_NATIVE_SUPPORT: - cf, addr = sock.recvfrom(CANFD_MTU) - channel = addr[0] if isinstance(addr, tuple) else addr - else: - data = ctypes.create_string_buffer(CANFD_MTU) - addr = ctypes.create_string_buffer(32) - addrlen = ctypes.c_int(len(addr)) - received = libc.recvfrom(sock.fileno(), data, len(data), 0, - addr, ctypes.byref(addrlen)) - cf = data.raw[:received] - # Figure out the channel name - family, ifindex = struct.unpack_from("Hi", addr.raw) - assert family == AF_CAN - data = struct.pack("16xi", ifindex) - res = fcntl.ioctl(sock, SIOCGIFNAME, data) - channel = ctypes.create_string_buffer(res).value.decode() + cf, addr = sock.recvfrom(CANFD_MTU) + channel = addr[0] if isinstance(addr, tuple) else addr else: cf = sock.recv(CANFD_MTU) channel = None @@ -634,13 +575,7 @@ def _send_once(self, data, channel=None): try: if self.channel == "" and channel: # Message must be addressed to a specific channel - if HAS_NATIVE_SUPPORT: - sent = self.socket.sendto(data, (channel, )) - else: - addr = get_addr(self.socket, channel) - sent = libc.sendto(self.socket.fileno(), - data, len(data), 0, - addr, len(addr)) + sent = self.socket.sendto(data, (channel, )) else: sent = self.socket.send(data) except socket.error as exc: From 5214e93342ca67f3b7ddb3748b275368e2f2886b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:34:25 +0200 Subject: [PATCH 034/252] remove conditional import, since it will now always work --- can/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/can/__init__.py b/can/__init__.py index b9b3b3e21..0606266a9 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -20,11 +20,7 @@ class CanError(IOError): pass -from .listener import Listener, BufferedReader, RedirectReader -try: - from .listener import AsyncBufferedReader -except ImportError: - pass +from .listener import Listener, BufferedReader, RedirectReader, AsyncBufferedReader from .io import Logger, Printer, LogReader, MessageSync from .io import ASCWriter, ASCReader From 83710cabf1fedc25a9dee90cbe90f93766b2b2de Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 18:48:18 +0200 Subject: [PATCH 035/252] run 2to3 and cleanup afterwards --- can/bus.py | 4 +--- can/interface.py | 5 ++--- can/interfaces/socketcan/socketcan.py | 2 +- can/interfaces/socketcan/utils.py | 1 + can/io/generic.py | 4 +--- can/listener.py | 4 +--- can/logger.py | 6 +++--- can/player.py | 2 +- can/viewer.py | 4 ++-- 9 files changed, 13 insertions(+), 19 deletions(-) diff --git a/can/bus.py b/can/bus.py index 9e46cbbf3..c8ade66d2 100644 --- a/can/bus.py +++ b/can/bus.py @@ -24,7 +24,7 @@ class BusState(Enum): ERROR = auto() -class BusABC(object): +class BusABC(metaclass=ABCMeta): """The CAN Bus Abstract Base Class that serves as the basis for all concrete interfaces. @@ -401,5 +401,3 @@ def _detect_available_configs(): for usage in the interface's bus constructor. """ raise NotImplementedError() - - __metaclass__ = ABCMeta diff --git a/can/interface.py b/can/interface.py index 9eba8bcc9..30bade25c 100644 --- a/can/interface.py +++ b/can/interface.py @@ -138,10 +138,9 @@ def detect_available_configs(interfaces=None): # Figure out where to search if interfaces is None: - # use an iterator over the keys so we do not have to copy it - interfaces = BACKENDS.keys() + interfaces = BACKENDS elif isinstance(interfaces, str): - interfaces = [interfaces, ] + interfaces = (interfaces, ) # else it is supposed to be an iterable of strings result = [] diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index 6979594f8..dc91471a5 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -657,7 +657,7 @@ def receiver(event): bind_socket(receiver_socket, 'vcan0') print("Receiver is waiting for a message...") event.set() - print("Receiver got: ", capture_message(receiver_socket)) + print(f"Receiver got: {capture_message(receiver_socket)}") def sender(event): event.wait() diff --git a/can/interfaces/socketcan/utils.py b/can/interfaces/socketcan/utils.py index 44d356920..ebd095f35 100644 --- a/can/interfaces/socketcan/utils.py +++ b/can/interfaces/socketcan/utils.py @@ -67,6 +67,7 @@ def find_available_interfaces(): log.debug("find_available_interfaces(): detected: %s", interface_names) return filter(_PATTERN_CAN_INTERFACE.match, interface_names) + def error_code_to_str(code): """ Converts a given error code (errno) to a useful and human readable string. diff --git a/can/io/generic.py b/can/io/generic.py index 26b85f8e3..b452d8404 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -9,7 +9,7 @@ from can import Listener -class BaseIOHandler: +class BaseIOHandler(metaclass=ABCMeta): """A generic file handler that can be used for reading and writing. Can be used as a context manager. @@ -19,8 +19,6 @@ class BaseIOHandler: was opened """ - __metaclass__ = ABCMeta - def __init__(self, file, mode='rt'): """ :param file: a path-like object to open a file, a file-like object diff --git a/can/listener.py b/can/listener.py index a44d1c04f..850a7d4a7 100644 --- a/can/listener.py +++ b/can/listener.py @@ -16,7 +16,7 @@ import asyncio -class Listener: +class Listener(metaclass=ABCMeta): """The basic listener that can be called directly to handle some CAN message:: @@ -32,8 +32,6 @@ class Listener: listener.stop() """ - __metaclass__ = ABCMeta - @abstractmethod def on_message_received(self, msg): """This method is called to handle the given message. diff --git a/can/logger.py b/can/logger.py index bc35252e9..3cf2758e4 100644 --- a/can/logger.py +++ b/can/logger.py @@ -76,7 +76,7 @@ def main(): can_filters = [] if len(results.filter) > 0: - print('Adding filter/s', results.filter) + print(f"Adding filter(s): {results.filter}") for filt in results.filter: if ':' in filt: _ = filt.split(":") @@ -99,8 +99,8 @@ def main(): elif results.passive: bus.state = BusState.PASSIVE - print('Connected to {}: {}'.format(bus.__class__.__name__, bus.channel_info)) - print('Can Logger (Started on {})\n'.format(datetime.now())) + print(f"Connected to {bus.__class__.__name__}: {bus.channel_info}") + print(f"Can Logger (Started on {datetime.now()})") logger = Logger(results.log_file) try: diff --git a/can/player.py b/can/player.py index e744837c3..d41396cf5 100644 --- a/can/player.py +++ b/can/player.py @@ -78,7 +78,7 @@ def main(): in_sync = MessageSync(reader, timestamps=results.timestamps, gap=results.gap, skip=results.skip) - print('Can LogReader (Started on {})'.format(datetime.now())) + print(f"Can LogReader (Started on {datetime.now()})") try: for m in in_sync: diff --git a/can/viewer.py b/can/viewer.py index d7e734fb9..cecc17037 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -145,7 +145,7 @@ def unpack_data(cmd, cmd_to_struct, data): # type: (int, Dict, bytes) -> List[U # These messages do not contain a data package return [] - for key in cmd_to_struct.keys(): + for key in cmd_to_struct: if cmd == key if isinstance(key, int) else cmd in key: value = cmd_to_struct[key] if isinstance(value, tuple): @@ -265,7 +265,7 @@ def draw_header(self): def redraw_screen(self): # Trigger a complete redraw self.draw_header() - for key in self.ids.keys(): + for key in self.ids: self.draw_can_bus_message(self.ids[key]['msg']) From 2c69db12ecaeab8db1200e6b02934b4e66bc781d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 03:04:03 +0200 Subject: [PATCH 036/252] address review comments --- can/interfaces/pcan/pcan.py | 3 ++- can/listener.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index f772ff576..aa4dec91d 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -15,7 +15,8 @@ from .basic import * try: - import uptime # isn't this from a library? + # use the "uptime" library if available + import uptime import datetime boottimeEpoch = (uptime.boottime() - datetime.datetime.utcfromtimestamp(0)).total_seconds() except: diff --git a/can/listener.py b/can/listener.py index 850a7d4a7..3f2d5a87d 100644 --- a/can/listener.py +++ b/can/listener.py @@ -146,7 +146,7 @@ def on_message_received(self, msg): """ self.buffer.put_nowait(msg) - def get_message(self): + async def get_message(self): """ Retrieve the latest message when awaited for:: @@ -155,7 +155,7 @@ def get_message(self): :rtype: can.Message :return: The CAN message. """ - return self.buffer.get() + return await self.buffer.get() def __aiter__(self): return self From 544f6c3bb8d87613668679a77c1dd4e4f793d989 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 03:20:06 +0200 Subject: [PATCH 037/252] remove universal wheel tag in setup.cfg --- setup.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index e9a7cb985..a0e8d5b6a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ -[bdist_wheel] -universal = 1 - [aliases] test=pytest From 25db5c9e48b42fe6f2b886a868e7efc692903a13 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Sun, 19 May 2019 16:29:32 +1000 Subject: [PATCH 038/252] Add a basic tox file to the repository (#599) --- tox.ini | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tox.ini diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..7447ccaf2 --- /dev/null +++ b/tox.ini @@ -0,0 +1,11 @@ +[tox] +envlist = py36, py37 + +[testenv] +commands = + pip install .[test] + pytest +passenv = CI +recreate = True +usedevelop = True +sitepackages=False From d8828ca69d92874e994daaf34bc601979d737203 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 14:06:29 +0200 Subject: [PATCH 039/252] remove unused mock and future dependencies; six will be done in a seperate PR --- setup.py | 2 -- test/serial_test.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index a5d452370..805b582be 100644 --- a/setup.py +++ b/setup.py @@ -29,12 +29,10 @@ } tests_require = [ - 'mock~=2.0', 'pytest~=4.3', 'pytest-timeout~=1.3', 'pytest-cov~=2.6', 'codecov~=2.0', - 'future', 'six', 'hypothesis' ] + extras_require['serial'] diff --git a/test/serial_test.py b/test/serial_test.py index c49c15354..a29379511 100644 --- a/test/serial_test.py +++ b/test/serial_test.py @@ -8,7 +8,7 @@ """ import unittest -from mock import patch +from unittest.mock import patch import can from can.interfaces.serial.serial_can import SerialBus From 048df91f0a0f4109b520fb44829a00d3f9d690dd Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 19:24:35 +0200 Subject: [PATCH 040/252] use new super syntax in can/** --- can/broadcastmanager.py | 8 ++++---- can/ctypesutil.py | 8 ++++---- can/interfaces/canalystii.py | 2 +- can/interfaces/ics_neovi/neovi_bus.py | 7 +++---- can/interfaces/iscan.py | 4 ++-- can/interfaces/ixxat/canlib.py | 4 ++-- can/interfaces/ixxat/exceptions.py | 3 ++- can/interfaces/kvaser/canlib.py | 4 ++-- can/interfaces/nican.py | 4 ++-- can/interfaces/pcan/pcan.py | 4 ++-- can/interfaces/serial/serial_can.py | 2 +- can/interfaces/slcan.py | 2 +- can/interfaces/socketcan/socketcan.py | 6 +++--- can/interfaces/systec/exceptions.py | 9 ++++++--- can/interfaces/systec/structures.py | 6 +++--- can/interfaces/systec/ucanbus.py | 6 +++--- can/interfaces/usb2can/usb2canInterface.py | 4 ++-- can/interfaces/vector/canlib.py | 2 +- can/interfaces/vector/exceptions.py | 3 +-- can/interfaces/virtual.py | 2 +- can/io/asc.py | 6 +++--- can/io/blf.py | 6 +++--- can/io/canutils.py | 4 ++-- can/io/csv.py | 4 ++-- can/io/generic.py | 2 +- can/io/printer.py | 2 +- can/io/sqlite.py | 6 +++--- can/thread_safe_bus.py | 2 +- can/viewer.py | 10 +++++----- 29 files changed, 67 insertions(+), 65 deletions(-) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 564d6ee49..05a27197a 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -44,7 +44,7 @@ def __init__(self, message, period): self.can_id = message.arbitration_id self.arbitration_id = message.arbitration_id self.period = period - super(CyclicSendTaskABC, self).__init__() + super().__init__() class LimitedDurationCyclicSendTaskABC(CyclicSendTaskABC): @@ -57,7 +57,7 @@ def __init__(self, message, period, duration): :param float duration: The duration to keep sending this message at given rate. """ - super(LimitedDurationCyclicSendTaskABC, self).__init__(message, period) + super().__init__(message, period) self.duration = duration @@ -99,7 +99,7 @@ def __init__(self, channel, message, count, initial_period, subsequent_period): :param float initial_period: :param float subsequent_period: """ - super(MultiRateCyclicSendTaskABC, self).__init__(channel, message, subsequent_period) + super().__init__(channel, message, subsequent_period) class ThreadBasedCyclicSendTask(ModifiableCyclicTaskABC, @@ -108,7 +108,7 @@ class ThreadBasedCyclicSendTask(ModifiableCyclicTaskABC, """Fallback cyclic send task using thread.""" def __init__(self, bus, lock, message, period, duration=None): - super(ThreadBasedCyclicSendTask, self).__init__(message, period, duration) + super().__init__(message, period, duration) self.bus = bus self.lock = lock self.stopped = True diff --git a/can/ctypesutil.py b/can/ctypesutil.py index 8a69b8df9..1df53e5fc 100644 --- a/can/ctypesutil.py +++ b/can/ctypesutil.py @@ -58,9 +58,9 @@ class CLibrary_Win32(_LibBase, LibraryMixin): def __init__(self, library_or_path): if (isinstance(library_or_path, str)): - super(CLibrary_Win32, self).__init__(library_or_path) + super().__init__(library_or_path) else: - super(CLibrary_Win32, self).__init__(library_or_path._name, library_or_path._handle) + super().__init__(library_or_path._name, library_or_path._handle) @property def function_type(self): @@ -72,9 +72,9 @@ class CLibrary_Unix(ctypes.CDLL, LibraryMixin): def __init__(self, library_or_path): if (isinstance(library_or_path, str)): - super(CLibrary_Unix, self).__init__(library_or_path) + super().__init__(library_or_path) else: - super(CLibrary_Unix, self).__init__(library_or_path._name, library_or_path._handle) + super().__init__(library_or_path._name, library_or_path._handle) @property def function_type(self): diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 2d4c69fcb..2626f607b 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -78,7 +78,7 @@ def __init__(self, channel, device=0, baud=None, Timing0=None, Timing1=None, can :param Timing1: :param can_filters: filters for packet """ - super(CANalystIIBus, self).__init__(channel, can_filters) + super().__init__(channel, can_filters) if isinstance(channel, (list, tuple)): self.channels = channel diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 4baee6177..b5218a4cf 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -45,7 +45,7 @@ def __init__( self, error_number, description_short, description_long, severity, restart_needed ): - super(ICSApiError, self).__init__(description_short) + super().__init__(description_short) self.error_number = error_number self.description_short = description_short self.description_long = description_long @@ -97,8 +97,7 @@ def __init__(self, channel, can_filters=None, **kwargs): if ics is None: raise ImportError('Please install python-ics') - super(NeoViBus, self).__init__( - channel=channel, can_filters=can_filters, **kwargs) + super().__init__(channel=channel, can_filters=can_filters, **kwargs) logger.info("CAN Filters: {}".format(can_filters)) logger.info("Got configuration of: {}".format(kwargs)) @@ -174,7 +173,7 @@ def get_serial_number(device): return str(device.SerialNumber) def shutdown(self): - super(NeoViBus, self).shutdown() + super().shutdown() ics.close_device(self.dev) @staticmethod diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index bfca3fdd3..bb02d261e 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -86,7 +86,7 @@ def __init__(self, channel, bitrate=500000, poll_interval=0.01, **kwargs): self.poll_interval = poll_interval iscan.isCAN_DeviceInitEx(self.channel, self.BAUDRATES[bitrate]) - super(IscanBus, self).__init__(channel=channel, bitrate=bitrate, + super().__init__(channel=channel, bitrate=bitrate, poll_interval=poll_interval, **kwargs) def _recv_internal(self, timeout): @@ -159,7 +159,7 @@ class IscanError(CanError): } def __init__(self, function, error_code, arguments): - super(IscanError, self).__init__() + super().__init__() # :Status code self.error_code = error_code # :Function that failed diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 63b422035..b0c9e8d3a 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -389,7 +389,7 @@ def __init__(self, channel, can_filters=None, **kwargs): except (VCITimeout, VCIRxQueueEmptyError): break - super(IXXATBus, self).__init__(channel=channel, can_filters=None, **kwargs) + super().__init__(channel=channel, can_filters=None, **kwargs) def _inWaiting(self): try: @@ -532,7 +532,7 @@ class CyclicSendTask(LimitedDurationCyclicSendTaskABC, """A message in the cyclic transmit list.""" def __init__(self, scheduler, msg, period, duration, resolution): - super(CyclicSendTask, self).__init__(msg, period, duration) + super().__init__(msg, period, duration) self._scheduler = scheduler self._index = None self._count = int(duration / period) if duration else 0 diff --git a/can/interfaces/ixxat/exceptions.py b/can/interfaces/ixxat/exceptions.py index ac1700dca..dde869032 100644 --- a/can/interfaces/ixxat/exceptions.py +++ b/can/interfaces/ixxat/exceptions.py @@ -23,8 +23,9 @@ class VCIError(CanError): class VCIRxQueueEmptyError(VCIError): """ Wraps the VCI_E_RXQUEUE_EMPTY error """ + def __init__(self): - super(VCIRxQueueEmptyError, self).__init__("Receive queue is empty") + super().__init__("Receive queue is empty") class VCIDeviceNotFoundError(CanError): diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index fdde4ef1e..6506a962d 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -67,7 +67,7 @@ class CANLIBError(CanError): """ def __init__(self, function, error_code, arguments): - super(CANLIBError, self).__init__() + super().__init__() self.error_code = error_code self.function = function self.arguments = arguments @@ -444,7 +444,7 @@ def __init__(self, channel, can_filters=None, **kwargs): self._timestamp_offset = time.time() - (timer.value * TIMESTAMP_FACTOR) self._is_filtered = False - super(KvaserBus, self).__init__(channel=channel, can_filters=can_filters, **kwargs) + super().__init__(channel=channel, can_filters=can_filters, **kwargs) def _apply_filters(self, filters): if filters and len(filters) == 1: diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index 0e962cd2f..9953a960f 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -201,7 +201,7 @@ def __init__(self, channel, can_filters=None, bitrate=None, log_errors=True, **k self.handle = ctypes.c_ulong() nican.ncOpenObject(channel, ctypes.byref(self.handle)) - super(NicanBus, self).__init__(channel=channel, + super().__init__(channel=channel, can_filters=can_filters, bitrate=bitrate, log_errors=log_errors, **kwargs) @@ -308,7 +308,7 @@ class NicanError(CanError): """Error from NI-CAN driver.""" def __init__(self, function, error_code, arguments): - super(NicanError, self).__init__() + super().__init__() #: Status code self.error_code = error_code #: Function that failed diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index aa4dec91d..f8291fc2a 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -195,7 +195,7 @@ def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000 if result != PCAN_ERROR_OK: raise PcanError(self._get_formatted_error(result)) - super(PcanBus, self).__init__(channel=channel, state=state, bitrate=bitrate, *args, **kwargs) + super().__init__(channel=channel, state=state, bitrate=bitrate, *args, **kwargs) def _get_formatted_error(self, error): """ @@ -399,7 +399,7 @@ def flash(self, flash): self.m_objPCANBasic.SetValue(self.m_PcanHandle, PCAN_CHANNEL_IDENTIFYING, bool(flash)) def shutdown(self): - super(PcanBus, self).shutdown() + super().shutdown() self.m_objPCANBasic.Uninitialize(self.m_PcanHandle) @property diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index 500614868..a30b7d6e5 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -58,7 +58,7 @@ def __init__(self, channel, baudrate=115200, timeout=0.1, rtscts=False, self.ser = serial.serial_for_url( channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) - super(SerialBus, self).__init__(channel=channel, *args, **kwargs) + super().__init__(channel=channel, *args, **kwargs) def shutdown(self): """ diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 70b3dc27b..cd2dffa60 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -99,7 +99,7 @@ def __init__(self, channel, ttyBaudrate=115200, bitrate=None, self.open() - super(slcanBus, self).__init__(channel, ttyBaudrate=115200, + super().__init__(channel, ttyBaudrate=115200, bitrate=None, rtscts=False, **kwargs) def write(self, string): diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index dc91471a5..d178da2b3 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -285,7 +285,7 @@ def __init__(self, bcm_socket, message, period, duration=None): :param float period: The rate in seconds at which to send the message. :param float duration: Approximate duration in seconds to send the message. """ - super(CyclicSendTask, self).__init__(message, period, duration) + super().__init__(message, period, duration) self.bcm_socket = bcm_socket self.duration = duration self._tx_setup(message) @@ -345,7 +345,7 @@ class MultiRateCyclicSendTask(CyclicSendTask): """ def __init__(self, channel, message, count, initial_period, subsequent_period): - super(MultiRateCyclicSendTask, self).__init__(channel, message, subsequent_period) + super().__init__(channel, message, subsequent_period) # Create a low level packed frame to pass to the kernel frame = build_can_frame(message) @@ -500,7 +500,7 @@ def __init__(self, channel="", receive_own_messages=False, fd=False, **kwargs): bind_socket(self.socket, channel) kwargs.update({'receive_own_messages': receive_own_messages, 'fd': fd}) - super(SocketcanBus, self).__init__(channel=channel, **kwargs) + super().__init__(channel=channel, **kwargs) def shutdown(self): """Stops all active periodic tasks and closes the socket.""" diff --git a/can/interfaces/systec/exceptions.py b/can/interfaces/systec/exceptions.py index d3525cd88..65d85d180 100644 --- a/can/interfaces/systec/exceptions.py +++ b/can/interfaces/systec/exceptions.py @@ -6,6 +6,7 @@ class UcanException(CanError): """ Base class for USB can errors. """ + def __init__(self, result, func, arguments): self.result = result.value self.func = func @@ -20,7 +21,7 @@ def __str__(self): class UcanError(UcanException): """ Exception class for errors from USB-CAN-library. """ def __init__(self, result, func, arguments): - super(UcanError, self).__init__(result, func, arguments) + super().__init__(result, func, arguments) self.return_msgs = { ReturnCode.ERR_RESOURCE: "could not created a resource (memory, handle, ...)", ReturnCode.ERR_MAXMODULES: "the maximum number of opened modules is reached", @@ -46,8 +47,9 @@ def __init__(self, result, func, arguments): class UcanCmdError(UcanException): """ Exception class for errors from firmware in USB-CANmodul.""" + def __init__(self, result, func, arguments): - super(UcanCmdError, self).__init__(result, func, arguments) + super().__init__(result, func, arguments) self.return_msgs = { ReturnCode.ERRCMD_NOTEQU: "the received response does not match to the transmitted command", ReturnCode.ERRCMD_REGTST: "no access to the CAN controller", @@ -69,8 +71,9 @@ def __init__(self, result, func, arguments): class UcanWarning(UcanException): """ Exception class for warnings, the function has been executed anyway. """ + def __init__(self, result, func, arguments): - super(UcanWarning, self).__init__(result, func, arguments) + super().__init__(result, func, arguments) self.return_msgs = { ReturnCode.WARN_NODATA: "no CAN messages received", ReturnCode.WARN_SYS_RXOVERRUN: "overrun in receive buffer of the kernel driver", diff --git a/can/interfaces/systec/structures.py b/can/interfaces/systec/structures.py index a521e044f..32af73aa1 100644 --- a/can/interfaces/systec/structures.py +++ b/can/interfaces/systec/structures.py @@ -36,7 +36,7 @@ class CanMsg(Structure): ] def __init__(self, id=0, frame_format=MsgFrameFormat.MSG_FF_STD, data=[]): - super(CanMsg, self).__init__(id, frame_format, len(data), (BYTE * 8)(*data), 0) + super().__init__(id, frame_format, len(data), (BYTE * 8)(*data), 0) def __eq__(self, other): if not isinstance(other, CanMsg): @@ -121,8 +121,8 @@ class InitCanParam(Structure): ] def __init__(self, mode, BTR, OCR, AMR, ACR, baudrate, rx_buffer_entries, tx_buffer_entries): - super(InitCanParam, self).__init__(sizeof(InitCanParam), mode, BTR >> 8, BTR, OCR, AMR, ACR, - baudrate, rx_buffer_entries, tx_buffer_entries) + super().__init__(sizeof(InitCanParam), mode, BTR >> 8, BTR, OCR, AMR, ACR, + baudrate, rx_buffer_entries, tx_buffer_entries) def __eq__(self, other): if not isinstance(other, InitCanParam): diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index d074a3028..56fcd598d 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -18,7 +18,7 @@ class Ucan(UcanServer): """ def __init__(self): - super(Ucan, self).__init__() + super().__init__() self._msg_received_event = Event() def can_msg_received_event(self, channel): @@ -29,7 +29,7 @@ def read_can_msg(self, channel, count, timeout): if self.get_msg_pending(channel, PendingFlags.PENDING_FLAG_RX_DLL) == 0: if not self._msg_received_event.wait(timeout): return None, False - return super(Ucan, self).read_can_msg(channel, 1) + return super().read_can_msg(channel, 1) class UcanBus(BusABC): @@ -132,7 +132,7 @@ def __init__(self, channel, can_filters=None, **kwargs): self.channel, self._ucan.get_baudrate_message(self.BITRATES[bitrate]) ) - super(UcanBus, self).__init__(channel=channel, can_filters=can_filters, **kwargs) + super().__init__(channel=channel, can_filters=can_filters, **kwargs) def _recv_internal(self, timeout): message, _ = self._ucan.read_can_msg(self.channel, 1, timeout) diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 1f2b38c32..e5693506b 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -109,8 +109,8 @@ def __init__(self, channel=None, dll="usb2can.dll", flags=0x00000008, connector = "{}; {}".format(device_id, baudrate) self.handle = self.can.open(connector, flags) - super(Usb2canBus, self).__init__(channel=channel, dll=dll, flags=flags, - bitrate=bitrate, *args, **kwargs) + super().__init__(channel=channel, dll=dll, flags=flags, bitrate=bitrate, + *args, **kwargs) def send(self, msg, timeout=None): tx = message_convert_tx(msg) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 251b9fa56..1ee98380c 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -210,7 +210,7 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, self._time_offset = time.time() - offset.value * 1e-9 self._is_filtered = False - super(VectorBus, self).__init__(channel=channel, can_filters=can_filters, **kwargs) + super().__init__(channel=channel, can_filters=can_filters, **kwargs) def _apply_filters(self, filters): if filters: diff --git a/can/interfaces/vector/exceptions.py b/can/interfaces/vector/exceptions.py index 8715c276f..b88f6c527 100644 --- a/can/interfaces/vector/exceptions.py +++ b/can/interfaces/vector/exceptions.py @@ -10,5 +10,4 @@ class VectorError(CanError): def __init__(self, error_code, error_string, function): self.error_code = error_code - text = "%s failed (%s)" % (function, error_string) - super(VectorError, self).__init__(text) + super().__init__(f"{function} failed ({error_string})") diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index 5594674c1..66b951c22 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -45,7 +45,7 @@ class VirtualBus(BusABC): """ def __init__(self, channel=None, receive_own_messages=False, rx_queue_size=0, **kwargs): - super(VirtualBus, self).__init__(channel=channel, receive_own_messages=receive_own_messages, **kwargs) + super().__init__(channel=channel, receive_own_messages=receive_own_messages, **kwargs) # the channel identifier may be an arbitrary object self.channel_id = channel diff --git a/can/io/asc.py b/can/io/asc.py index 6cbfeb60d..419b46247 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -37,7 +37,7 @@ def __init__(self, file): If this is a file-like object, is has to opened in text read mode, not binary read mode. """ - super(ASCReader, self).__init__(file, mode='r') + super().__init__(file, mode='r') @staticmethod def _extract_can_id(str_can_id): @@ -140,7 +140,7 @@ def __init__(self, file, channel=1): :param channel: a default channel to use when the message does not have a channel set """ - super(ASCWriter, self).__init__(file, mode='w') + super().__init__(file, mode='w') self.channel = channel # write start of file header @@ -157,7 +157,7 @@ def __init__(self, file, channel=1): def stop(self): if not self.file.closed: self.file.write("End TriggerBlock\n") - super(ASCWriter, self).stop() + super().stop() def log_event(self, message, timestamp=None): """Add a message to the log file. diff --git a/can/io/blf.py b/can/io/blf.py index 55aab0715..36d116950 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -140,7 +140,7 @@ def __init__(self, file): If this is a file-like object, is has to opened in binary read mode, not text read mode. """ - super(BLFReader, self).__init__(file, mode='rb') + super().__init__(file, mode='rb') data = self.file.read(FILE_HEADER_STRUCT.size) header = FILE_HEADER_STRUCT.unpack(data) if header[0] != b"LOGG": @@ -310,7 +310,7 @@ def __init__(self, file, channel=1): If this is a file-like object, is has to opened in binary write mode, not text write mode. """ - super(BLFWriter, self).__init__(file, mode='wb') + super().__init__(file, mode='wb') self.channel = channel # Header will be written after log is done self.file.write(b"\x00" * FILE_HEADER_SIZE) @@ -438,7 +438,7 @@ def stop(self): """Stops logging and closes the file.""" self._flush() filesize = self.file.tell() - super(BLFWriter, self).stop() + super().stop() # Write header in the beginning of the file header = [b"LOGG", FILE_HEADER_SIZE, diff --git a/can/io/canutils.py b/can/io/canutils.py index 2d1232c74..304c37f9c 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -39,7 +39,7 @@ def __init__(self, file): If this is a file-like object, is has to opened in text read mode, not binary read mode. """ - super(CanutilsLogReader, self).__init__(file, mode='r') + super().__init__(file, mode='r') def __iter__(self): for line in self.file: @@ -106,7 +106,7 @@ def __init__(self, file, channel="vcan0", append=False): the file, else the file is truncated """ mode = 'a' if append else 'w' - super(CanutilsLogWriter, self).__init__(file, mode=mode) + super().__init__(file, mode=mode) self.channel = channel self.last_timestamp = None diff --git a/can/io/csv.py b/can/io/csv.py index c85e6339f..8bafdf180 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -50,7 +50,7 @@ def __init__(self, file, append=False): written header line """ mode = 'a' if append else 'w' - super(CSVWriter, self).__init__(file, mode=mode) + super().__init__(file, mode=mode) # Write a header row if not append: @@ -85,7 +85,7 @@ def __init__(self, file): If this is a file-like object, is has to opened in text read mode, not binary read mode. """ - super(CSVReader, self).__init__(file, mode='r') + super().__init__(file, mode='r') def __iter__(self): # skip the header line diff --git a/can/io/generic.py b/can/io/generic.py index b452d8404..abb03345e 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -34,7 +34,7 @@ def __init__(self, file, mode='rt'): self.file = open(file, mode) # for multiple inheritance - super(BaseIOHandler, self).__init__() + super().__init__() def __enter__(self): return self diff --git a/can/io/printer.py b/can/io/printer.py index 7eac86935..c67285581 100644 --- a/can/io/printer.py +++ b/can/io/printer.py @@ -30,7 +30,7 @@ def __init__(self, file=None): write mode, not binary write mode. """ self.write_to_file = file is not None - super(Printer, self).__init__(file, mode='w') + super().__init__(file, mode='w') def on_message_received(self, msg): if self.write_to_file: diff --git a/can/io/sqlite.py b/can/io/sqlite.py index f61117384..94d0af485 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -43,7 +43,7 @@ def __init__(self, file, table_name="messages"): do not accept file-like objects as the `file` parameter. It also runs in ``append=True`` mode all the time. """ - super(SqliteReader, self).__init__(file=None) + super().__init__(file=None) self._conn = sqlite3.connect(file) self._cursor = self._conn.cursor() self.table_name = table_name @@ -81,7 +81,7 @@ def read_all(self): def stop(self): """Closes the connection to the database. """ - super(SqliteReader, self).stop() + super().stop() self._conn.close() @@ -139,7 +139,7 @@ def __init__(self, file, table_name="messages"): .. warning:: In contrary to all other readers/writers the Sqlite handlers do not accept file-like objects as the `file` parameter. """ - super(SqliteWriter, self).__init__(file=None) + super().__init__(file=None) self.table_name = table_name self._db_filename = file self._stop_running_event = threading.Event() diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 648285b05..6034e7b63 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -54,7 +54,7 @@ def __init__(self, *args, **kwargs): if import_exc is not None: raise import_exc - super(ThreadSafeBus, self).__init__(Bus(*args, **kwargs)) + super().__init__(Bus(*args, **kwargs)) # now, BusABC.send_periodic() does not need a lock anymore, but the # implementation still requires a context manager diff --git a/can/viewer.py b/can/viewer.py index cecc17037..d915a9ddb 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -277,11 +277,11 @@ def _get_default_metavar_for_optional(self, action): def _format_usage(self, usage, actions, groups, prefix): # Use uppercase for "Usage:" text - return super(SmartFormatter, self)._format_usage(usage, actions, groups, 'Usage: ') + return super()._format_usage(usage, actions, groups, 'Usage: ') def _format_args(self, action, default_metavar): if action.nargs != argparse.REMAINDER and action.nargs != argparse.ONE_OR_MORE: - return super(SmartFormatter, self)._format_args(action, default_metavar) + return super()._format_args(action, default_metavar) # Use the metavar if "REMAINDER" or "ONE_OR_MORE" is set get_metavar = self._metavar_formatter(action, default_metavar) @@ -289,7 +289,7 @@ def _format_args(self, action, default_metavar): def _format_action_invocation(self, action): if not action.option_strings or action.nargs == 0: - return super(SmartFormatter, self)._format_action_invocation(action) + return super()._format_action_invocation(action) # Modified so "-s ARGS, --long ARGS" is replaced with "-s, --long ARGS" else: @@ -307,14 +307,14 @@ def _split_lines(self, text, width): # Allow to manually split the lines if text.startswith('R|'): return text[2:].splitlines() - return super(SmartFormatter, self)._split_lines(text, width) + return super()._split_lines(text, width) def _fill_text(self, text, width, indent): if text.startswith('R|'): # noinspection PyTypeChecker return ''.join(indent + line + '\n' for line in text[2:].splitlines()) else: - return super(SmartFormatter, self)._fill_text(text, width, indent) + return super()._fill_text(text, width, indent) def parse_args(args): From 1b71a92d9235fbb58b9f450df962d613c2d8d642 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 19:25:06 +0200 Subject: [PATCH 041/252] add missing super() call --- can/interfaces/systec/structures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/systec/structures.py b/can/interfaces/systec/structures.py index 32af73aa1..cf84a01e2 100644 --- a/can/interfaces/systec/structures.py +++ b/can/interfaces/systec/structures.py @@ -273,7 +273,7 @@ class ChannelInfo(Structure): ] def __init__(self): - super(ChannelInfo, self).__init__(sizeof(ChannelInfo)) + super().__init__(sizeof(ChannelInfo)) def __eq__(self, other): if not isinstance(other, ChannelInfo): From c3a87fd063c907e8463522b487345d3f2e0e4d5e Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 18 May 2019 19:28:02 +0200 Subject: [PATCH 042/252] use new super() syntax in test/** --- test/logformats_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/logformats_test.py b/test/logformats_test.py index d9551e5d6..d658f4e83 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -313,7 +313,7 @@ class TestAscFileFormat(ReaderWriterTest): """Tests can.ASCWriter and can.ASCReader""" def _setup_instance(self): - super(TestAscFileFormat, self)._setup_instance_helper( + super()._setup_instance_helper( can.ASCWriter, can.ASCReader, check_fd=False, check_comments=True, @@ -325,7 +325,7 @@ class TestBlfFileFormat(ReaderWriterTest): """Tests can.BLFWriter and can.BLFReader""" def _setup_instance(self): - super(TestBlfFileFormat, self)._setup_instance_helper( + super()._setup_instance_helper( can.BLFWriter, can.BLFReader, binary_file=True, check_fd=False, @@ -359,7 +359,7 @@ class TestCanutilsFileFormat(ReaderWriterTest): """Tests can.CanutilsLogWriter and can.CanutilsLogReader""" def _setup_instance(self): - super(TestCanutilsFileFormat, self)._setup_instance_helper( + super()._setup_instance_helper( can.CanutilsLogWriter, can.CanutilsLogReader, check_fd=False, test_append=True, check_comments=False, @@ -371,7 +371,7 @@ class TestCsvFileFormat(ReaderWriterTest): """Tests can.ASCWriter and can.ASCReader""" def _setup_instance(self): - super(TestCsvFileFormat, self)._setup_instance_helper( + super()._setup_instance_helper( can.CSVWriter, can.CSVReader, check_fd=False, test_append=True, check_comments=False, @@ -383,7 +383,7 @@ class TestSqliteDatabaseFormat(ReaderWriterTest): """Tests can.SqliteWriter and can.SqliteReader""" def _setup_instance(self): - super(TestSqliteDatabaseFormat, self)._setup_instance_helper( + super()._setup_instance_helper( can.SqliteWriter, can.SqliteReader, check_fd=False, test_append=True, check_comments=False, From d975332d8f580a81907ea276e603243cfac1672c Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 16 May 2019 22:31:06 -0700 Subject: [PATCH 043/252] Enable pylint on Travis CI builds Run pylint on all Travis CI builds for rules that we currently are conformant to. This adds a .pylintrc-wip file, which is intended to be a temporary stop-gap that contains the current rules that the project is conformant to. This allows us to incrementally enable pylint rules and have them them enforced in subsequent code, rather than fixing the violations all at once. The .pylintrc file is intended to be the file containing all the preferred set of rules that the project will eventually be compliant against. --- .pylintrc | 570 +++++++++++++++++++++++++++++ .pylintrc-wip | 823 ++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 10 + requirements-lint.txt | 1 + 4 files changed, 1404 insertions(+) create mode 100644 .pylintrc create mode 100644 .pylintrc-wip create mode 100644 requirements-lint.txt diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..5a2613994 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,570 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=print-statement, + parameter-unpacking, + unpacking-in-except, + old-raise-syntax, + backtick, + long-suffix, + old-ne-operator, + old-octal-literal, + import-star-module-level, + non-ascii-bytes-literal, + raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + apply-builtin, + basestring-builtin, + buffer-builtin, + cmp-builtin, + coerce-builtin, + execfile-builtin, + file-builtin, + long-builtin, + raw_input-builtin, + reduce-builtin, + standarderror-builtin, + unicode-builtin, + xrange-builtin, + coerce-method, + delslice-method, + getslice-method, + setslice-method, + no-absolute-import, + old-division, + dict-iter-method, + dict-view-method, + next-method-called, + metaclass-assignment, + indexing-exception, + raising-string, + reload-builtin, + oct-method, + hex-method, + nonzero-method, + cmp-method, + input-builtin, + round-builtin, + intern-builtin, + unichr-builtin, + map-builtin-not-iterating, + zip-builtin-not-iterating, + range-builtin-not-iterating, + filter-builtin-not-iterating, + using-cmp-argument, + eq-without-hash, + div-method, + idiv-method, + rdiv-method, + exception-message-attribute, + invalid-str-codec, + sys-max-int, + bad-python3-import, + deprecated-string-function, + deprecated-str-translate-call, + deprecated-itertools-function, + deprecated-types-field, + next-method-defined, + dict-items-not-iterating, + dict-keys-not-iterating, + dict-values-not-iterating, + deprecated-operator-function, + deprecated-urllib-function, + xreadlines-attribute, + deprecated-sys-function, + exception-escape, + comprehension-escape + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[LOGGING] + +# Format style used to check logging format string. `old` means using % +# formatting, while `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[STRING] + +# This flag controls whether the implicit-str-concat-in-sequence should +# generate a warning on implicit string concatenation in sequences defined over +# several lines. +check-str-concat-over-line-jumps=no + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package.. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma, + dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled). +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled). +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement. +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception diff --git a/.pylintrc-wip b/.pylintrc-wip new file mode 100644 index 000000000..76429f197 --- /dev/null +++ b/.pylintrc-wip @@ -0,0 +1,823 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=blacklisted-name, + invalid-name, + missing-docstring, + empty-docstring, + unneeded-not, + singleton-comparison, + misplaced-comparison-constant, + unidiomatic-typecheck, + consider-using-enumerate, + consider-iterating-dictionary, + bad-classmethod-argument, + bad-mcs-method-argument, + bad-mcs-classmethod-argument, + single-string-used-for-slots, + line-too-long, + too-many-lines, + trailing-whitespace, + missing-final-newline, + trailing-newlines, + multiple-statements, + superfluous-parens, + bad-whitespace, + mixed-line-endings, + unexpected-line-ending-format, + bad-continuation, + wrong-spelling-in-comment, + wrong-spelling-in-docstring, + invalid-characters-in-docstring, + multiple-imports, + wrong-import-order, + ungrouped-imports, + wrong-import-position, + useless-import-alias, + len-as-condition, + syntax-error, + unrecognized-inline-option, + bad-option-value, + init-is-generator, + return-in-init, + function-redefined, + not-in-loop, + return-outside-function, + yield-outside-function, + return-arg-in-generator, + nonexistent-operator, + duplicate-argument-name, + abstract-class-instantiated, + bad-reversed-sequence, + too-many-star-expressions, + invalid-star-assignment-target, + star-needs-assignment-target, + nonlocal-and-global, + continue-in-finally, + nonlocal-without-binding, + used-prior-global-declaration, + misplaced-format-function, + method-hidden, + access-member-before-definition, + no-method-argument, + no-self-argument, + invalid-slots-object, + assigning-non-slot, + invalid-slots, + inherit-non-class, + inconsistent-mro, + duplicate-bases, + non-iterator-returned, + unexpected-special-method-signature, + invalid-length-returned, + import-error, + relative-beyond-top-level, + used-before-assignment, + undefined-variable, + undefined-all-variable, + invalid-all-object, + no-name-in-module, + unpacking-non-sequence, + bad-except-order, + raising-bad-type, + bad-exception-context, + misplaced-bare-raise, + raising-non-exception, + notimplemented-raised, + catching-non-exception, + bad-super-call, + missing-super-argument, + no-member, + not-callable, + assignment-from-no-return, + no-value-for-parameter, + too-many-function-args, + unexpected-keyword-arg, + redundant-keyword-arg, + missing-kwoa, + invalid-sequence-index, + invalid-slice-index, + assignment-from-none, + not-context-manager, + invalid-unary-operand-type, + unsupported-binary-operation, + repeated-keyword, + not-an-iterable, + not-a-mapping, + unsupported-membership-test, + unsubscriptable-object, + unsupported-assignment-operation, + unsupported-delete-operation, + invalid-metaclass, + unhashable-dict-key, + logging-unsupported-format, + logging-format-truncated, + logging-too-many-args, + logging-too-few-args, + bad-format-character, + truncated-format-string, + mixed-format-string, + format-needs-mapping, + missing-format-string-key, + too-many-format-args, + too-few-format-args, + bad-string-format-type, + bad-str-strip-call, + invalid-envvar-value, + print-statement, + parameter-unpacking, + unpacking-in-except, + old-raise-syntax, + backtick, + long-suffix, + old-ne-operator, + old-octal-literal, + import-star-module-level, + non-ascii-bytes-literal, + yield-inside-async-function, + not-async-context-manager, + fatal, + astroid-error, + parse-error, + method-check-failed, + raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + c-extension-no-member, + literal-comparison, + comparison-with-itself, + no-self-use, + no-classmethod-decorator, + no-staticmethod-decorator, + useless-object-inheritance, + cyclic-import, + duplicate-code, + too-many-ancestors, + too-many-instance-attributes, + too-few-public-methods, + too-many-public-methods, + too-many-return-statements, + too-many-branches, + too-many-arguments, + too-many-locals, + too-many-statements, + too-many-boolean-expressions, + consider-merging-isinstance, + too-many-nested-blocks, + simplifiable-if-statement, + redefined-argument-from-local, + no-else-return, + consider-using-ternary, + trailing-comma-tuple, + stop-iteration-return, + simplify-boolean-expression, + inconsistent-return-statements, + useless-return, + consider-swap-variables, + consider-using-join, + consider-using-in, + consider-using-get, + chained-comparison, + consider-using-dict-comprehension, + consider-using-set-comprehension, + simplifiable-if-expression, + no-else-raise, + unreachable, + dangerous-default-value, + pointless-statement, + pointless-string-statement, + expression-not-assigned, + unnecessary-pass, + unnecessary-lambda, + duplicate-key, + assign-to-new-keyword, + useless-else-on-loop, + exec-used, + eval-used, + confusing-with-statement, + using-constant-test, + comparison-with-callable, + lost-exception, + assert-on-tuple, + attribute-defined-outside-init, + bad-staticmethod-argument, + protected-access, + arguments-differ, + signature-differs, + abstract-method, + super-init-not-called, + no-init, + non-parent-init-called, + useless-super-delegation, + unnecessary-semicolon, + bad-indentation, + mixed-indentation, + wildcard-import, + deprecated-module, + relative-import, + reimported, + import-self, + misplaced-future, + fixme, + invalid-encoded-data, + global-variable-undefined, + global-variable-not-assigned, + global-statement, + global-at-module-level, + unused-import, + unused-variable, + unused-argument, + unused-wildcard-import, + redefined-outer-name, + redefined-builtin, + redefine-in-handler, + undefined-loop-variable, + unbalanced-tuple-unpacking, + cell-var-from-loop, + possibly-unused-variable, + self-cls-assignment, + bare-except, + broad-except, + duplicate-except, + try-except-raise, + binary-op-exception, + raising-format-tuple, + wrong-exception-operation, + keyword-arg-before-vararg, + logging-not-lazy, + logging-format-interpolation, + logging-fstring-interpolation, + bad-format-string-key, + unused-format-string-key, + bad-format-string, + missing-format-argument-key, + unused-format-string-argument, + format-combined-specification, + missing-format-attribute, + invalid-format-index, + duplicate-string-formatting-argument, + anomalous-backslash-in-string, + anomalous-unicode-escape-in-string, + implicit-str-concat-in-sequence, + bad-open-mode, + boolean-datetime, + redundant-unittest-assert, + deprecated-method, + bad-thread-instantiation, + shallow-copy-environ, + invalid-envvar-default, + subprocess-popen-preexec-fn, + apply-builtin, + basestring-builtin, + buffer-builtin, + cmp-builtin, + coerce-builtin, + execfile-builtin, + file-builtin, + long-builtin, + raw_input-builtin, + reduce-builtin, + standarderror-builtin, + unicode-builtin, + xrange-builtin, + coerce-method, + delslice-method, + getslice-method, + setslice-method, + no-absolute-import, + old-division, + dict-iter-method, + dict-view-method, + next-method-called, + metaclass-assignment, + indexing-exception, + raising-string, + reload-builtin, + oct-method, + hex-method, + nonzero-method, + cmp-method, + input-builtin, + round-builtin, + intern-builtin, + unichr-builtin, + map-builtin-not-iterating, + zip-builtin-not-iterating, + range-builtin-not-iterating, + filter-builtin-not-iterating, + using-cmp-argument, + eq-without-hash, + div-method, + idiv-method, + rdiv-method, + exception-message-attribute, + invalid-str-codec, + sys-max-int, + bad-python3-import, + deprecated-string-function, + deprecated-str-translate-call, + deprecated-itertools-function, + deprecated-types-field, + next-method-defined, + dict-items-not-iterating, + dict-keys-not-iterating, + dict-values-not-iterating, + deprecated-operator-function, + deprecated-urllib-function, + xreadlines-attribute, + deprecated-sys-function, + exception-escape, + comprehension-escape + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[LOGGING] + +# Format style used to check logging format string. `old` means using % +# formatting, while `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[STRING] + +# This flag controls whether the implicit-str-concat-in-sequence should +# generate a warning on implicit string concatenation in sequences defined over +# several lines. +check-str-concat-over-line-jumps=no + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package.. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma, + dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled). +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled). +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement. +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception diff --git a/.travis.yml b/.travis.yml index 6e84eb22a..29fac0557 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,6 +82,16 @@ jobs: # -a Write all files # -n nitpicky - python -m sphinx -an doc build + - stage: linter + name: "Linter Checks" + python: "3.7" + before_install: + - travis_retry pip install -r requirements-lint.txt + script: + # Slowly enable all pylint warnings by adding addressed classes of + # warnings to the .pylintrc-wip file to prevent them from being + # re-introduced + - pylint --rcfile=.pylintrc-wip can/ - stage: deploy name: "PyPi Deployment" python: "3.7" diff --git a/requirements-lint.txt b/requirements-lint.txt new file mode 100644 index 000000000..514974539 --- /dev/null +++ b/requirements-lint.txt @@ -0,0 +1 @@ +pylint==2.3.1 From 9cccf29d9e1c3079ca0d5a8ec7f31184c3d90762 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 22:56:31 +0200 Subject: [PATCH 044/252] remove trailing whitespace --- .pylintrc-wip | 3 --- can/interfaces/kvaser/canlib.py | 2 +- can/interfaces/pcan/pcan.py | 4 ++-- can/interfaces/vector/canlib.py | 15 +++++++-------- can/interfaces/vector/vxlapi.py | 4 ++-- can/listener.py | 8 ++++---- can/message.py | 2 +- can/util.py | 6 +++--- 8 files changed, 20 insertions(+), 24 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 76429f197..c9a7f9a9f 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -76,9 +76,6 @@ disable=blacklisted-name, single-string-used-for-slots, line-too-long, too-many-lines, - trailing-whitespace, - missing-final-newline, - trailing-newlines, multiple-statements, superfluous-parens, bad-whitespace, diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index 6506a962d..483814557 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -396,7 +396,7 @@ def __init__(self, channel, can_filters=None, **kwargs): canstat.canIOCTL_SET_TIMER_SCALE, ctypes.byref(ctypes.c_long(TIMESTAMP_RESOLUTION)), 4) - + if fd: if 'tseg1' not in kwargs and bitrate in BITRATE_FD: # Use predefined bitrate for arbitration diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index f8291fc2a..558150c0d 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -175,7 +175,7 @@ def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000 f_clock = "{}={}".format('f_clock_mhz', kwargs.get('f_clock_mhz', None)) else: f_clock = "{}={}".format('f_clock', kwargs.get('f_clock', None)) - + fd_parameters_values = [f_clock] + ["{}={}".format(key, kwargs.get(key, None)) for key in pcan_fd_parameter_list if kwargs.get(key, None) is not None] self.fd_bitrate = ' ,'.join(fd_parameters_values).encode("ascii") @@ -348,7 +348,7 @@ def send(self, msg, timeout=None): CANMsg = TPCANMsgFDMac() else: CANMsg = TPCANMsgFD() - + # configure the message. ID, Length of data, message type and data CANMsg.ID = msg.arbitration_id CANMsg.DLC = len2dlc(msg.dlc) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 1ee98380c..3a926e289 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -175,7 +175,7 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, self.canFdConf.sjwDbr = ctypes.c_uint(sjwDbr) self.canFdConf.tseg1Dbr = ctypes.c_uint(tseg1Dbr) self.canFdConf.tseg2Dbr = ctypes.c_uint(tseg2Dbr) - + vxlapi.xlCanFdSetConfiguration(self.port_handle, self.mask, self.canFdConf) LOG.info('SetFdConfig.: ABaudr.=%u, DBaudr.=%u', self.canFdConf.arbitrationBitRate, self.canFdConf.dataBitRate) LOG.info('SetFdConfig.: sjwAbr=%u, tseg1Abr=%u, tseg2Abr=%u', self.canFdConf.sjwAbr, self.canFdConf.tseg1Abr, self.canFdConf.tseg2Abr) @@ -336,14 +336,14 @@ def send(self, msg, timeout=None): flags |= vxlapi.XL_CAN_TXMSG_FLAG_BRS if msg.is_remote_frame: flags |= vxlapi.XL_CAN_TXMSG_FLAG_RTR - + message_count = 1 MsgCntSent = ctypes.c_uint(1) - + XLcanTxEvent = vxlapi.XLcanTxEvent() XLcanTxEvent.tag = vxlapi.XL_CAN_EV_TAG_TX_MSG XLcanTxEvent.transId = 0xffff - + XLcanTxEvent.tagData.canMsg.canId = msg_id XLcanTxEvent.tagData.canMsg.msgFlags = flags XLcanTxEvent.tagData.canMsg.dlc = len2dlc(msg.dlc) @@ -356,10 +356,10 @@ def send(self, msg, timeout=None): flags |= vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME message_count = ctypes.c_uint(1) - + 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 @@ -367,7 +367,6 @@ def send(self, msg, timeout=None): xl_event.tagData.msg.data[idx] = value vxlapi.xlCanTransmit(self.port_handle, mask, message_count, xl_event) - def flush_tx_buffer(self): vxlapi.xlCanFlushTransmitQueue(self.port_handle, self.mask) @@ -375,7 +374,7 @@ def shutdown(self): vxlapi.xlDeactivateChannel(self.port_handle, self.mask) vxlapi.xlClosePort(self.port_handle) vxlapi.xlCloseDriver() - + def reset(self): vxlapi.xlDeactivateChannel(self.port_handle, self.mask) vxlapi.xlActivateChannel(self.port_handle, self.mask, diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index ae87706c4..e1b93bdc7 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -46,7 +46,7 @@ XL_CAN_RXMSG_FLAG_BRS = 0x0002 XL_CAN_RXMSG_FLAG_ESI = 0x0004 XL_CAN_RXMSG_FLAG_RTR = 0x0010 -XL_CAN_RXMSG_FLAG_EF = 0x0200 +XL_CAN_RXMSG_FLAG_EF = 0x0200 XL_CAN_STD = 1 XL_CAN_EXT = 2 @@ -114,7 +114,7 @@ class s_rxTagData(ctypes.Union): class s_txTagData(ctypes.Union): _fields_ = [('canMsg', s_xl_can_tx_msg)] -# BASIC events +# BASIC events XLeventTag = ctypes.c_ubyte class XLevent(ctypes.Structure): diff --git a/can/listener.py b/can/listener.py index 3f2d5a87d..b3cc3a886 100644 --- a/can/listener.py +++ b/can/listener.py @@ -128,7 +128,7 @@ class AsyncBufferedReader(Listener): """A message buffer for use with :mod:`asyncio`. See :ref:`asyncio` for how to use with :class:`can.Notifier`. - + Can also be used as an asynchronous iterator:: async for msg in reader: @@ -141,7 +141,7 @@ def __init__(self, loop=None): def on_message_received(self, msg): """Append a message to the buffer. - + Must only be called inside an event loop! """ self.buffer.put_nowait(msg) @@ -149,7 +149,7 @@ def on_message_received(self, msg): async def get_message(self): """ Retrieve the latest message when awaited for:: - + msg = await reader.get_message() :rtype: can.Message @@ -159,6 +159,6 @@ async def get_message(self): def __aiter__(self): return self - + def __anext__(self): return self.buffer.get() diff --git a/can/message.py b/can/message.py index cc39642bf..ec2ee6875 100644 --- a/can/message.py +++ b/can/message.py @@ -152,7 +152,7 @@ def __repr__(self): args.append("is_error_frame={}".format(self.is_error_frame)) if self.channel is not None: - args.append("channel={!r}".format(self.channel)) + args.append("channel={!r}".format(self.channel)) data = ["{:#02x}".format(byte) for byte in self.data] args += ["dlc={}".format(self.dlc), diff --git a/can/util.py b/can/util.py index 7e106fde4..2e682548d 100644 --- a/can/util.py +++ b/can/util.py @@ -116,7 +116,7 @@ def load_config(path=None, config=None, context=None): kvaser, socketcan, pcan, usb2can, ixxat, nican, virtual. .. note:: - + The key ``bustype`` is copied to ``interface`` if that one is missing and does never appear in the result. @@ -189,7 +189,7 @@ def load_config(path=None, config=None, context=None): can.log.debug("can config: {}".format(config)) return config - + def set_logging_level(level_name=None): """Set the logging level for the "can" logger. Expects one of: 'critical', 'error', 'warning', 'info', 'debug', 'subdebug' @@ -235,7 +235,7 @@ def channel2int(channel): :param channel: Channel string (e.g. can0, CAN1) or integer - + :returns: Channel integer or `None` if unsuccessful :rtype: int """ From 525fba100bcf288a6d17a2363940b76a1ab361b5 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 22:56:44 +0200 Subject: [PATCH 045/252] remove trailing whitespace --- .pylintrc-wip | 1 - 1 file changed, 1 deletion(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index c9a7f9a9f..65e347a03 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -64,7 +64,6 @@ disable=blacklisted-name, invalid-name, missing-docstring, empty-docstring, - unneeded-not, singleton-comparison, misplaced-comparison-constant, unidiomatic-typecheck, From b9883e239e3d49d2fa5b8e7e8b1efa6be3f46943 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 22:59:05 +0200 Subject: [PATCH 046/252] fix actual bug by makeing linter more strict --- .pylintrc-wip | 10 ---------- can/interfaces/systec/structures.py | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 65e347a03..439c54f54 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -66,7 +66,6 @@ disable=blacklisted-name, empty-docstring, singleton-comparison, misplaced-comparison-constant, - unidiomatic-typecheck, consider-using-enumerate, consider-iterating-dictionary, bad-classmethod-argument, @@ -93,22 +92,13 @@ disable=blacklisted-name, syntax-error, unrecognized-inline-option, bad-option-value, - init-is-generator, return-in-init, - function-redefined, - not-in-loop, - return-outside-function, - yield-outside-function, - return-arg-in-generator, - nonexistent-operator, duplicate-argument-name, abstract-class-instantiated, bad-reversed-sequence, too-many-star-expressions, invalid-star-assignment-target, star-needs-assignment-target, - nonlocal-and-global, - continue-in-finally, nonlocal-without-binding, used-prior-global-declaration, misplaced-format-function, diff --git a/can/interfaces/systec/structures.py b/can/interfaces/systec/structures.py index cf84a01e2..40ce01dfa 100644 --- a/can/interfaces/systec/structures.py +++ b/can/interfaces/systec/structures.py @@ -165,7 +165,7 @@ def rx_buffer_entries(self, rx_buffer_entries): self.m_wNrOfRxBufferEntries = rx @property def tx_buffer_entries(self): return self.m_wNrOfTxBufferEntries - @rx_buffer_entries.setter + @tx_buffer_entries.setter def tx_buffer_entries(self, tx_buffer_entries): self.m_wNrOfTxBufferEntries = tx_buffer_entries From 2ab6ff2410b51d01af63ecde122b62a22d268a0b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 23:13:09 +0200 Subject: [PATCH 047/252] remove other excluded checks --- .pylintrc-wip | 60 ---------------------- can/interface.py | 4 +- can/interfaces/ixxat/canlib.py | 4 +- can/interfaces/kvaser/canlib.py | 2 +- can/interfaces/nican.py | 2 +- can/interfaces/pcan/pcan.py | 5 +- can/interfaces/serial/serial_can.py | 3 +- can/interfaces/systec/exceptions.py | 6 +-- can/interfaces/systec/ucanbus.py | 2 +- can/interfaces/usb2can/usb2canInterface.py | 4 +- can/interfaces/vector/canlib.py | 2 +- can/thread_safe_bus.py | 6 +-- 12 files changed, 20 insertions(+), 80 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 439c54f54..081bae7a6 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -99,18 +99,12 @@ disable=blacklisted-name, too-many-star-expressions, invalid-star-assignment-target, star-needs-assignment-target, - nonlocal-without-binding, - used-prior-global-declaration, - misplaced-format-function, method-hidden, access-member-before-definition, no-method-argument, no-self-argument, invalid-slots-object, assigning-non-slot, - invalid-slots, - inherit-non-class, - inconsistent-mro, duplicate-bases, non-iterator-returned, unexpected-special-method-signature, @@ -123,27 +117,15 @@ disable=blacklisted-name, invalid-all-object, no-name-in-module, unpacking-non-sequence, - bad-except-order, - raising-bad-type, bad-exception-context, misplaced-bare-raise, raising-non-exception, notimplemented-raised, catching-non-exception, - bad-super-call, - missing-super-argument, - no-member, - not-callable, - assignment-from-no-return, no-value-for-parameter, too-many-function-args, unexpected-keyword-arg, redundant-keyword-arg, - missing-kwoa, - invalid-sequence-index, - invalid-slice-index, - assignment-from-none, - not-context-manager, invalid-unary-operand-type, unsupported-binary-operation, repeated-keyword, @@ -258,15 +240,8 @@ disable=blacklisted-name, super-init-not-called, no-init, non-parent-init-called, - useless-super-delegation, - unnecessary-semicolon, bad-indentation, - mixed-indentation, wildcard-import, - deprecated-module, - relative-import, - reimported, - import-self, misplaced-future, fixme, invalid-encoded-data, @@ -283,57 +258,22 @@ disable=blacklisted-name, redefine-in-handler, undefined-loop-variable, unbalanced-tuple-unpacking, - cell-var-from-loop, possibly-unused-variable, - self-cls-assignment, - bare-except, broad-except, - duplicate-except, - try-except-raise, - binary-op-exception, - raising-format-tuple, - wrong-exception-operation, - keyword-arg-before-vararg, logging-not-lazy, logging-format-interpolation, logging-fstring-interpolation, - bad-format-string-key, - unused-format-string-key, bad-format-string, missing-format-argument-key, unused-format-string-argument, - format-combined-specification, - missing-format-attribute, invalid-format-index, duplicate-string-formatting-argument, - anomalous-backslash-in-string, - anomalous-unicode-escape-in-string, implicit-str-concat-in-sequence, - bad-open-mode, - boolean-datetime, redundant-unittest-assert, - deprecated-method, bad-thread-instantiation, shallow-copy-environ, invalid-envvar-default, subprocess-popen-preexec-fn, - apply-builtin, - basestring-builtin, - buffer-builtin, - cmp-builtin, - coerce-builtin, - execfile-builtin, - file-builtin, - long-builtin, - raw_input-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, no-absolute-import, old-division, dict-iter-method, diff --git a/can/interface.py b/can/interface.py index 30bade25c..8eb922051 100644 --- a/can/interface.py +++ b/can/interface.py @@ -64,13 +64,13 @@ class Bus(BusABC): """ @staticmethod - def __new__(cls, channel=None, *args, **kwargs): + def __new__(cls, *args, channel=None, **kwargs): """ Takes the same arguments as :class:`can.BusABC.__init__`. Some might have a special meaning, see below. :param channel: - Set to ``None`` to let it be reloved automatically from the default + Set to ``None`` to let it be resloved automatically from the default configuration. That might fail, see below. Expected type is backend dependent. diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index b0c9e8d3a..0f07d29a5 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -450,7 +450,7 @@ def _recv_internal(self, timeout): elif self._message.uMsgInfo.Bits.type == constants.CAN_MSGTYPE_TIMEOVR: pass else: - log.warn("Unexpected message info type") + log.warning("Unexpected message info type") if t0 is not None: remaining_ms = timeout_ms - int((_timer_function() - t0) * 1000) @@ -521,7 +521,7 @@ def set_filters(self, can_filers=None): """Unsupported. See note on :class:`~can.interfaces.ixxat.IXXATBus`. """ if self.__set_filters_has_been_called: - log.warn("using filters is not supported like this, see note on IXXATBus") + log.warning("using filters is not supported like this, see note on IXXATBus") else: # allow the constructor to call this without causing a warning self.__set_filters_has_been_called = True diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index 483814557..7e8cd4b4f 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -266,7 +266,7 @@ def init_kvaser_library(): log.debug("Initializing Kvaser CAN library") canInitializeLibrary() log.debug("CAN library initialized") - except: + except Exception: log.warning("Kvaser canlib could not be initialized.") diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index 9953a960f..b032e3eee 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -298,7 +298,7 @@ def set_filters(self, can_filers=None): """Unsupported. See note on :class:`~can.interfaces.nican.NicanBus`. """ if self.__set_filters_has_been_called: - logger.warn("using filters is not supported like this, see note on NicanBus") + logger.warning("using filters is not supported like this, see note on NicanBus") else: # allow the constructor to call this without causing a warning self.__set_filters_has_been_called = True diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 558150c0d..2d04ff5f4 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -19,7 +19,7 @@ import uptime import datetime boottimeEpoch = (uptime.boottime() - datetime.datetime.utcfromtimestamp(0)).total_seconds() -except: +except ImportError: boottimeEpoch = 0 try: @@ -62,7 +62,8 @@ class PcanBus(BusABC): - def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000, *args, **kwargs): + def __init__(self, *args, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000, + **kwargs): """A PCAN USB interface to CAN. On top of the usual :class:`~can.Bus` methods provided, diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index a30b7d6e5..1904d28e3 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -31,8 +31,7 @@ class SerialBus(BusABC): """ - def __init__(self, channel, baudrate=115200, timeout=0.1, rtscts=False, - *args, **kwargs): + def __init__(self, channel, *args, baudrate=115200, timeout=0.1, rtscts=False, **kwargs): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or diff --git a/can/interfaces/systec/exceptions.py b/can/interfaces/systec/exceptions.py index 65d85d180..622403d48 100644 --- a/can/interfaces/systec/exceptions.py +++ b/can/interfaces/systec/exceptions.py @@ -11,11 +11,11 @@ def __init__(self, result, func, arguments): self.result = result.value self.func = func self.arguments = arguments - self.return_msgs = NotImplemented + self.return_msgs = {} def __str__(self): - return "Function %s returned %d: %s" % \ - (self.func.__name__, self.result, self.return_msgs.get(self.result, "unknown")) + message = self.return_msgs.get(self.result, "unknown") + return f"Function {self.func.__name__} returned {self.result}: {message}" class UcanError(UcanException): diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index 56fcd598d..3238c1711 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -188,7 +188,7 @@ def _detect_available_configs(): configs.append({'interface': 'systec', 'channel': Channel.CHANNEL_CH1, 'device_number': hw_info_ex.device_number}) - except: + except Exception: log.warning("The SYSTEC ucan library has not been initialized.") return configs diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index e5693506b..4e7580feb 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -83,8 +83,8 @@ class Usb2canBus(BusABC): """ - def __init__(self, channel=None, dll="usb2can.dll", flags=0x00000008, - bitrate=500000, *args, **kwargs): + def __init__(self, *args, channel=None, dll="usb2can.dll", flags=0x00000008, + bitrate=500000, **kwargs): self.can = Usb2CanAbstractionLayer(dll) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 3a926e289..6fb49c5fb 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -402,6 +402,6 @@ def get_channel_configs(): vxlapi.xlOpenDriver() vxlapi.xlGetDriverConfig(driver_config) vxlapi.xlCloseDriver() - except: + except Exception: pass return [driver_config.channel[i] for i in range(driver_config.channelCount)] diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 6034e7b63..35f923910 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -64,11 +64,11 @@ def __init__(self, *args, **kwargs): self._lock_send = RLock() self._lock_recv = RLock() - def recv(self, timeout=None, *args, **kwargs): + def recv(self, *args, timeout=None, **kwargs): with self._lock_recv: return self.__wrapped__.recv(timeout=timeout, *args, **kwargs) - def send(self, msg, timeout=None, *args, **kwargs): + def send(self, msg, *args, timeout=None, **kwargs): with self._lock_send: return self.__wrapped__.send(msg, timeout=timeout, *args, **kwargs) @@ -85,7 +85,7 @@ def filters(self, filters): with self._lock_recv: self.__wrapped__.filters = filters - def set_filters(self, filters=None, *args, **kwargs): + def set_filters(self, *args, filters=None, **kwargs): with self._lock_recv: return self.__wrapped__.set_filters(filters=filters, *args, **kwargs) From 684dec27e6bbf28af570eb6e7f99cd8dcddc9b32 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 23:23:30 +0200 Subject: [PATCH 048/252] fix other linter stuff --- .pylintrc-wip | 39 +------------------ can/interfaces/ixxat/canlib.py | 4 +- can/interfaces/pcan/basic.py | 12 +++--- can/interfaces/usb2can/usb2canInterface.py | 11 ++---- .../usb2can/usb2canabstractionlayer.py | 14 +++---- 5 files changed, 22 insertions(+), 58 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 081bae7a6..0cb2fea88 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -205,16 +205,7 @@ disable=blacklisted-name, simplify-boolean-expression, inconsistent-return-statements, useless-return, - consider-swap-variables, - consider-using-join, - consider-using-in, - consider-using-get, - chained-comparison, - consider-using-dict-comprehension, - consider-using-set-comprehension, - simplifiable-if-expression, no-else-raise, - unreachable, dangerous-default-value, pointless-statement, pointless-string-statement, @@ -253,11 +244,6 @@ disable=blacklisted-name, unused-variable, unused-argument, unused-wildcard-import, - redefined-outer-name, - redefined-builtin, - redefine-in-handler, - undefined-loop-variable, - unbalanced-tuple-unpacking, possibly-unused-variable, broad-except, logging-not-lazy, @@ -275,39 +261,17 @@ disable=blacklisted-name, invalid-envvar-default, subprocess-popen-preexec-fn, no-absolute-import, - old-division, dict-iter-method, dict-view-method, next-method-called, metaclass-assignment, indexing-exception, raising-string, - reload-builtin, oct-method, hex-method, - nonzero-method, - cmp-method, - input-builtin, - round-builtin, - intern-builtin, - unichr-builtin, - map-builtin-not-iterating, - zip-builtin-not-iterating, - range-builtin-not-iterating, - filter-builtin-not-iterating, - using-cmp-argument, - eq-without-hash, - div-method, - idiv-method, - rdiv-method, exception-message-attribute, invalid-str-codec, sys-max-int, - bad-python3-import, - deprecated-string-function, - deprecated-str-translate-call, - deprecated-itertools-function, - deprecated-types-field, next-method-defined, dict-items-not-iterating, dict-keys-not-iterating, @@ -317,7 +281,8 @@ disable=blacklisted-name, xreadlines-attribute, deprecated-sys-function, exception-escape, - comprehension-escape + comprehension-escape, + redefined-builtin # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 0f07d29a5..3c91486d0 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -465,8 +465,8 @@ def _recv_internal(self, timeout): # so expect to see the value restarting from 0 rx_msg = Message( timestamp=self._message.dwTime / self._tick_resolution, # Relative time in s - is_remote_frame=True if self._message.uMsgInfo.Bits.rtr else False, - is_extended_id=True if self._message.uMsgInfo.Bits.ext else False, + is_remote_frame=bool(self._message.uMsgInfo.Bits.rtr), + is_extended_id=bool(self._message.uMsgInfo.Bits.ext), arbitration_id=self._message.dwMsgId, dlc=self._message.uMsgInfo.Bits.dlc, data=self._message.abData[:self._message.uMsgInfo.Bits.dlc], diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index 053197119..6ac38d850 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -658,13 +658,15 @@ def GetValue( 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: + if Parameter in (PCAN_API_VERSION, PCAN_HARDWARE_NAME, PCAN_CHANNEL_VERSION, + PCAN_LOG_LOCATION, PCAN_TRACE_LOCATION, PCAN_BITRATE_INFO_FD, + 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 + 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 @@ -694,13 +696,13 @@ def SetValue( A TPCANStatus error code """ try: - if Parameter == PCAN_LOG_LOCATION or Parameter == PCAN_LOG_TEXT or Parameter == PCAN_TRACE_LOCATION: + if Parameter in (PCAN_LOG_LOCATION, PCAN_LOG_TEXT, 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)) + res = self.__m_dllBasic.CAN_SetValue(Channel, Parameter, byref(mybuffer), sizeof(mybuffer)) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.SetValue") diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 4e7580feb..efdd9bf0e 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -89,10 +89,7 @@ def __init__(self, *args, channel=None, dll="usb2can.dll", flags=0x00000008, self.can = Usb2CanAbstractionLayer(dll) # get the serial number of the device - if "serial" in kwargs: - device_id = kwargs["serial"] - else: - device_id = channel + device_id = kwargs.get("serial", d=channel) # search for a serial number if the device_id is None or empty if not device_id: @@ -107,9 +104,9 @@ def __init__(self, *args, channel=None, dll="usb2can.dll", flags=0x00000008, self.channel_info = "USB2CAN device {}".format(device_id) connector = "{}; {}".format(device_id, baudrate) - self.handle = self.can.open(connector, flags) + self.handle = self.can.open(connector, flags_t) - super().__init__(channel=channel, dll=dll, flags=flags, bitrate=bitrate, + super().__init__(channel=channel, dll=dll, flags_t=flags_t, bitrate=bitrate, *args, **kwargs) def send(self, msg, timeout=None): @@ -137,7 +134,7 @@ def _recv_internal(self, timeout): if status == CANAL_ERROR_SUCCESS: rx = message_convert_rx(messagerx) - elif status == CANAL_ERROR_RCV_EMPTY or status == CANAL_ERROR_TIMEOUT: + elif status in (CANAL_ERROR_RCV_EMPTY, CANAL_ERROR_TIMEOUT): rx = None else: log.error('Canal Error %s', status) diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index 0086d542d..e17f255a8 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -14,10 +14,10 @@ log = logging.getLogger('can.usb2can') # type definitions -flags = c_ulong +flags_t = c_ulong pConfigureStr = c_char_p -handle = c_long -timeout = c_ulong +handle_t = c_long +timeout_t = c_ulong filter_t = c_ulong # flags mappings @@ -146,17 +146,17 @@ def blocking_receive(self, handle, msg, timeout): log.warning('Blocking Receive Failed') raise - def get_status(self, handle, CanalStatus): + def get_status(self, handle, status): try: - res = self.__m_dllBasic.CanalGetStatus(handle, CanalStatus) + res = self.__m_dllBasic.CanalGetStatus(handle, status) return res except: log.warning('Get status failed') raise - def get_statistics(self, handle, CanalStatistics): + def get_statistics(self, handle, statistics): try: - res = self.__m_dllBasic.CanalGetStatistics(handle, CanalStatistics) + res = self.__m_dllBasic.CanalGetStatistics(handle, statistics) return res except: log.warning('Get Statistics failed') From fb6c7d60082ea8282651223b0d0b852a9532812a Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 23:24:29 +0200 Subject: [PATCH 049/252] fix other linter stuff --- .pylintrc-wip | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 0cb2fea88..68e944bf0 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -131,12 +131,7 @@ disable=blacklisted-name, repeated-keyword, not-an-iterable, not-a-mapping, - unsupported-membership-test, - unsubscriptable-object, unsupported-assignment-operation, - unsupported-delete-operation, - invalid-metaclass, - unhashable-dict-key, logging-unsupported-format, logging-format-truncated, logging-too-many-args, @@ -146,29 +141,14 @@ disable=blacklisted-name, mixed-format-string, format-needs-mapping, missing-format-string-key, - too-many-format-args, - too-few-format-args, bad-string-format-type, bad-str-strip-call, invalid-envvar-value, print-statement, parameter-unpacking, unpacking-in-except, - old-raise-syntax, - backtick, - long-suffix, - old-ne-operator, - old-octal-literal, import-star-module-level, - non-ascii-bytes-literal, yield-inside-async-function, - not-async-context-manager, - fatal, - astroid-error, - parse-error, - method-check-failed, - raw-checker-failed, - bad-inline-option, locally-disabled, file-ignored, suppressed-message, From 4033a621ec2f530ea168a7f1b6dc6fb0a6a88c11 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 23:42:26 +0200 Subject: [PATCH 050/252] fix other linter stuff --- .pylintrc-wip | 70 ++--------------------- can/ctypesutil.py | 8 +-- can/interfaces/ixxat/canlib.py | 4 +- can/interfaces/kvaser/canlib.py | 2 +- can/interfaces/pcan/basic.py | 2 +- can/interfaces/usb2can/serial_selector.py | 4 +- can/interfaces/vector/canlib.py | 2 +- can/io/canutils.py | 5 +- can/io/csv.py | 6 +- can/logger.py | 2 +- can/message.py | 10 ++-- can/viewer.py | 8 +-- 12 files changed, 32 insertions(+), 91 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 68e944bf0..0c9bef035 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -64,41 +64,19 @@ disable=blacklisted-name, invalid-name, missing-docstring, empty-docstring, - singleton-comparison, - misplaced-comparison-constant, - consider-using-enumerate, - consider-iterating-dictionary, - bad-classmethod-argument, - bad-mcs-method-argument, - bad-mcs-classmethod-argument, single-string-used-for-slots, line-too-long, too-many-lines, - multiple-statements, - superfluous-parens, bad-whitespace, mixed-line-endings, unexpected-line-ending-format, bad-continuation, - wrong-spelling-in-comment, - wrong-spelling-in-docstring, - invalid-characters-in-docstring, multiple-imports, wrong-import-order, ungrouped-imports, wrong-import-position, - useless-import-alias, - len-as-condition, - syntax-error, - unrecognized-inline-option, - bad-option-value, - return-in-init, - duplicate-argument-name, - abstract-class-instantiated, - bad-reversed-sequence, too-many-star-expressions, invalid-star-assignment-target, - star-needs-assignment-target, method-hidden, access-member-before-definition, no-method-argument, @@ -151,16 +129,7 @@ disable=blacklisted-name, yield-inside-async-function, locally-disabled, file-ignored, - suppressed-message, - useless-suppression, - deprecated-pragma, - use-symbolic-message-instead, - c-extension-no-member, - literal-comparison, - comparison-with-itself, no-self-use, - no-classmethod-decorator, - no-staticmethod-decorator, useless-object-inheritance, cyclic-import, duplicate-code, @@ -173,18 +142,8 @@ disable=blacklisted-name, too-many-arguments, too-many-locals, too-many-statements, - too-many-boolean-expressions, - consider-merging-isinstance, too-many-nested-blocks, - simplifiable-if-statement, - redefined-argument-from-local, - no-else-return, - consider-using-ternary, - trailing-comma-tuple, - stop-iteration-return, - simplify-boolean-expression, inconsistent-return-statements, - useless-return, no-else-raise, dangerous-default-value, pointless-statement, @@ -216,10 +175,6 @@ disable=blacklisted-name, misplaced-future, fixme, invalid-encoded-data, - global-variable-undefined, - global-variable-not-assigned, - global-statement, - global-at-module-level, unused-import, unused-variable, unused-argument, @@ -233,18 +188,8 @@ disable=blacklisted-name, missing-format-argument-key, unused-format-string-argument, invalid-format-index, - duplicate-string-formatting-argument, - implicit-str-concat-in-sequence, - redundant-unittest-assert, - bad-thread-instantiation, - shallow-copy-environ, - invalid-envvar-default, - subprocess-popen-preexec-fn, no-absolute-import, dict-iter-method, - dict-view-method, - next-method-called, - metaclass-assignment, indexing-exception, raising-string, oct-method, @@ -253,16 +198,11 @@ disable=blacklisted-name, invalid-str-codec, sys-max-int, next-method-defined, - dict-items-not-iterating, - dict-keys-not-iterating, - dict-values-not-iterating, - deprecated-operator-function, - deprecated-urllib-function, - xreadlines-attribute, - deprecated-sys-function, - exception-escape, - comprehension-escape, - redefined-builtin + redefined-builtin, + no-else-return, + redefined-argument-from-local, + abstract-class-instantiated, + multiple-statements # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/can/ctypesutil.py b/can/ctypesutil.py index 1df53e5fc..0d8c967c9 100644 --- a/can/ctypesutil.py +++ b/can/ctypesutil.py @@ -34,7 +34,7 @@ def map_symbol(self, func_name, restype=None, argtypes=(), errcheck=None): :param callable errcheck: optional error checking function, see ctypes docs for _FuncPtr """ - if (argtypes): + if argtypes: prototype = self.function_type(restype, *argtypes) else: prototype = self.function_type(restype) @@ -46,7 +46,7 @@ def map_symbol(self, func_name, restype=None, argtypes=(), errcheck=None): setattr(symbol, "_name", func_name) log.debug('Wrapped function "{}", result type: {}, error_check {}'.format(func_name, type(restype), errcheck)) - if (errcheck): + if errcheck: symbol.errcheck = errcheck setattr(self, func_name, symbol) @@ -57,7 +57,7 @@ class CLibrary_Win32(_LibBase, LibraryMixin): " Basic ctypes.WinDLL derived class + LibraryMixin " def __init__(self, library_or_path): - if (isinstance(library_or_path, str)): + if isinstance(library_or_path, str): super().__init__(library_or_path) else: super().__init__(library_or_path._name, library_or_path._handle) @@ -71,7 +71,7 @@ class CLibrary_Unix(ctypes.CDLL, LibraryMixin): " Basic ctypes.CDLL derived class + LibraryMixin " def __init__(self, library_or_path): - if (isinstance(library_or_path, str)): + if isinstance(library_or_path, str): super().__init__(library_or_path) else: super().__init__(library_or_path._name, library_or_path._handle) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 3c91486d0..a87ad44a4 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -296,7 +296,7 @@ def __init__(self, channel, can_filters=None, **kwargs): # Usually comes as a string from the config file channel = int(channel) - if (bitrate not in self.CHANNEL_BITRATES[0]): + if bitrate not in self.CHANNEL_BITRATES[0]: raise ValueError("Invalid bitrate {}".format(bitrate)) self._device_handle = HANDLE() @@ -317,7 +317,7 @@ def __init__(self, channel, can_filters=None, **kwargs): try: _canlib.vciEnumDeviceNext(self._device_handle, ctypes.byref(self._device_info)) except StopIteration: - if (UniqueHardwareId is None): + 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)) diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index 7e8cd4b4f..af6eafff4 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -113,7 +113,7 @@ class c_canHandle(ctypes.c_int): def __handle_is_valid(handle): - return (handle.value > canINVALID_HANDLE) + return handle.value > canINVALID_HANDLE def __check_bus_handle_validity(handle, function, arguments): diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index 6ac38d850..79410a54e 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -361,7 +361,7 @@ def __init__(self): self.__m_dllBasic = cdll.LoadLibrary('libPCBUSB.dylib') else: self.__m_dllBasic = cdll.LoadLibrary("libpcanbasic.so") - if self.__m_dllBasic == None: + if self.__m_dllBasic is None: logger.error("Exception: The PCAN-Basic DLL couldn't be loaded!") def Initialize( diff --git a/can/interfaces/usb2can/serial_selector.py b/can/interfaces/usb2can/serial_selector.py index abaf19253..ddd7ff984 100644 --- a/can/interfaces/usb2can/serial_selector.py +++ b/can/interfaces/usb2can/serial_selector.py @@ -13,12 +13,12 @@ def WMIDateStringToDate(dtmDate): - if (dtmDate[4] == 0): + if dtmDate[4] == 0: strDateTime = dtmDate[5] + '/' else: strDateTime = dtmDate[4] + dtmDate[5] + '/' - if (dtmDate[6] == 0): + if dtmDate[6] == 0: strDateTime = strDateTime + dtmDate[7] + '/' else: strDateTime = strDateTime + dtmDate[6] + dtmDate[7] + '/' diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 6fb49c5fb..154c80f6d 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -99,7 +99,7 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, if channel_config.serialNumber == serial: if channel_config.hwChannel in self.channels: channel_index.append(channel_config.channelIndex) - if len(channel_index) > 0: + if channel_index: if len(channel_index) != len(self.channels): LOG.info("At least one defined channel wasn't found on the specified hardware.") self.channels = channel_index diff --git a/can/io/canutils.py b/can/io/canutils.py index 304c37f9c..66fd128f2 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -55,10 +55,7 @@ def __iter__(self): if channel.isdigit(): channel = int(channel) - if len(canId) > 3: - isExtended = True - else: - isExtended = False + isExtended = len(canId) > 3 canId = int(canId, 16) if data and data[0].lower() == 'r': diff --git a/can/io/csv.py b/can/io/csv.py index 8bafdf180..c755b1c5b 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -89,7 +89,11 @@ def __init__(self, file): def __iter__(self): # skip the header line - next(self.file) + try: + next(self.file) + except StopIteration: + # don't crash on a file with only a header + return for line in self.file: diff --git a/can/logger.py b/can/logger.py index 3cf2758e4..ec49c7e2c 100644 --- a/can/logger.py +++ b/can/logger.py @@ -75,7 +75,7 @@ def main(): can.set_logging_level(logging_level_name) can_filters = [] - if len(results.filter) > 0: + if results.filter: print(f"Adding filter(s): {results.filter}") for filt in results.filter: if ':' in filt: diff --git a/can/message.py b/can/message.py index ec2ee6875..0dfd170c2 100644 --- a/can/message.py +++ b/can/message.py @@ -227,21 +227,21 @@ def _check(self): raise ValueError("arbitration IDs may not be negative") if self.is_extended_id: - if 0x20000000 <= self.arbitration_id: + if self.arbitration_id >= 0x20000000: raise ValueError("Extended arbitration IDs must be less than 2^29") - elif 0x800 <= self.arbitration_id: + elif self.arbitration_id >= 0x800: raise ValueError("Normal arbitration IDs must be less than 2^11") if self.dlc < 0: raise ValueError("DLC may not be negative") if self.is_fd: - if 64 < self.dlc: + if self.dlc > 64: raise ValueError("DLC was {} but it should be <= 64 for CAN FD frames".format(self.dlc)) - elif 8 < self.dlc: + elif self.dlc > 8: raise ValueError("DLC was {} but it should be <= 8 for normal CAN frames".format(self.dlc)) if self.is_remote_frame: - if self.data is not None and len(self.data) != 0: + if self.data is not None and not self.data: raise ValueError("remote frames may not carry any data") elif self.dlc != len(self.data): raise ValueError("the DLC and the length of the data must match up for non remote frames") diff --git a/can/viewer.py b/can/viewer.py index d915a9ddb..5e6cf7d5b 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -141,7 +141,7 @@ def run(self): # Unpack the data and then convert it into SI-units @staticmethod def unpack_data(cmd, cmd_to_struct, data): # type: (int, Dict, bytes) -> List[Union[float, int]] - if not cmd_to_struct or len(data) == 0: + if not cmd_to_struct or data: # These messages do not contain a data package return [] @@ -403,7 +403,7 @@ def parse_args(args): choices=sorted(can.VALID_INTERFACES)) # Print help message when no arguments are given - if len(args) == 0: + if args: parser.print_help(sys.stderr) import errno raise SystemExit(errno.EINVAL) @@ -411,7 +411,7 @@ def parse_args(args): parsed_args = parser.parse_args(args) can_filters = [] - if len(parsed_args.filter) > 0: + if parsed_args.filter: # print('Adding filter/s', parsed_args.filter) for flt in parsed_args.filter: # print(filter) @@ -445,7 +445,7 @@ def parse_args(args): # In order to convert from raw integer value the real units are multiplied with the values and similarly the values # are divided by the value in order to convert from real units to raw integer values. data_structs = {} # type: Dict[Union[int, Tuple[int, ...]], Union[struct.Struct, Tuple, None]] - if len(parsed_args.decode) > 0: + if parsed_args.decode: if os.path.isfile(parsed_args.decode[0]): with open(parsed_args.decode[0], 'r') as f: structs = f.readlines() From 29349ecd4d714f5881c16b2ebc3dd2a660207e39 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 23:42:57 +0200 Subject: [PATCH 051/252] fix other linter stuff --- .pylintrc-wip | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 0c9bef035..4df60a88f 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -104,24 +104,6 @@ disable=blacklisted-name, too-many-function-args, unexpected-keyword-arg, redundant-keyword-arg, - invalid-unary-operand-type, - unsupported-binary-operation, - repeated-keyword, - not-an-iterable, - not-a-mapping, - unsupported-assignment-operation, - logging-unsupported-format, - logging-format-truncated, - logging-too-many-args, - logging-too-few-args, - bad-format-character, - truncated-format-string, - mixed-format-string, - format-needs-mapping, - missing-format-string-key, - bad-string-format-type, - bad-str-strip-call, - invalid-envvar-value, print-statement, parameter-unpacking, unpacking-in-except, From 53724a63300ec68ea2da722d096f0bbb44a18a9e Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 19 May 2019 23:56:03 +0200 Subject: [PATCH 052/252] fix other linter stuff --- .pylintrc-wip | 61 +++-------------------------- can/__init__.py | 1 - can/bus.py | 3 -- can/interfaces/ixxat/exceptions.py | 2 - can/interfaces/kvaser/canlib.py | 3 +- can/interfaces/pcan/pcan.py | 1 - can/interfaces/serial/serial_can.py | 2 +- can/interfaces/systec/structures.py | 3 +- can/interfaces/systec/ucan.py | 11 +----- can/io/blf.py | 2 +- can/listener.py | 1 - can/thread_safe_bus.py | 2 +- 12 files changed, 13 insertions(+), 79 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 4df60a88f..2c3f76bb6 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -75,46 +75,13 @@ disable=blacklisted-name, wrong-import-order, ungrouped-imports, wrong-import-position, - too-many-star-expressions, - invalid-star-assignment-target, method-hidden, - access-member-before-definition, - no-method-argument, - no-self-argument, - invalid-slots-object, - assigning-non-slot, - duplicate-bases, - non-iterator-returned, - unexpected-special-method-signature, - invalid-length-returned, - import-error, - relative-beyond-top-level, - used-before-assignment, - undefined-variable, - undefined-all-variable, - invalid-all-object, - no-name-in-module, - unpacking-non-sequence, - bad-exception-context, misplaced-bare-raise, - raising-non-exception, - notimplemented-raised, - catching-non-exception, - no-value-for-parameter, too-many-function-args, - unexpected-keyword-arg, - redundant-keyword-arg, print-statement, - parameter-unpacking, - unpacking-in-except, import-star-module-level, - yield-inside-async-function, locally-disabled, - file-ignored, no-self-use, - useless-object-inheritance, - cyclic-import, - duplicate-code, too-many-ancestors, too-many-instance-attributes, too-few-public-methods, @@ -125,22 +92,7 @@ disable=blacklisted-name, too-many-locals, too-many-statements, too-many-nested-blocks, - inconsistent-return-statements, no-else-raise, - dangerous-default-value, - pointless-statement, - pointless-string-statement, - expression-not-assigned, - unnecessary-pass, - unnecessary-lambda, - duplicate-key, - assign-to-new-keyword, - useless-else-on-loop, - exec-used, - eval-used, - confusing-with-statement, - using-constant-test, - comparison-with-callable, lost-exception, assert-on-tuple, attribute-defined-outside-init, @@ -154,9 +106,7 @@ disable=blacklisted-name, non-parent-init-called, bad-indentation, wildcard-import, - misplaced-future, fixme, - invalid-encoded-data, unused-import, unused-variable, unused-argument, @@ -172,19 +122,18 @@ disable=blacklisted-name, invalid-format-index, no-absolute-import, dict-iter-method, - indexing-exception, - raising-string, oct-method, hex-method, exception-message-attribute, - invalid-str-codec, - sys-max-int, - next-method-defined, redefined-builtin, no-else-return, redefined-argument-from-local, abstract-class-instantiated, - multiple-statements + multiple-statements, + cyclic-import, + useless-else-on-loop, + duplicate-code, + inconsistent-return-statements # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/can/__init__.py b/can/__init__.py index 0606266a9..4ad8bdee7 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -17,7 +17,6 @@ class CanError(IOError): """Indicates an error with the CAN network. """ - pass from .listener import Listener, BufferedReader, RedirectReader, AsyncBufferedReader diff --git a/can/bus.py b/can/bus.py index c8ade66d2..f0cb5080f 100644 --- a/can/bus.py +++ b/can/bus.py @@ -313,7 +313,6 @@ def _apply_filters(self, filters): :param Iterator[dict] filters: See :meth:`~can.BusABC.set_filters` for details. """ - pass def _matches_filters(self, msg): """Checks whether the given message matches at least one of the @@ -354,14 +353,12 @@ def _matches_filters(self, msg): 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. """ - pass def __enter__(self): return self diff --git a/can/interfaces/ixxat/exceptions.py b/can/interfaces/ixxat/exceptions.py index dde869032..efce76297 100644 --- a/can/interfaces/ixxat/exceptions.py +++ b/can/interfaces/ixxat/exceptions.py @@ -13,12 +13,10 @@ 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): diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index af6eafff4..3c042b485 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -41,7 +41,8 @@ def _unimplemented_function(*args): raise NotImplementedError('This function is not implemented in canlib') -def __get_canlib_function(func_name, argtypes=[], restype=None, errcheck=None): +def __get_canlib_function(func_name, argtypes=None, restype=None, errcheck=None): + argtypes = [] if argtypes is None else argtypes #log.debug('Wrapping function "%s"' % func_name) try: # e.g. canlib.canBusOn diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 2d04ff5f4..53b8d6b8b 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -426,4 +426,3 @@ class PcanError(CanError): """ A generic error on a PCAN bus. """ - pass diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index 1904d28e3..e61c80849 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -121,7 +121,7 @@ def _recv_internal(self, timeout): message are the default values. :rtype: - can.Message, bool + Tuple[can.Message, Bool] """ try: # ser.read can return an empty string diff --git a/can/interfaces/systec/structures.py b/can/interfaces/systec/structures.py index 40ce01dfa..11ffd0e39 100644 --- a/can/interfaces/systec/structures.py +++ b/can/interfaces/systec/structures.py @@ -35,7 +35,8 @@ class CanMsg(Structure): ("m_dwTime", DWORD,) # Receive time stamp in ms (for transmit messages no meaning) ] - def __init__(self, id=0, frame_format=MsgFrameFormat.MSG_FF_STD, data=[]): + def __init__(self, id=0, frame_format=MsgFrameFormat.MSG_FF_STD, data=None): + data = [] if data is None else data super().__init__(id, frame_format, len(data), (BYTE * 8)(*data), 0) def __eq__(self, other): diff --git a/can/interfaces/systec/ucan.py b/can/interfaces/systec/ucan.py index e42c187eb..0c153b939 100644 --- a/can/interfaces/systec/ucan.py +++ b/can/interfaces/systec/ucan.py @@ -291,7 +291,7 @@ def check_result(result, func, arguments): log.warning("Cannot load SYSTEC ucan library: %s.", ex) -class UcanServer(object): +class UcanServer: """ UcanServer is a Python wrapper class for using the usbcan32.dll / usbcan64.dll. """ @@ -987,7 +987,6 @@ def init_hw_event(self): .. note:: To be overridden by subclassing. """ - pass def init_can_event(self, channel): """ @@ -997,7 +996,6 @@ def init_can_event(self, channel): .. note:: To be overridden by subclassing. """ - pass def can_msg_received_event(self, channel): """ @@ -1009,7 +1007,6 @@ def can_msg_received_event(self, channel): .. note:: To be overridden by subclassing. """ - pass def status_event(self, channel): """ @@ -1021,7 +1018,6 @@ def status_event(self, channel): .. note:: To be overridden by subclassing. """ - pass def deinit_can_event(self, channel): """ @@ -1031,7 +1027,6 @@ def deinit_can_event(self, channel): .. note:: To be overridden by subclassing. """ - pass def deinit_hw_event(self): """ @@ -1039,7 +1034,6 @@ def deinit_hw_event(self): .. note:: To be overridden by subclassing. """ - pass def connect_event(self): """ @@ -1047,7 +1041,6 @@ def connect_event(self): .. note:: To be overridden by subclassing. """ - pass def disconnect_event(self): """ @@ -1055,7 +1048,6 @@ def disconnect_event(self): .. note:: To be overridden by subclassing. """ - pass def fatal_disconnect_event(self, device_number): """ @@ -1067,7 +1059,6 @@ def fatal_disconnect_event(self, device_number): .. note:: To be overridden by subclassing. """ - pass UcanServer._enum_callback_ref = EnumCallback(UcanServer._enum_callback) diff --git a/can/io/blf.py b/can/io/blf.py index 36d116950..ad895297e 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -28,7 +28,7 @@ class BLFParseError(Exception): """BLF file could not be parsed correctly.""" - pass + LOG = logging.getLogger(__name__) diff --git a/can/listener.py b/can/listener.py index b3cc3a886..2f773fcf0 100644 --- a/can/listener.py +++ b/can/listener.py @@ -39,7 +39,6 @@ def on_message_received(self, msg): :param can.Message msg: the delivered message """ - pass def __call__(self, msg): return self.on_message_received(msg) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 35f923910..d215add4a 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -18,7 +18,7 @@ from contextlib import nullcontext except ImportError: - class nullcontext(object): + class nullcontext: """A context manager that does nothing at all. A fallback for Python 3.7's :class:`contextlib.nullcontext` manager. """ From 29daaf93433e717538ba178866b3e8f6b5e36bff Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:03:54 +0200 Subject: [PATCH 053/252] fix other linter stuff --- .pylintrc-wip | 11 ++--------- can/interfaces/ixxat/canlib.py | 10 ---------- can/interfaces/nican.py | 15 ++------------- can/interfaces/systec/exceptions.py | 1 + can/interfaces/usb2can/usb2canInterface.py | 6 +++++- 5 files changed, 10 insertions(+), 33 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 2c3f76bb6..5581e5c1e 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -93,17 +93,9 @@ disable=blacklisted-name, too-many-statements, too-many-nested-blocks, no-else-raise, - lost-exception, - assert-on-tuple, attribute-defined-outside-init, - bad-staticmethod-argument, protected-access, - arguments-differ, - signature-differs, abstract-method, - super-init-not-called, - no-init, - non-parent-init-called, bad-indentation, wildcard-import, fixme, @@ -133,7 +125,8 @@ disable=blacklisted-name, cyclic-import, useless-else-on-loop, duplicate-code, - inconsistent-return-statements + inconsistent-return-statements, + arguments-differ # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index a87ad44a4..d33511660 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -516,16 +516,6 @@ def shutdown(self): _canlib.canControlClose(self._control_handle) _canlib.vciDeviceClose(self._device_handle) - __set_filters_has_been_called = False - def set_filters(self, can_filers=None): - """Unsupported. See note on :class:`~can.interfaces.ixxat.IXXATBus`. - """ - if self.__set_filters_has_been_called: - log.warning("using filters is not supported like this, see note on IXXATBus") - else: - # allow the constructor to call this without causing a warning - self.__set_filters_has_been_called = True - class CyclicSendTask(LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC): diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index b032e3eee..d05acf850 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -201,9 +201,8 @@ def __init__(self, channel, can_filters=None, bitrate=None, log_errors=True, **k self.handle = ctypes.c_ulong() nican.ncOpenObject(channel, ctypes.byref(self.handle)) - super().__init__(channel=channel, - can_filters=can_filters, bitrate=bitrate, - log_errors=log_errors, **kwargs) + super().__init__(channel=channel, can_filters=can_filters, bitrate=bitrate, + log_errors=log_errors, **kwargs) def _recv_internal(self, timeout): """ @@ -293,16 +292,6 @@ def shutdown(self): """Close object.""" nican.ncCloseObject(self.handle) - __set_filters_has_been_called = False - def set_filters(self, can_filers=None): - """Unsupported. See note on :class:`~can.interfaces.nican.NicanBus`. - """ - if self.__set_filters_has_been_called: - logger.warning("using filters is not supported like this, see note on NicanBus") - else: - # allow the constructor to call this without causing a warning - self.__set_filters_has_been_called = True - class NicanError(CanError): """Error from NI-CAN driver.""" diff --git a/can/interfaces/systec/exceptions.py b/can/interfaces/systec/exceptions.py index 622403d48..0d823895b 100644 --- a/can/interfaces/systec/exceptions.py +++ b/can/interfaces/systec/exceptions.py @@ -12,6 +12,7 @@ def __init__(self, result, func, arguments): self.func = func self.arguments = arguments self.return_msgs = {} + super().__init__() def __str__(self): message = self.return_msgs.get(self.result, "unknown") diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index efdd9bf0e..2cfe4d0d5 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -154,7 +154,11 @@ def shutdown(self): raise CanError("could not shut down bus: status == {}".format(status)) @staticmethod - def _detect_available_configs(serial_matcher=None): + def _detect_available_configs(): + return Usb2canBus.detect_available_configs() + + @staticmethod + def detect_available_configs(serial_matcher=None): """ Uses the Windows Management Instrumentation to identify serial devices. From cbea5ee74538c11c96724fc63fe319306d472357 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:04:17 +0200 Subject: [PATCH 054/252] fix other linter stuff --- .pylintrc-wip | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 5581e5c1e..6f2beb7a8 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -99,15 +99,7 @@ disable=blacklisted-name, bad-indentation, wildcard-import, fixme, - unused-import, - unused-variable, - unused-argument, - unused-wildcard-import, - possibly-unused-variable, broad-except, - logging-not-lazy, - logging-format-interpolation, - logging-fstring-interpolation, bad-format-string, missing-format-argument-key, unused-format-string-argument, From 8a0d980304d3e016f9a24de67e6ec93e7466aa73 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:26:50 +0200 Subject: [PATCH 055/252] fix other linter stuff --- .pylintrc-wip | 15 ++++++++------- can/broadcastmanager.py | 1 - can/bus.py | 5 ++--- can/ctypesutil.py | 3 +-- can/interface.py | 7 ++----- can/interfaces/ics_neovi/neovi_bus.py | 2 +- can/interfaces/ixxat/canlib.py | 4 ++-- can/interfaces/kvaser/canlib.py | 9 ++++----- can/interfaces/pcan/pcan.py | 4 +--- can/interfaces/socketcan/socketcan.py | 5 ++--- can/interfaces/socketcan/utils.py | 1 - can/interfaces/systec/ucan.py | 4 ++-- can/interfaces/systec/ucanbus.py | 5 ++++- can/interfaces/vector/canlib.py | 3 +-- can/io/canutils.py | 2 -- can/io/generic.py | 4 +--- can/io/logger.py | 2 +- can/io/sqlite.py | 7 +++---- can/message.py | 1 - can/thread_safe_bus.py | 4 ++-- can/util.py | 2 -- 21 files changed, 37 insertions(+), 53 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 6f2beb7a8..c58a7d56f 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -93,22 +93,16 @@ disable=blacklisted-name, too-many-statements, too-many-nested-blocks, no-else-raise, - attribute-defined-outside-init, - protected-access, - abstract-method, bad-indentation, wildcard-import, fixme, broad-except, bad-format-string, - missing-format-argument-key, unused-format-string-argument, - invalid-format-index, no-absolute-import, dict-iter-method, oct-method, hex-method, - exception-message-attribute, redefined-builtin, no-else-return, redefined-argument-from-local, @@ -118,7 +112,14 @@ disable=blacklisted-name, useless-else-on-loop, duplicate-code, inconsistent-return-statements, - arguments-differ + arguments-differ, + unused-wildcard-import, + logging-fstring-interpolation, + logging-format-interpolation, + unused-argument, + abstract-method, + attribute-defined-outside-init, + protected-access # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 05a27197a..9cf4cb36c 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -11,7 +11,6 @@ import logging import threading import time -import warnings log = logging.getLogger('can.bcm') diff --git a/can/bus.py b/can/bus.py index f0cb5080f..7ba4ae872 100644 --- a/can/bus.py +++ b/can/bus.py @@ -8,7 +8,6 @@ import logging import threading from time import time -from collections import namedtuple from aenum import Enum, auto from .broadcastmanager import ThreadBasedCyclicSendTask @@ -239,8 +238,8 @@ def _send_periodic_internal(self, msg, period, duration=None): :rtype: can.broadcastmanager.CyclicSendTaskABC """ if not hasattr(self, "_lock_send_periodic"): - # Create a send lock for this bus - self._lock_send_periodic = threading.Lock() + # Create a send lock for this bus, but not for buses which override this method + self._lock_send_periodic = threading.Lock() # pylint: disable=attribute-defined-outside-init task = ThreadBasedCyclicSendTask(self, self._lock_send_periodic, msg, period, duration) return task diff --git a/can/ctypesutil.py b/can/ctypesutil.py index 0d8c967c9..eaac336e3 100644 --- a/can/ctypesutil.py +++ b/can/ctypesutil.py @@ -4,7 +4,6 @@ This module contains common `ctypes` utils. """ -import binascii import ctypes import logging import sys @@ -44,7 +43,7 @@ def map_symbol(self, func_name, restype=None, argtypes=(), errcheck=None): 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)) + log.debug(f'Wrapped function "{func_name}", result type: {type(restype)}, error_check {errcheck}') if errcheck: symbol.errcheck = errcheck diff --git a/can/interface.py b/can/interface.py index 8eb922051..81481072c 100644 --- a/can/interface.py +++ b/can/interface.py @@ -6,13 +6,10 @@ CyclicSendTasks. """ -import sys import importlib import logging -import can from .bus import BusABC -from .broadcastmanager import CyclicSendTaskABC, MultiRateCyclicSendTaskABC from .util import load_config from .interfaces import BACKENDS @@ -56,7 +53,7 @@ def _get_class_for_interface(interface): return bus_class -class Bus(BusABC): +class Bus(BusABC): # pylint disable=abstract-method """Bus wrapper with configuration loading. Instantiates a CAN Bus of the given ``interface``, falls back to reading a @@ -154,7 +151,7 @@ def detect_available_configs(interfaces=None): # get available channels try: - available = list(bus_class._detect_available_configs()) + available = list(bus_class._detect_available_configs()) # pylint: disable=protected-access except NotImplementedError: log_autodetect.debug('interface "%s" does not support detection of available configurations', interface) else: diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index b5218a4cf..e8404e6d9 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -234,7 +234,7 @@ def _process_msg_queue(self, timeout=0.1): continue self.rx_buffer.append(ics_msg) if errors: - logger.warning("%d error(s) found" % errors) + logger.warning("%d error(s) found", errors) for msg in ics.get_error_messages(self.dev): error = ICSApiError(*msg) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index d33511660..cbe241be9 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -18,7 +18,7 @@ import logging import sys -from can import CanError, BusABC, Message +from can import BusABC, Message from can.broadcastmanager import (LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC) from can.ctypesutil import CLibrary, HANDLE, PHANDLE, HRESULT as ctypes_HRESULT @@ -383,7 +383,7 @@ def __init__(self, channel, can_filters=None, **kwargs): # 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): + for _ in range(rxFifoSize): try: _canlib.canChannelReadMessage(self._channel_handle, 0, ctypes.byref(self._message)) except (VCITimeout, VCIRxQueueEmptyError): diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index 3c042b485..c89d0e047 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -375,10 +375,9 @@ def __init__(self, channel, can_filters=None, **kwargs): self.single_handle = single_handle num_channels = ctypes.c_int(0) - res = canGetNumberOfChannels(ctypes.byref(num_channels)) - #log.debug("Res: {}".format(res)) + #log.debug("Res: %d", canGetNumberOfChannels(ctypes.byref(num_channels))) num_channels = int(num_channels.value) - log.info('Found %d available channels' % num_channels) + 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) @@ -391,7 +390,7 @@ def __init__(self, channel, can_filters=None, **kwargs): if fd: flags |= canstat.canOPEN_CAN_FD - log.debug('Creating read handle to bus channel: %s' % channel) + log.debug('Creating read handle to bus channel: %s', channel) self._read_handle = canOpenChannel(channel, flags) canIoCtl(self._read_handle, canstat.canIOCTL_SET_TIMER_SCALE, @@ -427,7 +426,7 @@ def __init__(self, channel, can_filters=None, **kwargs): 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) + log.debug('Creating separate handle for TX on channel: %s', channel) self._write_handle = canOpenChannel(channel, flags) canBusOn(self._read_handle) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 53b8d6b8b..d76f185a6 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -5,10 +5,8 @@ """ import logging -import sys import time -import can from can import CanError, Message, BusABC from can.bus import BusState from can.util import len2dlc, dlc2len @@ -165,7 +163,7 @@ def __init__(self, *args, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate self.m_PcanHandle = globals()[channel] if state is BusState.ACTIVE or state is BusState.PASSIVE: - self.state = state + self._state = state else: raise ArgumentError("BusState must be Active or Passive") diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index d178da2b3..e793194c5 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -3,7 +3,6 @@ import logging import ctypes import ctypes.util -import os import select import socket import struct @@ -25,8 +24,7 @@ from can.broadcastmanager import ModifiableCyclicTaskABC, \ RestartableCyclicTaskABC, LimitedDurationCyclicSendTaskABC from can.interfaces.socketcan.constants import * # CAN_RAW, CAN_*_FLAG -from can.interfaces.socketcan.utils import \ - pack_filters, find_available_interfaces, error_code_to_str +from can.interfaces.socketcan.utils import pack_filters, find_available_interfaces # Setup BCM struct @@ -478,6 +476,7 @@ def __init__(self, channel="", receive_own_messages=False, fd=False, **kwargs): self.channel = channel self.channel_info = "socketcan channel '%s'" % channel self._bcm_sockets = {} + self._is_filtered = False # set the receive_own_messages parameter try: diff --git a/can/interfaces/socketcan/utils.py b/can/interfaces/socketcan/utils.py index ebd095f35..f2c1879c5 100644 --- a/can/interfaces/socketcan/utils.py +++ b/can/interfaces/socketcan/utils.py @@ -8,7 +8,6 @@ import os import errno import struct -import sys import subprocess import re diff --git a/can/interfaces/systec/ucan.py b/can/interfaces/systec/ucan.py index 0c153b939..8aded3804 100644 --- a/can/interfaces/systec/ucan.py +++ b/can/interfaces/systec/ucan.py @@ -947,7 +947,7 @@ def _connect_control(self, event, param, arg): - CbEvent.EVENT_FATALDISCON: USB-CAN-Handle of the disconnected module :param arg: Additional parameter defined with :meth:`init_hardware_ex` (not used in this wrapper class). """ - log.debug("Event: %s, Param: %s" % (event, param)) + log.debug("Event: %s, Param: %s", event, param) if event == CbEvent.EVENT_FATALDISCON: self.fatal_disconnect_event(param) @@ -966,7 +966,7 @@ def _callback(self, handle, event, channel, arg): CAN channel (:data:`Channel.CHANNEL_CH0`, :data:`Channel.CHANNEL_CH1` or :data:`Channel.CHANNEL_ANY`). :param arg: Additional parameter defined with :meth:`init_hardware_ex`. """ - log.debug("Handle: %s, Event: %s, Channel: %s" % (handle, event, channel)) + log.debug("Handle: %s, Event: %s, Channel: %s", handle, event, channel) if event == CbEvent.EVENT_INITHW: self.init_hw_event() diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index 3238c1711..b4852db91 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -132,6 +132,8 @@ def __init__(self, channel, can_filters=None, **kwargs): self.channel, self._ucan.get_baudrate_message(self.BITRATES[bitrate]) ) + self._is_filtered = False + super().__init__(channel=channel, can_filters=can_filters, **kwargs) def _recv_internal(self, timeout): @@ -180,7 +182,8 @@ def send(self, msg, timeout=None): def _detect_available_configs(): configs = [] try: - for index, is_used, hw_info_ex, init_info in Ucan.enumerate_hardware(): + # index, is_used, hw_info_ex, init_info + for _, _, hw_info_ex, _ in Ucan.enumerate_hardware(): configs.append({'interface': 'systec', 'channel': Channel.CHANNEL_CH0, 'device_number': hw_info_ex.device_number}) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 154c80f6d..01ee1a0a8 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -10,7 +10,6 @@ # ============================== import ctypes import logging -import sys import time try: @@ -28,7 +27,7 @@ # Import Modules # ============== -from can import BusABC, Message, CanError +from can import BusABC, Message from can.util import len2dlc, dlc2len from .exceptions import VectorError diff --git a/can/io/canutils.py b/can/io/canutils.py index 66fd128f2..bea1696a1 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -6,8 +6,6 @@ (https://github.com/linux-can/can-utils). """ -import time -import datetime import logging from can.message import Message diff --git a/can/io/generic.py b/can/io/generic.py index abb03345e..56ce82860 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -4,9 +4,7 @@ Contains a generic class for file IO. """ -from abc import ABCMeta, abstractmethod - -from can import Listener +from abc import ABCMeta class BaseIOHandler(metaclass=ABCMeta): diff --git a/can/io/logger.py b/can/io/logger.py index da3edb2b5..2863b0ea5 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -18,7 +18,7 @@ log = logging.getLogger("can.io.logger") -class Logger(BaseIOHandler, Listener): +class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method """ Logs CAN messages to a file. diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 94d0af485..eccba8dc8 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -6,7 +6,6 @@ .. note:: The database schema is given in the documentation of the loggers. """ -import sys import time import threading import logging @@ -143,10 +142,12 @@ def __init__(self, file, table_name="messages"): self.table_name = table_name self._db_filename = file self._stop_running_event = threading.Event() + self._conn = None self._writer_thread = threading.Thread(target=self._db_writer_thread) self._writer_thread.start() self.num_frames = 0 self.last_write = time.time() + self._insert_template = f"INSERT INTO {self.table_name} VALUES (?, ?, ?, ?, ?, ?, ?)" def _create_db(self): """Creates a new databae or opens a connection to an existing one. @@ -173,8 +174,6 @@ def _create_db(self): """.format(self.table_name)) self._conn.commit() - self._insert_template = "INSERT INTO {} VALUES (?, ?, ?, ?, ?, ?, ?)".format(self.table_name) - def _db_writer_thread(self): self._create_db() @@ -198,7 +197,7 @@ def _db_writer_thread(self): if time.time() - self.last_write > self.MAX_TIME_BETWEEN_WRITES or \ len(messages) > self.MAX_BUFFER_SIZE_BEFORE_WRITES: - break + break else: # just go on msg = self.get_message(self.GET_MESSAGE_TIMEOUT) diff --git a/can/message.py b/can/message.py index 0dfd170c2..eb37ab11a 100644 --- a/can/message.py +++ b/can/message.py @@ -9,7 +9,6 @@ """ -import warnings from copy import deepcopy from math import isinf, isnan diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index d215add4a..712958526 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -33,7 +33,7 @@ def __exit__(self, *args): pass -class ThreadSafeBus(ObjectProxy): +class ThreadSafeBus(ObjectProxy): # pylint: disable=abstract-method """ Contains a thread safe :class:`can.BusABC` implementation that wraps around an existing interface instance. All public methods @@ -58,7 +58,7 @@ def __init__(self, *args, **kwargs): # now, BusABC.send_periodic() does not need a lock anymore, but the # implementation still requires a context manager - self.__wrapped__._lock_send_periodic = nullcontext() + self.__wrapped__._lock_send_periodic = nullcontext() # pylint: disable=protected-access # init locks for sending and receiving separately self._lock_send = RLock() diff --git a/can/util.py b/can/util.py index 2e682548d..c567d8ada 100644 --- a/can/util.py +++ b/can/util.py @@ -6,11 +6,9 @@ import os import os.path -import sys import platform import re import logging -import warnings from configparser import ConfigParser import can From a13701b0c57ee41b3003f3f05dda5cc6833fbd95 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:30:35 +0200 Subject: [PATCH 056/252] fix other linter stuff --- .pylintrc-wip | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index c58a7d56f..2854b1132 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -60,33 +60,21 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=blacklisted-name, - invalid-name, +disable=invalid-name, missing-docstring, empty-docstring, - single-string-used-for-slots, line-too-long, too-many-lines, bad-whitespace, - mixed-line-endings, - unexpected-line-ending-format, bad-continuation, - multiple-imports, wrong-import-order, ungrouped-imports, wrong-import-position, - method-hidden, - misplaced-bare-raise, too-many-function-args, - print-statement, - import-star-module-level, locally-disabled, - no-self-use, - too-many-ancestors, too-many-instance-attributes, too-few-public-methods, too-many-public-methods, - too-many-return-statements, too-many-branches, too-many-arguments, too-many-locals, @@ -97,12 +85,6 @@ disable=blacklisted-name, wildcard-import, fixme, broad-except, - bad-format-string, - unused-format-string-argument, - no-absolute-import, - dict-iter-method, - oct-method, - hex-method, redefined-builtin, no-else-return, redefined-argument-from-local, From bfea27a805ad24c1dbfb25be3b8592097a2c905b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:38:18 +0200 Subject: [PATCH 057/252] fix freshly introduced bug --- can/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/message.py b/can/message.py index eb37ab11a..c3be33aa7 100644 --- a/can/message.py +++ b/can/message.py @@ -240,7 +240,7 @@ def _check(self): raise ValueError("DLC was {} but it should be <= 8 for normal CAN frames".format(self.dlc)) if self.is_remote_frame: - if self.data is not None and not self.data: + if self.data: raise ValueError("remote frames may not carry any data") elif self.dlc != len(self.data): raise ValueError("the DLC and the length of the data must match up for non remote frames") From 527f71526220ea82390afbf2b867d4474489beb4 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:40:19 +0200 Subject: [PATCH 058/252] fix freshly introduced bug --- can/viewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/viewer.py b/can/viewer.py index 5e6cf7d5b..2030c052d 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -141,7 +141,7 @@ def run(self): # Unpack the data and then convert it into SI-units @staticmethod def unpack_data(cmd, cmd_to_struct, data): # type: (int, Dict, bytes) -> List[Union[float, int]] - if not cmd_to_struct or data: + if not cmd_to_struct or not data: # These messages do not contain a data package return [] From 6f361c224e9497adf590247035163a782a8542ea Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:54:47 +0200 Subject: [PATCH 059/252] fix problem with keyword-arg-before-vararg --- .pylintrc-wip | 3 ++- can/interface.py | 2 +- can/interfaces/pcan/pcan.py | 2 +- can/interfaces/serial/serial_can.py | 2 +- can/interfaces/usb2can/usb2canInterface.py | 2 +- can/thread_safe_bus.py | 6 +++--- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.pylintrc-wip b/.pylintrc-wip index 2854b1132..c028f9f3d 100644 --- a/.pylintrc-wip +++ b/.pylintrc-wip @@ -101,7 +101,8 @@ disable=invalid-name, unused-argument, abstract-method, attribute-defined-outside-init, - protected-access + protected-access, + keyword-arg-before-vararg # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/can/interface.py b/can/interface.py index 81481072c..5fd342181 100644 --- a/can/interface.py +++ b/can/interface.py @@ -61,7 +61,7 @@ class Bus(BusABC): # pylint disable=abstract-method """ @staticmethod - def __new__(cls, *args, channel=None, **kwargs): + def __new__(cls, channel=None, *args, **kwargs): """ Takes the same arguments as :class:`can.BusABC.__init__`. Some might have a special meaning, see below. diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index d76f185a6..9ef526a84 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -60,7 +60,7 @@ class PcanBus(BusABC): - def __init__(self, *args, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000, + def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000, *args, **kwargs): """A PCAN USB interface to CAN. diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index e61c80849..6e1327a15 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -31,7 +31,7 @@ class SerialBus(BusABC): """ - def __init__(self, channel, *args, baudrate=115200, timeout=0.1, rtscts=False, **kwargs): + def __init__(self, channel, baudrate=115200, timeout=0.1, rtscts=False, *args, **kwargs): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 2cfe4d0d5..ffe04979a 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -83,7 +83,7 @@ class Usb2canBus(BusABC): """ - def __init__(self, *args, channel=None, dll="usb2can.dll", flags=0x00000008, + def __init__(self, channel=None, dll="usb2can.dll", flags=0x00000008, *args, bitrate=500000, **kwargs): self.can = Usb2CanAbstractionLayer(dll) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 712958526..aaa6d7655 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -64,11 +64,11 @@ def __init__(self, *args, **kwargs): self._lock_send = RLock() self._lock_recv = RLock() - def recv(self, *args, timeout=None, **kwargs): + def recv(self, timeout=None, *args, **kwargs): with self._lock_recv: return self.__wrapped__.recv(timeout=timeout, *args, **kwargs) - def send(self, msg, *args, timeout=None, **kwargs): + def send(self, msg, timeout=None, *args, **kwargs): with self._lock_send: return self.__wrapped__.send(msg, timeout=timeout, *args, **kwargs) @@ -85,7 +85,7 @@ def filters(self, filters): with self._lock_recv: self.__wrapped__.filters = filters - def set_filters(self, *args, filters=None, **kwargs): + def set_filters(self, filters=None, *args, **kwargs): with self._lock_recv: return self.__wrapped__.set_filters(filters=filters, *args, **kwargs) From 047f4d8be1d46bab1cac87a9614e4b8bed688fe1 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 00:57:39 +0200 Subject: [PATCH 060/252] fix freshly introduced bug --- can/viewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/viewer.py b/can/viewer.py index 2030c052d..1332766df 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -403,7 +403,7 @@ def parse_args(args): choices=sorted(can.VALID_INTERFACES)) # Print help message when no arguments are given - if args: + if not args: parser.print_help(sys.stderr) import errno raise SystemExit(errno.EINVAL) From 19b4ddf940b83b393268c7f424600acd6af59496 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 09:19:07 +0200 Subject: [PATCH 061/252] use set for membership testing --- can/interfaces/pcan/basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index 79410a54e..f27db0de1 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -658,9 +658,9 @@ def GetValue( A touple with 2 values """ try: - if Parameter in (PCAN_API_VERSION, PCAN_HARDWARE_NAME, PCAN_CHANNEL_VERSION, + if Parameter in {PCAN_API_VERSION, PCAN_HARDWARE_NAME, PCAN_CHANNEL_VERSION, PCAN_LOG_LOCATION, PCAN_TRACE_LOCATION, PCAN_BITRATE_INFO_FD, - PCAN_IP_ADDRESS): + PCAN_IP_ADDRESS}: mybuffer = create_string_buffer(256) else: mybuffer = c_int(0) @@ -696,7 +696,7 @@ def SetValue( A TPCANStatus error code """ try: - if Parameter in (PCAN_LOG_LOCATION, PCAN_LOG_TEXT, PCAN_TRACE_LOCATION): + if Parameter in {PCAN_LOG_LOCATION, PCAN_LOG_TEXT, PCAN_TRACE_LOCATION}: mybuffer = create_string_buffer(256) else: mybuffer = c_int(0) From 266180929d0633ab9271dcac7fdb9c2acac962dc Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 20 May 2019 09:22:57 +0200 Subject: [PATCH 062/252] revert state setter code in PCAN and add pylint ignore + comment --- can/interfaces/pcan/pcan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 9ef526a84..a7c8bd9ec 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -163,7 +163,7 @@ def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000 self.m_PcanHandle = globals()[channel] if state is BusState.ACTIVE or state is BusState.PASSIVE: - self._state = state + self.state = state else: raise ArgumentError("BusState must be Active or Passive") @@ -407,8 +407,8 @@ def state(self): @state.setter def state(self, new_state): - - self._state = new_state + # declare here, which is called by __init__() + self._state = new_state # pylint: disable=attribute-defined-outside-init if new_state is BusState.ACTIVE: self.m_objPCANBasic.SetValue(self.m_PcanHandle, PCAN_LISTEN_ONLY, PCAN_PARAMETER_OFF) From c21861efa61c84b1ff3fefd9dca136894aeb1f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Thu, 23 May 2019 09:49:36 -0400 Subject: [PATCH 063/252] Adding CAN FD frame support to asc writer --- can/io/asc.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index 3ed50f04a..ca7f5f12c 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -131,6 +131,25 @@ class ASCWriter(BaseIOHandler, Listener): """ FORMAT_MESSAGE = "{channel} {id:<15} Rx {dtype} {data}" + FORMAT_MESSAGE_FD = " ".join([ + "CANFD", + "{channel:>3}", + "{dir:<4}", + "{id:>8} {symbolic_name:>32}", + "{brs}", + "{esi}", + "{dlc}", + "{data_length:>2}", + "{data}", + "{message_duration:>8}", + "{message_length:>4}", + "{flags:>8X}", + "{crc:>8}", + "{bit_timing_conf_arb:>8}", + "{bit_timing_conf_data:>8}", + "{bit_timing_conf_ext_arb:>8}", + "{bit_timing_conf_ext_data:>8}" + ]) FORMAT_DATE = "%a %b %m %I:%M:%S.{} %p %Y" FORMAT_EVENT = "{timestamp: 9.6f} {message}\n" @@ -217,9 +236,39 @@ def on_message_received(self, msg): # Many interfaces start channel numbering at 0 which is invalid channel += 1 - serialized = self.FORMAT_MESSAGE.format(channel=channel, - id=arb_id, - dtype=dtype, - data=' '.join(data)) + if msg.is_fd: + flags = 0 + flags |= 1 << 12 + if msg.bitrate_switch: + flags |= 1 << 13 + if msg.error_state_indicator: + flags |= 1 << 14 + + serialized = self.FORMAT_MESSAGE_FD.format( + channel=channel, + id=arb_id, + dir="Rx", + symbolic_name="", + brs=1 if msg.bitrate_switch else 0, + esi=1 if msg.error_state_indicator else 0, + dlc=msg.dlc, + data_length=len(data), + data=' '.join(data), + message_duration=0, + message_length=0, + flags=flags, + crc=0, + bit_timing_conf_arb=0, + bit_timing_conf_data=0, + bit_timing_conf_ext_arb=0, + bit_timing_conf_ext_data=0 + ) + else: + serialized = self.FORMAT_MESSAGE.format( + channel=channel, + id=arb_id, + dtype=dtype, + data=' '.join(data) + ) self.log_event(serialized, msg.timestamp) From 2e50e18bdbc9b5f1260511242716a213c3d3a510 Mon Sep 17 00:00:00 2001 From: Karl Date: Mon, 20 May 2019 20:58:27 -0700 Subject: [PATCH 064/252] Add black to requirements-lint.txt --- requirements-lint.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-lint.txt b/requirements-lint.txt index 514974539..6a81fe2eb 100644 --- a/requirements-lint.txt +++ b/requirements-lint.txt @@ -1 +1,2 @@ pylint==2.3.1 +black==19.3b0 From 27a7535dc1d437680fca8eb02e73b6d42ee918e4 Mon Sep 17 00:00:00 2001 From: Karl Date: Mon, 20 May 2019 20:58:43 -0700 Subject: [PATCH 065/252] Enable black in Travis CI checks --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 29fac0557..5c24c1cd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,6 +92,14 @@ jobs: # warnings to the .pylintrc-wip file to prevent them from being # re-introduced - pylint --rcfile=.pylintrc-wip can/ + - stage: linter + name: "Formatting Checks" + python: "3.7" + before_install: + - travis_retry pip install -r requirements-lint.txt + script: + - black --diff . + - black --check . - stage: deploy name: "PyPi Deployment" python: "3.7" From 0b97098f1e94750fb758db6fc3f78e45c152101e Mon Sep 17 00:00:00 2001 From: Karl Date: Mon, 20 May 2019 21:25:43 -0700 Subject: [PATCH 066/252] Fix pylint directive comment Disable the pylint warning for the block, as black will reformat this over multiple lines instead of the previous one-liner. --- can/thread_safe_bus.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index aaa6d7655..7e0d058f2 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -58,7 +58,9 @@ def __init__(self, *args, **kwargs): # now, BusABC.send_periodic() does not need a lock anymore, but the # implementation still requires a context manager - self.__wrapped__._lock_send_periodic = nullcontext() # pylint: disable=protected-access + # pylint: disable=protected-access + self.__wrapped__._lock_send_periodic = nullcontext() + # pylint: enable=protected-access # init locks for sending and receiving separately self._lock_send = RLock() From ad4d46e5d26bf2ee5eada45491ea8eba6f054ce4 Mon Sep 17 00:00:00 2001 From: Karl Date: Mon, 20 May 2019 21:07:57 -0700 Subject: [PATCH 067/252] Format files using black formatter --- can/__init__.py | 15 +- can/broadcastmanager.py | 9 +- can/bus.py | 20 +- can/ctypesutil.py | 15 +- can/interface.py | 59 +- can/interfaces/__init__.py | 42 +- can/interfaces/canalystii.py | 84 +- can/interfaces/ics_neovi/neovi_bus.py | 86 +- can/interfaces/iscan.py | 42 +- can/interfaces/ixxat/canlib.py | 457 +++++++--- can/interfaces/ixxat/constants.py | 202 ++--- can/interfaces/ixxat/exceptions.py | 2 +- can/interfaces/ixxat/structures.py | 55 +- can/interfaces/kvaser/canlib.py | 501 ++++++----- can/interfaces/kvaser/constants.py | 8 +- can/interfaces/kvaser/structures.py | 9 +- can/interfaces/nican.py | 166 ++-- can/interfaces/pcan/basic.py | 791 ++++++++++-------- can/interfaces/pcan/pcan.py | 193 +++-- can/interfaces/serial/serial_can.py | 39 +- can/interfaces/slcan.py | 98 ++- can/interfaces/socketcan/constants.py | 84 +- can/interfaces/socketcan/socketcan.py | 142 ++-- can/interfaces/socketcan/utils.py | 19 +- can/interfaces/systec/constants.py | 50 +- can/interfaces/systec/exceptions.py | 15 +- can/interfaces/systec/structures.py | 260 ++++-- can/interfaces/systec/ucan.py | 188 ++++- can/interfaces/systec/ucanbus.py | 98 ++- can/interfaces/usb2can/serial_selector.py | 26 +- can/interfaces/usb2can/usb2canInterface.py | 39 +- .../usb2can/usb2canabstractionlayer.py | 80 +- can/interfaces/vector/canlib.py | 220 +++-- can/interfaces/vector/exceptions.py | 1 - can/interfaces/vector/vxlapi.py | 292 ++++--- can/interfaces/virtual.py | 10 +- can/io/asc.py | 67 +- can/io/blf.py | 208 +++-- can/io/canutils.py | 57 +- can/io/csv.py | 36 +- can/io/generic.py | 4 +- can/io/logger.py | 2 +- can/io/player.py | 6 +- can/io/printer.py | 6 +- can/io/sqlite.py | 62 +- can/logger.py | 89 +- can/message.py | 110 ++- can/notifier.py | 19 +- can/player.py | 112 ++- can/thread_safe_bus.py | 4 +- can/util.py | 70 +- can/viewer.py | 334 ++++---- doc/conf.py | 110 ++- examples/asyncio_demo.py | 15 +- examples/cyclic.py | 28 +- examples/receive_all.py | 6 +- examples/send_one.py | 10 +- examples/serial_com.py | 15 +- examples/vcan_filtered.py | 10 +- examples/virtual_can_demo.py | 6 +- setup.py | 45 +- test/back2back_test.py | 148 ++-- test/config.py | 24 +- test/contextmanager_test.py | 15 +- test/data/example_data.py | 247 +++--- test/listener_test.py | 55 +- test/logformats_test.py | 185 ++-- test/message_helper.py | 23 +- test/network_test.py | 22 +- test/notifier_test.py | 12 +- test/serial_test.py | 17 +- test/simplecyclic_test.py | 54 +- test/test_detect_available_configs.py | 33 +- test/test_kvaser.py | 119 ++- test/test_load_file_config.py | 62 +- test/test_message_class.py | 40 +- test/test_message_filtering.py | 17 +- test/test_message_sync.py | 25 +- test/test_scripts.py | 23 +- test/test_slcan.py | 53 +- test/test_socketcan_helpers.py | 8 +- test/test_systec.py | 136 +-- test/test_viewer.py | 197 +++-- test/zero_dlc_test.py | 13 +- 84 files changed, 4408 insertions(+), 2968 deletions(-) diff --git a/can/__init__.py b/can/__init__.py index 4ad8bdee7..e23f5a9b8 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -8,7 +8,7 @@ __version__ = "3.2.0" -log = logging.getLogger('can') +log = logging.getLogger("can") rc = dict() @@ -38,9 +38,10 @@ class CanError(IOError): from . import interface from .interface import Bus, detect_available_configs -from .broadcastmanager import \ - CyclicSendTaskABC, \ - LimitedDurationCyclicSendTaskABC, \ - ModifiableCyclicTaskABC, \ - MultiRateCyclicSendTaskABC, \ - RestartableCyclicTaskABC +from .broadcastmanager import ( + CyclicSendTaskABC, + LimitedDurationCyclicSendTaskABC, + ModifiableCyclicTaskABC, + MultiRateCyclicSendTaskABC, + RestartableCyclicTaskABC, +) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 9cf4cb36c..ae5d126fd 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -12,7 +12,7 @@ import threading import time -log = logging.getLogger('can.bcm') +log = logging.getLogger("can.bcm") class CyclicTask: @@ -47,7 +47,6 @@ def __init__(self, message, period): class LimitedDurationCyclicSendTaskABC(CyclicSendTaskABC): - def __init__(self, message, period, duration): """Message send task with a defined duration and period. @@ -101,9 +100,9 @@ def __init__(self, channel, message, count, initial_period, subsequent_period): super().__init__(channel, message, subsequent_period) -class ThreadBasedCyclicSendTask(ModifiableCyclicTaskABC, - LimitedDurationCyclicSendTaskABC, - RestartableCyclicTaskABC): +class ThreadBasedCyclicSendTask( + ModifiableCyclicTaskABC, LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC +): """Fallback cyclic send task using thread.""" def __init__(self, bus, lock, message, period, duration=None): diff --git a/can/bus.py b/can/bus.py index 7ba4ae872..7fcf910b0 100644 --- a/can/bus.py +++ b/can/bus.py @@ -31,7 +31,7 @@ class BusABC(metaclass=ABCMeta): """ #: a string describing the underlying bus and/or channel - channel_info = 'unknown' + channel_info = "unknown" #: Log level for received messages RECV_LOGGING_LEVEL = 9 @@ -81,7 +81,7 @@ def recv(self, timeout=None): # return it, if it matches if msg and (already_filtered or self._matches_filters(msg)): - LOG.log(self.RECV_LOGGING_LEVEL, 'Received: %s', msg) + LOG.log(self.RECV_LOGGING_LEVEL, "Received: %s", msg) return msg # if not, and timeout is None, try indefinitely @@ -213,6 +213,7 @@ def wrapped_stop_method(remove_task=True): except ValueError: pass original_stop_method() + task.stop = wrapped_stop_method if store_task: @@ -239,8 +240,12 @@ def _send_periodic_internal(self, msg, period, duration=None): """ if not hasattr(self, "_lock_send_periodic"): # Create a send lock for this bus, but not for buses which override this method - self._lock_send_periodic = threading.Lock() # pylint: disable=attribute-defined-outside-init - task = ThreadBasedCyclicSendTask(self, self._lock_send_periodic, msg, period, duration) + self._lock_send_periodic = ( + threading.Lock() + ) # pylint: disable=attribute-defined-outside-init + task = ThreadBasedCyclicSendTask( + self, self._lock_send_periodic, msg, period, duration + ) return task def stop_all_periodic_tasks(self, remove_tasks=True): @@ -332,13 +337,12 @@ def _matches_filters(self, msg): for _filter in self._filters: # check if this filter even applies to the message - if 'extended' in _filter and \ - _filter['extended'] != msg.is_extended_id: + if "extended" in _filter and _filter["extended"] != msg.is_extended_id: continue # then check for the mask and id - can_id = _filter['can_id'] - can_mask = _filter['can_mask'] + can_id = _filter["can_id"] + can_mask = _filter["can_mask"] # basically, we compute # `msg.arbitration_id & can_mask == can_id & can_mask` diff --git a/can/ctypesutil.py b/can/ctypesutil.py index eaac336e3..8dca8dc14 100644 --- a/can/ctypesutil.py +++ b/can/ctypesutil.py @@ -8,9 +8,9 @@ import logging import sys -log = logging.getLogger('can.ctypesutil') +log = logging.getLogger("can.ctypesutil") -__all__ = ['CLibrary', 'HANDLE', 'PHANDLE', 'HRESULT'] +__all__ = ["CLibrary", "HANDLE", "PHANDLE", "HRESULT"] try: _LibBase = ctypes.WinDLL @@ -40,10 +40,16 @@ def map_symbol(self, func_name, restype=None, argtypes=(), errcheck=None): try: symbol = prototype((func_name, self)) except AttributeError: - raise ImportError("Could not map function '{}' from library {}".format(func_name, self._name)) + raise ImportError( + "Could not map function '{}' from library {}".format( + func_name, self._name + ) + ) setattr(symbol, "_name", func_name) - log.debug(f'Wrapped function "{func_name}", result type: {type(restype)}, error_check {errcheck}') + log.debug( + f'Wrapped function "{func_name}", result type: {type(restype)}, error_check {errcheck}' + ) if errcheck: symbol.errcheck = errcheck @@ -95,4 +101,5 @@ class HRESULT(ctypes.c_long): class HANDLE(ctypes.c_void_p): pass + PHANDLE = ctypes.POINTER(HANDLE) diff --git a/can/interface.py b/can/interface.py index 5fd342181..9e32d9ca3 100644 --- a/can/interface.py +++ b/can/interface.py @@ -13,8 +13,8 @@ from .util import load_config from .interfaces import BACKENDS -log = logging.getLogger('can.interface') -log_autodetect = log.getChild('detect_available_configs') +log = logging.getLogger("can.interface") +log_autodetect = log.getChild("detect_available_configs") def _get_class_for_interface(interface): @@ -38,7 +38,9 @@ def _get_class_for_interface(interface): module = importlib.import_module(module_name) except Exception as e: raise ImportError( - "Cannot import module {} for CAN interface '{}': {}".format(module_name, interface, e) + "Cannot import module {} for CAN interface '{}': {}".format( + module_name, interface, e + ) ) # Get the correct class @@ -46,14 +48,15 @@ def _get_class_for_interface(interface): bus_class = 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) + "Cannot import class {} from module {} for CAN interface '{}': {}".format( + class_name, module_name, interface, e + ) ) return bus_class -class Bus(BusABC): # pylint disable=abstract-method +class Bus(BusABC): # pylint disable=abstract-method """Bus wrapper with configuration loading. Instantiates a CAN Bus of the given ``interface``, falls back to reading a @@ -85,26 +88,26 @@ def __new__(cls, channel=None, *args, **kwargs): # figure out the rest of the configuration; this might raise an error if channel is not None: - kwargs['channel'] = channel - if 'context' in kwargs: - context = kwargs['context'] - del kwargs['context'] + kwargs["channel"] = channel + if "context" in kwargs: + context = kwargs["context"] + del kwargs["context"] else: context = None kwargs = load_config(config=kwargs, context=context) # resolve the bus class to use for that interface - cls = _get_class_for_interface(kwargs['interface']) + cls = _get_class_for_interface(kwargs["interface"]) # remove the 'interface' key so it doesn't get passed to the backend - del kwargs['interface'] + del kwargs["interface"] # make sure the bus can handle this config format - if 'channel' not in kwargs: + if "channel" not in kwargs: raise ValueError("'channel' argument missing") else: - channel = kwargs['channel'] - del kwargs['channel'] + channel = kwargs["channel"] + del kwargs["channel"] if channel is None: # Use the default channel for the backend @@ -137,7 +140,7 @@ def detect_available_configs(interfaces=None): if interfaces is None: interfaces = BACKENDS elif isinstance(interfaces, str): - interfaces = (interfaces, ) + interfaces = (interfaces,) # else it is supposed to be an iterable of strings result = [] @@ -146,21 +149,33 @@ def detect_available_configs(interfaces=None): try: bus_class = _get_class_for_interface(interface) except ImportError: - log_autodetect.debug('interface "%s" can not be loaded for detection of available configurations', interface) + log_autodetect.debug( + 'interface "%s" can not be loaded for detection of available configurations', + interface, + ) continue # get available channels try: - available = list(bus_class._detect_available_configs()) # pylint: disable=protected-access + available = list( + bus_class._detect_available_configs() + ) # pylint: disable=protected-access except NotImplementedError: - log_autodetect.debug('interface "%s" does not support detection of available configurations', interface) + log_autodetect.debug( + 'interface "%s" does not support detection of available configurations', + interface, + ) else: - log_autodetect.debug('interface "%s" detected %i available configurations', interface, len(available)) + log_autodetect.debug( + 'interface "%s" detected %i available configurations', + interface, + len(available), + ) # add the interface name to the configs if it is not already present for config in available: - if 'interface' not in config: - config['interface'] = interface + if "interface" not in config: + config["interface"] = interface # append to result result += available diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index c4c7f52f7..e64f6f488 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -10,25 +10,29 @@ # interface_name => (module, classname) BACKENDS = { - 'kvaser': ('can.interfaces.kvaser', 'KvaserBus'), - 'socketcan': ('can.interfaces.socketcan', 'SocketcanBus'), - '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.ics_neovi', 'NeoViBus'), - 'vector': ('can.interfaces.vector', 'VectorBus'), - 'slcan': ('can.interfaces.slcan', 'slcanBus'), - 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus'), - 'systec': ('can.interfaces.systec', 'UcanBus') + "kvaser": ("can.interfaces.kvaser", "KvaserBus"), + "socketcan": ("can.interfaces.socketcan", "SocketcanBus"), + "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.ics_neovi", "NeoViBus"), + "vector": ("can.interfaces.vector", "VectorBus"), + "slcan": ("can.interfaces.slcan", "slcanBus"), + "canalystii": ("can.interfaces.canalystii", "CANalystIIBus"), + "systec": ("can.interfaces.systec", "UcanBus"), } -BACKENDS.update({ - interface.name: (interface.module_name, interface.attrs[0]) - for interface in iter_entry_points('can.interface') -}) +BACKENDS.update( + { + interface.name: (interface.module_name, interface.attrs[0]) + for interface in iter_entry_points("can.interface") + } +) -VALID_INTERFACES = frozenset(list(BACKENDS.keys()) + ['socketcan_native', 'socketcan_ctypes']) +VALID_INTERFACES = frozenset( + list(BACKENDS.keys()) + ["socketcan_native", "socketcan_ctypes"] +) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 2626f607b..11f4acfbd 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -9,25 +9,29 @@ class VCI_INIT_CONFIG(Structure): - _fields_ = [("AccCode", c_int32), - ("AccMask", c_int32), - ("Reserved", c_int32), - ("Filter", c_ubyte), - ("Timing0", c_ubyte), - ("Timing1", c_ubyte), - ("Mode", c_ubyte)] + _fields_ = [ + ("AccCode", c_int32), + ("AccMask", c_int32), + ("Reserved", c_int32), + ("Filter", c_ubyte), + ("Timing0", c_ubyte), + ("Timing1", c_ubyte), + ("Mode", c_ubyte), + ] class VCI_CAN_OBJ(Structure): - _fields_ = [("ID", c_uint), - ("TimeStamp", c_int), - ("TimeFlag", c_byte), - ("SendType", c_byte), - ("RemoteFlag", c_byte), - ("ExternFlag", c_byte), - ("DataLen", c_byte), - ("Data", c_ubyte * 8), - ("Reserved", c_byte * 3)] + _fields_ = [ + ("ID", c_uint), + ("TimeStamp", c_int), + ("TimeFlag", c_byte), + ("SendType", c_byte), + ("RemoteFlag", c_byte), + ("ExternFlag", c_byte), + ("DataLen", c_byte), + ("Data", c_ubyte * 8), + ("Reserved", c_byte * 3), + ] VCI_USBCAN2 = 4 @@ -68,7 +72,9 @@ class VCI_CAN_OBJ(Structure): class CANalystIIBus(BusABC): - def __init__(self, channel, device=0, baud=None, Timing0=None, Timing1=None, can_filters=None): + def __init__( + self, channel, device=0, baud=None, Timing0=None, Timing1=None, can_filters=None + ): """ :param channel: channel number @@ -86,11 +92,13 @@ def __init__(self, channel, device=0, baud=None, Timing0=None, Timing1=None, can self.channels = [channel] else: # Assume comma separated string of channels - self.channels = [int(ch.strip()) for ch in channel.split(',')] + self.channels = [int(ch.strip()) for ch in channel.split(",")] self.device = device - self.channel_info = "CANalyst-II: device {}, channels {}".format(self.device, self.channels) + self.channel_info = "CANalyst-II: device {}, channels {}".format( + self.device, self.channels + ) if baud is not None: try: @@ -107,7 +115,12 @@ def __init__(self, channel, device=0, baud=None, Timing0=None, Timing1=None, can logger.error("VCI_OpenDevice Error") for channel in self.channels: - if CANalystII.VCI_InitCAN(VCI_USBCAN2, self.device, channel, byref(self.init_config)) == STATUS_ERR: + if ( + CANalystII.VCI_InitCAN( + VCI_USBCAN2, self.device, channel, byref(self.init_config) + ) + == STATUS_ERR + ): logger.error("VCI_InitCAN Error") self.shutdown() return @@ -125,17 +138,28 @@ def send(self, msg, timeout=None): :return: """ extern_flag = 1 if msg.is_extended_id else 0 - raw_message = VCI_CAN_OBJ(msg.arbitration_id, 0, 0, 1, msg.is_remote_frame, extern_flag, msg.dlc, (c_ubyte * 8)(*msg.data), (c_byte * 3)(*[0, 0, 0])) + raw_message = VCI_CAN_OBJ( + msg.arbitration_id, + 0, + 0, + 1, + msg.is_remote_frame, + extern_flag, + msg.dlc, + (c_ubyte * 8)(*msg.data), + (c_byte * 3)(*[0, 0, 0]), + ) if msg.channel is not None: channel = msg.channel elif len(self.channels) == 1: channel = self.channels[0] else: - raise ValueError( - "msg.channel must be set when using multiple channels.") + raise ValueError("msg.channel must be set when using multiple channels.") - CANalystII.VCI_Transmit(VCI_USBCAN2, self.device, channel, byref(raw_message), 1) + CANalystII.VCI_Transmit( + VCI_USBCAN2, self.device, channel, byref(raw_message), 1 + ) def _recv_internal(self, timeout=None): """ @@ -147,7 +171,17 @@ def _recv_internal(self, timeout=None): timeout = -1 if timeout is None else int(timeout * 1000) - if CANalystII.VCI_Receive(VCI_USBCAN2, self.device, self.channels[0], byref(raw_message), 1, timeout) <= STATUS_ERR: + if ( + CANalystII.VCI_Receive( + VCI_USBCAN2, + self.device, + self.channels[0], + byref(raw_message), + 1, + timeout, + ) + <= STATUS_ERR + ): return None, False else: return ( diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index e8404e6d9..3df0ccc39 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -22,7 +22,8 @@ except ImportError as ie: logger.warning( "You won't be able to use the ICS NeoVi can backend without the " - "python-ics module installed!: %s", ie + "python-ics module installed!: %s", + ie, ) ics = None @@ -42,8 +43,12 @@ class ICSApiError(CanError): ICS_SPY_ERR_INFORMATION = 0x40 def __init__( - self, error_number, description_short, description_long, - severity, restart_needed + self, + error_number, + description_short, + description_long, + severity, + restart_needed, ): super().__init__(description_short) self.error_number = error_number @@ -95,15 +100,15 @@ def __init__(self, channel, can_filters=None, **kwargs): Absolute path or relative path to the library including filename. """ if ics is None: - raise ImportError('Please install python-ics') + raise ImportError("Please install python-ics") super().__init__(channel=channel, can_filters=can_filters, **kwargs) logger.info("CAN Filters: {}".format(can_filters)) logger.info("Got configuration of: {}".format(kwargs)) - if 'override_library_name' in kwargs: - ics.override_library_name(kwargs.get('override_library_name')) + if "override_library_name" in kwargs: + ics.override_library_name(kwargs.get("override_library_name")) if isinstance(channel, (list, tuple)): self.channels = channel @@ -111,34 +116,31 @@ def __init__(self, channel, can_filters=None, **kwargs): self.channels = [channel] else: # Assume comma separated string of channels - self.channels = [ch.strip() for ch in channel.split(',')] + self.channels = [ch.strip() for ch in channel.split(",")] self.channels = [NeoViBus.channel_to_netid(ch) for ch in self.channels] - type_filter = kwargs.get('type_filter') - serial = kwargs.get('serial') + type_filter = kwargs.get("type_filter") + serial = kwargs.get("serial") self.dev = self._find_device(type_filter, serial) ics.open_device(self.dev) - if 'bitrate' in kwargs: + if "bitrate" in kwargs: for channel in self.channels: - ics.set_bit_rate(self.dev, kwargs.get('bitrate'), channel) + ics.set_bit_rate(self.dev, kwargs.get("bitrate"), channel) - fd = kwargs.get('fd', False) + fd = kwargs.get("fd", False) if fd: - if 'data_bitrate' in kwargs: + if "data_bitrate" in kwargs: for channel in self.channels: - ics.set_fd_bit_rate( - self.dev, kwargs.get('data_bitrate'), channel) + ics.set_fd_bit_rate(self.dev, kwargs.get("data_bitrate"), channel) - self._use_system_timestamp = bool( - kwargs.get('use_system_timestamp', False) - ) - self._receive_own_messages = kwargs.get('receive_own_messages', True) + self._use_system_timestamp = bool(kwargs.get("use_system_timestamp", False)) + self._receive_own_messages = kwargs.get("receive_own_messages", True) - self.channel_info = '%s %s CH:%s' % ( + self.channel_info = "%s %s CH:%s" % ( self.dev.Name, self.get_serial_number(self.dev), - self.channels + self.channels, ) logger.info("Using device: {}".format(self.channel_info)) @@ -154,8 +156,7 @@ def channel_to_netid(channel_name_or_id): channel = getattr(ics, netid) else: raise ValueError( - 'channel must be an integer or ' - 'a valid ICS channel name' + "channel must be an integer or " "a valid ICS channel name" ) return channel @@ -195,10 +196,10 @@ def _detect_available_configs(): return [] # TODO: add the channel(s) - return [{ - 'interface': 'neovi', - 'serial': NeoViBus.get_serial_number(device) - } for device in devices] + return [ + {"interface": "neovi", "serial": NeoViBus.get_serial_number(device)} + for device in devices + ] def _find_device(self, type_filter=None, serial=None): if type_filter is not None: @@ -211,14 +212,14 @@ def _find_device(self, type_filter=None, serial=None): dev = device break else: - msg = ['No device'] + msg = ["No device"] if type_filter is not None: - msg.append('with type {}'.format(type_filter)) + msg.append("with type {}".format(type_filter)) if serial is not None: - msg.append('with serial {}'.format(serial)) - msg.append('found.') - raise Exception(' '.join(msg)) + msg.append("with serial {}".format(serial)) + msg.append("found.") + raise Exception(" ".join(msg)) return dev def _process_msg_queue(self, timeout=0.1): @@ -263,18 +264,16 @@ def _ics_msg_to_message(self, ics_msg): if is_fd: if ics_msg.ExtraDataPtrEnabled: - data = ics_msg.ExtraDataPtr[:ics_msg.NumberBytesData] + data = ics_msg.ExtraDataPtr[: ics_msg.NumberBytesData] else: - data = ics_msg.Data[:ics_msg.NumberBytesData] + data = ics_msg.Data[: ics_msg.NumberBytesData] return Message( timestamp=self._get_timestamp_for_msg(ics_msg), arbitration_id=ics_msg.ArbIDOrHeader, data=data, dlc=ics_msg.NumberBytesData, - is_extended_id=bool( - ics_msg.StatusBitField & ics.SPY_STATUS_XTD_FRAME - ), + is_extended_id=bool(ics_msg.StatusBitField & ics.SPY_STATUS_XTD_FRAME), is_fd=is_fd, is_remote_frame=bool( ics_msg.StatusBitField & ics.SPY_STATUS_REMOTE_FRAME @@ -285,22 +284,20 @@ def _ics_msg_to_message(self, ics_msg): bitrate_switch=bool( ics_msg.StatusBitField3 & ics.SPY_STATUS3_CANFD_BRS ), - channel=ics_msg.NetworkID + channel=ics_msg.NetworkID, ) else: return Message( timestamp=self._get_timestamp_for_msg(ics_msg), arbitration_id=ics_msg.ArbIDOrHeader, - data=ics_msg.Data[:ics_msg.NumberBytesData], + data=ics_msg.Data[: ics_msg.NumberBytesData], dlc=ics_msg.NumberBytesData, - is_extended_id=bool( - ics_msg.StatusBitField & ics.SPY_STATUS_XTD_FRAME - ), + is_extended_id=bool(ics_msg.StatusBitField & ics.SPY_STATUS_XTD_FRAME), is_fd=is_fd, is_remote_frame=bool( ics_msg.StatusBitField & ics.SPY_STATUS_REMOTE_FRAME ), - channel=ics_msg.NetworkID + channel=ics_msg.NetworkID, ) def _recv_internal(self, timeout=0.1): @@ -346,8 +343,7 @@ def send(self, msg, timeout=None): elif len(self.channels) == 1: message.NetworkID = self.channels[0] else: - raise ValueError( - "msg.channel must be set when using multiple channels.") + raise ValueError("msg.channel must be set when using multiple channels.") try: ics.transmit_messages(self.dev, message) diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index bb02d261e..e0774dded 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -61,7 +61,7 @@ class IscanBus(BusABC): 250000: 6, 500000: 7, 800000: 8, - 1000000: 9 + 1000000: 9, } def __init__(self, channel, bitrate=500000, poll_interval=0.01, **kwargs): @@ -86,8 +86,9 @@ def __init__(self, channel, bitrate=500000, poll_interval=0.01, **kwargs): self.poll_interval = poll_interval iscan.isCAN_DeviceInitEx(self.channel, self.BAUDRATES[bitrate]) - super().__init__(channel=channel, bitrate=bitrate, - poll_interval=poll_interval, **kwargs) + super().__init__( + channel=channel, bitrate=bitrate, poll_interval=poll_interval, **kwargs + ) def _recv_internal(self, timeout): raw_msg = MessageExStruct() @@ -108,21 +109,25 @@ def _recv_internal(self, timeout): # A message was received break - msg = Message(arbitration_id=raw_msg.message_id, - is_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) + msg = Message( + arbitration_id=raw_msg.message_id, + is_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, + ) return msg, False 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)) + 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): @@ -155,7 +160,7 @@ class IscanError(CanError): 31: "Transmission not acknowledged on bus", 32: "Error critical bus", 35: "Callbackthread is blocked, stopping thread failed", - 40: "Need a licence number under NT4" + 40: "Need a licence number under NT4", } def __init__(self, function, error_code, arguments): @@ -168,6 +173,7 @@ def __init__(self, function, error_code, arguments): self.arguments = arguments def __str__(self): - description = self.ERROR_CODES.get(self.error_code, - "Error code %d" % self.error_code) + description = self.ERROR_CODES.get( + self.error_code, "Error code %d" % self.error_code + ) return "Function %s failed: %s" % (self.function.__name__, description) diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index cbe241be9..0dae6a513 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -19,16 +19,24 @@ import sys from can import BusABC, Message -from can.broadcastmanager import (LimitedDurationCyclicSendTaskABC, - RestartableCyclicTaskABC) +from can.broadcastmanager import ( + LimitedDurationCyclicSendTaskABC, + RestartableCyclicTaskABC, +) from can.ctypesutil import CLibrary, HANDLE, PHANDLE, HRESULT as ctypes_HRESULT from . import constants, structures from .exceptions import * -__all__ = ["VCITimeout", "VCIError", "VCIDeviceNotFoundError", "IXXATBus", "vciFormatError"] +__all__ = [ + "VCITimeout", + "VCIError", + "VCIDeviceNotFoundError", + "IXXATBus", + "vciFormatError", +] -log = logging.getLogger('can.ixxat') +log = logging.getLogger("can.ixxat") from time import perf_counter as _timer_function @@ -66,10 +74,9 @@ def __vciFormatErrorExtended(library_instance, function, HRESULT, arguments): :return: Formatted string """ - #TODO: make sure we don't generate another exception + # TODO: make sure we don't generate another exception return "{} - arguments were {}".format( - __vciFormatError(library_instance, function, HRESULT), - arguments + __vciFormatError(library_instance, function, HRESULT), arguments ) @@ -87,7 +94,9 @@ def __vciFormatError(library_instance, function, HRESULT): buf = ctypes.create_string_buffer(constants.VCI_MAX_ERRSTRLEN) ctypes.memset(buf, 0, constants.VCI_MAX_ERRSTRLEN) library_instance.vciFormatError(HRESULT, buf, constants.VCI_MAX_ERRSTRLEN) - return "function {} failed ({})".format(function._name, buf.value.decode('utf-8', 'replace')) + return "function {} failed ({})".format( + function._name, buf.value.decode("utf-8", "replace") + ) def __check_status(result, function, arguments): @@ -118,19 +127,22 @@ def __check_status(result, function, arguments): elif result == constants.VCI_E_NO_MORE_ITEMS: raise StopIteration() elif result == constants.VCI_E_ACCESSDENIED: - pass # not a real error, might happen if another program has initialized the bus + pass # not a real error, might happen if another program has initialized the bus elif result != constants.VCI_OK: raise VCIError(vciFormatError(function, result)) return result + try: # Map all required symbols and initialize library --------------------------- - #HRESULT VCIAPI vciInitialize ( void ); + # 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)) + # 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) @@ -139,70 +151,188 @@ def __check_status(result, function, arguments): # 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) + _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) + _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) + _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) + _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) + _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("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 ); + # 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) + # 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 @@ -213,20 +343,20 @@ def __check_status(result, function, arguments): CAN_INFO_MESSAGES = { - constants.CAN_INFO_START: "CAN started", - constants.CAN_INFO_STOP: "CAN stopped", - constants.CAN_INFO_RESET: "CAN reset", + 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", + 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): @@ -251,7 +381,7 @@ class IXXATBus(BusABC): 250000: constants.CAN_BT0_250KB, 500000: constants.CAN_BT0_500KB, 800000: constants.CAN_BT0_800KB, - 1000000: constants.CAN_BT0_1000KB + 1000000: constants.CAN_BT0_1000KB, }, 1: { 10000: constants.CAN_BT1_10KB, @@ -262,8 +392,8 @@ class IXXATBus(BusABC): 250000: constants.CAN_BT1_250KB, 500000: constants.CAN_BT1_500KB, 800000: constants.CAN_BT1_800KB, - 1000000: constants.CAN_BT1_1000KB - } + 1000000: constants.CAN_BT1_1000KB, + }, } def __init__(self, channel, can_filters=None, **kwargs): @@ -284,15 +414,17 @@ def __init__(self, channel, can_filters=None, **kwargs): 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.") + 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", kwargs) # Configuration options - bitrate = kwargs.get('bitrate', 500000) - UniqueHardwareId = kwargs.get('UniqueHardwareId', None) - rxFifoSize = kwargs.get('rxFifoSize', 16) - txFifoSize = kwargs.get('txFifoSize', 16) - self._receive_own_messages = kwargs.get('receive_own_messages', False) + bitrate = kwargs.get("bitrate", 500000) + UniqueHardwareId = kwargs.get("UniqueHardwareId", None) + rxFifoSize = kwargs.get("rxFifoSize", 16) + txFifoSize = kwargs.get("txFifoSize", 16) + self._receive_own_messages = kwargs.get("receive_own_messages", False) # Usually comes as a string from the config file channel = int(channel) @@ -315,62 +447,99 @@ def __init__(self, channel, can_filters=None, **kwargs): _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle)) while True: try: - _canlib.vciEnumDeviceNext(self._device_handle, ctypes.byref(self._device_info)) + _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).") + 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)) + 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')): + if (UniqueHardwareId is None) or ( + self._device_info.UniqueHardwareId.AsChar + == bytes(UniqueHardwareId, "ascii") + ): break else: - log.debug("Ignoring IXXAT with hardware id '%s'.", self._device_info.UniqueHardwareId.AsChar.decode("ascii")) + log.debug( + "Ignoring IXXAT with hardware id '%s'.", + self._device_info.UniqueHardwareId.AsChar.decode("ascii"), + ) _canlib.vciEnumDeviceClose(self._device_handle) - _canlib.vciDeviceOpen(ctypes.byref(self._device_info.VciObjectId), ctypes.byref(self._device_handle)) + _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)) + 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.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, + constants.CAN_OPMODE_STANDARD + | constants.CAN_OPMODE_EXTENDED + | constants.CAN_OPMODE_ERRFRAME, self.CHANNEL_BITRATES[0][bitrate], - self.CHANNEL_BITRATES[1][bitrate] + self.CHANNEL_BITRATES[1][bitrate], + ) + _canlib.canControlGetCaps( + self._control_handle, ctypes.byref(self._channel_capabilities) ) - _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) + self._tick_resolution = float( + self._channel_capabilities.dwClockFreq + / self._channel_capabilities.dwTscDivisor + ) # Setup filters before starting the channel if can_filters: log.info("The IXXAT VCI backend is filtering messages") # Disable every message coming in for extended in (0, 1): - _canlib.canControlSetAccFilter(self._control_handle, - extended, - constants.CAN_ACC_CODE_NONE, - constants.CAN_ACC_MASK_NONE) + _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) + 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 @@ -385,7 +554,9 @@ def __init__(self, channel, can_filters=None, **kwargs): # Clear the FIFO by filter them out with low timeout for _ in range(rxFifoSize): try: - _canlib.canChannelReadMessage(self._channel_handle, 0, ctypes.byref(self._message)) + _canlib.canChannelReadMessage( + self._channel_handle, 0, ctypes.byref(self._message) + ) except (VCITimeout, VCIRxQueueEmptyError): break @@ -413,7 +584,9 @@ def _recv_internal(self, timeout): if timeout == 0: # Peek without waiting try: - _canlib.canChannelPeekMessage(self._channel_handle, ctypes.byref(self._message)) + _canlib.canChannelPeekMessage( + self._channel_handle, ctypes.byref(self._message) + ) except (VCITimeout, VCIRxQueueEmptyError): return None, True else: @@ -431,7 +604,9 @@ def _recv_internal(self, timeout): while True: try: - _canlib.canChannelReadMessage(self._channel_handle, remaining_ms, ctypes.byref(self._message)) + _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 @@ -442,12 +617,31 @@ def _recv_internal(self, timeout): 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: + 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.warning("Unexpected message info type") @@ -464,13 +658,14 @@ def _recv_internal(self, timeout): # The _message.dwTime is a 32bit tick value and will overrun, # so expect to see the value restarting from 0 rx_msg = Message( - timestamp=self._message.dwTime / self._tick_resolution, # Relative time in s + timestamp=self._message.dwTime + / self._tick_resolution, # Relative time in s is_remote_frame=bool(self._message.uMsgInfo.Bits.rtr), is_extended_id=bool(self._message.uMsgInfo.Bits.ext), arbitration_id=self._message.dwMsgId, dlc=self._message.uMsgInfo.Bits.dlc, - data=self._message.abData[:self._message.uMsgInfo.Bits.dlc], - channel=self.channel + data=self._message.abData[: self._message.uMsgInfo.Bits.dlc], + channel=self.channel, ) return rx_msg, True @@ -491,7 +686,8 @@ def send(self, msg, timeout=None): if timeout: _canlib.canChannelSendMessage( - self._channel_handle, int(timeout * 1000), message) + self._channel_handle, int(timeout * 1000), message + ) else: _canlib.canChannelPostMessage(self._channel_handle, message) @@ -499,14 +695,14 @@ def _send_periodic_internal(self, msg, period, duration=None): """Send a message using built-in cyclic transmit list functionality.""" if self._scheduler is None: self._scheduler = HANDLE() - _canlib.canSchedulerOpen(self._device_handle, self.channel, - self._scheduler) + _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) + return CyclicSendTask( + self._scheduler, msg, period, duration, self._scheduler_resolution + ) def shutdown(self): if self._scheduler is not None: @@ -517,8 +713,7 @@ def shutdown(self): _canlib.vciDeviceClose(self._device_handle) -class CyclicSendTask(LimitedDurationCyclicSendTaskABC, - RestartableCyclicTaskABC): +class CyclicSendTask(LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC): """A message in the cyclic transmit list.""" def __init__(self, scheduler, msg, period, duration, resolution): @@ -542,12 +737,8 @@ 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) + _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).""" diff --git a/can/interfaces/ixxat/constants.py b/can/interfaces/ixxat/constants.py index d466e096d..46daccf8d 100644 --- a/can/interfaces/ixxat/constants.py +++ b/can/interfaces/ixxat/constants.py @@ -6,143 +6,143 @@ Copyright (C) 2016 Giuseppe Corbelli """ -FALSE = 0 -TRUE = 1 +FALSE = 0 +TRUE = 1 -INFINITE = 0xFFFFFFFF +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 +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 +SEV_INFO = 0x40000000 +SEV_WARN = 0x80000000 +SEV_ERROR = 0xC0000000 +SEV_MASK = 0xC0000000 +SEV_SUCCESS = 0x00000000 -RESERVED_FLAG = 0x10000000 -CUSTOMER_FLAG = 0x20000000 +RESERVED_FLAG = 0x10000000 +CUSTOMER_FLAG = 0x20000000 -STATUS_MASK = 0x0000FFFF -FACILITY_MASK = 0x0FFF0000 +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 +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_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_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_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 +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_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_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_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 +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 +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_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 +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 +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 +CAN_ACC_MASK_NONE = 0xFFFFFFFF +CAN_ACC_CODE_NONE = 0x80000000 diff --git a/can/interfaces/ixxat/exceptions.py b/can/interfaces/ixxat/exceptions.py index efce76297..d89a98334 100644 --- a/can/interfaces/ixxat/exceptions.py +++ b/can/interfaces/ixxat/exceptions.py @@ -8,7 +8,7 @@ from can import CanError -__all__ = ['VCITimeout', 'VCIError', 'VCIRxQueueEmptyError', 'VCIDeviceNotFoundError'] +__all__ = ["VCITimeout", "VCIError", "VCIRxQueueEmptyError", "VCIDeviceNotFoundError"] class VCITimeout(CanError): diff --git a/can/interfaces/ixxat/structures.py b/can/interfaces/ixxat/structures.py index 65b177d94..881d47a13 100644 --- a/can/interfaces/ixxat/structures.py +++ b/can/interfaces/ixxat/structures.py @@ -8,19 +8,18 @@ import ctypes + class LUID(ctypes.Structure): - _fields_ = [ - ("LowPart", ctypes.c_uint32), - ("HighPart", ctypes.c_int32), - ] + _fields_ = [("LowPart", ctypes.c_uint32), ("HighPart", ctypes.c_int32)] + + PLUID = ctypes.POINTER(LUID) class VCIID(ctypes.Union): - _fields_ = [ - ("AsLuid", LUID), - ("AsInt64", ctypes.c_int64), - ] + _fields_ = [("AsLuid", LUID), ("AsInt64", ctypes.c_int64)] + + PVCIID = ctypes.POINTER(VCIID) @@ -35,10 +34,8 @@ class GUID(ctypes.Structure): class VCIDEVICEINFO(ctypes.Structure): class UniqueHardwareId(ctypes.Union): - _fields_ = [ - ("AsChar", ctypes.c_char * 16), - ("AsGuid", GUID), - ] + _fields_ = [("AsChar", ctypes.c_char * 16), ("AsGuid", GUID)] + _fields_ = [ ("VciObjectId", VCIID), ("DeviceClass", GUID), @@ -66,8 +63,10 @@ def __str__(self): self.DriverReleaseVersion, self.DriverMajorVersion, self.DriverMinorVersion, - self.DriverBuildVersion + self.DriverBuildVersion, ) + + PVCIDEVICEINFO = ctypes.POINTER(VCIDEVICEINFO) @@ -77,8 +76,10 @@ class CANLINESTATUS(ctypes.Structure): ("bBtReg0", ctypes.c_uint8), ("bBtReg1", ctypes.c_uint8), ("bBusLoad", ctypes.c_uint8), - ("dwStatus", ctypes.c_uint32) + ("dwStatus", ctypes.c_uint32), ] + + PCANLINESTATUS = ctypes.POINTER(CANLINESTATUS) @@ -88,8 +89,10 @@ class CANCHANSTATUS(ctypes.Structure): ("fActivated", ctypes.c_uint32), ("fRxOverrun", ctypes.c_uint32), ("bRxFifoLoad", ctypes.c_uint8), - ("bTxFifoLoad", ctypes.c_uint8) + ("bTxFifoLoad", ctypes.c_uint8), ] + + PCANCHANSTATUS = ctypes.POINTER(CANCHANSTATUS) @@ -103,8 +106,10 @@ class CANCAPABILITIES(ctypes.Structure): ("dwCmsDivisor", ctypes.c_uint32), ("dwCmsMaxTicks", ctypes.c_uint32), ("dwDtxDivisor", ctypes.c_uint32), - ("dwDtxMaxTicks", ctypes.c_uint32) + ("dwDtxMaxTicks", ctypes.c_uint32), ] + + PCANCAPABILITIES = ctypes.POINTER(CANCAPABILITIES) @@ -128,13 +133,12 @@ class Bits(ctypes.Structure): ("srr", ctypes.c_uint32, 1), ("rtr", ctypes.c_uint32, 1), ("ext", ctypes.c_uint32, 1), - ("afc", ctypes.c_uint32, 8) + ("afc", ctypes.c_uint32, 8), ] - _fields_ = [ - ("Bytes", Bytes), - ("Bits", Bits) - ] + _fields_ = [("Bytes", Bytes), ("Bits", Bits)] + + PCANMSGINFO = ctypes.POINTER(CANMSGINFO) @@ -143,10 +147,13 @@ class CANMSG(ctypes.Structure): ("dwTime", ctypes.c_uint32), ("dwMsgId", ctypes.c_uint32), ("uMsgInfo", CANMSGINFO), - ("abData", ctypes.c_uint8 * 8) + ("abData", ctypes.c_uint8 * 8), ] + + PCANMSG = ctypes.POINTER(CANMSG) + class CANCYCLICTXMSG(ctypes.Structure): _fields_ = [ ("wCycleTime", ctypes.c_uint16), @@ -154,6 +161,8 @@ class CANCYCLICTXMSG(ctypes.Structure): ("bByteIndex", ctypes.c_uint8), ("dwMsgId", ctypes.c_uint32), ("uMsgInfo", CANMSGINFO), - ("abData", ctypes.c_uint8 * 8) + ("abData", ctypes.c_uint8 * 8), ] + + PCANCYCLICTXMSG = ctypes.POINTER(CANCYCLICTXMSG) diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index c89d0e047..f16004c3b 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -18,7 +18,7 @@ from . import constants as canstat from . import structures -log = logging.getLogger('can.kvaser') +log = logging.getLogger("can.kvaser") # Resolution in us TIMESTAMP_RESOLUTION = 10 @@ -38,22 +38,22 @@ def _unimplemented_function(*args): - raise NotImplementedError('This function is not implemented in canlib') + raise NotImplementedError("This function is not implemented in canlib") def __get_canlib_function(func_name, argtypes=None, restype=None, errcheck=None): argtypes = [] if argtypes is None else argtypes - #log.debug('Wrapping function "%s"' % func_name) + # 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) + # 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) + # log.debug('Result type is: %s' % type(restype)) + # log.debug('Error check function is: %s' % errcheck) retval.argtypes = argtypes retval.restype = restype if errcheck: @@ -74,8 +74,10 @@ def __init__(self, function, error_code, arguments): self.arguments = arguments def __str__(self): - return "Function %s failed - %s" % (self.function.__name__, - self.__get_error_message()) + return "Function %s failed - %s" % ( + self.function.__name__, + self.__get_error_message(), + ) def __get_error_message(self): errmsg = ctypes.create_string_buffer(128) @@ -84,7 +86,7 @@ def __get_error_message(self): def __convert_can_status_to_int(result): - #log.debug("converting can status to int {} ({})".format(result, type(result))) + # log.debug("converting can status to int {} ({})".format(result, type(result))) if isinstance(result, int): return result else: @@ -94,7 +96,7 @@ def __convert_can_status_to_int(result): 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') + # log.debug('Detected error while checking CAN status') raise CANLIBError(function, result, arguments) return result @@ -102,7 +104,7 @@ def __check_status(result, function, arguments): 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') + # log.debug('Detected error in which checking status read') raise CANLIBError(function, result, arguments) return result @@ -110,6 +112,7 @@ def __check_status_read(result, function, arguments): class c_canHandle(ctypes.c_int): pass + canINVALID_HANDLE = -1 @@ -124,141 +127,187 @@ def __check_bus_handle_validity(handle, function, 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) + 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) - - canSetBusParamsFd = __get_canlib_function("canSetBusParamsFd", - argtypes=[c_canHandle, ctypes.c_long, - 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) + 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, + ) + + canSetBusParamsFd = __get_canlib_function( + "canSetBusParamsFd", + argtypes=[ + c_canHandle, + ctypes.c_long, + 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) - - canRequestBusStatistics = __get_canlib_function("canRequestBusStatistics", - argtypes=[c_canHandle], - restype=canstat.c_canStatus, - errcheck=__check_status) - - canGetBusStatistics = __get_canlib_function("canGetBusStatistics", - argtypes=[c_canHandle, - ctypes.POINTER(structures.BusStatistics), - ctypes.c_size_t], - restype=canstat.c_canStatus, - errcheck=__check_status) + 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, + ) + + canRequestBusStatistics = __get_canlib_function( + "canRequestBusStatistics", + argtypes=[c_canHandle], + restype=canstat.c_canStatus, + errcheck=__check_status, + ) + + canGetBusStatistics = __get_canlib_function( + "canGetBusStatistics", + argtypes=[ + c_canHandle, + ctypes.POINTER(structures.BusStatistics), + ctypes.c_size_t, + ], + restype=canstat.c_canStatus, + errcheck=__check_status, + ) def init_kvaser_library(): @@ -284,7 +333,7 @@ def init_kvaser_library(): 83000: canstat.canBITRATE_83K, 62000: canstat.canBITRATE_62K, 50000: canstat.canBITRATE_50K, - 10000: canstat.canBITRATE_10K + 10000: canstat.canBITRATE_10K, } BITRATE_FD = { @@ -292,7 +341,7 @@ def init_kvaser_library(): 1000000: canstat.canFD_BITRATE_1M_80P, 2000000: canstat.canFD_BITRATE_2M_80P, 4000000: canstat.canFD_BITRATE_4M_80P, - 8000000: canstat.canFD_BITRATE_8M_60P + 8000000: canstat.canFD_BITRATE_8M_60P, } @@ -353,34 +402,34 @@ def __init__(self, channel, can_filters=None, **kwargs): log.info("CAN Filters: {}".format(can_filters)) log.info("Got configuration of: {}".format(kwargs)) - bitrate = kwargs.get('bitrate', 500000) - tseg1 = kwargs.get('tseg1', 0) - tseg2 = kwargs.get('tseg2', 0) - sjw = kwargs.get('sjw', 0) - no_samp = kwargs.get('no_samp', 0) - driver_mode = kwargs.get('driver_mode', DRIVER_MODE_NORMAL) - single_handle = kwargs.get('single_handle', False) - receive_own_messages = kwargs.get('receive_own_messages', False) - accept_virtual = kwargs.get('accept_virtual', True) - fd = kwargs.get('fd', False) - data_bitrate = kwargs.get('data_bitrate', None) + bitrate = kwargs.get("bitrate", 500000) + tseg1 = kwargs.get("tseg1", 0) + tseg2 = kwargs.get("tseg2", 0) + sjw = kwargs.get("sjw", 0) + no_samp = kwargs.get("no_samp", 0) + driver_mode = kwargs.get("driver_mode", DRIVER_MODE_NORMAL) + single_handle = kwargs.get("single_handle", False) + receive_own_messages = kwargs.get("receive_own_messages", False) + accept_virtual = kwargs.get("accept_virtual", True) + fd = kwargs.get("fd", False) + data_bitrate = kwargs.get("data_bitrate", None) try: channel = int(channel) except ValueError: - raise ValueError('channel must be an integer') + raise ValueError("channel must be an integer") self.channel = channel - log.debug('Initialising bus instance') + log.debug("Initialising bus instance") self.single_handle = single_handle num_channels = ctypes.c_int(0) - #log.debug("Res: %d", canGetNumberOfChannels(ctypes.byref(num_channels))) + # log.debug("Res: %d", canGetNumberOfChannels(ctypes.byref(num_channels))) num_channels = int(num_channels.value) - log.info('Found %d available channels', num_channels) + 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) + log.info("%d: %s", idx, channel_info) if idx == channel: self.channel_info = channel_info @@ -390,15 +439,17 @@ def __init__(self, channel, can_filters=None, **kwargs): if fd: flags |= canstat.canOPEN_CAN_FD - log.debug('Creating read handle to bus channel: %s', channel) + log.debug("Creating read handle to bus channel: %s", channel) self._read_handle = canOpenChannel(channel, flags) - canIoCtl(self._read_handle, - canstat.canIOCTL_SET_TIMER_SCALE, - ctypes.byref(ctypes.c_long(TIMESTAMP_RESOLUTION)), - 4) + canIoCtl( + self._read_handle, + canstat.canIOCTL_SET_TIMER_SCALE, + ctypes.byref(ctypes.c_long(TIMESTAMP_RESOLUTION)), + 4, + ) if fd: - if 'tseg1' not in kwargs and bitrate in BITRATE_FD: + if "tseg1" not in kwargs and bitrate in BITRATE_FD: # Use predefined bitrate for arbitration bitrate = BITRATE_FD[bitrate] if data_bitrate in BITRATE_FD: @@ -409,7 +460,7 @@ def __init__(self, channel, can_filters=None, **kwargs): data_bitrate = bitrate canSetBusParamsFd(self._read_handle, data_bitrate, tseg1, tseg2, sjw) else: - if 'tseg1' not in kwargs and bitrate in BITRATE_OBJS: + if "tseg1" not in kwargs and bitrate in BITRATE_OBJS: bitrate = BITRATE_OBJS[bitrate] canSetBusParams(self._read_handle, bitrate, tseg1, tseg2, sjw, no_samp, 0) @@ -417,22 +468,28 @@ def __init__(self, channel, can_filters=None, **kwargs): 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) + 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) + log.debug("Creating separate handle for TX on channel: %s", channel) self._write_handle = canOpenChannel(channel, flags) canBusOn(self._read_handle) - can_driver_mode = canstat.canDRIVER_SILENT if driver_mode == DRIVER_MODE_SILENT else canstat.canDRIVER_NORMAL + 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') + log.debug("Going bus on TX handle") canBusOn(self._write_handle) timer = ctypes.c_uint(0) @@ -448,22 +505,22 @@ def __init__(self, channel, can_filters=None, **kwargs): def _apply_filters(self, filters): if filters and len(filters) == 1: - can_id = filters[0]['can_id'] - can_mask = filters[0]['can_mask'] - extended = 1 if filters[0].get('extended') else 0 + can_id = filters[0]["can_id"] + can_mask = filters[0]["can_mask"] + extended = 1 if 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: self._is_filtered = False - log.error('Filtering is not supported - %s', e) + log.error("Filtering is not supported - %s", e) else: self._is_filtered = True - log.info('canlib is filtering on ID 0x%X, mask 0x%X', can_id, can_mask) + log.info("canlib is filtering on ID 0x%X, mask 0x%X", can_id, can_mask) else: self._is_filtered = False - log.info('Hardware filtering has been disabled') + log.info("Hardware filtering has been disabled") try: for handle in (self._read_handle, self._write_handle): for extended in (0, 1): @@ -494,7 +551,7 @@ def _recv_internal(self, timeout=None): else: timeout = int(timeout * 1000) - #log.log(9, 'Reading for %d ms on handle: %s' % (timeout, self._read_handle)) + # log.log(9, 'Reading for %d ms on handle: %s' % (timeout, self._read_handle)) status = canReadWait( self._read_handle, ctypes.byref(arb_id), @@ -502,11 +559,11 @@ def _recv_internal(self, timeout=None): ctypes.byref(dlc), ctypes.byref(flags), ctypes.byref(timestamp), - timeout # This is an X ms blocking read + timeout, # This is an X ms blocking read ) if status == canstat.canOK: - #log.debug('read complete -> status OK') + # log.debug('read complete -> status OK') data_array = data.raw flags = flags.value is_extended = bool(flags & canstat.canMSG_EXT) @@ -516,25 +573,27 @@ def _recv_internal(self, timeout=None): bitrate_switch = bool(flags & canstat.canFDMSG_BRS) error_state_indicator = bool(flags & canstat.canFDMSG_ESI) msg_timestamp = timestamp.value * TIMESTAMP_FACTOR - rx_msg = Message(arbitration_id=arb_id.value, - data=data_array[:dlc.value], - dlc=dlc.value, - is_extended_id=is_extended, - is_error_frame=is_error_frame, - is_remote_frame=is_remote_frame, - is_fd=is_fd, - bitrate_switch=bitrate_switch, - error_state_indicator=error_state_indicator, - channel=self.channel, - timestamp=msg_timestamp + self._timestamp_offset) - #log.debug('Got message: %s' % rx_msg) + rx_msg = Message( + arbitration_id=arb_id.value, + data=data_array[: dlc.value], + dlc=dlc.value, + is_extended_id=is_extended, + is_error_frame=is_error_frame, + is_remote_frame=is_remote_frame, + is_fd=is_fd, + bitrate_switch=bitrate_switch, + error_state_indicator=error_state_indicator, + channel=self.channel, + timestamp=msg_timestamp + self._timestamp_offset, + ) + # log.debug('Got message: %s' % rx_msg) return rx_msg, self._is_filtered else: - #log.debug('read complete -> status not okay') + # log.debug('read complete -> status not okay') return None, self._is_filtered def send(self, msg, timeout=None): - #log.debug("Writing a message: {}".format(msg)) + # log.debug("Writing a message: {}".format(msg)) flags = canstat.canMSG_EXT if msg.is_extended_id else canstat.canMSG_STD if msg.is_remote_frame: flags |= canstat.canMSG_RTR @@ -546,11 +605,9 @@ def send(self, msg, timeout=None): flags |= canstat.canFDMSG_BRS ArrayConstructor = ctypes.c_byte * msg.dlc buf = ArrayConstructor(*msg.data) - canWrite(self._write_handle, - msg.arbitration_id, - ctypes.byref(buf), - msg.dlc, - flags) + canWrite( + self._write_handle, msg.arbitration_id, ctypes.byref(buf), msg.dlc, flags + ) if timeout: canWriteSync(self._write_handle, int(timeout * 1000)) @@ -567,7 +624,7 @@ def flash(self, flash=True): try: kvFlashLeds(self._read_handle, action, 30000) except (CANLIBError, NotImplementedError) as e: - log.error('Could not flash LEDs (%s)', e) + log.error("Could not flash LEDs (%s)", e) def shutdown(self): # Wait for transmit queue to be cleared @@ -597,9 +654,9 @@ def get_stats(self): """ canRequestBusStatistics(self._write_handle) stats = structures.BusStatistics() - canGetBusStatistics(self._write_handle, - ctypes.pointer(stats), - ctypes.sizeof(stats)) + canGetBusStatistics( + self._write_handle, ctypes.pointer(stats), ctypes.sizeof(stats) + ) return stats @staticmethod @@ -610,7 +667,7 @@ def _detect_available_configs(): except Exception: pass return [ - {'interface': 'kvaser', 'channel': channel} + {"interface": "kvaser", "channel": channel} for channel in range(num_channels.value) ] @@ -620,18 +677,30 @@ def get_channel_info(channel): 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) + 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() diff --git a/can/interfaces/kvaser/constants.py b/can/interfaces/kvaser/constants.py index 0188235be..bc31381c6 100644 --- a/can/interfaces/kvaser/constants.py +++ b/can/interfaces/kvaser/constants.py @@ -14,6 +14,7 @@ class c_canStatus(ctypes.c_int): pass + # TODO better formatting canOK = 0 canERR_PARAM = -1 @@ -54,7 +55,8 @@ class c_canStatus(ctypes.c_int): def CANSTATUS_SUCCESS(status): return status >= canOK -canMSG_MASK = 0x00ff + +canMSG_MASK = 0x00FF canMSG_RTR = 0x0001 canMSG_STD = 0x0002 canMSG_EXT = 0x0004 @@ -68,7 +70,7 @@ def CANSTATUS_SUCCESS(status): canFDMSG_BRS = 0x020000 canFDMSG_ESI = 0x040000 -canMSGERR_MASK = 0xff00 +canMSGERR_MASK = 0xFF00 canMSGERR_HW_OVERRUN = 0x0200 canMSGERR_SW_OVERRUN = 0x0400 canMSGERR_STUFF = 0x0800 @@ -153,7 +155,7 @@ def CANSTATUS_SUCCESS(status): canTRANSCEIVER_TYPE_LINX_J1708: "LINX_J1708", canTRANSCEIVER_TYPE_LINX_K: "LINX_K", canTRANSCEIVER_TYPE_LINX_SWC: "LINX_SWC", - canTRANSCEIVER_TYPE_LINX_LS: "LINX_LS" + canTRANSCEIVER_TYPE_LINX_LS: "LINX_LS", } canDRIVER_NORMAL = 4 diff --git a/can/interfaces/kvaser/structures.py b/can/interfaces/kvaser/structures.py index 21eefe86f..5b6d416d8 100644 --- a/can/interfaces/kvaser/structures.py +++ b/can/interfaces/kvaser/structures.py @@ -15,6 +15,7 @@ class BusStatistics(ctypes.Structure): .. seealso:: :meth:`KvaserBus.get_stats` """ + _fields_ = [ ("m_stdData", ctypes.c_ulong), ("m_stdRemote", ctypes.c_ulong), @@ -22,12 +23,14 @@ class BusStatistics(ctypes.Structure): ("m_extRemote", ctypes.c_ulong), ("m_errFrame", ctypes.c_ulong), ("m_busLoad", ctypes.c_ulong), - ("m_overruns", ctypes.c_ulong) + ("m_overruns", ctypes.c_ulong), ] def __str__(self): - return ("std_data: {}, std_remote: {}, ext_data: {}, ext_remote: {}, " - "err_frame: {}, bus_load: {:.1f}%, overruns: {}").format( + return ( + "std_data: {}, std_remote: {}, ext_data: {}, ext_remote: {}, " + "err_frame: {}, bus_load: {:.1f}%, overruns: {}" + ).format( self.std_data, self.std_remote, self.ext_data, diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index d05acf850..2d5abf0d0 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -23,35 +23,35 @@ logger = logging.getLogger(__name__) -NC_SUCCESS = 0 -NC_ERR_TIMEOUT = 1 -TIMEOUT_ERROR_CODE = -1074388991 +NC_SUCCESS = 0 +NC_ERR_TIMEOUT = 1 +TIMEOUT_ERROR_CODE = -1074388991 -NC_DURATION_INFINITE = 0xFFFFFFFF +NC_DURATION_INFINITE = 0xFFFFFFFF -NC_OP_START = 0x80000001 -NC_OP_STOP = 0x80000002 -NC_OP_RESET = 0x80000003 +NC_OP_START = 0x80000001 +NC_OP_STOP = 0x80000002 +NC_OP_RESET = 0x80000003 -NC_FRMTYPE_REMOTE = 1 -NC_FRMTYPE_COMM_ERR = 2 +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_ST_READ_AVAIL = 0x00000001 +NC_ST_WRITE_SUCCESS = 0x00000002 +NC_ST_ERROR = 0x00000010 +NC_ST_WARNING = 0x00000020 -NC_ATTR_BAUD_RATE = 0x80000007 +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_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 +NC_FL_CAN_ARBID_XTD = 0x20000000 CanData = ctypes.c_ubyte * 8 @@ -66,6 +66,7 @@ class RxMessageStruct(ctypes.Structure): ("data", CanData), ] + class TxMessageStruct(ctypes.Structure): _fields_ = [ ("arb_id", ctypes.c_ulong), @@ -98,7 +99,11 @@ def get_error_message(status_code): 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] + 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 @@ -108,14 +113,18 @@ def get_error_message(status_code): 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] + 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] + 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. @@ -129,7 +138,9 @@ class NicanBus(BusABC): """ - def __init__(self, channel, can_filters=None, bitrate=None, log_errors=True, **kwargs): + 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') @@ -150,42 +161,47 @@ def __init__(self, channel, can_filters=None, bitrate=None, log_errors=True, **k """ if nican is None: - raise ImportError("The NI-CAN driver could not be loaded. " - "Check that you are using 32-bit Python on Windows.") + raise ImportError( + "The NI-CAN driver could not be loaded. " + "Check that you are using 32-bit Python on Windows." + ) self.channel = channel 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) - ] + 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) - ]) + 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) - ]) + 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), - ]) + 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)) @@ -193,16 +209,23 @@ def __init__(self, channel, can_filters=None, bitrate=None, log_errors=True, **k 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)) + 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)) - super().__init__(channel=channel, can_filters=can_filters, bitrate=bitrate, - log_errors=log_errors, **kwargs) + super().__init__( + channel=channel, + can_filters=can_filters, + bitrate=bitrate, + log_errors=log_errors, + **kwargs + ) def _recv_internal(self, timeout): """ @@ -222,7 +245,8 @@ def _recv_internal(self, timeout): state = ctypes.c_ulong() try: nican.ncWaitForState( - self.handle, NC_ST_READ_AVAIL, timeout, ctypes.byref(state)) + self.handle, NC_ST_READ_AVAIL, timeout, ctypes.byref(state) + ) except NicanError as e: if e.error_code == TIMEOUT_ERROR_CODE: return None, True @@ -240,14 +264,16 @@ def _recv_internal(self, timeout): if not is_error_frame: arb_id &= 0x1FFFFFFF dlc = raw_msg.dlc - msg = Message(timestamp=timestamp, - channel=self.channel, - is_remote_frame=is_remote_frame, - is_error_frame=is_error_frame, - is_extended_id=is_extended, - arbitration_id=arb_id, - dlc=dlc, - data=raw_msg.data[:dlc]) + msg = Message( + timestamp=timestamp, + channel=self.channel, + is_remote_frame=is_remote_frame, + is_error_frame=is_error_frame, + is_extended_id=is_extended, + arbitration_id=arb_id, + dlc=dlc, + data=raw_msg.data[:dlc], + ) return msg, True def send(self, msg, timeout=None): @@ -264,20 +290,18 @@ def send(self, msg, timeout=None): arb_id = msg.arbitration_id if msg.is_extended_id: 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)) + 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)) # TODO: # 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( + # state = ctypes.c_ulong() + # nican.ncWaitForState( # self.handle, NC_ST_WRITE_SUCCESS, int(timeout * 1000), ctypes.byref(state)) def reset(self): @@ -307,4 +331,6 @@ def __init__(self, function, error_code, arguments): def __str__(self): return "Function %s failed:\n%s" % ( - self.function.__name__, get_error_message(self.error_code)) + self.function.__name__, + get_error_message(self.error_code), + ) diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index f27db0de1..7557f036c 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -17,244 +17,360 @@ import platform import logging -logger = logging.getLogger('can.pcan') +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 - -#/////////////////////////////////////////////////////////// +# /////////////////////////////////////////////////////////// + +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 +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_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_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_ALLOW_STATUS_FRAMES = TPCANParameter(0x1E) # Status messages reception status within a PCAN-Channel -PCAN_ALLOW_RTR_FRAMES = TPCANParameter(0x1F) # RTR messages reception status within a PCAN-Channel -PCAN_ALLOW_ERROR_FRAMES = TPCANParameter(0x20) # Error messages reception status within a PCAN-Channel -PCAN_INTERFRAME_DELAY = TPCANParameter(0x21) # Delay, in microseconds, between sending frames -PCAN_ACCEPTANCE_FILTER_11BIT = TPCANParameter(0x22) # Filter over code and mask patterns for 11-Bit messages -PCAN_ACCEPTANCE_FILTER_29BIT = TPCANParameter(0x23) # Filter over code and mask patterns for 29-Bit messages -PCAN_IO_DIGITAL_CONFIGURATION = TPCANParameter(0x24) # Output mode of 32 digital I/O pin of a PCAN-USB Chip. 1: Output-Active 0 : Output Inactive -PCAN_IO_DIGITAL_VALUE = TPCANParameter(0x25) # Value assigned to a 32 digital I/O pins of a PCAN-USB Chip -PCAN_IO_DIGITAL_SET = TPCANParameter(0x26) # Value assigned to a 32 digital I/O pins of a PCAN-USB Chip - Multiple digital I/O pins to 1 = High -PCAN_IO_DIGITAL_CLEAR = TPCANParameter(0x27) # Clear multiple digital I/O pins to 0 -PCAN_IO_ANALOG_VALUE = TPCANParameter(0x28) # Get value of a single analog input pin +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_ALLOW_STATUS_FRAMES = TPCANParameter( + 0x1E +) # Status messages reception status within a PCAN-Channel +PCAN_ALLOW_RTR_FRAMES = TPCANParameter( + 0x1F +) # RTR messages reception status within a PCAN-Channel +PCAN_ALLOW_ERROR_FRAMES = TPCANParameter( + 0x20 +) # Error messages reception status within a PCAN-Channel +PCAN_INTERFRAME_DELAY = TPCANParameter( + 0x21 +) # Delay, in microseconds, between sending frames +PCAN_ACCEPTANCE_FILTER_11BIT = TPCANParameter( + 0x22 +) # Filter over code and mask patterns for 11-Bit messages +PCAN_ACCEPTANCE_FILTER_29BIT = TPCANParameter( + 0x23 +) # Filter over code and mask patterns for 29-Bit messages +PCAN_IO_DIGITAL_CONFIGURATION = TPCANParameter( + 0x24 +) # Output mode of 32 digital I/O pin of a PCAN-USB Chip. 1: Output-Active 0 : Output Inactive +PCAN_IO_DIGITAL_VALUE = TPCANParameter( + 0x25 +) # Value assigned to a 32 digital I/O pins of a PCAN-USB Chip +PCAN_IO_DIGITAL_SET = TPCANParameter( + 0x26 +) # Value assigned to a 32 digital I/O pins of a PCAN-USB Chip - Multiple digital I/O pins to 1 = High +PCAN_IO_DIGITAL_CLEAR = TPCANParameter(0x27) # Clear multiple digital I/O pins to 0 +PCAN_IO_ANALOG_VALUE = TPCANParameter(0x28) # Get value of a single analog input pin # 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) -FEATURE_DELAY_CAPABLE = int(0x02) # Device supports a delay between sending frames (FPGA based USB devices) -FEATURE_IO_CAPABLE = int(0x04) # Device supports I/O functionality for electronic circuits (USB-Chip devices) - -SERVICE_STATUS_STOPPED = int(0x01) # The service is not running -SERVICE_STATUS_RUNNING = int(0x04) # The service is running +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) +FEATURE_DELAY_CAPABLE = int( + 0x02 +) # Device supports a delay between sending frames (FPGA based USB devices) +FEATURE_IO_CAPABLE = int( + 0x04 +) # Device supports I/O functionality for electronic circuits (USB-Chip devices) + +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_ERRFRAME = TPCANMessageType(0x40) # The PCAN message represents an error frame -PCAN_MESSAGE_STATUS = TPCANMessageType(0x80) # The PCAN message represents a PCAN status message +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_ERRFRAME = TPCANMessageType( + 0x40 +) # The PCAN message represents an error frame +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 +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 +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: @@ -264,90 +380,120 @@ # Example: # f_clock=80000000,nom_brp=10,nom_tseg1=5,nom_tseg2=2,nom_sjw=1,data_brp=4,data_tseg1=7,data_tseg2=2,data_sjw=1 # -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") +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 +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 -class TPCANMsg (Structure): +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]) + _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]) -class TPCANMsgMac (Structure): + +class TPCANMsgMac(Structure): """ Represents a PCAN message """ - _fields_ = [ ("ID", c_ulong), # 11/29-bit message identifier - was changed from u_uint to c_ulong, so it is compatible with the PCAN-USB Driver for macOS - ("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]) + + _fields_ = [ + ( + "ID", + c_ulong, + ), # 11/29-bit message identifier - was changed from u_uint to c_ulong, so it is compatible with the PCAN-USB Driver for macOS + ("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]) -class TPCANTimestamp (Structure): +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 + _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 -class TPCANTimestampMac (Structure): + +class TPCANTimestampMac(Structure): """ Represents a timestamp of a received PCAN message Total Microseconds = micros + 1000 * millis + 0x100000000 * 1000 * millis_overflow """ - _fields_ = [ ("millis", c_ulong), # Base-value: milliseconds: 0.. 2^32-1 - was changed from u_uint to c_ulong, so it is compatible with the PCAN-USB Driver for macOS - ("millis_overflow", c_ushort), # Roll-arounds of millis - ("micros", c_ushort) ] # Microseconds: 0..999 + + _fields_ = [ + ( + "millis", + c_ulong, + ), # Base-value: milliseconds: 0.. 2^32-1 - was changed from u_uint to c_ulong, so it is compatible with the PCAN-USB Driver for macOS + ("millis_overflow", c_ushort), # Roll-arounds of millis + ("micros", c_ushort), + ] # Microseconds: 0..999 -class TPCANMsgFD (Structure): +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]) -class TPCANMsgFDMac (Structure): + _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]) + + +class TPCANMsgFDMac(Structure): """ Represents a PCAN message """ - _fields_ = [ ("ID", c_ulong), # 11/29-bit message identifier - was changed from u_uint to c_ulong, so it is compatible with the PCAN-USB Driver for macOS - ("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]) -#/////////////////////////////////////////////////////////// + _fields_ = [ + ( + "ID", + c_ulong, + ), # 11/29-bit message identifier - was changed from u_uint to c_ulong, so it is compatible with the PCAN-USB Driver for macOS + ("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 -#/////////////////////////////////////////////////////////// +# /////////////////////////////////////////////////////////// + class PCANBasic: """PCAN-Basic API class implementation @@ -355,10 +501,10 @@ class PCANBasic: def __init__(self): # Loads the PCANBasic.dll - if platform.system() == 'Windows': + if platform.system() == "Windows": self.__m_dllBasic = windll.LoadLibrary("PCANBasic") - elif platform.system() == 'Darwin': - self.__m_dllBasic = cdll.LoadLibrary('libPCBUSB.dylib') + elif platform.system() == "Darwin": + self.__m_dllBasic = cdll.LoadLibrary("libPCBUSB.dylib") else: self.__m_dllBasic = cdll.LoadLibrary("libpcanbasic.so") if self.__m_dllBasic is None: @@ -368,9 +514,10 @@ def Initialize( self, Channel, Btr0Btr1, - HwType = TPCANType(0), - IOPort = c_uint(0), - Interrupt = c_ushort(0)): + HwType=TPCANType(0), + IOPort=c_uint(0), + Interrupt=c_ushort(0), + ): """ Initializes a PCAN Channel @@ -386,16 +533,15 @@ def Initialize( A TPCANStatus error code """ try: - res = self.__m_dllBasic.CAN_Initialize(Channel,Btr0Btr1,HwType,IOPort,Interrupt) + res = self.__m_dllBasic.CAN_Initialize( + Channel, Btr0Btr1, HwType, IOPort, Interrupt + ) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.Initialize") raise - def InitializeFD( - self, - Channel, - BitrateFD): + def InitializeFD(self, Channel, BitrateFD): """ Initializes a FD capable PCAN Channel @@ -419,15 +565,13 @@ def InitializeFD( A TPCANStatus error code """ try: - res = self.__m_dllBasic.CAN_InitializeFD(Channel,BitrateFD) + res = self.__m_dllBasic.CAN_InitializeFD(Channel, BitrateFD) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.InitializeFD") raise - def Uninitialize( - self, - Channel): + def Uninitialize(self, Channel): """ Uninitializes one or all PCAN Channels initialized by CAN_Initialize @@ -448,9 +592,7 @@ def Uninitialize( logger.error("Exception on PCANBasic.Uninitialize") raise - def Reset( - self, - Channel): + def Reset(self, Channel): """ Resets the receive and transmit queues of the PCAN Channel @@ -471,9 +613,7 @@ def Reset( logger.error("Exception on PCANBasic.Reset") raise - def GetStatus( - self, - Channel): + def GetStatus(self, Channel): """ Gets the current status of a PCAN Channel @@ -491,9 +631,7 @@ def GetStatus( logger.error("Exception on PCANBasic.GetStatus") raise - def Read( - self, - Channel): + def Read(self, Channel): """ Reads a CAN message from the receive queue of a PCAN Channel @@ -513,21 +651,19 @@ def Read( A touple with three values """ try: - if platform.system() == 'Darwin': + if platform.system() == "Darwin": msg = TPCANMsgMac() timestamp = TPCANTimestampMac() else: msg = TPCANMsg() timestamp = TPCANTimestamp() - res = self.__m_dllBasic.CAN_Read(Channel,byref(msg),byref(timestamp)) - return TPCANStatus(res),msg,timestamp + res = self.__m_dllBasic.CAN_Read(Channel, byref(msg), byref(timestamp)) + return TPCANStatus(res), msg, timestamp except: logger.error("Exception on PCANBasic.Read") raise - def ReadFD( - self, - Channel): + def ReadFD(self, Channel): """ Reads a CAN message from the receive queue of a FD capable PCAN Channel @@ -547,21 +683,18 @@ def ReadFD( A touple with three values """ try: - if platform.system() == 'Darwin': + if platform.system() == "Darwin": msg = TPCANMsgFDMac() else: msg = TPCANMsgFD() timestamp = TPCANTimestampFD() - res = self.__m_dllBasic.CAN_ReadFD(Channel,byref(msg),byref(timestamp)) - return TPCANStatus(res),msg,timestamp + res = self.__m_dllBasic.CAN_ReadFD(Channel, byref(msg), byref(timestamp)) + return TPCANStatus(res), msg, timestamp except: logger.error("Exception on PCANBasic.ReadFD") raise - def Write( - self, - Channel, - MessageBuffer): + def Write(self, Channel, MessageBuffer): """ Transmits a CAN message @@ -574,16 +707,13 @@ def Write( A TPCANStatus error code """ try: - res = self.__m_dllBasic.CAN_Write(Channel,byref(MessageBuffer)) + res = self.__m_dllBasic.CAN_Write(Channel, byref(MessageBuffer)) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.Write") raise - def WriteFD( - self, - Channel, - MessageBuffer): + def WriteFD(self, Channel, MessageBuffer): """ Transmits a CAN message over a FD capable PCAN Channel @@ -596,18 +726,13 @@ def WriteFD( A TPCANStatus error code """ try: - res = self.__m_dllBasic.CAN_WriteFD(Channel,byref(MessageBuffer)) + res = self.__m_dllBasic.CAN_WriteFD(Channel, byref(MessageBuffer)) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.WriteFD") raise - def FilterMessages( - self, - Channel, - FromID, - ToID, - Mode): + def FilterMessages(self, Channel, FromID, ToID, Mode): """ Configures the reception filter @@ -627,16 +752,13 @@ def FilterMessages( A TPCANStatus error code """ try: - res = self.__m_dllBasic.CAN_FilterMessages(Channel,FromID,ToID,Mode) + res = self.__m_dllBasic.CAN_FilterMessages(Channel, FromID, ToID, Mode) return TPCANStatus(res) except: logger.error("Exception on PCANBasic.FilterMessages") raise - def GetValue( - self, - Channel, - Parameter): + def GetValue(self, Channel, Parameter): """ Retrieves a PCAN Channel value @@ -658,24 +780,28 @@ def GetValue( A touple with 2 values """ try: - if Parameter in {PCAN_API_VERSION, PCAN_HARDWARE_NAME, PCAN_CHANNEL_VERSION, - PCAN_LOG_LOCATION, PCAN_TRACE_LOCATION, PCAN_BITRATE_INFO_FD, - PCAN_IP_ADDRESS}: + if Parameter in { + PCAN_API_VERSION, + PCAN_HARDWARE_NAME, + PCAN_CHANNEL_VERSION, + PCAN_LOG_LOCATION, + PCAN_TRACE_LOCATION, + PCAN_BITRATE_INFO_FD, + 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)) + 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 - def SetValue( - self, - Channel, - Parameter, - Buffer): + def SetValue(self, Channel, Parameter, Buffer): """ Returns a descriptive text of a given TPCANStatus error @@ -702,16 +828,15 @@ def SetValue( mybuffer = c_int(0) mybuffer.value = Buffer - res = self.__m_dllBasic.CAN_SetValue(Channel, Parameter, byref(mybuffer), sizeof(mybuffer)) + 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): + def GetErrorText(self, Error, Language=0): """ Configures or sets a PCAN Channel value @@ -735,8 +860,8 @@ def GetErrorText( """ try: mybuffer = create_string_buffer(256) - res = self.__m_dllBasic.CAN_GetErrorText(Error,Language,byref(mybuffer)) - return TPCANStatus(res),mybuffer.value + res = self.__m_dllBasic.CAN_GetErrorText(Error, Language, byref(mybuffer)) + return TPCANStatus(res), mybuffer.value except: logger.error("Exception on PCANBasic.GetErrorText") raise diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index a7c8bd9ec..80b6054d0 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -16,7 +16,10 @@ # use the "uptime" library if available import uptime import datetime - boottimeEpoch = (uptime.boottime() - datetime.datetime.utcfromtimestamp(0)).total_seconds() + + boottimeEpoch = ( + uptime.boottime() - datetime.datetime.utcfromtimestamp(0) + ).total_seconds() except ImportError: boottimeEpoch = 0 @@ -24,44 +27,62 @@ # 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 # 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} - - -pcan_fd_parameter_list = ['nom_brp', 'nom_tseg1', 'nom_tseg2', 'nom_sjw', 'data_brp', 'data_tseg1', 'data_tseg2', 'data_sjw'] +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, +} + + +pcan_fd_parameter_list = [ + "nom_brp", + "nom_tseg1", + "nom_tseg2", + "nom_sjw", + "data_brp", + "data_tseg1", + "data_tseg2", + "data_sjw", +] class PcanBus(BusABC): - - def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000, *args, - **kwargs): + def __init__( + self, + channel="PCAN_USBBUS1", + state=BusState.ACTIVE, + bitrate=500000, + *args, + **kwargs + ): """A PCAN USB interface to CAN. On top of the usual :class:`~can.Bus` methods provided, @@ -152,7 +173,7 @@ def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000 """ self.channel_info = channel - self.fd = kwargs.get('fd', False) + self.fd = kwargs.get("fd", False) pcan_bitrate = pcan_bitrate_objs.get(bitrate, PCAN_BAUD_500K) hwtype = PCAN_TYPE_ISA @@ -167,22 +188,28 @@ def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000 else: raise ArgumentError("BusState must be Active or Passive") - if self.fd: - f_clock_val = kwargs.get('f_clock', None) + f_clock_val = kwargs.get("f_clock", None) if f_clock_val is None: - f_clock = "{}={}".format('f_clock_mhz', kwargs.get('f_clock_mhz', None)) + f_clock = "{}={}".format("f_clock_mhz", kwargs.get("f_clock_mhz", None)) else: - f_clock = "{}={}".format('f_clock', kwargs.get('f_clock', None)) - - fd_parameters_values = [f_clock] + ["{}={}".format(key, kwargs.get(key, None)) for key in pcan_fd_parameter_list if kwargs.get(key, None) is not None] + f_clock = "{}={}".format("f_clock", kwargs.get("f_clock", None)) - self.fd_bitrate = ' ,'.join(fd_parameters_values).encode("ascii") + fd_parameters_values = [f_clock] + [ + "{}={}".format(key, kwargs.get(key, None)) + for key in pcan_fd_parameter_list + if kwargs.get(key, None) is not None + ] + self.fd_bitrate = " ,".join(fd_parameters_values).encode("ascii") - result = self.m_objPCANBasic.InitializeFD(self.m_PcanHandle, self.fd_bitrate) + result = self.m_objPCANBasic.InitializeFD( + self.m_PcanHandle, self.fd_bitrate + ) else: - result = self.m_objPCANBasic.Initialize(self.m_PcanHandle, pcan_bitrate, hwtype, ioport, interrupt) + 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)) @@ -190,7 +217,8 @@ def __init__(self, channel='PCAN_USBBUS1', state=BusState.ACTIVE, bitrate=500000 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) + self.m_PcanHandle, PCAN_RECEIVE_EVENT, self._recv_event + ) if result != PCAN_ERROR_OK: raise PcanError(self._get_formatted_error(result)) @@ -212,7 +240,7 @@ def bits(n): """ while n: # Create a mask to mask the lowest set bit in n - mask = (~n + 1) + mask = ~n + 1 masked_value = n & mask yield masked_value # Toggle the lowest set bit @@ -225,15 +253,17 @@ def bits(n): 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) + text = "An error occurred. Error-code's text ({0:X}h) couldn't be retrieved".format( + error + ) else: - text = stsReturn[1].decode('utf-8', errors='replace') + text = stsReturn[1].decode("utf-8", errors="replace") strings.append(text) - complete_text = '\n'.join(strings) + complete_text = "\n".join(strings) else: - complete_text = stsReturn[1].decode('utf-8', errors='replace') + complete_text = stsReturn[1].decode("utf-8", errors="replace") return complete_text @@ -269,7 +299,7 @@ def _recv_internal(self, timeout): # Calculate max time end_time = time.perf_counter() + timeout - #log.debug("Trying to read a msg") + # log.debug("Trying to read a msg") result = None while result is None: @@ -297,39 +327,60 @@ def _recv_internal(self, timeout): theMsg = result[1] itsTimeStamp = result[2] - #log.debug("Received a message") + # log.debug("Received a message") - is_extended_id = (theMsg.MSGTYPE & PCAN_MESSAGE_EXTENDED.value) == PCAN_MESSAGE_EXTENDED.value - is_remote_frame = (theMsg.MSGTYPE & PCAN_MESSAGE_RTR.value) == PCAN_MESSAGE_RTR.value + is_extended_id = ( + theMsg.MSGTYPE & PCAN_MESSAGE_EXTENDED.value + ) == PCAN_MESSAGE_EXTENDED.value + is_remote_frame = ( + theMsg.MSGTYPE & PCAN_MESSAGE_RTR.value + ) == PCAN_MESSAGE_RTR.value is_fd = (theMsg.MSGTYPE & PCAN_MESSAGE_FD.value) == PCAN_MESSAGE_FD.value - bitrate_switch = (theMsg.MSGTYPE & PCAN_MESSAGE_BRS.value) == PCAN_MESSAGE_BRS.value - error_state_indicator = (theMsg.MSGTYPE & PCAN_MESSAGE_ESI.value) == PCAN_MESSAGE_ESI.value - is_error_frame = (theMsg.MSGTYPE & PCAN_MESSAGE_ERRFRAME.value) == PCAN_MESSAGE_ERRFRAME.value - + bitrate_switch = ( + theMsg.MSGTYPE & PCAN_MESSAGE_BRS.value + ) == PCAN_MESSAGE_BRS.value + error_state_indicator = ( + theMsg.MSGTYPE & PCAN_MESSAGE_ESI.value + ) == PCAN_MESSAGE_ESI.value + is_error_frame = ( + theMsg.MSGTYPE & PCAN_MESSAGE_ERRFRAME.value + ) == PCAN_MESSAGE_ERRFRAME.value if self.fd: dlc = dlc2len(theMsg.DLC) timestamp = boottimeEpoch + (itsTimeStamp.value / (1000.0 * 1000.0)) else: dlc = theMsg.LEN - timestamp = boottimeEpoch + ((itsTimeStamp.micros + 1000 * itsTimeStamp.millis + 0x100000000 * 1000 * itsTimeStamp.millis_overflow) / (1000.0 * 1000.0)) - - - rx_msg = Message(timestamp=timestamp, - arbitration_id=theMsg.ID, - is_extended_id=is_extended_id, - is_remote_frame=is_remote_frame, - is_error_frame=is_error_frame, - dlc=dlc, - data=theMsg.DATA[:dlc], - is_fd=is_fd, - bitrate_switch=bitrate_switch, - error_state_indicator=error_state_indicator) + timestamp = boottimeEpoch + ( + ( + itsTimeStamp.micros + + 1000 * itsTimeStamp.millis + + 0x100000000 * 1000 * itsTimeStamp.millis_overflow + ) + / (1000.0 * 1000.0) + ) + + rx_msg = Message( + timestamp=timestamp, + arbitration_id=theMsg.ID, + is_extended_id=is_extended_id, + is_remote_frame=is_remote_frame, + is_error_frame=is_error_frame, + dlc=dlc, + data=theMsg.DATA[:dlc], + is_fd=is_fd, + bitrate_switch=bitrate_switch, + error_state_indicator=error_state_indicator, + ) return rx_msg, False def send(self, msg, timeout=None): - msgType = PCAN_MESSAGE_EXTENDED.value if msg.is_extended_id else PCAN_MESSAGE_STANDARD.value + msgType = ( + PCAN_MESSAGE_EXTENDED.value + if msg.is_extended_id + else PCAN_MESSAGE_STANDARD.value + ) if msg.is_remote_frame: msgType |= PCAN_MESSAGE_RTR.value if msg.is_error_frame: @@ -343,7 +394,7 @@ def send(self, msg, timeout=None): if self.fd: # create a TPCANMsg message structure - if platform.system() == 'Darwin': + if platform.system() == "Darwin": CANMsg = TPCANMsgFDMac() else: CANMsg = TPCANMsgFD() @@ -364,7 +415,7 @@ def send(self, msg, timeout=None): else: # create a TPCANMsg message structure - if platform.system() == 'Darwin': + if platform.system() == "Darwin": CANMsg = TPCANMsgMac() else: CANMsg = TPCANMsg() @@ -395,7 +446,9 @@ 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)) + self.m_objPCANBasic.SetValue( + self.m_PcanHandle, PCAN_CHANNEL_IDENTIFYING, bool(flash) + ) def shutdown(self): super().shutdown() @@ -408,16 +461,20 @@ def state(self): @state.setter def state(self, new_state): # declare here, which is called by __init__() - self._state = new_state # pylint: disable=attribute-defined-outside-init + self._state = new_state # pylint: disable=attribute-defined-outside-init if new_state is BusState.ACTIVE: - self.m_objPCANBasic.SetValue(self.m_PcanHandle, PCAN_LISTEN_ONLY, PCAN_PARAMETER_OFF) + self.m_objPCANBasic.SetValue( + self.m_PcanHandle, PCAN_LISTEN_ONLY, PCAN_PARAMETER_OFF + ) elif new_state is BusState.PASSIVE: # When this mode is set, the CAN controller does not take part on active events (eg. transmit CAN messages) # but stays in a passive mode (CAN monitor), in which it can analyse the traffic on the CAN bus used by a # PCAN channel. See also the Philips Data Sheet "SJA1000 Stand-alone CAN controller". - self.m_objPCANBasic.SetValue(self.m_PcanHandle, PCAN_LISTEN_ONLY, PCAN_PARAMETER_ON) + self.m_objPCANBasic.SetValue( + self.m_PcanHandle, PCAN_LISTEN_ONLY, PCAN_PARAMETER_ON + ) class PcanError(CanError): diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index 6e1327a15..ba397f7bf 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -12,13 +12,15 @@ from can import BusABC, Message -logger = logging.getLogger('can.serial') +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!") + logger.warning( + "You won't be able to use the serial can backend without " + "the serial module installed!" + ) serial = None @@ -31,7 +33,9 @@ class SerialBus(BusABC): """ - def __init__(self, channel, baudrate=115200, timeout=0.1, rtscts=False, *args, **kwargs): + def __init__( + self, channel, baudrate=115200, timeout=0.1, rtscts=False, *args, **kwargs + ): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or @@ -55,7 +59,8 @@ def __init__(self, channel, baudrate=115200, timeout=0.1, rtscts=False, *args, * self.channel_info = "Serial interface: " + channel self.ser = serial.serial_for_url( - channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) + channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts + ) super().__init__(channel=channel, *args, **kwargs) @@ -84,13 +89,13 @@ def send(self, msg, timeout=None): """ try: - timestamp = struct.pack(' # @@ -152,7 +156,7 @@ def build_can_frame(msg): if msg.error_state_indicator: flags |= CANFD_ESI max_len = 64 if msg.is_fd else 8 - data = bytes(msg.data).ljust(max_len, b'\x00') + data = bytes(msg.data).ljust(max_len, b"\x00") return CAN_FRAME_HEADER_STRUCT.pack(can_id, msg.dlc, flags) + data @@ -186,8 +190,9 @@ def build_bcm_tx_delete_header(can_id, flags): return build_bcm_header(opcode, flags, 0, 0, 0, 0, 0, can_id, 1) -def build_bcm_transmit_header(can_id, count, initial_period, subsequent_period, - msg_flags): +def build_bcm_transmit_header( + can_id, count, initial_period, subsequent_period, msg_flags +): opcode = CAN_BCM_TX_SETUP flags = msg_flags | SETTIMER | STARTTIMER @@ -206,7 +211,17 @@ def split_time(value): 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) + return build_bcm_header( + opcode, + flags, + count, + ival1_seconds, + ival1_usec, + ival2_seconds, + ival2_usec, + can_id, + nframes, + ) def build_bcm_update_header(can_id, msg_flags): @@ -218,7 +233,7 @@ def dissect_can_frame(frame): if len(frame) != CANFD_MTU: # Flags not valid in non-FD frames flags = 0 - return can_id, can_dlc, flags, frame[8:8+can_dlc] + return can_id, can_dlc, flags, frame[8 : 8 + can_dlc] def create_bcm_socket(channel): @@ -235,10 +250,14 @@ def send_bcm(bcm_socket, data): try: return bcm_socket.send(data) except OSError as e: - base = "Couldn't send CAN BCM frame. OS Error {}: {}\n".format(e.errno, e.strerror) + base = "Couldn't send CAN BCM frame. OS Error {}: {}\n".format( + e.errno, e.strerror + ) if e.errno == errno.EINVAL: - raise can.CanError(base + "You are probably referring to a non-existing frame.") + 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.") @@ -265,8 +284,9 @@ def _add_flags_to_can_id(message): return can_id -class CyclicSendTask(LimitedDurationCyclicSendTaskABC, - ModifiableCyclicTaskABC, RestartableCyclicTaskABC): +class CyclicSendTask( + LimitedDurationCyclicSendTaskABC, ModifiableCyclicTaskABC, RestartableCyclicTaskABC +): """ A socketcan cyclic send task supports: @@ -302,8 +322,9 @@ def _tx_setup(self, message): count = 0 ival1 = 0 ival2 = self.period - header = build_bcm_transmit_header(self.can_id_with_flags, count, ival1, - ival2, self.flags) + header = build_bcm_transmit_header( + self.can_id_with_flags, count, ival1, ival2, self.flags + ) frame = build_can_frame(message) log.debug("Sending BCM command") send_bcm(self.bcm_socket, header + frame) @@ -326,7 +347,9 @@ def modify_data(self, message): Note the Message must have the same :attr:`~can.Message.arbitration_id` like the first message. """ - assert message.arbitration_id == self.can_id, "You cannot modify the can identifier" + assert ( + message.arbitration_id == self.can_id + ), "You cannot modify the can identifier" self.message = message header = build_bcm_update_header(self.can_id_with_flags, self.flags) frame = build_can_frame(message) @@ -348,11 +371,8 @@ def __init__(self, channel, message, count, initial_period, subsequent_period): # Create a low level packed frame to pass to the kernel frame = build_can_frame(message) header = build_bcm_transmit_header( - self.can_id_with_flags, - count, - initial_period, - subsequent_period, - self.flags) + self.can_id_with_flags, count, initial_period, subsequent_period, self.flags + ) log.info("Sending BCM TX_SETUP command") send_bcm(self.bcm_socket, header + frame) @@ -364,12 +384,12 @@ def create_socket(): """ sock = socket.socket(PF_CAN, socket.SOCK_RAW, CAN_RAW) - log.info('Created a socket') + log.info("Created a socket") return sock -def bind_socket(sock, channel='can0'): +def bind_socket(sock, channel="can0"): """ Binds the given socket to the given interface. @@ -378,9 +398,9 @@ def bind_socket(sock, channel='can0'): :raises OSError: If the specified interface isn't found. """ - log.debug('Binding socket to channel=%s', channel) + log.debug("Binding socket to channel=%s", channel) sock.bind((channel,)) - log.debug('Bound socket.') + log.debug("Bound socket.") def capture_message(sock, get_channel=False): @@ -406,7 +426,7 @@ def capture_message(sock, get_channel=False): raise can.CanError("Error receiving: %s" % exc) can_id, can_dlc, flags, data = dissect_can_frame(cf) - #log.debug('Received: can_id=%x, can_dlc=%x, data=%s', can_id, can_dlc, data) + # log.debug('Received: can_id=%x, can_dlc=%x, data=%s', can_id, can_dlc, data) # Fetching the timestamp binary_structure = "@LL" @@ -428,26 +448,28 @@ def capture_message(sock, get_channel=False): error_state_indicator = bool(flags & CANFD_ESI) if is_extended_frame_format: - #log.debug("CAN: Extended") + # log.debug("CAN: Extended") # TODO does this depend on SFF or EFF? arbitration_id = can_id & 0x1FFFFFFF else: - #log.debug("CAN: Standard") + # log.debug("CAN: Standard") arbitration_id = can_id & 0x000007FF - msg = Message(timestamp=timestamp, - channel=channel, - arbitration_id=arbitration_id, - is_extended_id=is_extended_frame_format, - is_remote_frame=is_remote_transmission_request, - is_error_frame=is_error_frame, - is_fd=is_fd, - bitrate_switch=bitrate_switch, - error_state_indicator=error_state_indicator, - dlc=can_dlc, - data=data) + msg = Message( + timestamp=timestamp, + channel=channel, + arbitration_id=arbitration_id, + is_extended_id=is_extended_frame_format, + is_remote_frame=is_remote_transmission_request, + is_error_frame=is_error_frame, + is_fd=is_fd, + bitrate_switch=bitrate_switch, + error_state_indicator=error_state_indicator, + dlc=can_dlc, + data=data, + ) - #log_rx.debug('Received: %s', msg) + # log_rx.debug('Received: %s', msg) return msg @@ -480,25 +502,21 @@ def __init__(self, channel="", receive_own_messages=False, fd=False, **kwargs): # set the receive_own_messages parameter try: - self.socket.setsockopt(SOL_CAN_RAW, - CAN_RAW_RECV_OWN_MSGS, - 1 if receive_own_messages else 0) + self.socket.setsockopt( + SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, 1 if receive_own_messages else 0 + ) except socket.error as e: log.error("Could not receive own messages (%s)", e) if fd: # TODO handle errors - self.socket.setsockopt(SOL_CAN_RAW, - CAN_RAW_FD_FRAMES, - 1) + self.socket.setsockopt(SOL_CAN_RAW, CAN_RAW_FD_FRAMES, 1) # Enable error frames - self.socket.setsockopt(SOL_CAN_RAW, - CAN_RAW_ERR_FILTER, - 0x1FFFFFFF) + self.socket.setsockopt(SOL_CAN_RAW, CAN_RAW_ERR_FILTER, 0x1FFFFFFF) bind_socket(self.socket, channel) - kwargs.update({'receive_own_messages': receive_own_messages, 'fd': fd}) + kwargs.update({"receive_own_messages": receive_own_messages, "fd": fd}) super().__init__(channel=channel, **kwargs) def shutdown(self): @@ -522,7 +540,7 @@ def _recv_internal(self, timeout): # something bad happened (e.g. the interface went down) raise can.CanError("Failed to receive: %s" % exc) - if ready_receive_sockets: # not empty or True + if ready_receive_sockets: # not empty or True get_channel = self.channel == "" msg = capture_message(self.socket, get_channel) if not msg.channel and self.channel: @@ -574,7 +592,7 @@ def _send_once(self, data, channel=None): try: if self.channel == "" and channel: # Message must be addressed to a specific channel - sent = self.socket.sendto(data, (channel, )) + sent = self.socket.sendto(data, (channel,)) else: sent = self.socket.send(data) except socket.error as exc: @@ -618,14 +636,15 @@ def _get_bcm_socket(self, channel): def _apply_filters(self, filters): try: - self.socket.setsockopt(SOL_CAN_RAW, - CAN_RAW_FILTER, - pack_filters(filters)) + self.socket.setsockopt(SOL_CAN_RAW, CAN_RAW_FILTER, pack_filters(filters)) except socket.error as err: # fall back to "software filtering" (= not in kernel) self._is_filtered = False # TODO Is this serious enough to raise a CanError exception? - log.error('Setting filters failed; falling back to software filtering (not in kernel): %s', err) + log.error( + "Setting filters failed; falling back to software filtering (not in kernel): %s", + err, + ) else: self._is_filtered = True @@ -634,8 +653,10 @@ def fileno(self): @staticmethod def _detect_available_configs(): - return [{'interface': 'socketcan', 'channel': channel} - for channel in find_available_interfaces()] + return [ + {"interface": "socketcan", "channel": channel} + for channel in find_available_interfaces() + ] if __name__ == "__main__": @@ -653,7 +674,7 @@ def _detect_available_configs(): def receiver(event): receiver_socket = create_socket() - bind_socket(receiver_socket, 'vcan0') + bind_socket(receiver_socket, "vcan0") print("Receiver is waiting for a message...") event.set() print(f"Receiver got: {capture_message(receiver_socket)}") @@ -661,12 +682,13 @@ def receiver(event): def sender(event): event.wait() sender_socket = create_socket() - bind_socket(sender_socket, 'vcan0') - msg = Message(arbitration_id=0x01, data=b'\x01\x02\x03') + bind_socket(sender_socket, "vcan0") + msg = Message(arbitration_id=0x01, data=b"\x01\x02\x03") sender_socket.send(build_can_frame(msg)) print("Sender sent a message.") import threading + e = threading.Event() threading.Thread(target=receiver, args=(e,)).start() threading.Thread(target=sender, args=(e,)).start() diff --git a/can/interfaces/socketcan/utils.py b/can/interfaces/socketcan/utils.py index f2c1879c5..5733443a1 100644 --- a/can/interfaces/socketcan/utils.py +++ b/can/interfaces/socketcan/utils.py @@ -15,23 +15,21 @@ log = logging.getLogger(__name__) + def pack_filters(can_filters=None): if can_filters is None: # Pass all messages - can_filters = [{ - 'can_id': 0, - 'can_mask': 0 - }] + can_filters = [{"can_id": 0, "can_mask": 0}] can_filter_fmt = "={}I".format(2 * len(can_filters)) filter_data = [] for can_filter in can_filters: - can_id = can_filter['can_id'] - can_mask = can_filter['can_mask'] - if 'extended' in can_filter: + can_id = can_filter["can_id"] + can_mask = can_filter["can_mask"] + if "extended" in can_filter: # Match on either 11-bit OR 29-bit messages instead of both can_mask |= CAN_EFF_FLAG - if can_filter['extended']: + if can_filter["extended"]: can_id |= CAN_EFF_FLAG filter_data.append(can_id) filter_data.append(can_mask) @@ -41,6 +39,7 @@ def pack_filters(can_filters=None): _PATTERN_CAN_INTERFACE = re.compile(r"v?can\d+") + def find_available_interfaces(): """Returns the names of all open can/vcan interfaces using the ``ip link list`` command. If the lookup fails, an error @@ -54,12 +53,12 @@ def find_available_interfaces(): command = ["ip", "-o", "link", "list", "up"] output = subprocess.check_output(command, universal_newlines=True) - except Exception as e: # subprocess.CalledProcessError was too specific + except Exception as e: # subprocess.CalledProcessError was too specific log.error("failed to fetch opened can devices: %s", e) return [] else: - #log.debug("find_available_interfaces(): output=\n%s", output) + # log.debug("find_available_interfaces(): output=\n%s", output) # output contains some lines like "1: vcan42: ..." # extract the "vcan42" of each line interface_names = [line.split(": ", 3)[1] for line in output.splitlines()] diff --git a/can/interfaces/systec/constants.py b/can/interfaces/systec/constants.py index 64122dac9..648ae2edc 100644 --- a/can/interfaces/systec/constants.py +++ b/can/interfaces/systec/constants.py @@ -364,6 +364,7 @@ class OutputControl(BYTE): These values are only important for GW-001 and GW-002. They does not have an effect on systec USB-CANmoduls. """ + #: default OCR value for the standard USB-CANmodul GW-001/GW-002 OCR_DEFAULT = 0x1A #: OCR value for RS485 interface and galvanic isolation @@ -433,42 +434,45 @@ class ResetFlags(DWORD): RESET_FIRMWARE = 0xFFFFFFFF #: no reset of all message counters - RESET_NO_COUNTER_ALL = (RESET_NO_TXCOUNTER | RESET_NO_RXCOUNTER) + RESET_NO_COUNTER_ALL = RESET_NO_TXCOUNTER | RESET_NO_RXCOUNTER #: no reset of transmit message buffers at communication level (firmware, kernel and library) - RESET_NO_TXBUFFER_COMM = (RESET_NO_TXBUFFER_DLL | 0x40 | RESET_NO_TXBUFFER_FW) + RESET_NO_TXBUFFER_COMM = RESET_NO_TXBUFFER_DLL | 0x40 | RESET_NO_TXBUFFER_FW #: no reset of receive message buffers at communication level (firmware, kernel and library) - RESET_NO_RXBUFFER_COMM = (RESET_NO_RXBUFFER_DLL | RESET_NO_RXBUFFER_SYS | RESET_NO_RXBUFFER_FW) + RESET_NO_RXBUFFER_COMM = ( + RESET_NO_RXBUFFER_DLL | RESET_NO_RXBUFFER_SYS | RESET_NO_RXBUFFER_FW + ) #: no reset of all transmit message buffers - RESET_NO_TXBUFFER_ALL = (RESET_NO_TXBUFFER_CH | RESET_NO_TXBUFFER_COMM) + RESET_NO_TXBUFFER_ALL = RESET_NO_TXBUFFER_CH | RESET_NO_TXBUFFER_COMM #: no reset of all receive message buffers - RESET_NO_RXBUFFER_ALL = (RESET_NO_RXBUFFER_CH | RESET_NO_RXBUFFER_COMM) + RESET_NO_RXBUFFER_ALL = RESET_NO_RXBUFFER_CH | RESET_NO_RXBUFFER_COMM #: no reset of all message buffers at communication level (firmware, kernel and library) - RESET_NO_BUFFER_COMM = (RESET_NO_TXBUFFER_COMM | RESET_NO_RXBUFFER_COMM) + RESET_NO_BUFFER_COMM = RESET_NO_TXBUFFER_COMM | RESET_NO_RXBUFFER_COMM #: no reset of all message buffers - RESET_NO_BUFFER_ALL = (RESET_NO_TXBUFFER_ALL | RESET_NO_RXBUFFER_ALL) + RESET_NO_BUFFER_ALL = RESET_NO_TXBUFFER_ALL | RESET_NO_RXBUFFER_ALL #: reset of the CAN status only - RESET_ONLY_STATUS = (0xFFFF & ~RESET_NO_STATUS) + RESET_ONLY_STATUS = 0xFFFF & ~RESET_NO_STATUS #: reset of the CAN controller only - RESET_ONLY_CANCTRL = (0xFFFF & ~RESET_NO_CANCTRL) + RESET_ONLY_CANCTRL = 0xFFFF & ~RESET_NO_CANCTRL #: reset of the transmit buffer in firmware only - RESET_ONLY_TXBUFFER_FW = (0xFFFF & ~RESET_NO_TXBUFFER_FW) + RESET_ONLY_TXBUFFER_FW = 0xFFFF & ~RESET_NO_TXBUFFER_FW #: reset of the receive buffer in firmware only - RESET_ONLY_RXBUFFER_FW = (0xFFFF & ~RESET_NO_RXBUFFER_FW) + RESET_ONLY_RXBUFFER_FW = 0xFFFF & ~RESET_NO_RXBUFFER_FW #: reset of the specified channel of the receive buffer only - RESET_ONLY_RXCHANNEL_BUFF = (0xFFFF & ~RESET_NO_RXBUFFER_CH) + RESET_ONLY_RXCHANNEL_BUFF = 0xFFFF & ~RESET_NO_RXBUFFER_CH #: reset of the specified channel of the transmit buffer only - RESET_ONLY_TXCHANNEL_BUFF = (0xFFFF & ~RESET_NO_TXBUFFER_CH) + RESET_ONLY_TXCHANNEL_BUFF = 0xFFFF & ~RESET_NO_TXBUFFER_CH #: reset of the receive buffer and receive message counter only - RESET_ONLY_RX_BUFF = (0xFFFF & ~(RESET_NO_RXBUFFER_ALL | RESET_NO_RXCOUNTER)) + RESET_ONLY_RX_BUFF = 0xFFFF & ~(RESET_NO_RXBUFFER_ALL | RESET_NO_RXCOUNTER) #: reset of the receive buffer and receive message counter (for GW-002) only - RESET_ONLY_RX_BUFF_GW002 = (0xFFFF & ~(RESET_NO_RXBUFFER_ALL | RESET_NO_RXCOUNTER | - RESET_NO_TXBUFFER_FW)) + RESET_ONLY_RX_BUFF_GW002 = 0xFFFF & ~( + RESET_NO_RXBUFFER_ALL | RESET_NO_RXCOUNTER | RESET_NO_TXBUFFER_FW + ) #: reset of the transmit buffer and transmit message counter only - RESET_ONLY_TX_BUFF = (0xFFFF & ~(RESET_NO_TXBUFFER_ALL | RESET_NO_TXCOUNTER)) + RESET_ONLY_TX_BUFF = 0xFFFF & ~(RESET_NO_TXBUFFER_ALL | RESET_NO_TXCOUNTER) #: reset of all buffers and all message counters only - RESET_ONLY_ALL_BUFF = (RESET_ONLY_RX_BUFF & RESET_ONLY_TX_BUFF) + RESET_ONLY_ALL_BUFF = RESET_ONLY_RX_BUFF & RESET_ONLY_TX_BUFF #: reset of all message counters only - RESET_ONLY_ALL_COUNTER = (0xFFFF & ~RESET_NO_COUNTER_ALL) + RESET_ONLY_ALL_COUNTER = 0xFFFF & ~RESET_NO_COUNTER_ALL PRODCODE_PID_TWO_CHA = 0x1 @@ -480,7 +484,7 @@ class ResetFlags(DWORD): PRODCODE_MASK_DID = 0xFFFF0000 PRODCODE_MASK_PID = 0xFFFF -PRODCODE_MASK_PIDG3 = (PRODCODE_MASK_PID & 0xFFFFFFBF) +PRODCODE_MASK_PIDG3 = PRODCODE_MASK_PID & 0xFFFFFFBF class ProductCode(WORD): @@ -597,11 +601,11 @@ class PendingFlags(BYTE): #: number of pending CAN messages in transmit buffer of firmware PENDING_FLAG_TX_FW = 0x40 #: number of pending CAN messages in all receive buffers - PENDING_FLAG_RX_ALL = (PENDING_FLAG_RX_DLL | PENDING_FLAG_RX_SYS | PENDING_FLAG_RX_FW) + PENDING_FLAG_RX_ALL = PENDING_FLAG_RX_DLL | PENDING_FLAG_RX_SYS | PENDING_FLAG_RX_FW #: number of pending CAN messages in all transmit buffers - PENDING_FLAG_TX_ALL = (PENDING_FLAG_TX_DLL | PENDING_FLAG_TX_SYS | PENDING_FLAG_TX_FW) + PENDING_FLAG_TX_ALL = PENDING_FLAG_TX_DLL | PENDING_FLAG_TX_SYS | PENDING_FLAG_TX_FW #: number of pending CAN messages in all buffers - PENDING_FLAG_ALL = (PENDING_FLAG_RX_ALL | PENDING_FLAG_TX_ALL) + PENDING_FLAG_ALL = PENDING_FLAG_RX_ALL | PENDING_FLAG_TX_ALL class Mode(BYTE): diff --git a/can/interfaces/systec/exceptions.py b/can/interfaces/systec/exceptions.py index 0d823895b..49a974cac 100644 --- a/can/interfaces/systec/exceptions.py +++ b/can/interfaces/systec/exceptions.py @@ -21,6 +21,7 @@ def __str__(self): class UcanError(UcanException): """ Exception class for errors from USB-CAN-library. """ + def __init__(self, result, func, arguments): super().__init__(result, func, arguments) self.return_msgs = { @@ -29,7 +30,7 @@ def __init__(self, result, func, arguments): ReturnCode.ERR_HWINUSE: "the specified module is already in use", ReturnCode.ERR_ILLVERSION: "the software versions of the module and library are incompatible", ReturnCode.ERR_ILLHW: "the module with the specified device number is not connected " - "(or used by an other application)", + "(or used by an other application)", ReturnCode.ERR_ILLHANDLE: "wrong USB-CAN-Handle handed over to the function", ReturnCode.ERR_ILLPARAM: "wrong parameter handed over to the function", ReturnCode.ERR_BUSY: "instruction can not be processed at this time", @@ -60,13 +61,13 @@ def __init__(self, result, func, arguments): ReturnCode.ERRCMD_RESERVED2: "reserved", ReturnCode.ERRCMD_RESERVED3: "reserved", ReturnCode.ERRCMD_ILLBDR: "illegal baud rate value specified in BTR0/BTR1 for systec " - "USB-CANmoduls", + "USB-CANmoduls", ReturnCode.ERRCMD_NOTINIT: "CAN channel is not initialized", ReturnCode.ERRCMD_ALREADYINIT: "CAN channel is already initialized", ReturnCode.ERRCMD_ILLSUBCMD: "illegal sub-command specified", ReturnCode.ERRCMD_ILLIDX: "illegal index specified (e.g. index for cyclic CAN messages)", ReturnCode.ERRCMD_RUNNING: "cyclic CAN message(s) can not be defined because transmission of " - "cyclic CAN messages is already running", + "cyclic CAN messages is already running", } @@ -82,12 +83,12 @@ def __init__(self, result, func, arguments): ReturnCode.WARN_RESERVED1: "reserved", ReturnCode.WARN_RESERVED2: "reserved", ReturnCode.WARN_FW_TXOVERRUN: "overrun in transmit buffer of the firmware (but this CAN message " - "was successfully stored in buffer of the ibrary)", + "was successfully stored in buffer of the ibrary)", ReturnCode.WARN_FW_RXOVERRUN: "overrun in receive buffer of the firmware (but this CAN message " - "was successfully read)", + "was successfully read)", ReturnCode.WARN_FW_TXMSGLOST: "reserved", ReturnCode.WARN_NULL_PTR: "pointer is NULL", ReturnCode.WARN_TXLIMIT: "not all CAN messages could be stored to the transmit buffer in " - "USB-CAN-library", - ReturnCode.WARN_BUSY: "reserved" + "USB-CAN-library", + ReturnCode.WARN_BUSY: "reserved", } diff --git a/can/interfaces/systec/structures.py b/can/interfaces/systec/structures.py index 11ffd0e39..9fd542e04 100644 --- a/can/interfaces/systec/structures.py +++ b/can/interfaces/systec/structures.py @@ -1,8 +1,15 @@ # coding: utf-8 from ctypes import Structure, POINTER, sizeof -from ctypes import c_ubyte as BYTE, c_ushort as WORD, c_ulong as DWORD, c_long as BOOL, c_void_p as LPVOID +from ctypes import ( + c_ubyte as BYTE, + c_ushort as WORD, + c_ulong as DWORD, + c_long as BOOL, + c_void_p as LPVOID, +) import os + # Workaround for Unix based platforms to be able to load structures for testing, etc... if os.name == "nt": from ctypes import WINFUNCTYPE as FUNCTYPE @@ -26,13 +33,17 @@ class CanMsg(Structure): :meth:`UcanServer.read_cyclic_can_msg` """ + _pack_ = 1 _fields_ = [ ("m_dwID", DWORD), # CAN Identifier ("m_bFF", BYTE), # CAN Frame Format (see enum :class:`MsgFrameFormat`) ("m_bDLC", BYTE), # CAN Data Length Code ("m_bData", BYTE * 8), # CAN Data (array of 8 bytes) - ("m_dwTime", DWORD,) # Receive time stamp in ms (for transmit messages no meaning) + ( + "m_dwTime", + DWORD, + ), # Receive time stamp in ms (for transmit messages no meaning) ] def __init__(self, id=0, frame_format=MsgFrameFormat.MSG_FF_STD, data=None): @@ -43,22 +54,31 @@ def __eq__(self, other): if not isinstance(other, CanMsg): return False - return self.id == other.id and self.frame_format == other.frame_format and self.data == other.data + return ( + self.id == other.id + and self.frame_format == other.frame_format + and self.data == other.data + ) @property - def id(self): return self.m_dwID + def id(self): + return self.m_dwID @id.setter - def id(self, id): self.m_dwID = id + def id(self, id): + self.m_dwID = id @property - def frame_format(self): return self.m_bFF + def frame_format(self): + return self.m_bFF @frame_format.setter - def frame_format(self, frame_format): self.m_bFF = frame_format + def frame_format(self, frame_format): + self.m_bFF = frame_format @property - def data(self): return self.m_bData[:self.m_bDLC] + def data(self): + return self.m_bData[: self.m_bDLC] @data.setter def data(self, data): @@ -66,7 +86,8 @@ def data(self, data): self.m_bData((BYTE * 8)(*data)) @property - def time(self): return self.m_dwTime + def time(self): + return self.m_dwTime class Status(Structure): @@ -80,6 +101,7 @@ class Status(Structure): :meth:`UcanServer.get_can_status_message` """ + _pack_ = 1 _fields_ = [ ("m_wCanStatus", WORD), # CAN error status (see enum :class:`CanStatus`) @@ -90,13 +112,17 @@ def __eq__(self, other): if not isinstance(other, Status): return False - return self.can_status == other.can_status and self.usb_status == other.usb_status + return ( + self.can_status == other.can_status and self.usb_status == other.usb_status + ) @property - def can_status(self): return self.m_wCanStatus + def can_status(self): + return self.m_wCanStatus @property - def usb_status(self): return self.m_wUsbStatus + def usb_status(self): + return self.m_wUsbStatus class InitCanParam(Structure): @@ -105,69 +131,114 @@ class InitCanParam(Structure): .. note:: This structure is only used internally. """ + _pack_ = 1 _fields_ = [ ("m_dwSize", DWORD), # size of this structure (only used internally) - ("m_bMode", BYTE), # selects the mode of CAN controller (see enum :class:`Mode`) + ( + "m_bMode", + BYTE, + ), # selects the mode of CAN controller (see enum :class:`Mode`) # Baudrate Registers for GW-001 or GW-002 ("m_bBTR0", BYTE), # Bus Timing Register 0 (see enum :class:`Baudrate`) ("m_bBTR1", BYTE), # Bus Timing Register 1 (see enum :class:`Baudrate`) ("m_bOCR", BYTE), # Output Control Register (see enum :class:`OutputControl`) - ("m_dwAMR", DWORD), # Acceptance Mask Register (see method :meth:`UcanServer.set_acceptance`) - ("m_dwACR", DWORD), # Acceptance Code Register (see method :meth:`UcanServer.set_acceptance`) + ( + "m_dwAMR", + DWORD, + ), # Acceptance Mask Register (see method :meth:`UcanServer.set_acceptance`) + ( + "m_dwACR", + DWORD, + ), # Acceptance Code Register (see method :meth:`UcanServer.set_acceptance`) ("m_dwBaudrate", DWORD), # Baudrate Register for all systec USB-CANmoduls - # (see enum :class:`BaudrateEx`) - ("m_wNrOfRxBufferEntries", WORD), # number of receive buffer entries (default is 4096) - ("m_wNrOfTxBufferEntries", WORD), # number of transmit buffer entries (default is 4096) + # (see enum :class:`BaudrateEx`) + ( + "m_wNrOfRxBufferEntries", + WORD, + ), # number of receive buffer entries (default is 4096) + ( + "m_wNrOfTxBufferEntries", + WORD, + ), # number of transmit buffer entries (default is 4096) ] - def __init__(self, mode, BTR, OCR, AMR, ACR, baudrate, rx_buffer_entries, tx_buffer_entries): - super().__init__(sizeof(InitCanParam), mode, BTR >> 8, BTR, OCR, AMR, ACR, - baudrate, rx_buffer_entries, tx_buffer_entries) + def __init__( + self, mode, BTR, OCR, AMR, ACR, baudrate, rx_buffer_entries, tx_buffer_entries + ): + super().__init__( + sizeof(InitCanParam), + mode, + BTR >> 8, + BTR, + OCR, + AMR, + ACR, + baudrate, + rx_buffer_entries, + tx_buffer_entries, + ) def __eq__(self, other): if not isinstance(other, InitCanParam): return False - return self.mode == other.mode and self.BTR == other.BTR and self.OCR == other.OCR and \ - self.baudrate == other.baudrate and self.rx_buffer_entries == other.rx_buffer_entries and \ - self.tx_buffer_entries == other.tx_buffer_entries + return ( + self.mode == other.mode + and self.BTR == other.BTR + and self.OCR == other.OCR + and self.baudrate == other.baudrate + and self.rx_buffer_entries == other.rx_buffer_entries + and self.tx_buffer_entries == other.tx_buffer_entries + ) @property - def mode(self): return self.m_bMode + def mode(self): + return self.m_bMode @mode.setter - def mode(self, mode): self.m_bMode = mode + def mode(self, mode): + self.m_bMode = mode @property - def BTR(self): return self.m_bBTR0 << 8 | self.m_bBTR1 + def BTR(self): + return self.m_bBTR0 << 8 | self.m_bBTR1 @BTR.setter - def BTR(self, BTR): self.m_bBTR0, self.m_bBTR1 = BTR >> 8, BTR + def BTR(self, BTR): + self.m_bBTR0, self.m_bBTR1 = BTR >> 8, BTR @property - def OCR(self): return self.m_bOCR + def OCR(self): + return self.m_bOCR @OCR.setter - def OCR(self, OCR): self.m_bOCR = OCR + def OCR(self, OCR): + self.m_bOCR = OCR @property - def baudrate(self): return self.m_dwBaudrate + def baudrate(self): + return self.m_dwBaudrate @baudrate.setter - def baudrate(self, baudrate): self.m_dwBaudrate = baudrate + def baudrate(self, baudrate): + self.m_dwBaudrate = baudrate @property - def rx_buffer_entries(self): return self.m_wNrOfRxBufferEntries + def rx_buffer_entries(self): + return self.m_wNrOfRxBufferEntries @rx_buffer_entries.setter - def rx_buffer_entries(self, rx_buffer_entries): self.m_wNrOfRxBufferEntries = rx_buffer_entries + def rx_buffer_entries(self, rx_buffer_entries): + self.m_wNrOfRxBufferEntries = rx_buffer_entries @property - def tx_buffer_entries(self): return self.m_wNrOfTxBufferEntries + def tx_buffer_entries(self): + return self.m_wNrOfTxBufferEntries @tx_buffer_entries.setter - def tx_buffer_entries(self, tx_buffer_entries): self.m_wNrOfTxBufferEntries = tx_buffer_entries + def tx_buffer_entries(self, tx_buffer_entries): + self.m_wNrOfTxBufferEntries = tx_buffer_entries class Handle(BYTE): @@ -181,6 +252,7 @@ class HardwareInfoEx(Structure): .. seealso:: :meth:`UcanServer.get_hardware_info` """ + _pack_ = 1 _fields_ = [ ("m_dwSize", DWORD), # size of this structure (only used internally) @@ -204,27 +276,43 @@ def __eq__(self, other): if not isinstance(other, HardwareInfoEx): return False - return self.device_number == other.device_number and self.serial == other.serial and \ - self.fw_version == other.fw_version and self.product_code == other.product_code and \ - self.unique_id == other.unique_id and self.flags == other.flags + return ( + self.device_number == other.device_number + and self.serial == other.serial + and self.fw_version == other.fw_version + and self.product_code == other.product_code + and self.unique_id == other.unique_id + and self.flags == other.flags + ) @property - def device_number(self): return self.m_bDeviceNr + def device_number(self): + return self.m_bDeviceNr @property - def serial(self): return self.m_dwSerialNr + def serial(self): + return self.m_dwSerialNr @property - def fw_version(self): return self.m_dwFwVersionEx + def fw_version(self): + return self.m_dwFwVersionEx @property - def product_code(self): return self.m_dwProductCode + def product_code(self): + return self.m_dwProductCode @property - def unique_id(self): return self.m_dwUniqueId0, self.m_dwUniqueId1, self.m_dwUniqueId2, self.m_dwUniqueId3 + def unique_id(self): + return ( + self.m_dwUniqueId0, + self.m_dwUniqueId1, + self.m_dwUniqueId2, + self.m_dwUniqueId3, + ) @property - def flags(self): return self.m_dwFlags + def flags(self): + return self.m_dwFlags # void PUBLIC UcanCallbackFktEx (Handle UcanHandle_p, DWORD dwEvent_p, @@ -240,13 +328,20 @@ class HardwareInitInfo(Structure): .. note:: This structure is only used internally. """ + _pack_ = 1 _fields_ = [ ("m_dwSize", DWORD), # size of this structure - ("m_fDoInitialize", BOOL), # specifies if the found module should be initialized by the DLL + ( + "m_fDoInitialize", + BOOL, + ), # specifies if the found module should be initialized by the DLL ("m_pUcanHandle", Handle), # pointer to variable receiving the USB-CAN-Handle ("m_fpCallbackFktEx", CallbackFktEx), # pointer to callback function - ("m_pCallbackArg", LPVOID), # pointer to user defined parameter for callback function + ( + "m_pCallbackArg", + LPVOID, + ), # pointer to user defined parameter for callback function ("m_fTryNext", BOOL), # specifies if a further module should be found ] @@ -258,6 +353,7 @@ class ChannelInfo(Structure): .. seealso:: :meth:`UcanServer.get_hardware_info` """ + _pack_ = 1 _fields_ = [ ("m_dwSize", DWORD), # size of this structure @@ -265,12 +361,24 @@ class ChannelInfo(Structure): ("m_bBTR0", BYTE), # Bus Timing Register 0 (see enum :class:`Baudrate`) ("m_bBTR1", BYTE), # Bus Timing Register 1 (see enum :class:`Baudrate`) ("m_bOCR", BYTE), # Output Control Register (see enum :class:`OutputControl`) - ("m_dwAMR", DWORD), # Acceptance Mask Register (see method :meth:`UcanServer.set_acceptance`) - ("m_dwACR", DWORD), # Acceptance Code Register (see method :meth:`UcanServer.set_acceptance`) + ( + "m_dwAMR", + DWORD, + ), # Acceptance Mask Register (see method :meth:`UcanServer.set_acceptance`) + ( + "m_dwACR", + DWORD, + ), # Acceptance Code Register (see method :meth:`UcanServer.set_acceptance`) ("m_dwBaudrate", DWORD), # Baudrate Register for all systec USB-CANmoduls - # (see enum :class:`BaudrateEx`) - ("m_fCanIsInit", BOOL), # True if the CAN interface is initialized, otherwise false - ("m_wCanStatus", WORD), # CAN status (same as received by method :meth:`UcanServer.get_status`) + # (see enum :class:`BaudrateEx`) + ( + "m_fCanIsInit", + BOOL, + ), # True if the CAN interface is initialized, otherwise false + ( + "m_wCanStatus", + WORD, + ), # CAN status (same as received by method :meth:`UcanServer.get_status`) ] def __init__(self): @@ -280,33 +388,48 @@ def __eq__(self, other): if not isinstance(other, ChannelInfo): return False - return self.mode == other.mode and self.BTR == other.BTR and self.OCR == other.OCR and \ - self.AMR == other.AMR and self.ACR == other.ACR and self.baudrate == other.baudrate and \ - self.can_is_init == other.can_is_init and self.can_status == other.can_status + return ( + self.mode == other.mode + and self.BTR == other.BTR + and self.OCR == other.OCR + and self.AMR == other.AMR + and self.ACR == other.ACR + and self.baudrate == other.baudrate + and self.can_is_init == other.can_is_init + and self.can_status == other.can_status + ) @property - def mode(self): return self.m_bMode + def mode(self): + return self.m_bMode @property - def BTR(self): return self.m_bBTR0 << 8 | self.m_bBTR1 + def BTR(self): + return self.m_bBTR0 << 8 | self.m_bBTR1 @property - def OCR(self): return self.m_bOCR + def OCR(self): + return self.m_bOCR @property - def AMR(self): return self.m_dwAMR + def AMR(self): + return self.m_dwAMR @property - def ACR(self): return self.m_dwACR + def ACR(self): + return self.m_dwACR @property - def baudrate(self): return self.m_dwBaudrate + def baudrate(self): + return self.m_dwBaudrate @property - def can_is_init(self): return self.m_fCanIsInit + def can_is_init(self): + return self.m_fCanIsInit @property - def can_status(self): return self.m_wCanStatus + def can_status(self): + return self.m_wCanStatus class MsgCountInfo(Structure): @@ -318,16 +441,19 @@ class MsgCountInfo(Structure): .. note:: This structure is only used internally. """ + _fields_ = [ ("m_wSentMsgCount", WORD), # number of sent CAN messages ("m_wRecvdMsgCount", WORD), # number of received CAN messages ] @property - def sent_msg_count(self): return self.m_wSentMsgCount + def sent_msg_count(self): + return self.m_wSentMsgCount @property - def recv_msg_count(self): return self.m_wRecvdMsgCount + def recv_msg_count(self): + return self.m_wRecvdMsgCount # void (PUBLIC *ConnectControlFktEx) (DWORD dwEvent_p, DWORD dwParam_p, void* pArg_p); @@ -335,4 +461,6 @@ def recv_msg_count(self): return self.m_wRecvdMsgCount # typedef void (PUBLIC *EnumCallback) (DWORD dwIndex_p, BOOL fIsUsed_p, # HardwareInfoEx* pHwInfoEx_p, HardwareInitInfo* pInitInfo_p, void* pArg_p); -EnumCallback = FUNCTYPE(None, DWORD, BOOL, POINTER(HardwareInfoEx), POINTER(HardwareInitInfo), LPVOID) +EnumCallback = FUNCTYPE( + None, DWORD, BOOL, POINTER(HardwareInfoEx), POINTER(HardwareInitInfo), LPVOID +) diff --git a/can/interfaces/systec/ucan.py b/can/interfaces/systec/ucan.py index 8aded3804..1b08c51a0 100644 --- a/can/interfaces/systec/ucan.py +++ b/can/interfaces/systec/ucan.py @@ -21,7 +21,9 @@ def check_valid_rx_can_msg(result): :return: True if a valid CAN messages was received, otherwise False. :rtype: bool """ - return (result.value == ReturnCode.SUCCESSFUL) or (result.value > ReturnCode.WARNING) + return (result.value == ReturnCode.SUCCESSFUL) or ( + result.value > ReturnCode.WARNING + ) def check_tx_ok(result): @@ -37,7 +39,9 @@ def check_tx_ok(result): .. :seealso: :const:`ReturnCode.WARN_TXLIMIT` """ - return (result.value == ReturnCode.SUCCESSFUL) or (result.value > ReturnCode.WARNING) + return (result.value == ReturnCode.SUCCESSFUL) or ( + result.value > ReturnCode.WARNING + ) def check_tx_success(result): @@ -81,7 +85,9 @@ def check_error(result): :return: True if a function returned error, otherwise False. :rtype: bool """ - return (result.value != ReturnCode.SUCCESSFUL) and (result.value < ReturnCode.WARNING) + return (result.value != ReturnCode.SUCCESSFUL) and ( + result.value < ReturnCode.WARNING + ) def check_error_cmd(result): @@ -113,7 +119,7 @@ def check_result(result, func, arguments): try: # Select the proper dll architecture - lib = WinDLL('usbcan64.dll' if sys.maxsize > 2 ** 32 else 'usbcan32.dll') + lib = WinDLL("usbcan64.dll" if sys.maxsize > 2 ** 32 else "usbcan32.dll") # BOOL PUBLIC UcanSetDebugMode (DWORD dwDbgLevel_p, _TCHAR* pszFilePathName_p, DWORD dwFlags_p); UcanSetDebugMode = lib.UcanSetDebugMode @@ -149,7 +155,17 @@ def check_result(result, func, arguments): # DWORD dwProductCodeLow_p, DWORD dwProductCodeHigh_p); UcanEnumerateHardware = lib.UcanEnumerateHardware UcanEnumerateHardware.restype = DWORD - UcanEnumerateHardware.argtypes = [EnumCallback, LPVOID, BOOL, BYTE, BYTE, DWORD, DWORD, DWORD, DWORD] + UcanEnumerateHardware.argtypes = [ + EnumCallback, + LPVOID, + BOOL, + BYTE, + BYTE, + DWORD, + DWORD, + DWORD, + DWORD, + ] # BYTE PUBLIC UcanInitHardwareEx (Handle* pUcanHandle_p, BYTE bDeviceNr_p, # CallbackFktEx fpCallbackFktEx_p, void* pCallbackArg_p); @@ -176,8 +192,12 @@ def check_result(result, func, arguments): # ChannelInfo* pCanInfoCh0_p, ChannelInfo* pCanInfoCh1_p); UcanGetHardwareInfoEx2 = lib.UcanGetHardwareInfoEx2 UcanGetHardwareInfoEx2.restype = ReturnCode - UcanGetHardwareInfoEx2.argtypes = [Handle, POINTER(HardwareInfoEx), POINTER(ChannelInfo), - POINTER(ChannelInfo)] + UcanGetHardwareInfoEx2.argtypes = [ + Handle, + POINTER(HardwareInfoEx), + POINTER(ChannelInfo), + POINTER(ChannelInfo), + ] UcanGetHardwareInfoEx2.errcheck = check_result # BYTE PUBLIC UcanInitCanEx2 (Handle UcanHandle_p, BYTE bChannel_p, tUcaninit_canParam* pinit_canParam_p); @@ -210,7 +230,12 @@ def check_result(result, func, arguments): # CanMsg* pCanMsg_p, DWORD* pdwCount_p); UcanReadCanMsgEx = lib.UcanReadCanMsgEx UcanReadCanMsgEx.restype = ReturnCode - UcanReadCanMsgEx.argtypes = [Handle, POINTER(BYTE), POINTER(CanMsg), POINTER(DWORD)] + UcanReadCanMsgEx.argtypes = [ + Handle, + POINTER(BYTE), + POINTER(CanMsg), + POINTER(DWORD), + ] UcanReadCanMsgEx.errcheck = check_result # BYTE PUBLIC UcanWriteCanMsgEx (Handle UcanHandle_p, BYTE bChannel_p, @@ -295,6 +320,7 @@ class UcanServer: """ UcanServer is a Python wrapper class for using the usbcan32.dll / usbcan64.dll. """ + _modules_found = [] _connect_control_ref = None @@ -304,7 +330,7 @@ def __init__(self): self._hw_is_initialized = False self._ch_is_initialized = { Channel.CHANNEL_CH0: False, - Channel.CHANNEL_CH1: False + Channel.CHANNEL_CH1: False, } self._callback_ref = CallbackFktEx(self._callback) if self._connect_control_ref is None: @@ -343,16 +369,33 @@ def is_can1_initialized(self): @classmethod def _enum_callback(cls, index, is_used, hw_info_ex, init_info, arg): - cls._modules_found.append((index, bool(is_used), hw_info_ex.contents, init_info.contents)) + cls._modules_found.append( + (index, bool(is_used), hw_info_ex.contents, init_info.contents) + ) @classmethod - def enumerate_hardware(cls, device_number_low=0, device_number_high=-1, serial_low=0, serial_high=-1, - product_code_low=0, product_code_high=-1, enum_used_devices=False): + def enumerate_hardware( + cls, + device_number_low=0, + device_number_high=-1, + serial_low=0, + serial_high=-1, + product_code_low=0, + product_code_high=-1, + enum_used_devices=False, + ): cls._modules_found = [] - UcanEnumerateHardware(cls._enum_callback_ref, None, enum_used_devices, - device_number_low, device_number_high, - serial_low, serial_high, - product_code_low, product_code_high) + UcanEnumerateHardware( + cls._enum_callback_ref, + None, + enum_used_devices, + device_number_low, + device_number_high, + serial_low, + serial_high, + product_code_low, + product_code_high, + ) return cls._modules_found def init_hardware(self, serial=None, device_number=ANY_MODULE): @@ -365,14 +408,27 @@ def init_hardware(self, serial=None, device_number=ANY_MODULE): if not self._hw_is_initialized: # initialize hardware either by device number or serial if serial is None: - UcanInitHardwareEx(byref(self._handle), device_number, self._callback_ref, None) + UcanInitHardwareEx( + byref(self._handle), device_number, self._callback_ref, None + ) else: - UcanInitHardwareEx2(byref(self._handle), serial, self._callback_ref, None) + UcanInitHardwareEx2( + byref(self._handle), serial, self._callback_ref, None + ) self._hw_is_initialized = True - def init_can(self, channel=Channel.CHANNEL_CH0, BTR=Baudrate.BAUD_1MBit, baudrate=BaudrateEx.BAUDEX_USE_BTR01, - AMR=AMR_ALL, ACR=ACR_ALL, mode=Mode.MODE_NORMAL, OCR=OutputControl.OCR_DEFAULT, - rx_buffer_entries=DEFAULT_BUFFER_ENTRIES, tx_buffer_entries=DEFAULT_BUFFER_ENTRIES): + def init_can( + self, + channel=Channel.CHANNEL_CH0, + BTR=Baudrate.BAUD_1MBit, + baudrate=BaudrateEx.BAUDEX_USE_BTR01, + AMR=AMR_ALL, + ACR=ACR_ALL, + mode=Mode.MODE_NORMAL, + OCR=OutputControl.OCR_DEFAULT, + rx_buffer_entries=DEFAULT_BUFFER_ENTRIES, + tx_buffer_entries=DEFAULT_BUFFER_ENTRIES, + ): """ Initializes a specific CAN channel of a device. @@ -388,7 +444,9 @@ def init_can(self, channel=Channel.CHANNEL_CH0, BTR=Baudrate.BAUD_1MBit, baudrat :param int tx_buffer_entries: The number of maximum entries in the transmit buffer. """ if not self._ch_is_initialized.get(channel, False): - init_param = InitCanParam(mode, BTR, OCR, AMR, ACR, baudrate, rx_buffer_entries, tx_buffer_entries) + init_param = InitCanParam( + mode, BTR, OCR, AMR, ACR, baudrate, rx_buffer_entries, tx_buffer_entries + ) UcanInitCanEx2(self._handle, channel, init_param) self._ch_is_initialized[channel] = True @@ -407,7 +465,7 @@ def read_can_msg(self, channel, count): c_can_msg = (CanMsg * count)() c_count = DWORD(count) UcanReadCanMsgEx(self._handle, byref(c_channel), c_can_msg, byref(c_count)) - return c_can_msg[:c_count.value], c_channel.value + return c_can_msg[: c_count.value], c_channel.value def write_can_msg(self, channel, can_msg): """ @@ -493,7 +551,9 @@ def get_hardware_info(self): """ hw_info_ex = HardwareInfoEx() can_info_ch0, can_info_ch1 = ChannelInfo(), ChannelInfo() - UcanGetHardwareInfoEx2(self._handle, byref(hw_info_ex), byref(can_info_ch0), byref(can_info_ch1)) + UcanGetHardwareInfoEx2( + self._handle, byref(hw_info_ex), byref(can_info_ch0), byref(can_info_ch1) + ) return hw_info_ex, can_info_ch0, can_info_ch1 def get_fw_version(self): @@ -534,7 +594,7 @@ def read_cyclic_can_msg(self, channel, count): c_can_msg = (CanMsg * count)() c_count = DWORD(count) UcanReadCyclicCanMsg(self._handle, byref(c_channel), c_can_msg, c_count) - return c_can_msg[:c_count.value] + return c_can_msg[: c_count.value] def enable_cyclic_can_msg(self, channel, flags): """ @@ -570,7 +630,9 @@ def get_can_error_counter(self, channel): """ tx_error_counter = DWORD(0) rx_error_counter = DWORD(0) - UcanGetCanErrorCounter(self._handle, channel, byref(tx_error_counter), byref(rx_error_counter)) + UcanGetCanErrorCounter( + self._handle, channel, byref(tx_error_counter), byref(rx_error_counter) + ) return tx_error_counter, rx_error_counter def set_tx_timeout(self, channel, timeout): @@ -593,7 +655,11 @@ def shutdown(self, channel=Channel.CHANNEL_ALL, shutdown_hardware=True): """ # shutdown each channel if it's initialized for _channel, is_initialized in self._ch_is_initialized.items(): - if is_initialized and (_channel == channel or channel == Channel.CHANNEL_ALL or shutdown_hardware): + if is_initialized and ( + _channel == channel + or channel == Channel.CHANNEL_ALL + or shutdown_hardware + ): UcanDeinitCanEx(self._handle, _channel) self._ch_is_initialized[_channel] = False @@ -651,8 +717,13 @@ def get_can_status_message(can_status): CanStatus.CANERR_OVERRUN: "Rx-buffer is full", CanStatus.CANERR_XMTFULL: "Tx-buffer is full", } - return "OK" if can_status == CanStatus.CANERR_OK \ - else ", ".join(msg for status, msg in status_msgs.items() if can_status & status) + return ( + "OK" + if can_status == CanStatus.CANERR_OK + else ", ".join( + msg for status, msg in status_msgs.items() if can_status & status + ) + ) @staticmethod def get_baudrate_message(baudrate): @@ -729,7 +800,9 @@ def get_product_code_message(product_code): ProductCode.PRODCODE_PID_RESERVED1: "Reserved", ProductCode.PRODCODE_PID_RESERVED2: "Reserved", } - return product_code_msgs.get(product_code & PRODCODE_MASK_PID, "Product code is unknown") + return product_code_msgs.get( + product_code & PRODCODE_MASK_PID, "Product code is unknown" + ) @classmethod def convert_to_major_ver(cls, version): @@ -775,8 +848,10 @@ def check_version_is_equal_or_higher(cls, version, cmp_major, cmp_minor): :return: True if equal or higher, otherwise False. :rtype: bool """ - return (cls.convert_to_major_ver(version) > cmp_major) or \ - (cls.convert_to_major_ver(version) == cmp_major and cls.convert_to_minor_ver(version) >= cmp_minor) + return (cls.convert_to_major_ver(version) > cmp_major) or ( + cls.convert_to_major_ver(version) == cmp_major + and cls.convert_to_minor_ver(version) >= cmp_minor + ) @classmethod def check_is_systec(cls, hw_info_ex): @@ -788,7 +863,9 @@ def check_is_systec(cls, hw_info_ex): :return: True when the module is a systec USB-CANmodul, otherwise False. :rtype: bool """ - return (hw_info_ex.m_dwProductCode & PRODCODE_MASK_PID) >= ProductCode.PRODCODE_PID_MULTIPORT + return ( + hw_info_ex.m_dwProductCode & PRODCODE_MASK_PID + ) >= ProductCode.PRODCODE_PID_MULTIPORT @classmethod def check_is_G4(cls, hw_info_ex): @@ -824,8 +901,9 @@ def check_support_cyclic_msg(cls, hw_info_ex): :return: True when the module does support cyclic CAN messages, otherwise False. :rtype: bool """ - return cls.check_is_systec(hw_info_ex) and \ - cls.check_version_is_equal_or_higher(hw_info_ex.m_dwFwVersionEx, 3, 6) + return cls.check_is_systec(hw_info_ex) and cls.check_version_is_equal_or_higher( + hw_info_ex.m_dwFwVersionEx, 3, 6 + ) @classmethod def check_support_two_channel(cls, hw_info_ex): @@ -837,7 +915,9 @@ def check_support_two_channel(cls, hw_info_ex): :return: True when the module (logical device) does support two CAN channels, otherwise False. :rtype: bool """ - return cls.check_is_systec(hw_info_ex) and (hw_info_ex.m_dwProductCode & PRODCODE_PID_TWO_CHA) + return cls.check_is_systec(hw_info_ex) and ( + hw_info_ex.m_dwProductCode & PRODCODE_PID_TWO_CHA + ) @classmethod def check_support_term_resistor(cls, hw_info_ex): @@ -861,9 +941,17 @@ def check_support_user_port(cls, hw_info_ex): :return: True when the module supports a user I/O port, otherwise False. :rtype: bool """ - return ((hw_info_ex.m_dwProductCode & PRODCODE_MASK_PID) != ProductCode.PRODCODE_PID_BASIC) \ - and ((hw_info_ex.m_dwProductCode & PRODCODE_MASK_PID) != ProductCode.PRODCODE_PID_RESERVED1) \ - and cls.check_version_is_equal_or_higher(hw_info_ex.m_dwFwVersionEx, 2, 16) + return ( + ( + (hw_info_ex.m_dwProductCode & PRODCODE_MASK_PID) + != ProductCode.PRODCODE_PID_BASIC + ) + and ( + (hw_info_ex.m_dwProductCode & PRODCODE_MASK_PID) + != ProductCode.PRODCODE_PID_RESERVED1 + ) + and cls.check_version_is_equal_or_higher(hw_info_ex.m_dwFwVersionEx, 2, 16) + ) @classmethod def check_support_rb_user_port(cls, hw_info_ex): @@ -899,8 +987,9 @@ def check_support_ucannet(cls, hw_info_ex): :return: True when the module does support the usage of the USB-CANnetwork driver, otherwise False. :rtype: bool """ - return cls.check_is_systec(hw_info_ex) and \ - cls.check_version_is_equal_or_higher(hw_info_ex.m_dwFwVersionEx, 3, 8) + return cls.check_is_systec(hw_info_ex) and cls.check_version_is_equal_or_higher( + hw_info_ex.m_dwFwVersionEx, 3, 8 + ) @classmethod def calculate_amr(cls, is_extended, from_id, to_id, rtr_only=False, rtr_too=True): @@ -915,8 +1004,14 @@ def calculate_amr(cls, is_extended, from_id, to_id, rtr_only=False, rtr_too=True :return: Value for AMR. :rtype: int """ - return (((from_id ^ to_id) << 3) | (0x7 if rtr_too and not rtr_only else 0x3)) if is_extended else \ - (((from_id ^ to_id) << 21) | (0x1FFFFF if rtr_too and not rtr_only else 0xFFFFF)) + return ( + (((from_id ^ to_id) << 3) | (0x7 if rtr_too and not rtr_only else 0x3)) + if is_extended + else ( + ((from_id ^ to_id) << 21) + | (0x1FFFFF if rtr_too and not rtr_only else 0xFFFFF) + ) + ) @classmethod def calculate_acr(cls, is_extended, from_id, to_id, rtr_only=False, rtr_too=True): @@ -931,8 +1026,11 @@ def calculate_acr(cls, is_extended, from_id, to_id, rtr_only=False, rtr_too=True :return: Value for ACR. :rtype: int """ - return (((from_id & to_id) << 3) | (0x04 if rtr_only else 0)) if is_extended else \ - (((from_id & to_id) << 21) | (0x100000 if rtr_only else 0)) + return ( + (((from_id & to_id) << 3) | (0x04 if rtr_only else 0)) + if is_extended + else (((from_id & to_id) << 21) | (0x100000 if rtr_only else 0)) + ) def _connect_control(self, event, param, arg): """ diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index b4852db91..29a76f468 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -9,7 +9,7 @@ from .structures import * from .ucan import UcanServer -log = logging.getLogger('can.systec') +log = logging.getLogger("can.systec") class Ucan(UcanServer): @@ -46,7 +46,7 @@ class UcanBus(BusABC): 250000: Baudrate.BAUD_250kBit, 500000: Baudrate.BAUD_500kBit, 800000: Baudrate.BAUD_800kBit, - 1000000: Baudrate.BAUD_1MBit + 1000000: Baudrate.BAUD_1MBit, } def __init__(self, channel, can_filters=None, **kwargs): @@ -97,14 +97,14 @@ def __init__(self, channel, can_filters=None, **kwargs): raise ImportError("The SYSTEC ucan library has not been initialized.") self.channel = int(channel) - device_number = int(kwargs.get('device_number', ANY_MODULE)) + device_number = int(kwargs.get("device_number", ANY_MODULE)) # configuration options - bitrate = kwargs.get('bitrate', 500000) + bitrate = kwargs.get("bitrate", 500000) if bitrate not in self.BITRATES: raise ValueError("Invalid bitrate {}".format(bitrate)) - state = kwargs.get('state', BusState.ACTIVE) + state = kwargs.get("state", BusState.ACTIVE) if state is BusState.ACTIVE or state is BusState.PASSIVE: self._state = state else: @@ -112,10 +112,10 @@ def __init__(self, channel, can_filters=None, **kwargs): # get parameters self._params = { - "mode": Mode.MODE_NORMAL | - (Mode.MODE_TX_ECHO if kwargs.get('receive_own_messages') else 0) | - (Mode.MODE_LISTEN_ONLY if state is BusState.PASSIVE else 0), - "BTR": self.BITRATES[bitrate] + "mode": Mode.MODE_NORMAL + | (Mode.MODE_TX_ECHO if kwargs.get("receive_own_messages") else 0) + | (Mode.MODE_LISTEN_ONLY if state is BusState.PASSIVE else 0), + "BTR": self.BITRATES[bitrate], } # get extra parameters if kwargs.get("rx_buffer_entries"): @@ -126,11 +126,11 @@ def __init__(self, channel, can_filters=None, **kwargs): self._ucan.init_hardware(device_number=device_number) self._ucan.init_can(self.channel, **self._params) hw_info_ex, _, _ = self._ucan.get_hardware_info() - self.channel_info = '%s, S/N %s, CH %s, BTR %s' % ( + self.channel_info = "%s, S/N %s, CH %s, BTR %s" % ( self._ucan.get_product_code_message(hw_info_ex.product_code), hw_info_ex.serial, self.channel, - self._ucan.get_baudrate_message(self.BITRATES[bitrate]) + self._ucan.get_baudrate_message(self.BITRATES[bitrate]), ) self._is_filtered = False @@ -141,12 +141,14 @@ def _recv_internal(self, timeout): if not message: return None, False - msg = Message(timestamp=float(message[0].time) / 1000.0, - is_remote_frame=bool(message[0].frame_format & MsgFrameFormat.MSG_FF_RTR), - is_extended_id=bool(message[0].frame_format & MsgFrameFormat.MSG_FF_EXT), - arbitration_id=message[0].id, - dlc=len(message[0].data), - data=message[0].data) + msg = Message( + timestamp=float(message[0].time) / 1000.0, + is_remote_frame=bool(message[0].frame_format & MsgFrameFormat.MSG_FF_RTR), + is_extended_id=bool(message[0].frame_format & MsgFrameFormat.MSG_FF_EXT), + arbitration_id=message[0].id, + dlc=len(message[0].data), + data=message[0].data, + ) return msg, self._is_filtered def send(self, msg, timeout=None): @@ -171,11 +173,13 @@ def send(self, msg, timeout=None): if timeout is not None and timeout >= 0: self._ucan.set_tx_timeout(self.channel, int(timeout * 1000)) - message = CanMsg(msg.arbitration_id, - MsgFrameFormat.MSG_FF_STD | - (MsgFrameFormat.MSG_FF_EXT if msg.is_extended_id else 0) | - (MsgFrameFormat.MSG_FF_RTR if msg.is_remote_frame else 0), - msg.data) + message = CanMsg( + msg.arbitration_id, + MsgFrameFormat.MSG_FF_STD + | (MsgFrameFormat.MSG_FF_EXT if msg.is_extended_id else 0) + | (MsgFrameFormat.MSG_FF_RTR if msg.is_remote_frame else 0), + msg.data, + ) self._ucan.write_can_msg(self.channel, [message]) @staticmethod @@ -184,28 +188,36 @@ def _detect_available_configs(): try: # index, is_used, hw_info_ex, init_info for _, _, hw_info_ex, _ in Ucan.enumerate_hardware(): - configs.append({'interface': 'systec', - 'channel': Channel.CHANNEL_CH0, - 'device_number': hw_info_ex.device_number}) + configs.append( + { + "interface": "systec", + "channel": Channel.CHANNEL_CH0, + "device_number": hw_info_ex.device_number, + } + ) if Ucan.check_support_two_channel(hw_info_ex): - configs.append({'interface': 'systec', - 'channel': Channel.CHANNEL_CH1, - 'device_number': hw_info_ex.device_number}) + configs.append( + { + "interface": "systec", + "channel": Channel.CHANNEL_CH1, + "device_number": hw_info_ex.device_number, + } + ) except Exception: log.warning("The SYSTEC ucan library has not been initialized.") return configs def _apply_filters(self, filters): if filters and len(filters) == 1: - can_id = filters[0]['can_id'] - can_mask = filters[0]['can_mask'] + can_id = filters[0]["can_id"] + can_mask = filters[0]["can_mask"] self._ucan.set_acceptance(self.channel, can_mask, can_id) self._is_filtered = True - log.info('Hardware filtering on ID 0x%X, mask 0x%X', can_id, can_mask) + log.info("Hardware filtering on ID 0x%X, mask 0x%X", can_id, can_mask) else: self._ucan.set_acceptance(self.channel) self._is_filtered = False - log.info('Hardware filtering has been disabled') + log.info("Hardware filtering has been disabled") def flush_tx_buffer(self): """ @@ -214,7 +226,7 @@ def flush_tx_buffer(self): :raises can.CanError: If flushing of the transmit buffer failed. """ - log.info('Flushing transmit buffer') + log.info("Flushing transmit buffer") self._ucan.reset_can(self.channel, ResetFlags.RESET_ONLY_TX_BUFF) @staticmethod @@ -239,11 +251,17 @@ def create_filter(extended, from_id, to_id, rtr_only, rtr_too): :return: Returns list with one filter containing a "can_id", a "can_mask" and "extended" key. """ - return [{ - "can_id": Ucan.calculate_acr(extended, from_id, to_id, rtr_only, rtr_too), - "can_mask": Ucan.calculate_amr(extended, from_id, to_id, rtr_only, rtr_too), - "extended": extended - }] + return [ + { + "can_id": Ucan.calculate_acr( + extended, from_id, to_id, rtr_only, rtr_too + ), + "can_mask": Ucan.calculate_amr( + extended, from_id, to_id, rtr_only, rtr_too + ), + "extended": extended, + } + ] @property def state(self): @@ -251,7 +269,9 @@ def state(self): @state.setter def state(self, new_state): - if self._state is not BusState.ERROR and (new_state is BusState.ACTIVE or new_state is BusState.PASSIVE): + if self._state is not BusState.ERROR and ( + new_state is BusState.ACTIVE or new_state is BusState.PASSIVE + ): # close the CAN channel self._ucan.shutdown(self.channel, False) # set mode diff --git a/can/interfaces/usb2can/serial_selector.py b/can/interfaces/usb2can/serial_selector.py index ddd7ff984..f124df196 100644 --- a/can/interfaces/usb2can/serial_selector.py +++ b/can/interfaces/usb2can/serial_selector.py @@ -14,16 +14,30 @@ def WMIDateStringToDate(dtmDate): if dtmDate[4] == 0: - strDateTime = dtmDate[5] + '/' + strDateTime = dtmDate[5] + "/" else: - strDateTime = dtmDate[4] + dtmDate[5] + '/' + strDateTime = dtmDate[4] + dtmDate[5] + "/" if dtmDate[6] == 0: - strDateTime = strDateTime + dtmDate[7] + '/' + 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] + 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 diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index ffe04979a..81ba027ce 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -12,7 +12,7 @@ from .serial_selector import find_serial_devices # Set up logging -log = logging.getLogger('can.usb2can') +log = logging.getLogger("can.usb2can") def message_convert_tx(msg): @@ -46,13 +46,15 @@ def message_convert_rx(message_rx): is_remote_frame = bool(message_rx.flags & IS_REMOTE_FRAME) is_error_frame = bool(message_rx.flags & IS_ERROR_FRAME) - return Message(timestamp=message_rx.timestamp, - is_remote_frame=is_remote_frame, - is_extended_id=is_extended_id, - is_error_frame=is_error_frame, - arbitration_id=message_rx.id, - dlc=message_rx.sizeData, - data=message_rx.data[:message_rx.sizeData]) + return Message( + timestamp=message_rx.timestamp, + is_remote_frame=is_remote_frame, + is_extended_id=is_extended_id, + is_error_frame=is_error_frame, + arbitration_id=message_rx.id, + dlc=message_rx.sizeData, + data=message_rx.data[: message_rx.sizeData], + ) class Usb2canBus(BusABC): @@ -83,8 +85,15 @@ class Usb2canBus(BusABC): """ - def __init__(self, channel=None, dll="usb2can.dll", flags=0x00000008, *args, - bitrate=500000, **kwargs): + def __init__( + self, + channel=None, + dll="usb2can.dll", + flags=0x00000008, + *args, + bitrate=500000, + **kwargs + ): self.can = Usb2CanAbstractionLayer(dll) @@ -106,8 +115,9 @@ def __init__(self, channel=None, dll="usb2can.dll", flags=0x00000008, *args, connector = "{}; {}".format(device_id, baudrate) self.handle = self.can.open(connector, flags_t) - super().__init__(channel=channel, dll=dll, flags_t=flags_t, bitrate=bitrate, - *args, **kwargs) + super().__init__( + channel=channel, dll=dll, flags_t=flags_t, bitrate=bitrate, *args, **kwargs + ) def send(self, msg, timeout=None): tx = message_convert_tx(msg) @@ -120,7 +130,6 @@ def send(self, msg, timeout=None): if status != CANAL_ERROR_SUCCESS: raise CanError("could not send message: status == {}".format(status)) - def _recv_internal(self, timeout): messagerx = CanalMsg() @@ -137,7 +146,7 @@ def _recv_internal(self, timeout): elif status in (CANAL_ERROR_RCV_EMPTY, CANAL_ERROR_TIMEOUT): rx = None else: - log.error('Canal Error %s', status) + log.error("Canal Error %s", status) rx = None return rx, False @@ -170,4 +179,4 @@ def detect_available_configs(serial_matcher=None): else: channels = find_serial_devices() - return [{'interface': 'usb2can', 'channel': c} for c in channels] + return [{"interface": "usb2can", "channel": c} for c in channels] diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index e17f255a8..0aaf1b4f2 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -11,7 +11,7 @@ import can -log = logging.getLogger('can.usb2can') +log = logging.getLogger("can.usb2can") # type definitions flags_t = c_ulong @@ -31,33 +31,39 @@ 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)] + _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)] + _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)] + _fields_ = [ + ("flags", c_ulong), + ("obid", c_ulong), + ("id", c_ulong), + ("sizeData", c_ubyte), + ("data", c_ubyte * 8), + ("timestamp", c_ulong), + ] class Usb2CanAbstractionLayer: @@ -75,7 +81,7 @@ def __init__(self, dll="usb2can.dll"): self.__m_dllBasic = windll.LoadLibrary(dll) if self.__m_dllBasic is None: - log.warning('DLL failed to load at path: {}'.format(dll)) + log.warning("DLL failed to load at path: {}".format(dll)) def open(self, configuration, flags): """ @@ -90,19 +96,25 @@ def open(self, configuration, flags): try: # we need to convert this into bytes, since the underlying DLL cannot # handle non-ASCII configuration strings - config_ascii = configuration.encode('ascii', 'ignore') + config_ascii = configuration.encode("ascii", "ignore") result = self.__m_dllBasic.CanalOpen(config_ascii, flags) except Exception as ex: # catch any errors thrown by this call and re-raise - raise can.CanError('CanalOpen() failed, configuration: "{}", error: {}' - .format(configuration, ex)) + raise can.CanError( + 'CanalOpen() failed, configuration: "{}", error: {}'.format( + configuration, ex + ) + ) else: # any greater-than-zero return value indicates a success # (see https://grodansparadis.gitbooks.io/the-vscp-daemon/canal_interface_specification.html) # raise an error if the return code is <= 0 if result <= 0: - raise can.CanError('CanalOpen() failed, configuration: "{}", return code: {}' - .format(configuration, result)) + raise can.CanError( + 'CanalOpen() failed, configuration: "{}", return code: {}'.format( + configuration, result + ) + ) else: return result @@ -111,7 +123,7 @@ def close(self, handle): res = self.__m_dllBasic.CanalClose(handle) return res except: - log.warning('Failed to close') + log.warning("Failed to close") raise def send(self, handle, msg): @@ -119,7 +131,7 @@ def send(self, handle, msg): res = self.__m_dllBasic.CanalSend(handle, msg) return res except: - log.warning('Sending error') + log.warning("Sending error") raise can.CanError("Failed to transmit frame") def receive(self, handle, msg): @@ -127,7 +139,7 @@ def receive(self, handle, msg): res = self.__m_dllBasic.CanalReceive(handle, msg) return res except: - log.warning('Receive error') + log.warning("Receive error") raise def blocking_send(self, handle, msg, timeout): @@ -135,7 +147,7 @@ def blocking_send(self, handle, msg, timeout): res = self.__m_dllBasic.CanalBlockingSend(handle, msg, timeout) return res except: - log.warning('Blocking send error') + log.warning("Blocking send error") raise def blocking_receive(self, handle, msg, timeout): @@ -143,7 +155,7 @@ def blocking_receive(self, handle, msg, timeout): res = self.__m_dllBasic.CanalBlockingReceive(handle, msg, timeout) return res except: - log.warning('Blocking Receive Failed') + log.warning("Blocking Receive Failed") raise def get_status(self, handle, status): @@ -151,7 +163,7 @@ def get_status(self, handle, status): res = self.__m_dllBasic.CanalGetStatus(handle, status) return res except: - log.warning('Get status failed') + log.warning("Get status failed") raise def get_statistics(self, handle, statistics): @@ -159,7 +171,7 @@ def get_statistics(self, handle, statistics): res = self.__m_dllBasic.CanalGetStatistics(handle, statistics) return res except: - log.warning('Get Statistics failed') + log.warning("Get Statistics failed") raise def get_version(self): @@ -167,7 +179,7 @@ def get_version(self): res = self.__m_dllBasic.CanalGetVersion() return res except: - log.warning('Failed to get version info') + log.warning("Failed to get version info") raise def get_library_version(self): @@ -175,7 +187,7 @@ def get_library_version(self): res = self.__m_dllBasic.CanalGetDllVersion() return res except: - log.warning('Failed to get DLL version') + log.warning("Failed to get DLL version") raise def get_vendor_string(self): @@ -183,5 +195,5 @@ def get_vendor_string(self): res = self.__m_dllBasic.CanalGetVendorString() return res except: - log.warning('Failed to get vendor string') + log.warning("Failed to get vendor string") raise diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 01ee1a0a8..a34ebe853 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -15,11 +15,13 @@ try: # Try builtin Python 3 Windows API from _winapi import WaitForSingleObject, INFINITE + HAS_EVENTS = True except ImportError: try: # Try pywin32 package from win32event import WaitForSingleObject, INFINITE + HAS_EVENTS = True except ImportError: # Use polling instead @@ -40,17 +42,32 @@ try: from . import vxlapi except Exception as exc: - LOG.warning('Could not import vxlapi: %s', 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, - receive_own_messages=False, - bitrate=None, rx_queue_size=2**14, app_name="CANalyzer", - serial=None, fd=False, data_bitrate=None, sjwAbr=2, tseg1Abr=6, - tseg2Abr=3, sjwDbr=2, tseg1Dbr=6, tseg2Dbr=3, **kwargs): + def __init__( + self, + channel, + can_filters=None, + poll_interval=0.01, + receive_own_messages=False, + bitrate=None, + rx_queue_size=2 ** 14, + app_name="CANalyzer", + serial=None, + fd=False, + data_bitrate=None, + sjwAbr=2, + tseg1Abr=6, + tseg2Abr=3, + sjwDbr=2, + tseg1Dbr=6, + tseg2Dbr=3, + **kwargs + ): """ :param list channel: The channel indexes to create this bus with. @@ -85,10 +102,12 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, 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() if app_name is not None else '' - self.channel_info = 'Application %s: %s' % ( - app_name, ', '.join('CAN %d' % (ch + 1) for ch in self.channels)) + self.channels = [int(ch.strip()) for ch in channel.split(",")] + self._app_name = app_name.encode() if app_name is not None else "" + self.channel_info = "Application %s: %s" % ( + app_name, + ", ".join("CAN %d" % (ch + 1) for ch in self.channels), + ) if serial is not None: app_name = None @@ -100,11 +119,15 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, channel_index.append(channel_config.channelIndex) if channel_index: if len(channel_index) != len(self.channels): - LOG.info("At least one defined channel wasn't found on the specified hardware.") + LOG.info( + "At least one defined channel wasn't found on the specified hardware." + ) self.channels = channel_index else: # Is there any better way to raise the error? - raise Exception("None of the configured channels could be found on the specified hardware.") + raise Exception( + "None of the configured channels could be found on the specified hardware." + ) vxlapi.xlOpenDriver() self.port_handle = vxlapi.XLportHandle(vxlapi.XL_INVALID_PORTHANDLE) @@ -120,19 +143,28 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, 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) - idx = vxlapi.xlGetChannelIndex(hw_type.value, hw_index.value, - hw_channel.value) + vxlapi.xlGetApplConfig( + self._app_name, + channel, + hw_type, + hw_index, + hw_channel, + vxlapi.XL_BUS_TYPE_CAN, + ) + LOG.debug("Channel index %d found", channel) + idx = vxlapi.xlGetChannelIndex( + hw_type.value, hw_index.value, hw_channel.value + ) if idx < 0: # Undocumented behavior! See issue #353. # If hardware is unavailable, this function returns -1. # Raise an exception as if the driver # would have signalled XL_ERR_HW_NOT_PRESENT. - raise VectorError(vxlapi.XL_ERR_HW_NOT_PRESENT, - "XL_ERR_HW_NOT_PRESENT", - "xlGetChannelIndex") + raise VectorError( + vxlapi.XL_ERR_HW_NOT_PRESENT, + "XL_ERR_HW_NOT_PRESENT", + "xlGetChannelIndex", + ) else: # Channel already given as global channel idx = channel @@ -146,16 +178,30 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, if bitrate or fd: permission_mask.value = self.mask if fd: - vxlapi.xlOpenPort(self.port_handle, self._app_name, self.mask, - permission_mask, rx_queue_size, - vxlapi.XL_INTERFACE_VERSION_V4, vxlapi.XL_BUS_TYPE_CAN) + vxlapi.xlOpenPort( + self.port_handle, + self._app_name, + self.mask, + permission_mask, + rx_queue_size, + vxlapi.XL_INTERFACE_VERSION_V4, + vxlapi.XL_BUS_TYPE_CAN, + ) else: - vxlapi.xlOpenPort(self.port_handle, self._app_name, self.mask, - permission_mask, rx_queue_size, - vxlapi.XL_INTERFACE_VERSION, vxlapi.XL_BUS_TYPE_CAN) + 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) + "Open Port: PortHandle: %d, PermissionMask: 0x%X", + self.port_handle.value, + permission_mask.value, + ) if permission_mask.value == self.mask: if fd: @@ -175,16 +221,34 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, self.canFdConf.tseg1Dbr = ctypes.c_uint(tseg1Dbr) self.canFdConf.tseg2Dbr = ctypes.c_uint(tseg2Dbr) - vxlapi.xlCanFdSetConfiguration(self.port_handle, self.mask, self.canFdConf) - LOG.info('SetFdConfig.: ABaudr.=%u, DBaudr.=%u', self.canFdConf.arbitrationBitRate, self.canFdConf.dataBitRate) - LOG.info('SetFdConfig.: sjwAbr=%u, tseg1Abr=%u, tseg2Abr=%u', self.canFdConf.sjwAbr, self.canFdConf.tseg1Abr, self.canFdConf.tseg2Abr) - LOG.info('SetFdConfig.: sjwDbr=%u, tseg1Dbr=%u, tseg2Dbr=%u', self.canFdConf.sjwDbr, self.canFdConf.tseg1Dbr, self.canFdConf.tseg2Dbr) + vxlapi.xlCanFdSetConfiguration( + self.port_handle, self.mask, self.canFdConf + ) + LOG.info( + "SetFdConfig.: ABaudr.=%u, DBaudr.=%u", + self.canFdConf.arbitrationBitRate, + self.canFdConf.dataBitRate, + ) + LOG.info( + "SetFdConfig.: sjwAbr=%u, tseg1Abr=%u, tseg2Abr=%u", + self.canFdConf.sjwAbr, + self.canFdConf.tseg1Abr, + self.canFdConf.tseg2Abr, + ) + LOG.info( + "SetFdConfig.: sjwDbr=%u, tseg1Dbr=%u, tseg2Dbr=%u", + self.canFdConf.sjwDbr, + self.canFdConf.tseg1Dbr, + self.canFdConf.tseg2Dbr, + ) else: if bitrate: - vxlapi.xlCanSetChannelBitrate(self.port_handle, permission_mask, bitrate) - LOG.info('SetChannelBitrate: baudr.=%u',bitrate) + vxlapi.xlCanSetChannelBitrate( + self.port_handle, permission_mask, bitrate + ) + LOG.info("SetChannelBitrate: baudr.=%u", bitrate) else: - LOG.info('No init access!') + LOG.info("No init access!") # Enable/disable TX receipts tx_receipts = 1 if receive_own_messages else 0 @@ -194,11 +258,12 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, self.event_handle = vxlapi.XLhandle() vxlapi.xlSetNotification(self.port_handle, self.event_handle, 1) else: - LOG.info('Install pywin32 to avoid polling') + LOG.info("Install pywin32 to avoid polling") try: - vxlapi.xlActivateChannel(self.port_handle, self.mask, - vxlapi.XL_BUS_TYPE_CAN, 0) + vxlapi.xlActivateChannel( + self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0 + ) except VectorError: self.shutdown() raise @@ -214,13 +279,21 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, def _apply_filters(self, filters): if filters: # Only up to one filter per ID type allowed - if len(filters) == 1 or (len(filters) == 2 and - filters[0].get("extended") != filters[1].get("extended")): + if len(filters) == 1 or ( + len(filters) == 2 + and filters[0].get("extended") != filters[1].get("extended") + ): try: for can_filter in filters: - 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) + 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) # go to fallback @@ -234,8 +307,12 @@ def _apply_filters(self, filters): # fallback: reset filters self._is_filtered = False try: - vxlapi.xlCanSetChannelAcceptance(self.port_handle, self.mask, 0x0, 0x0, vxlapi.XL_CAN_EXT) - vxlapi.xlCanSetChannelAcceptance(self.port_handle, self.mask, 0x0, 0x0, vxlapi.XL_CAN_STD) + vxlapi.xlCanSetChannelAcceptance( + self.port_handle, self.mask, 0x0, 0x0, vxlapi.XL_CAN_EXT + ) + vxlapi.xlCanSetChannelAcceptance( + self.port_handle, self.mask, 0x0, 0x0, vxlapi.XL_CAN_STD + ) except VectorError as exc: LOG.warning("Could not reset filters: %s", exc) @@ -256,7 +333,10 @@ def _recv_internal(self, timeout): if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY: raise else: - if event.tag == vxlapi.XL_CAN_EV_TAG_RX_OK or event.tag == vxlapi.XL_CAN_EV_TAG_TX_OK: + if ( + event.tag == vxlapi.XL_CAN_EV_TAG_RX_OK + or event.tag == vxlapi.XL_CAN_EV_TAG_TX_OK + ): msg_id = event.tagData.canRxOkMsg.canId dlc = dlc2len(event.tagData.canRxOkMsg.dlc) flags = event.tagData.canRxOkMsg.msgFlags @@ -269,11 +349,14 @@ def _recv_internal(self, timeout): is_remote_frame=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_RTR), is_error_frame=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_EF), is_fd=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_EDL), - error_state_indicator=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_ESI), + error_state_indicator=bool( + flags & vxlapi.XL_CAN_RXMSG_FLAG_ESI + ), bitrate_switch=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_BRS), dlc=dlc, data=event.tagData.canRxOkMsg.data[:dlc], - channel=channel) + channel=channel, + ) return msg, self._is_filtered else: event_count.value = 1 @@ -293,12 +376,17 @@ def _recv_internal(self, timeout): timestamp=timestamp + self._time_offset, arbitration_id=msg_id & 0x1FFFFFFF, is_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), + is_remote_frame=bool( + flags & vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME + ), + is_error_frame=bool( + flags & vxlapi.XL_CAN_MSG_FLAG_ERROR_FRAME + ), is_fd=False, dlc=dlc, data=event.tagData.msg.data[:dlc], - channel=channel) + channel=channel, + ) return msg, self._is_filtered if end_time is not None and time.time() > end_time: @@ -341,14 +429,16 @@ def send(self, msg, timeout=None): XLcanTxEvent = vxlapi.XLcanTxEvent() XLcanTxEvent.tag = vxlapi.XL_CAN_EV_TAG_TX_MSG - XLcanTxEvent.transId = 0xffff + XLcanTxEvent.transId = 0xFFFF XLcanTxEvent.tagData.canMsg.canId = msg_id XLcanTxEvent.tagData.canMsg.msgFlags = flags XLcanTxEvent.tagData.canMsg.dlc = len2dlc(msg.dlc) for idx, value in enumerate(msg.data): XLcanTxEvent.tagData.canMsg.data[idx] = value - vxlapi.xlCanTransmitEx(self.port_handle, mask, message_count, MsgCntSent, XLcanTxEvent) + vxlapi.xlCanTransmitEx( + self.port_handle, mask, message_count, MsgCntSent, XLcanTxEvent + ) else: if msg.is_remote_frame: @@ -376,23 +466,29 @@ def shutdown(self): def reset(self): vxlapi.xlDeactivateChannel(self.port_handle, self.mask) - vxlapi.xlActivateChannel(self.port_handle, self.mask, - vxlapi.XL_BUS_TYPE_CAN, 0) + vxlapi.xlActivateChannel(self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0) @staticmethod def _detect_available_configs(): configs = [] channel_configs = get_channel_configs() - LOG.info('Found %d channels', len(channel_configs)) + LOG.info("Found %d channels", len(channel_configs)) for channel_config in channel_configs: - LOG.info('Channel index %d: %s', - channel_config.channelIndex, - channel_config.name.decode('ascii')) - configs.append({'interface': 'vector', - 'app_name': None, - 'channel': channel_config.channelIndex}) + LOG.info( + "Channel index %d: %s", + channel_config.channelIndex, + channel_config.name.decode("ascii"), + ) + configs.append( + { + "interface": "vector", + "app_name": None, + "channel": channel_config.channelIndex, + } + ) return configs + def get_channel_configs(): if vxlapi is None: return [] diff --git a/can/interfaces/vector/exceptions.py b/can/interfaces/vector/exceptions.py index b88f6c527..8e4de1aff 100644 --- a/can/interfaces/vector/exceptions.py +++ b/can/interfaces/vector/exceptions.py @@ -7,7 +7,6 @@ class VectorError(CanError): - def __init__(self, error_code, error_string, function): self.error_code = error_code super().__init__(f"{function} failed ({error_string})") diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index e1b93bdc7..5669490ac 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -20,7 +20,7 @@ # Vector XL API Definitions # ========================= # Load Windows DLL -DLL_NAME = 'vxlapi64' if platform.architecture()[0] == '64bit' else 'vxlapi' +DLL_NAME = "vxlapi64" if platform.architecture()[0] == "64bit" else "vxlapi" _xlapi_dll = ctypes.windll.LoadLibrary(DLL_NAME) XL_BUS_TYPE_CAN = 0x00000001 @@ -67,137 +67,207 @@ # 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)] - + _fields_ = [ + ("id", ctypes.c_ulong), + ("flags", ctypes.c_ushort), + ("dlc", ctypes.c_ushort), + ("res1", XLuint64), + ("data", ctypes.c_ubyte * MAX_MSG_LEN), + ("res2", XLuint64), + ] class s_xl_can_ev_error(ctypes.Structure): - _fields_ = [('errorCode', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte * 95)] + _fields_ = [("errorCode", ctypes.c_ubyte), ("reserved", ctypes.c_ubyte * 95)] + class s_xl_can_ev_chip_state(ctypes.Structure): - _fields_ = [('busStatus', ctypes.c_ubyte), ('txErrorCounter', ctypes.c_ubyte), - ('rxErrorCounter', ctypes.c_ubyte),('reserved', ctypes.c_ubyte), - ('reserved0', ctypes.c_uint)] + _fields_ = [ + ("busStatus", ctypes.c_ubyte), + ("txErrorCounter", ctypes.c_ubyte), + ("rxErrorCounter", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte), + ("reserved0", ctypes.c_uint), + ] + class s_xl_can_ev_sync_pulse(ctypes.Structure): - _fields_ = [('triggerSource', ctypes.c_uint), ('reserved', ctypes.c_uint), - ('time', XLuint64)] + _fields_ = [ + ("triggerSource", ctypes.c_uint), + ("reserved", ctypes.c_uint), + ("time", XLuint64), + ] + # BASIC bus message structure class s_xl_tag_data(ctypes.Union): - _fields_ = [('msg', s_xl_can_msg)] + _fields_ = [("msg", s_xl_can_msg)] + # CAN FD messages class s_xl_can_ev_rx_msg(ctypes.Structure): - _fields_ = [('canId', ctypes.c_uint), ('msgFlags', ctypes.c_uint), - ('crc', ctypes.c_uint), ('reserved1', ctypes.c_ubyte * 12), - ('totalBitCnt', ctypes.c_ushort), ('dlc', ctypes.c_ubyte), - ('reserved', ctypes.c_ubyte * 5), ('data', ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN)] + _fields_ = [ + ("canId", ctypes.c_uint), + ("msgFlags", ctypes.c_uint), + ("crc", ctypes.c_uint), + ("reserved1", ctypes.c_ubyte * 12), + ("totalBitCnt", ctypes.c_ushort), + ("dlc", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte * 5), + ("data", ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN), + ] + class s_xl_can_ev_tx_request(ctypes.Structure): - _fields_ = [('canId', ctypes.c_uint), ('msgFlags', ctypes.c_uint), - ('dlc', ctypes.c_ubyte),('txAttemptConf', ctypes.c_ubyte), - ('reserved', ctypes.c_ushort), ('data', ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN)] + _fields_ = [ + ("canId", ctypes.c_uint), + ("msgFlags", ctypes.c_uint), + ("dlc", ctypes.c_ubyte), + ("txAttemptConf", ctypes.c_ubyte), + ("reserved", ctypes.c_ushort), + ("data", ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN), + ] + class s_xl_can_tx_msg(ctypes.Structure): - _fields_ = [('canId', ctypes.c_uint), ('msgFlags', ctypes.c_uint), - ('dlc', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte * 7), - ('data', ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN)] + _fields_ = [ + ("canId", ctypes.c_uint), + ("msgFlags", ctypes.c_uint), + ("dlc", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte * 7), + ("data", ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN), + ] + class s_rxTagData(ctypes.Union): - _fields_ = [('canRxOkMsg', s_xl_can_ev_rx_msg), ('canTxOkMsg', s_xl_can_ev_rx_msg), - ('canTxRequest', s_xl_can_ev_tx_request),('canError', s_xl_can_ev_error), - ('canChipState', s_xl_can_ev_chip_state),('canSyncPulse', s_xl_can_ev_sync_pulse)] + _fields_ = [ + ("canRxOkMsg", s_xl_can_ev_rx_msg), + ("canTxOkMsg", s_xl_can_ev_rx_msg), + ("canTxRequest", s_xl_can_ev_tx_request), + ("canError", s_xl_can_ev_error), + ("canChipState", s_xl_can_ev_chip_state), + ("canSyncPulse", s_xl_can_ev_sync_pulse), + ] + class s_txTagData(ctypes.Union): - _fields_ = [('canMsg', s_xl_can_tx_msg)] + _fields_ = [("canMsg", s_xl_can_tx_msg)] + # BASIC events 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)] + _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), + ] + # CAN FD events class XLcanRxEvent(ctypes.Structure): - _fields_ = [('size',ctypes.c_int),('tag', ctypes.c_ushort), - ('chanIndex', ctypes.c_ubyte),('reserved', ctypes.c_ubyte), - ('userHandle', ctypes.c_int),('flagsChip', ctypes.c_ushort), - ('reserved0', ctypes.c_ushort),('reserved1', XLuint64), - ('timeStamp', XLuint64),('tagData', s_rxTagData)] + _fields_ = [ + ("size", ctypes.c_int), + ("tag", ctypes.c_ushort), + ("chanIndex", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte), + ("userHandle", ctypes.c_int), + ("flagsChip", ctypes.c_ushort), + ("reserved0", ctypes.c_ushort), + ("reserved1", XLuint64), + ("timeStamp", XLuint64), + ("tagData", s_rxTagData), + ] + class XLcanTxEvent(ctypes.Structure): - _fields_ = [('tag', ctypes.c_ushort), ('transId', ctypes.c_ushort), - ('chanIndex', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte * 3), - ('tagData', s_txTagData)] + _fields_ = [ + ("tag", ctypes.c_ushort), + ("transId", ctypes.c_ushort), + ("chanIndex", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte * 3), + ("tagData", s_txTagData), + ] + # CAN FD configuration structure class XLcanFdConf(ctypes.Structure): - _fields_ = [('arbitrationBitRate', ctypes.c_uint), ('sjwAbr', ctypes.c_uint), - ('tseg1Abr', ctypes.c_uint), ('tseg2Abr', ctypes.c_uint), - ('dataBitRate', ctypes.c_uint), ('sjwDbr', ctypes.c_uint), - ('tseg1Dbr', ctypes.c_uint), ('tseg2Dbr', ctypes.c_uint), - ('reserved', ctypes.c_uint * 2)] + _fields_ = [ + ("arbitrationBitRate", ctypes.c_uint), + ("sjwAbr", ctypes.c_uint), + ("tseg1Abr", ctypes.c_uint), + ("tseg2Abr", ctypes.c_uint), + ("dataBitRate", ctypes.c_uint), + ("sjwDbr", ctypes.c_uint), + ("tseg1Dbr", ctypes.c_uint), + ("tseg2Dbr", ctypes.c_uint), + ("reserved", ctypes.c_uint * 2), + ] + class XLchannelConfig(ctypes.Structure): _pack_ = 1 _fields_ = [ - ('name', ctypes.c_char * 32), - ('hwType', ctypes.c_ubyte), - ('hwIndex', ctypes.c_ubyte), - ('hwChannel', ctypes.c_ubyte), - ('transceiverType', ctypes.c_ushort), - ('transceiverState', ctypes.c_ushort), - ('configError', ctypes.c_ushort), - ('channelIndex', ctypes.c_ubyte), - ('channelMask', XLuint64), - ('channelCapabilities', ctypes.c_uint), - ('channelBusCapabilities', ctypes.c_uint), - ('isOnBus', ctypes.c_ubyte), - ('connectedBusType', ctypes.c_uint), - ('busParams', ctypes.c_ubyte * 32), - ('_doNotUse', ctypes.c_uint), - ('driverVersion', ctypes.c_uint), - ('interfaceVersion', ctypes.c_uint), - ('raw_data', ctypes.c_uint * 10), - ('serialNumber', ctypes.c_uint), - ('articleNumber', ctypes.c_uint), - ('transceiverName', ctypes.c_char * 32), - ('specialCabFlags', ctypes.c_uint), - ('dominantTimeout', ctypes.c_uint), - ('dominantRecessiveDelay', ctypes.c_ubyte), - ('recessiveDominantDelay', ctypes.c_ubyte), - ('connectionInfo', ctypes.c_ubyte), - ('currentlyAvailableTimestamps', ctypes.c_ubyte), - ('minimalSupplyVoltage', ctypes.c_ushort), - ('maximalSupplyVoltage', ctypes.c_ushort), - ('maximalBaudrate', ctypes.c_uint), - ('fpgaCoreCapabilities', ctypes.c_ubyte), - ('specialDeviceStatus', ctypes.c_ubyte), - ('channelBusActiveCapabilities', ctypes.c_ushort), - ('breakOffset', ctypes.c_ushort), - ('delimiterOffset', ctypes.c_ushort), - ('reserved', ctypes.c_uint * 3) + ("name", ctypes.c_char * 32), + ("hwType", ctypes.c_ubyte), + ("hwIndex", ctypes.c_ubyte), + ("hwChannel", ctypes.c_ubyte), + ("transceiverType", ctypes.c_ushort), + ("transceiverState", ctypes.c_ushort), + ("configError", ctypes.c_ushort), + ("channelIndex", ctypes.c_ubyte), + ("channelMask", XLuint64), + ("channelCapabilities", ctypes.c_uint), + ("channelBusCapabilities", ctypes.c_uint), + ("isOnBus", ctypes.c_ubyte), + ("connectedBusType", ctypes.c_uint), + ("busParams", ctypes.c_ubyte * 32), + ("_doNotUse", ctypes.c_uint), + ("driverVersion", ctypes.c_uint), + ("interfaceVersion", ctypes.c_uint), + ("raw_data", ctypes.c_uint * 10), + ("serialNumber", ctypes.c_uint), + ("articleNumber", ctypes.c_uint), + ("transceiverName", ctypes.c_char * 32), + ("specialCabFlags", ctypes.c_uint), + ("dominantTimeout", ctypes.c_uint), + ("dominantRecessiveDelay", ctypes.c_ubyte), + ("recessiveDominantDelay", ctypes.c_ubyte), + ("connectionInfo", ctypes.c_ubyte), + ("currentlyAvailableTimestamps", ctypes.c_ubyte), + ("minimalSupplyVoltage", ctypes.c_ushort), + ("maximalSupplyVoltage", ctypes.c_ushort), + ("maximalBaudrate", ctypes.c_uint), + ("fpgaCoreCapabilities", ctypes.c_ubyte), + ("specialDeviceStatus", ctypes.c_ubyte), + ("channelBusActiveCapabilities", ctypes.c_ushort), + ("breakOffset", ctypes.c_ushort), + ("delimiterOffset", ctypes.c_ushort), + ("reserved", ctypes.c_uint * 3), ] + class XLdriverConfig(ctypes.Structure): _fields_ = [ - ('dllVersion', ctypes.c_uint), - ('channelCount', ctypes.c_uint), - ('reserved', ctypes.c_uint * 10), - ('channel', XLchannelConfig * 64) + ("dllVersion", ctypes.c_uint), + ("channelCount", ctypes.c_uint), + ("reserved", ctypes.c_uint * 10), + ("channel", XLchannelConfig * 64), ] + # driver status XLstatus = ctypes.c_short # porthandle -XL_INVALID_PORTHANDLE = (-1) +XL_INVALID_PORTHANDLE = -1 XLportHandle = ctypes.c_long @@ -224,8 +294,12 @@ def check_status(result, function, arguments): 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 + 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 @@ -240,8 +314,13 @@ def check_status(result, function, arguments): 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 + 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 @@ -257,22 +336,17 @@ def check_status(result, function, arguments): xlClosePort.errcheck = check_status xlSetNotification = _xlapi_dll.xlSetNotification -xlSetNotification.argtypes = [XLportHandle, ctypes.POINTER(XLhandle), - ctypes.c_int] +xlSetNotification.argtypes = [XLportHandle, ctypes.POINTER(XLhandle), ctypes.c_int] xlSetNotification.restype = XLstatus xlSetNotification.errcheck = check_status xlCanSetChannelMode = _xlapi_dll.xlCanSetChannelMode -xlCanSetChannelMode.argtypes = [ - XLportHandle, XLaccess, ctypes.c_int, ctypes.c_int -] +xlCanSetChannelMode.argtypes = [XLportHandle, XLaccess, ctypes.c_int, ctypes.c_int] xlCanSetChannelMode.restype = XLstatus xlCanSetChannelMode.errcheck = check_status xlActivateChannel = _xlapi_dll.xlActivateChannel -xlActivateChannel.argtypes = [ - XLportHandle, XLaccess, ctypes.c_uint, ctypes.c_uint -] +xlActivateChannel.argtypes = [XLportHandle, XLaccess, ctypes.c_uint, ctypes.c_uint] xlActivateChannel.restype = XLstatus xlActivateChannel.errcheck = check_status @@ -288,15 +362,15 @@ def check_status(result, function, arguments): xlReceive = _xlapi_dll.xlReceive xlReceive.argtypes = [ - XLportHandle, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLevent) + XLportHandle, + ctypes.POINTER(ctypes.c_uint), + ctypes.POINTER(XLevent), ] xlReceive.restype = XLstatus xlReceive.errcheck = check_status xlCanReceive = _xlapi_dll.xlCanReceive -xlCanReceive.argtypes = [ - XLportHandle, ctypes.POINTER(XLcanRxEvent) -] +xlCanReceive.argtypes = [XLportHandle, ctypes.POINTER(XLcanRxEvent)] xlCanReceive.restype = XLstatus xlCanReceive.errcheck = check_status @@ -311,14 +385,21 @@ def check_status(result, function, arguments): xlCanTransmit = _xlapi_dll.xlCanTransmit xlCanTransmit.argtypes = [ - XLportHandle, XLaccess, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLevent) + XLportHandle, + XLaccess, + ctypes.POINTER(ctypes.c_uint), + ctypes.POINTER(XLevent), ] xlCanTransmit.restype = XLstatus xlCanTransmit.errcheck = check_status xlCanTransmitEx = _xlapi_dll.xlCanTransmitEx xlCanTransmitEx.argtypes = [ - XLportHandle, XLaccess, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLcanTxEvent) + XLportHandle, + XLaccess, + ctypes.c_uint, + ctypes.POINTER(ctypes.c_uint), + ctypes.POINTER(XLcanTxEvent), ] xlCanTransmitEx.restype = XLstatus xlCanTransmitEx.errcheck = check_status @@ -330,7 +411,12 @@ def check_status(result, function, arguments): xlCanSetChannelAcceptance = _xlapi_dll.xlCanSetChannelAcceptance xlCanSetChannelAcceptance.argtypes = [ - XLportHandle, XLaccess, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_uint] + XLportHandle, + XLaccess, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_uint, +] xlCanSetChannelAcceptance.restype = XLstatus xlCanSetChannelAcceptance.errcheck = check_status diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index 66b951c22..b153150ee 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -44,8 +44,12 @@ class VirtualBus(BusABC): if a message is sent to 5 receivers with the timeout set to 1.0. """ - def __init__(self, channel=None, receive_own_messages=False, rx_queue_size=0, **kwargs): - super().__init__(channel=channel, receive_own_messages=receive_own_messages, **kwargs) + def __init__( + self, channel=None, receive_own_messages=False, rx_queue_size=0, **kwargs + ): + super().__init__( + channel=channel, receive_own_messages=receive_own_messages, **kwargs + ) # the channel identifier may be an arbitrary object self.channel_id = channel @@ -133,6 +137,6 @@ def _detect_available_configs(): available_channels += [extra] return [ - {'interface': 'virtual', 'channel': channel} + {"interface": "virtual", "channel": channel} for channel in available_channels ] diff --git a/can/io/asc.py b/can/io/asc.py index 419b46247..2e25e5c2c 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -21,7 +21,7 @@ CAN_MSG_EXT = 0x80000000 CAN_ID_MASK = 0x1FFFFFFF -logger = logging.getLogger('can.io.asc') +logger = logging.getLogger("can.io.asc") class ASCReader(BaseIOHandler): @@ -37,11 +37,11 @@ def __init__(self, file): If this is a file-like object, is has to opened in text read mode, not binary read mode. """ - super().__init__(file, mode='r') + super().__init__(file, mode="r") @staticmethod def _extract_can_id(str_can_id): - if str_can_id[-1:].lower() == 'x': + if str_can_id[-1:].lower() == "x": is_extended = True can_id = int(str_can_id[0:-1], 16) else: @@ -51,14 +51,16 @@ def _extract_can_id(str_can_id): def __iter__(self): for line in self.file: - #logger.debug("ASCReader: parsing line: '%s'", line.splitlines()[0]) + # logger.debug("ASCReader: parsing line: '%s'", line.splitlines()[0]) temp = line.strip() if not temp or not temp[0].isdigit(): continue try: - timestamp, channel, dummy = temp.split(None, 2) # , frameType, dlc, frameData + timestamp, channel, dummy = temp.split( + None, 2 + ) # , frameType, dlc, frameData except ValueError: # we parsed an empty comment continue @@ -70,22 +72,26 @@ def __iter__(self): except ValueError: pass - if dummy.strip()[0:10].lower() == 'errorframe': - msg = Message(timestamp=timestamp, is_error_frame=True, - channel=channel) + if dummy.strip()[0:10].lower() == "errorframe": + msg = Message(timestamp=timestamp, is_error_frame=True, channel=channel) yield msg - elif not isinstance(channel, int) or dummy.strip()[0:10].lower() == 'statistic:': + elif ( + not isinstance(channel, int) + or dummy.strip()[0:10].lower() == "statistic:" + ): pass - elif dummy[-1:].lower() == 'r': + elif dummy[-1:].lower() == "r": can_id_str, _ = dummy.split(None, 1) can_id_num, is_extended_id = self._extract_can_id(can_id_str) - msg = Message(timestamp=timestamp, - arbitration_id=can_id_num & CAN_ID_MASK, - is_extended_id=is_extended_id, - is_remote_frame=True, - channel=channel) + msg = Message( + timestamp=timestamp, + arbitration_id=can_id_num & CAN_ID_MASK, + is_extended_id=is_extended_id, + is_remote_frame=True, + channel=channel, + ) yield msg else: @@ -94,9 +100,9 @@ def __iter__(self): can_id_str, _, _, dlc, data = dummy.split(None, 4) except ValueError: # but if not, we only want to get the stuff up to the dlc - can_id_str, _, _, dlc = dummy.split(None, 3) + can_id_str, _, _, dlc = dummy.split(None, 3) # and we set data to an empty sequence manually - data = '' + data = "" dlc = int(dlc) frame = bytearray() @@ -113,7 +119,7 @@ def __iter__(self): is_remote_frame=False, dlc=dlc, data=frame, - channel=channel + channel=channel, ) self.stop() @@ -140,7 +146,7 @@ def __init__(self, file, channel=1): :param channel: a default channel to use when the message does not have a channel set """ - super().__init__(file, mode='w') + super().__init__(file, mode="w") self.channel = channel # write start of file header @@ -166,19 +172,21 @@ def log_event(self, message, timestamp=None): :param float timestamp: the absolute timestamp of the event """ - if not message: # if empty or None + if not message: # if empty or None logger.debug("ASCWriter: ignoring empty message") return # this is the case for the very first message: if not self.header_written: - self.last_timestamp = (timestamp or 0.0) + self.last_timestamp = timestamp or 0.0 self.started = self.last_timestamp - mlsec = repr(self.last_timestamp).split('.')[1][:3] - formatted_date = time.strftime(self.FORMAT_DATE.format(mlsec), time.localtime(self.last_timestamp)) + mlsec = repr(self.last_timestamp).split(".")[1][:3] + formatted_date = time.strftime( + self.FORMAT_DATE.format(mlsec), time.localtime(self.last_timestamp) + ) self.file.write("Begin Triggerblock %s\n" % formatted_date) self.header_written = True - self.log_event("Start of measurement") # caution: this is a recursive call! + self.log_event("Start of measurement") # caution: this is a recursive call! # Use last known timestamp if unknown if timestamp is None: @@ -198,7 +206,7 @@ def on_message_received(self, msg): return if msg.is_remote_frame: - dtype = 'r' + dtype = "r" data = [] else: dtype = "d {}".format(msg.dlc) @@ -206,7 +214,7 @@ def on_message_received(self, msg): arb_id = "{:X}".format(msg.arbitration_id) if msg.is_extended_id: - arb_id += 'x' + arb_id += "x" channel = channel2int(msg.channel) if channel is None: @@ -215,9 +223,8 @@ def on_message_received(self, msg): # Many interfaces start channel numbering at 0 which is invalid channel += 1 - serialized = self.FORMAT_MESSAGE.format(channel=channel, - id=arb_id, - dtype=dtype, - data=' '.join(data)) + serialized = self.FORMAT_MESSAGE.format( + channel=channel, id=arb_id, dtype=dtype, data=" ".join(data) + ) self.log_event(serialized, msg.timestamp) diff --git a/can/io/blf.py b/can/io/blf.py index ad895297e..06afd6552 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -112,15 +112,29 @@ def timestamp_to_systemtime(timestamp): # Probably not a Unix timestamp return (0, 0, 0, 0, 0, 0, 0, 0) t = datetime.datetime.fromtimestamp(timestamp) - return (t.year, t.month, t.isoweekday() % 7, t.day, - t.hour, t.minute, t.second, int(round(t.microsecond / 1000.0))) + return ( + t.year, + t.month, + t.isoweekday() % 7, + t.day, + t.hour, + t.minute, + t.second, + int(round(t.microsecond / 1000.0)), + ) def systemtime_to_timestamp(systemtime): try: t = datetime.datetime( - systemtime[0], systemtime[1], systemtime[3], - systemtime[4], systemtime[5], systemtime[6], systemtime[7] * 1000) + systemtime[0], + systemtime[1], + systemtime[3], + systemtime[4], + systemtime[5], + systemtime[6], + systemtime[7] * 1000, + ) return time.mktime(t.timetuple()) + systemtime[7] / 1000.0 except ValueError: return 0 @@ -140,7 +154,7 @@ def __init__(self, file): If this is a file-like object, is has to opened in binary read mode, not text read mode. """ - super().__init__(file, mode='rb') + super().__init__(file, mode="rb") data = self.file.read(FILE_HEADER_STRUCT.size) header = FILE_HEADER_STRUCT.unpack(data) if header[0] != b"LOGG": @@ -171,9 +185,8 @@ def __iter__(self): self.file.read(obj_data_size % 4) if obj_type == LOG_CONTAINER: - method, uncompressed_size = LOG_CONTAINER_STRUCT.unpack_from( - obj_data) - container_data = obj_data[LOG_CONTAINER_STRUCT.size:] + method, uncompressed_size = LOG_CONTAINER_STRUCT.unpack_from(obj_data) + container_data = obj_data[LOG_CONTAINER_STRUCT.size :] if method == NO_COMPRESSION: data = container_data elif method == ZLIB_DEFLATE: @@ -188,7 +201,7 @@ def __iter__(self): pos = 0 while pos + OBJ_HEADER_BASE_STRUCT.size < len(data): header = OBJ_HEADER_BASE_STRUCT.unpack_from(data, pos) - #print(header) + # print(header) if header[0] != b"LOBJ": raise BLFParseError() @@ -203,14 +216,20 @@ def __iter__(self): # Read rest of header header_version = header[2] if header_version == 1: - flags, _, _, timestamp = OBJ_HEADER_V1_STRUCT.unpack_from(data, pos) + flags, _, _, timestamp = OBJ_HEADER_V1_STRUCT.unpack_from( + data, pos + ) pos += OBJ_HEADER_V1_STRUCT.size elif header_version == 2: - flags, _, _, timestamp, _ = OBJ_HEADER_V2_STRUCT.unpack_from(data, pos) + flags, _, _, timestamp, _ = OBJ_HEADER_V2_STRUCT.unpack_from( + data, pos + ) pos += OBJ_HEADER_V2_STRUCT.size else: # Unknown header version - LOG.warning("Unknown object header version (%d)", header_version) + LOG.warning( + "Unknown object header version (%d)", header_version + ) pos = next_pos continue @@ -223,40 +242,62 @@ def __iter__(self): obj_type = header[4] # Both CAN message types have the same starting content if obj_type in (CAN_MESSAGE, CAN_MESSAGE2): - (channel, flags, dlc, can_id, - can_data) = CAN_MSG_STRUCT.unpack_from(data, pos) - msg = Message(timestamp=timestamp, - arbitration_id=can_id & 0x1FFFFFFF, - is_extended_id=bool(can_id & CAN_MSG_EXT), - is_remote_frame=bool(flags & REMOTE_FLAG), - dlc=dlc, - data=can_data[:dlc], - channel=channel - 1) + ( + channel, + flags, + dlc, + can_id, + can_data, + ) = CAN_MSG_STRUCT.unpack_from(data, pos) + msg = Message( + timestamp=timestamp, + arbitration_id=can_id & 0x1FFFFFFF, + is_extended_id=bool(can_id & CAN_MSG_EXT), + is_remote_frame=bool(flags & REMOTE_FLAG), + dlc=dlc, + data=can_data[:dlc], + channel=channel - 1, + ) yield msg elif obj_type == CAN_FD_MESSAGE: - (channel, flags, dlc, can_id, _, _, fd_flags, - _, can_data) = CAN_FD_MSG_STRUCT.unpack_from(data, pos) + ( + channel, + flags, + dlc, + can_id, + _, + _, + fd_flags, + _, + can_data, + ) = CAN_FD_MSG_STRUCT.unpack_from(data, pos) length = dlc2len(dlc) - msg = Message(timestamp=timestamp, - arbitration_id=can_id & 0x1FFFFFFF, - is_extended_id=bool(can_id & CAN_MSG_EXT), - is_remote_frame=bool(flags & REMOTE_FLAG), - is_fd=bool(fd_flags & EDL), - bitrate_switch=bool(fd_flags & BRS), - error_state_indicator=bool(fd_flags & ESI), - dlc=length, - data=can_data[:length], - channel=channel - 1) + msg = Message( + timestamp=timestamp, + arbitration_id=can_id & 0x1FFFFFFF, + is_extended_id=bool(can_id & CAN_MSG_EXT), + is_remote_frame=bool(flags & REMOTE_FLAG), + is_fd=bool(fd_flags & EDL), + bitrate_switch=bool(fd_flags & BRS), + error_state_indicator=bool(fd_flags & ESI), + dlc=length, + data=can_data[:length], + channel=channel - 1, + ) yield msg elif obj_type == CAN_FD_MESSAGE_64: ( - channel, dlc, _, _, can_id, _, fd_flags - ) = CAN_FD_MSG_64_STRUCT.unpack_from(data, pos)[:7] + channel, + dlc, + _, + _, + can_id, + _, + fd_flags, + ) = CAN_FD_MSG_64_STRUCT.unpack_from(data, pos)[:7] length = dlc2len(dlc) can_data = struct.unpack_from( - "<{}s".format(length), - data, - pos + CAN_FD_MSG_64_STRUCT.size + "<{}s".format(length), data, pos + CAN_FD_MSG_64_STRUCT.size )[0] msg = Message( timestamp=timestamp, @@ -268,19 +309,31 @@ def __iter__(self): error_state_indicator=bool(fd_flags & ESI_64), dlc=length, data=can_data[:length], - channel=channel - 1 + channel=channel - 1, ) yield msg elif obj_type == CAN_ERROR_EXT: - (channel, _, _, _, _, dlc, _, can_id, _, - can_data) = CAN_ERROR_EXT_STRUCT.unpack_from(data, pos) - msg = Message(timestamp=timestamp, - is_error_frame=True, - is_extended_id=bool(can_id & CAN_MSG_EXT), - arbitration_id=can_id & 0x1FFFFFFF, - dlc=dlc, - data=can_data[:dlc], - channel=channel - 1) + ( + channel, + _, + _, + _, + _, + dlc, + _, + can_id, + _, + can_data, + ) = CAN_ERROR_EXT_STRUCT.unpack_from(data, pos) + msg = Message( + timestamp=timestamp, + is_error_frame=True, + is_extended_id=bool(can_id & CAN_MSG_EXT), + arbitration_id=can_id & 0x1FFFFFFF, + dlc=dlc, + data=can_data[:dlc], + channel=channel - 1, + ) yield msg # else: # LOG.warning("Unknown object type (%d)", obj_type) @@ -310,7 +363,7 @@ def __init__(self, file, channel=1): If this is a file-like object, is has to opened in binary write mode, not text write mode. """ - super().__init__(file, mode='wb') + super().__init__(file, mode="wb") self.channel = channel # Header will be written after log is done self.file.write(b"\x00" * FILE_HEADER_SIZE) @@ -336,16 +389,18 @@ def on_message_received(self, msg): data = bytes(msg.data) if msg.is_error_frame: - data = CAN_ERROR_EXT_STRUCT.pack(channel, - 0, # length - 0, # flags - 0, # ecc - 0, # position - len2dlc(msg.dlc), - 0, # frame length - arb_id, - 0, # ext flags - data) + data = CAN_ERROR_EXT_STRUCT.pack( + channel, + 0, # length + 0, # flags + 0, # ecc + 0, # position + len2dlc(msg.dlc), + 0, # frame length + arb_id, + 0, # ext flags + data, + ) self._add_object(CAN_ERROR_EXT, data, msg.timestamp) elif msg.is_fd: fd_flags = EDL @@ -353,8 +408,9 @@ def on_message_received(self, msg): fd_flags |= BRS if msg.error_state_indicator: fd_flags |= ESI - data = CAN_FD_MSG_STRUCT.pack(channel, flags, len2dlc(msg.dlc), - arb_id, 0, 0, fd_flags, msg.dlc, data) + data = CAN_FD_MSG_STRUCT.pack( + channel, flags, len2dlc(msg.dlc), arb_id, 0, 0, fd_flags, msg.dlc, data + ) self._add_object(CAN_FD_MESSAGE, data, msg.timestamp) else: data = CAN_MSG_STRUCT.pack(channel, flags, msg.dlc, arb_id, data) @@ -377,7 +433,8 @@ def log_event(self, text, timestamp=None): 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)) + 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): @@ -390,7 +447,8 @@ def _add_object(self, obj_type, data, timestamp=None): header_size = OBJ_HEADER_BASE_STRUCT.size + OBJ_HEADER_V1_STRUCT.size obj_size = header_size + len(data) base_header = OBJ_HEADER_BASE_STRUCT.pack( - b"LOBJ", header_size, 1, obj_size, obj_type) + b"LOBJ", header_size, 1, obj_size, obj_type + ) obj_header = OBJ_HEADER_V1_STRUCT.pack(TIME_ONE_NANS, 0, 0, max(timestamp, 0)) self.cache.append(base_header) @@ -413,19 +471,21 @@ def _flush(self): if not cache: # Nothing to write return - uncompressed_data = cache[:self.MAX_CACHE_SIZE] + uncompressed_data = cache[: self.MAX_CACHE_SIZE] # Save data that comes after max size to next round - tail = cache[self.MAX_CACHE_SIZE:] + 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_V1_STRUCT.size + LOG_CONTAINER_STRUCT.size + - len(compressed_data)) + compressed_data = zlib.compress(uncompressed_data, self.COMPRESSION_LEVEL) + obj_size = ( + OBJ_HEADER_V1_STRUCT.size + LOG_CONTAINER_STRUCT.size + len(compressed_data) + ) base_header = OBJ_HEADER_BASE_STRUCT.pack( - b"LOBJ", OBJ_HEADER_BASE_STRUCT.size, 1, obj_size, LOG_CONTAINER) + b"LOBJ", OBJ_HEADER_BASE_STRUCT.size, 1, obj_size, LOG_CONTAINER + ) container_header = LOG_CONTAINER_STRUCT.pack( - ZLIB_DEFLATE, len(uncompressed_data)) + ZLIB_DEFLATE, len(uncompressed_data) + ) self.file.write(base_header) self.file.write(container_header) self.file.write(compressed_data) @@ -441,11 +501,9 @@ def stop(self): super().stop() # Write header in the beginning of the file - header = [b"LOGG", FILE_HEADER_SIZE, - APPLICATION_ID, 0, 0, 0, 2, 6, 8, 1] + header = [b"LOGG", FILE_HEADER_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([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.file.name, "r+b") as f: diff --git a/can/io/canutils.py b/can/io/canutils.py index bea1696a1..a151b56e1 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -13,12 +13,12 @@ from .generic import BaseIOHandler -log = logging.getLogger('can.io.canutils') +log = logging.getLogger("can.io.canutils") -CAN_MSG_EXT = 0x80000000 -CAN_ERR_FLAG = 0x20000000 -CAN_ERR_BUSERROR = 0x00000080 -CAN_ERR_DLC = 8 +CAN_MSG_EXT = 0x80000000 +CAN_ERR_FLAG = 0x20000000 +CAN_ERR_BUSERROR = 0x00000080 +CAN_ERR_DLC = 8 class CanutilsLogReader(BaseIOHandler): @@ -37,7 +37,7 @@ def __init__(self, file): If this is a file-like object, is has to opened in text read mode, not binary read mode. """ - super().__init__(file, mode='r') + super().__init__(file, mode="r") def __iter__(self): for line in self.file: @@ -49,14 +49,14 @@ def __iter__(self): timestamp, channel, frame = temp.split() timestamp = float(timestamp[1:-1]) - canId, data = frame.split('#') + canId, data = frame.split("#") if channel.isdigit(): channel = int(channel) isExtended = len(canId) > 3 canId = int(canId, 16) - if data and data[0].lower() == 'r': + if data and data[0].lower() == "r": isRemoteFrame = True if len(data) > 1: dlc = int(data[1:]) @@ -68,14 +68,20 @@ def __iter__(self): dlc = len(data) // 2 dataBin = bytearray() for i in range(0, len(data), 2): - dataBin.append(int(data[i:(i + 2)], 16)) + 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, - is_extended_id=isExtended, is_remote_frame=isRemoteFrame, - dlc=dlc, data=dataBin, channel=channel) + msg = Message( + timestamp=timestamp, + arbitration_id=canId & 0x1FFFFFFF, + is_extended_id=isExtended, + is_remote_frame=isRemoteFrame, + dlc=dlc, + data=dataBin, + channel=channel, + ) yield msg self.stop() @@ -100,7 +106,7 @@ def __init__(self, file, channel="vcan0", append=False): :param bool append: if set to `True` messages are appended to the file, else the file is truncated """ - mode = 'a' if append else 'w' + mode = "a" if append else "w" super().__init__(file, mode=mode) self.channel = channel @@ -109,7 +115,7 @@ def __init__(self, file, channel="vcan0", append=False): def on_message_received(self, msg): # this is the case for the very first message: if self.last_timestamp is None: - self.last_timestamp = (msg.timestamp or 0.0) + self.last_timestamp = msg.timestamp or 0.0 # figure out the correct timestamp if msg.timestamp is None or msg.timestamp < self.last_timestamp: @@ -120,17 +126,30 @@ def on_message_received(self, msg): channel = msg.channel if msg.channel is not None else self.channel if msg.is_error_frame: - self.file.write("(%f) %s %08X#0000000000000000\n" % (timestamp, channel, CAN_ERR_FLAG | CAN_ERR_BUSERROR)) + self.file.write( + "(%f) %s %08X#0000000000000000\n" + % (timestamp, channel, CAN_ERR_FLAG | CAN_ERR_BUSERROR) + ) elif msg.is_remote_frame: if msg.is_extended_id: - self.file.write("(%f) %s %08X#R\n" % (timestamp, channel, msg.arbitration_id)) + self.file.write( + "(%f) %s %08X#R\n" % (timestamp, channel, msg.arbitration_id) + ) else: - self.file.write("(%f) %s %03X#R\n" % (timestamp, channel, msg.arbitration_id)) + self.file.write( + "(%f) %s %03X#R\n" % (timestamp, channel, msg.arbitration_id) + ) else: data = ["{:02X}".format(byte) for byte in msg.data] if msg.is_extended_id: - self.file.write("(%f) %s %08X#%s\n" % (timestamp, channel, msg.arbitration_id, ''.join(data))) + self.file.write( + "(%f) %s %08X#%s\n" + % (timestamp, channel, msg.arbitration_id, "".join(data)) + ) else: - self.file.write("(%f) %s %03X#%s\n" % (timestamp, channel, msg.arbitration_id, ''.join(data))) + self.file.write( + "(%f) %s %03X#%s\n" + % (timestamp, channel, msg.arbitration_id, "".join(data)) + ) diff --git a/can/io/csv.py b/can/io/csv.py index c755b1c5b..af8fee6d8 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -49,7 +49,7 @@ def __init__(self, file, append=False): the file is truncated and starts with a newly written header line """ - mode = 'a' if append else 'w' + mode = "a" if append else "w" super().__init__(file, mode=mode) # Write a header row @@ -57,17 +57,19 @@ def __init__(self, file, append=False): self.file.write("timestamp,arbitration_id,extended,remote,error,dlc,data\n") def on_message_received(self, msg): - row = ','.join([ - repr(msg.timestamp), # cannot use str() here because that is rounding - hex(msg.arbitration_id), - '1' if msg.is_extended_id else '0', - '1' if msg.is_remote_frame else '0', - '1' if msg.is_error_frame else '0', - str(msg.dlc), - b64encode(msg.data).decode('utf8') - ]) + row = ",".join( + [ + repr(msg.timestamp), # cannot use str() here because that is rounding + hex(msg.arbitration_id), + "1" if msg.is_extended_id else "0", + "1" if msg.is_remote_frame else "0", + "1" if msg.is_error_frame else "0", + str(msg.dlc), + b64encode(msg.data).decode("utf8"), + ] + ) self.file.write(row) - self.file.write('\n') + self.file.write("\n") class CSVReader(BaseIOHandler): @@ -85,7 +87,7 @@ def __init__(self, file): If this is a file-like object, is has to opened in text read mode, not binary read mode. """ - super().__init__(file, mode='r') + super().__init__(file, mode="r") def __iter__(self): # skip the header line @@ -97,13 +99,15 @@ def __iter__(self): for line in self.file: - timestamp, arbitration_id, extended, remote, error, dlc, data = line.split(',') + timestamp, arbitration_id, extended, remote, error, dlc, data = line.split( + "," + ) yield Message( timestamp=float(timestamp), - is_remote_frame=(remote == '1'), - is_extended_id=(extended == '1'), - is_error_frame=(error == '1'), + is_remote_frame=(remote == "1"), + is_extended_id=(extended == "1"), + is_error_frame=(error == "1"), arbitration_id=int(arbitration_id, base=16), dlc=int(dlc), data=b64decode(data), diff --git a/can/io/generic.py b/can/io/generic.py index 56ce82860..62bae18d4 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -17,14 +17,14 @@ class BaseIOHandler(metaclass=ABCMeta): was opened """ - def __init__(self, file, mode='rt'): + def __init__(self, file, mode="rt"): """ :param file: a path-like object to open a file, a file-like object to be used as a file or `None` to not use a file at all :param str mode: the mode that should be used to open the file, see :func:`open`, ignored if *file* is `None` """ - if file is None or (hasattr(file, 'read') and hasattr(file, 'write')): + if file is None or (hasattr(file, "read") and hasattr(file, "write")): # file is None or some file-like object self.file = file else: diff --git a/can/io/logger.py b/can/io/logger.py index 2863b0ea5..edffe1c78 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -18,7 +18,7 @@ log = logging.getLogger("can.io.logger") -class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method +class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method """ Logs CAN messages to a file. diff --git a/can/io/player.py b/can/io/player.py index 3e856767b..3455fe97a 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -16,7 +16,7 @@ from .csv import CSVReader from .sqlite import SqliteReader -log = logging.getLogger('can.io.player') +log = logging.getLogger("can.io.player") class LogReader(BaseIOHandler): @@ -60,7 +60,9 @@ def __new__(cls, filename, *args, **kwargs): elif filename.endswith(".log"): return CanutilsLogReader(filename, *args, **kwargs) else: - raise NotImplementedError("No read support for this log format: {}".format(filename)) + raise NotImplementedError( + "No read support for this log format: {}".format(filename) + ) class MessageSync: diff --git a/can/io/printer.py b/can/io/printer.py index c67285581..f6a4b28e0 100644 --- a/can/io/printer.py +++ b/can/io/printer.py @@ -9,7 +9,7 @@ from can.listener import Listener from .generic import BaseIOHandler -log = logging.getLogger('can.io.printer') +log = logging.getLogger("can.io.printer") class Printer(BaseIOHandler, Listener): @@ -30,10 +30,10 @@ def __init__(self, file=None): write mode, not binary write mode. """ self.write_to_file = file is not None - super().__init__(file, mode='w') + super().__init__(file, mode="w") def on_message_received(self, msg): if self.write_to_file: - self.file.write(str(msg) + '\n') + self.file.write(str(msg) + "\n") else: print(msg) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index eccba8dc8..b9e5c2673 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -15,7 +15,7 @@ from can.message import Message from .generic import BaseIOHandler -log = logging.getLogger('can.io.sqlite') +log = logging.getLogger("can.io.sqlite") class SqliteReader(BaseIOHandler): @@ -48,7 +48,9 @@ def __init__(self, file, table_name="messages"): self.table_name = table_name def __iter__(self): - for frame_data in self._cursor.execute("SELECT * FROM {}".format(self.table_name)): + for frame_data in self._cursor.execute( + "SELECT * FROM {}".format(self.table_name) + ): yield SqliteReader._assemble_message(frame_data) @staticmethod @@ -61,7 +63,7 @@ def _assemble_message(frame_data): is_error_frame=bool(is_error), arbitration_id=can_id, dlc=dlc, - data=data + data=data, ) def __len__(self): @@ -74,7 +76,9 @@ def read_all(self): :rtype: Generator[can.Message] """ - result = self._cursor.execute("SELECT * FROM {}".format(self.table_name)).fetchall() + result = self._cursor.execute( + "SELECT * FROM {}".format(self.table_name) + ).fetchall() return (SqliteReader._assemble_message(frame) for frame in result) def stop(self): @@ -147,7 +151,9 @@ def __init__(self, file, table_name="messages"): self._writer_thread.start() self.num_frames = 0 self.last_write = time.time() - self._insert_template = f"INSERT INTO {self.table_name} VALUES (?, ?, ?, ?, ?, ?, ?)" + self._insert_template = ( + f"INSERT INTO {self.table_name} VALUES (?, ?, ?, ?, ?, ?, ?)" + ) def _create_db(self): """Creates a new databae or opens a connection to an existing one. @@ -160,7 +166,8 @@ def _create_db(self): self._conn = sqlite3.connect(self._db_filename) # create table structure - self._conn.cursor().execute(""" + self._conn.cursor().execute( + """ CREATE TABLE IF NOT EXISTS {} ( ts REAL, @@ -171,7 +178,10 @@ def _create_db(self): dlc INTEGER, data BLOB ) - """.format(self.table_name)) + """.format( + self.table_name + ) + ) self._conn.commit() def _db_writer_thread(self): @@ -179,24 +189,28 @@ def _db_writer_thread(self): try: while True: - messages = [] # reset buffer + messages = [] # reset buffer msg = self.get_message(self.GET_MESSAGE_TIMEOUT) while msg is not None: - #log.debug("SqliteWriter: buffering message") - - messages.append(( - msg.timestamp, - msg.arbitration_id, - msg.is_extended_id, - msg.is_remote_frame, - msg.is_error_frame, - msg.dlc, - memoryview(msg.data) - )) - - if time.time() - self.last_write > self.MAX_TIME_BETWEEN_WRITES or \ - len(messages) > self.MAX_BUFFER_SIZE_BEFORE_WRITES: + # log.debug("SqliteWriter: buffering message") + + messages.append( + ( + msg.timestamp, + msg.arbitration_id, + msg.is_extended_id, + msg.is_remote_frame, + msg.is_error_frame, + msg.dlc, + memoryview(msg.data), + ) + ) + + if ( + time.time() - self.last_write > self.MAX_TIME_BETWEEN_WRITES + or len(messages) > self.MAX_BUFFER_SIZE_BEFORE_WRITES + ): break else: # just go on @@ -205,9 +219,9 @@ def _db_writer_thread(self): count = len(messages) if count > 0: with self._conn: - #log.debug("Writing %d frames to db", count) + # log.debug("Writing %d frames to db", count) self._conn.executemany(self._insert_template, messages) - self._conn.commit() # make the changes visible to the entire database + self._conn.commit() # make the changes visible to the entire database self.num_frames += count self.last_write = time.time() diff --git a/can/logger.py b/can/logger.py index ec49c7e2c..9b326946c 100644 --- a/can/logger.py +++ b/can/logger.py @@ -28,62 +28,93 @@ 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, for supported types see can.Logger.""", - 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. + 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, for supported types see can.Logger.""", + 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: + 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='') + """, + nargs=argparse.REMAINDER, + default="", + ) - parser.add_argument('-b', '--bitrate', type=int, - help='''Bitrate to use for the CAN bus.''') + parser.add_argument( + "-b", "--bitrate", type=int, help="""Bitrate to use for the CAN bus.""" + ) state_group = parser.add_mutually_exclusive_group(required=False) - state_group.add_argument('--active', help="Start the bus as active, this is applied by default.", - action='store_true') - state_group.add_argument('--passive', help="Start the bus as passive.", - action='store_true') + state_group.add_argument( + "--active", + help="Start the bus as active, this is applied by default.", + action="store_true", + ) + state_group.add_argument( + "--passive", help="Start the bus as passive.", action="store_true" + ) # print help message when no arguments wre given if len(sys.argv) < 2: parser.print_help(sys.stderr) import errno + raise SystemExit(errno.EINVAL) results = parser.parse_args() verbosity = results.verbosity - logging_level_name = ['critical', 'error', 'warning', 'info', 'debug', 'subdebug'][min(5, verbosity)] + logging_level_name = ["critical", "error", "warning", "info", "debug", "subdebug"][ + min(5, verbosity) + ] can.set_logging_level(logging_level_name) can_filters = [] if results.filter: print(f"Adding filter(s): {results.filter}") for filt in results.filter: - if ':' in filt: + 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_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}) diff --git a/can/message.py b/can/message.py index c3be33aa7..b3fd38139 100644 --- a/can/message.py +++ b/can/message.py @@ -43,14 +43,24 @@ class Message: "is_fd", "bitrate_switch", "error_state_indicator", - "__weakref__" # support weak references to messages + "__weakref__", # support weak references to messages ) - def __init__(self, timestamp=0.0, arbitration_id=0, is_extended_id=True, - is_remote_frame=False, is_error_frame=False, channel=None, - dlc=None, data=None, - is_fd=False, bitrate_switch=False, error_state_indicator=False, - check=False): + def __init__( + self, + timestamp=0.0, + arbitration_id=0, + is_extended_id=True, + is_remote_frame=False, + is_error_frame=False, + channel=None, + dlc=None, + data=None, + is_fd=False, + bitrate_switch=False, + error_state_indicator=False, + check=False, + ): """ To create a message object, simply provide any of the below attributes together with additional parameters as keyword arguments to the constructor. @@ -100,14 +110,16 @@ def __str__(self): arbitration_id_string = "ID: {0:04x}".format(self.arbitration_id) field_strings.append(arbitration_id_string.rjust(12, " ")) - flag_string = " ".join([ - "X" if self.is_extended_id else "S", - "E" if self.is_error_frame else " ", - "R" if self.is_remote_frame else " ", - "F" if self.is_fd else " ", - "BS" if self.bitrate_switch else " ", - "EI" if self.error_state_indicator else " " - ]) + flag_string = " ".join( + [ + "X" if self.is_extended_id else "S", + "E" if self.is_error_frame else " ", + "R" if self.is_remote_frame else " ", + "F" if self.is_fd else " ", + "BS" if self.bitrate_switch else " ", + "EI" if self.error_state_indicator else " ", + ] + ) field_strings.append(flag_string) @@ -122,7 +134,7 @@ def __str__(self): field_strings.append(" " * 24) if (self.data is not None) and (self.data.isalnum()): - field_strings.append("'{}'".format(self.data.decode('utf-8', 'replace'))) + field_strings.append("'{}'".format(self.data.decode("utf-8", "replace"))) if self.channel is not None: try: @@ -140,9 +152,11 @@ def __bool__(self): return True def __repr__(self): - args = ["timestamp={}".format(self.timestamp), - "arbitration_id={:#x}".format(self.arbitration_id), - "is_extended_id={}".format(self.is_extended_id)] + args = [ + "timestamp={}".format(self.timestamp), + "arbitration_id={:#x}".format(self.arbitration_id), + "is_extended_id={}".format(self.is_extended_id), + ] if self.is_remote_frame: args.append("is_remote_frame={}".format(self.is_remote_frame)) @@ -154,8 +168,7 @@ def __repr__(self): args.append("channel={!r}".format(self.channel)) data = ["{:#02x}".format(byte) for byte in self.data] - args += ["dlc={}".format(self.dlc), - "data=[{}]".format(", ".join(data))] + args += ["dlc={}".format(self.dlc), "data=[{}]".format(", ".join(data))] if self.is_fd: args.append("is_fd=True") @@ -185,7 +198,7 @@ def __copy__(self): data=self.data, is_fd=self.is_fd, bitrate_switch=self.bitrate_switch, - error_state_indicator=self.error_state_indicator + error_state_indicator=self.error_state_indicator, ) return new @@ -201,7 +214,7 @@ def __deepcopy__(self, memo): data=deepcopy(self.data, memo), is_fd=self.is_fd, bitrate_switch=self.bitrate_switch, - error_state_indicator=self.error_state_indicator + error_state_indicator=self.error_state_indicator, ) return new @@ -220,7 +233,9 @@ def _check(self): raise ValueError("the timestamp may not be NaN") if self.is_remote_frame and self.is_error_frame: - raise ValueError("a message cannot be a remote and an error frame at the sane time") + raise ValueError( + "a message cannot be a remote and an error frame at the sane time" + ) if self.arbitration_id < 0: raise ValueError("arbitration IDs may not be negative") @@ -235,21 +250,33 @@ def _check(self): raise ValueError("DLC may not be negative") if self.is_fd: if self.dlc > 64: - raise ValueError("DLC was {} but it should be <= 64 for CAN FD frames".format(self.dlc)) + raise ValueError( + "DLC was {} but it should be <= 64 for CAN FD frames".format( + self.dlc + ) + ) elif self.dlc > 8: - raise ValueError("DLC was {} but it should be <= 8 for normal CAN frames".format(self.dlc)) + raise ValueError( + "DLC was {} but it should be <= 8 for normal CAN frames".format( + self.dlc + ) + ) if self.is_remote_frame: if self.data: raise ValueError("remote frames may not carry any data") elif self.dlc != len(self.data): - raise ValueError("the DLC and the length of the data must match up for non remote frames") + raise ValueError( + "the DLC and the length of the data must match up for non remote frames" + ) if not self.is_fd: if self.bitrate_switch: raise ValueError("bitrate switch is only allowed for CAN FD frames") if self.error_state_indicator: - raise ValueError("error state indicator is only allowed for CAN FD frames") + raise ValueError( + "error state indicator is only allowed for CAN FD frames" + ) def equals(self, other, timestamp_delta=1.0e-6): """ @@ -268,22 +295,23 @@ def equals(self, other, timestamp_delta=1.0e-6): # on why a delta of 1.0e-6 was chosen return ( # check for identity first and finish fast - self is other or + self is other + or # then check for equality by value ( ( - timestamp_delta is None or - abs(self.timestamp - other.timestamp) <= timestamp_delta - ) and - self.arbitration_id == other.arbitration_id and - self.is_extended_id == other.is_extended_id 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 and - self.channel == other.channel and - self.is_fd == other.is_fd and - self.bitrate_switch == other.bitrate_switch and - self.error_state_indicator == other.error_state_indicator + timestamp_delta is None + or abs(self.timestamp - other.timestamp) <= timestamp_delta + ) + and self.arbitration_id == other.arbitration_id + and self.is_extended_id == other.is_extended_id + 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 + and self.channel == other.channel + and self.is_fd == other.is_fd + and self.bitrate_switch == other.bitrate_switch + and self.error_state_indicator == other.error_state_indicator ) ) diff --git a/can/notifier.py b/can/notifier.py index 256085a7b..5d0642ee6 100644 --- a/can/notifier.py +++ b/can/notifier.py @@ -9,11 +9,10 @@ import time import asyncio -logger = logging.getLogger('can.Notifier') +logger = logging.getLogger("can.Notifier") class Notifier: - def __init__(self, bus, listeners, timeout=1.0, loop=None): """Manages the distribution of :class:`can.Message` instances to listeners. @@ -53,13 +52,16 @@ def add_bus(self, bus): :param can.BusABC bus: CAN bus instance. """ - if self._loop is not None and hasattr(bus, 'fileno') and bus.fileno() >= 0: + if self._loop is not None and hasattr(bus, "fileno") and bus.fileno() >= 0: # Use file descriptor to watch for messages reader = bus.fileno() self._loop.add_reader(reader, self._on_message_available, bus) else: - reader = threading.Thread(target=self._rx_thread, args=(bus,), - name='can.notifier for bus "{}"'.format(bus.channel_info)) + reader = threading.Thread( + target=self._rx_thread, + args=(bus,), + name='can.notifier for bus "{}"'.format(bus.channel_info), + ) reader.daemon = True reader.start() self._readers.append(reader) @@ -83,7 +85,7 @@ def stop(self, timeout=5): # reader is a file descriptor self._loop.remove_reader(reader) for listener in self.listeners: - if hasattr(listener, 'stop'): + if hasattr(listener, "stop"): listener.stop() def _rx_thread(self, bus): @@ -94,7 +96,8 @@ def _rx_thread(self, bus): with self._lock: if self._loop is not None: self._loop.call_soon_threadsafe( - self._on_message_received, msg) + self._on_message_received, msg + ) else: self._on_message_received(msg) msg = bus.recv(self.timeout) @@ -120,7 +123,7 @@ def _on_message_received(self, msg): def _on_error(self, exc): for listener in self.listeners: - if hasattr(listener, 'on_error'): + if hasattr(listener, "on_error"): listener.on_error(exc) def add_listener(self, listener): diff --git a/can/player.py b/can/player.py index d41396cf5..b279579f7 100644 --- a/can/player.py +++ b/can/player.py @@ -17,53 +17,90 @@ 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, for supported types see can.LogReader.""", - 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. + "python -m can.player", description="Replay CAN traffic." + ) + + parser.add_argument( + "-f", + "--file_name", + dest="log_file", + help="""Path and base log filename, for supported types see can.LogReader.""", + 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''', - default=0.0001) - 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. For supported types see can.LogReader.') + 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""", + default=0.0001, + ) + 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. For supported types see can.LogReader.", + ) # print help message when no arguments were given if len(sys.argv) < 2: parser.print_help(sys.stderr) import errno + raise SystemExit(errno.EINVAL) results = parser.parse_args() verbosity = results.verbosity - logging_level_name = ['critical', 'error', 'warning', 'info', 'debug', 'subdebug'][min(5, verbosity)] + logging_level_name = ["critical", "error", "warning", "info", "debug", "subdebug"][ + min(5, verbosity) + ] can.set_logging_level(logging_level_name) config = {"single_handle": True} @@ -75,8 +112,9 @@ def main(): reader = LogReader(results.infile) - in_sync = MessageSync(reader, timestamps=results.timestamps, - gap=results.gap, skip=results.skip) + in_sync = MessageSync( + reader, timestamps=results.timestamps, gap=results.gap, skip=results.skip + ) print(f"Can LogReader (Started on {datetime.now()})") diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 7e0d058f2..91f7e6d2c 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -6,6 +6,7 @@ # Only raise an exception on instantiation but allow module # to be imported from wrapt import ObjectProxy + import_exc = None except ImportError as exc: ObjectProxy = object @@ -18,6 +19,7 @@ from contextlib import nullcontext except ImportError: + class nullcontext: """A context manager that does nothing at all. A fallback for Python 3.7's :class:`contextlib.nullcontext` manager. @@ -33,7 +35,7 @@ def __exit__(self, *args): pass -class ThreadSafeBus(ObjectProxy): # pylint: disable=abstract-method +class ThreadSafeBus(ObjectProxy): # pylint: disable=abstract-method """ Contains a thread safe :class:`can.BusABC` implementation that wraps around an existing interface instance. All public methods diff --git a/can/util.py b/can/util.py index c567d8ada..c04bb70c9 100644 --- a/can/util.py +++ b/can/util.py @@ -14,37 +14,20 @@ import can from can.interfaces import VALID_INTERFACES -log = logging.getLogger('can.util') +log = logging.getLogger("can.util") # List of valid data lengths for a CAN FD message -CAN_FD_DLC = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, - 12, 16, 20, 24, 32, 48, 64 -] +CAN_FD_DLC = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64] -REQUIRED_KEYS = [ - 'interface', - 'channel', -] +REQUIRED_KEYS = ["interface", "channel"] -CONFIG_FILES = ['~/can.conf'] +CONFIG_FILES = ["~/can.conf"] if platform.system() == "Linux": - CONFIG_FILES.extend( - [ - '/etc/can.conf', - '~/.can', - '~/.canrc' - ] - ) + 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') - ] - ) + CONFIG_FILES.extend(["can.ini", os.path.join(os.getenv("APPDATA", ""), "can.ini")]) def load_file_config(path=None, section=None): @@ -69,11 +52,10 @@ def load_file_config(path=None, section=None): _config = {} - section = section if section is not None else 'default' + section = section if section is not None else "default" if config.has_section(section): - if config.has_section('default'): - _config.update( - dict((key, val) for key, val in config.items('default'))) + if config.has_section("default"): + _config.update(dict((key, val) for key, val in config.items("default"))) _config.update(dict((key, val) for key, val in config.items(section))) return _config @@ -89,14 +71,12 @@ def load_environment_config(): """ mapper = { - 'interface': 'CAN_INTERFACE', - 'channel': 'CAN_CHANNEL', - 'bitrate': 'CAN_BITRATE', + "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 + (key, os.environ.get(val)) for key, val in mapper.items() if val in os.environ ) @@ -156,7 +136,7 @@ def load_config(path=None, config=None, context=None): given_config, can.rc, lambda _context: load_environment_config(), # context is not supported - lambda _context: load_file_config(path, _context) + lambda _context: load_file_config(path, _context), ] # Slightly complex here to only search for the file config if required @@ -164,10 +144,10 @@ def load_config(path=None, config=None, context=None): if callable(cfg): cfg = cfg(context) # remove legacy operator (and copy to interface if not already present) - if 'bustype' in cfg: - if 'interface' not in cfg or not cfg['interface']: - cfg['interface'] = cfg['bustype'] - del cfg['bustype'] + if "bustype" in cfg: + if "interface" not in cfg or not cfg["interface"]: + cfg["interface"] = cfg["bustype"] + del cfg["bustype"] # copy all new parameters for key in cfg: if key not in config: @@ -178,11 +158,13 @@ def load_config(path=None, config=None, context=None): if key not in config: config[key] = None - if config['interface'] not in VALID_INTERFACES: - raise NotImplementedError('Invalid CAN Bus Type - {}'.format(config['interface'])) + if config["interface"] not in VALID_INTERFACES: + raise NotImplementedError( + "Invalid CAN Bus Type - {}".format(config["interface"]) + ) - if 'bitrate' in config: - config['bitrate'] = int(config['bitrate']) + if "bitrate" in config: + config["bitrate"] = int(config["bitrate"]) can.log.debug("can config: {}".format(config)) return config @@ -192,7 +174,7 @@ 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') + can_logger = logging.getLogger("can") try: can_logger.setLevel(getattr(logging, level_name.upper())) @@ -243,7 +225,7 @@ def channel2int(channel): return channel # String and byte objects have a lower() method if hasattr(channel, "lower"): - match = re.match(r'.*(\d+)$', channel) + match = re.match(r".*(\d+)$", channel) if match: return int(match.group(1)) return None diff --git a/can/viewer.py b/can/viewer.py index 1332766df..5f8d18858 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -32,20 +32,20 @@ import can from can import __version__ -logger = logging.getLogger('can.serial') +logger = logging.getLogger("can.serial") try: import curses from curses.ascii import ESC as KEY_ESC, SP as KEY_SPACE except ImportError: # Probably on windows - logger.warning("You won't be able to use the viewer program without " - "curses installed!") + logger.warning( + "You won't be able to use the viewer program without " "curses installed!" + ) curses = None class CanViewer: - def __init__(self, stdscr, bus, data_structs, testing=False): self.stdscr = stdscr self.bus = bus @@ -79,37 +79,37 @@ def run(self): # Do not read the CAN-Bus when in paused mode if not self.paused: # Read the CAN-Bus and draw it in the terminal window - msg = self.bus.recv(timeout=1. / 1000.) + msg = self.bus.recv(timeout=1.0 / 1000.0) if msg is not None: self.draw_can_bus_message(msg) else: # Sleep 1 ms, so the application does not use 100 % of the CPU resources - time.sleep(1. / 1000.) + time.sleep(1.0 / 1000.0) # Read the terminal input key = self.stdscr.getch() # Stop program if the user presses ESC or 'q' - if key == KEY_ESC or key == ord('q'): + if key == KEY_ESC or key == ord("q"): break # Clear by pressing 'c' - elif key == ord('c'): + elif key == ord("c"): self.ids = {} self.start_time = None self.scroll = 0 self.draw_header() # Sort by pressing 's' - elif key == ord('s'): + elif key == ord("s"): # Sort frames based on the CAN-Bus ID self.draw_header() for i, key in enumerate(sorted(self.ids.keys())): # Set the new row index, but skip the header - self.ids[key]['row'] = i + 1 + self.ids[key]["row"] = i + 1 # Do a recursive call, so the frames are repositioned - self.draw_can_bus_message(self.ids[key]['msg'], sorting=True) + self.draw_can_bus_message(self.ids[key]["msg"], sorting=True) # Pause by pressing space elif key == KEY_SPACE: @@ -131,7 +131,7 @@ def run(self): resized = curses.is_term_resized(self.y, self.x) if resized is True: self.y, self.x = self.stdscr.getmaxyx() - if hasattr(curses, 'resizeterm'): # pragma: no cover + if hasattr(curses, "resizeterm"): # pragma: no cover curses.resizeterm(self.y, self.x) self.redraw_screen() @@ -140,7 +140,9 @@ def run(self): # Unpack the data and then convert it into SI-units @staticmethod - def unpack_data(cmd, cmd_to_struct, data): # type: (int, Dict, bytes) -> List[Union[float, int]] + def unpack_data( + cmd, cmd_to_struct, data + ): # type: (int, Dict, bytes) -> List[Union[float, int]] if not cmd_to_struct or not data: # These messages do not contain a data package return [] @@ -153,8 +155,10 @@ def unpack_data(cmd, cmd_to_struct, data): # type: (int, Dict, bytes) -> List[U struct_t = value[0] # type: struct.Struct # The conversion from raw values to SI-units are given in the rest of the tuple - values = [d // val if isinstance(val, int) else float(d) / val - for d, val in zip(struct_t.unpack(data), value[1:])] + values = [ + d // val if isinstance(val, int) else float(d) / val + for d, val in zip(struct_t.unpack(data), value[1:]) + ] else: # No conversion from SI-units is needed struct_t = value # type: struct.Struct @@ -162,7 +166,7 @@ def unpack_data(cmd, cmd_to_struct, data): # type: (int, Dict, bytes) -> List[U return values else: - raise ValueError('Unknown command: 0x{:02X}'.format(cmd)) + raise ValueError("Unknown command: 0x{:02X}".format(cmd)) def draw_can_bus_message(self, msg, sorting=False): # Use the CAN-Bus ID as the key in the dict @@ -170,7 +174,7 @@ def draw_can_bus_message(self, msg, sorting=False): # Sort the extended IDs at the bottom by setting the 32-bit high if msg.is_extended_id: - key |= (1 << 32) + key |= 1 << 32 new_id_added, length_changed = False, False if not sorting: @@ -180,35 +184,37 @@ def draw_can_bus_message(self, msg, sorting=False): # Set the start time when the first message has been received if not self.start_time: self.start_time = msg.timestamp - elif msg.dlc != self.ids[key]['msg'].dlc: + elif msg.dlc != self.ids[key]["msg"].dlc: length_changed = True if new_id_added or length_changed: # Increment the index if it was just added, but keep it if the length just changed - row = len(self.ids) + 1 if new_id_added else self.ids[key]['row'] + row = len(self.ids) + 1 if new_id_added else self.ids[key]["row"] # It's a new message ID or the length has changed, so add it to the dict # The first index is the row index, the second is the frame counter, # the third is a copy of the CAN-Bus frame # and the forth index is the time since the previous message - self.ids[key] = {'row': row, 'count': 0, 'msg': msg, 'dt': 0} + self.ids[key] = {"row": row, "count": 0, "msg": msg, "dt": 0} else: # Calculate the time since the last message and save the timestamp - self.ids[key]['dt'] = msg.timestamp - self.ids[key]['msg'].timestamp + self.ids[key]["dt"] = msg.timestamp - self.ids[key]["msg"].timestamp # Copy the CAN-Bus frame - this is used for sorting - self.ids[key]['msg'] = msg + self.ids[key]["msg"] = msg # Increment frame counter - self.ids[key]['count'] += 1 + self.ids[key]["count"] += 1 # Format the CAN-Bus ID as a hex value - arbitration_id_string = '0x{0:0{1}X}'.format(msg.arbitration_id, 8 if msg.is_extended_id else 3) + arbitration_id_string = "0x{0:0{1}X}".format( + msg.arbitration_id, 8 if msg.is_extended_id else 3 + ) # Generate data string - data_string = '' + data_string = "" if msg.dlc > 0: - data_string = ' '.join('{:02X}'.format(x) for x in msg.data) + data_string = " ".join("{:02X}".format(x) for x in msg.data) # Use red for error frames if msg.is_error_frame: @@ -217,24 +223,32 @@ def draw_can_bus_message(self, msg, sorting=False): color = curses.color_pair(0) # Now draw the CAN-Bus message on the terminal window - self.draw_line(self.ids[key]['row'], 0, str(self.ids[key]['count']), color) - self.draw_line(self.ids[key]['row'], 8, '{0:.6f}'.format(self.ids[key]['msg'].timestamp - self.start_time), - color) - self.draw_line(self.ids[key]['row'], 23, '{0:.6f}'.format(self.ids[key]['dt']), color) - self.draw_line(self.ids[key]['row'], 35, arbitration_id_string, color) - self.draw_line(self.ids[key]['row'], 47, str(msg.dlc), color) - self.draw_line(self.ids[key]['row'], 52, data_string, color) + self.draw_line(self.ids[key]["row"], 0, str(self.ids[key]["count"]), color) + self.draw_line( + self.ids[key]["row"], + 8, + "{0:.6f}".format(self.ids[key]["msg"].timestamp - self.start_time), + color, + ) + self.draw_line( + self.ids[key]["row"], 23, "{0:.6f}".format(self.ids[key]["dt"]), color + ) + self.draw_line(self.ids[key]["row"], 35, arbitration_id_string, color) + self.draw_line(self.ids[key]["row"], 47, str(msg.dlc), color) + self.draw_line(self.ids[key]["row"], 52, data_string, color) if self.data_structs: try: values_list = [] - for x in self.unpack_data(msg.arbitration_id, self.data_structs, msg.data): + for x in self.unpack_data( + msg.arbitration_id, self.data_structs, msg.data + ): if isinstance(x, float): - values_list.append('{0:.6f}'.format(x)) + values_list.append("{0:.6f}".format(x)) else: values_list.append(str(x)) - values_string = ' '.join(values_list) - self.draw_line(self.ids[key]['row'], 77, values_string, color) + values_string = " ".join(values_list) + self.draw_line(self.ids[key]["row"], 77, values_string, color) except (ValueError, struct.error): pass @@ -253,31 +267,30 @@ def draw_line(self, row, col, txt, *args): def draw_header(self): self.stdscr.erase() - self.draw_line(0, 0, 'Count', curses.A_BOLD) - self.draw_line(0, 8, 'Time', curses.A_BOLD) - self.draw_line(0, 23, 'dt', curses.A_BOLD) - self.draw_line(0, 35, 'ID', curses.A_BOLD) - self.draw_line(0, 47, 'DLC', curses.A_BOLD) - self.draw_line(0, 52, 'Data', curses.A_BOLD) + self.draw_line(0, 0, "Count", curses.A_BOLD) + self.draw_line(0, 8, "Time", curses.A_BOLD) + self.draw_line(0, 23, "dt", curses.A_BOLD) + self.draw_line(0, 35, "ID", curses.A_BOLD) + self.draw_line(0, 47, "DLC", curses.A_BOLD) + self.draw_line(0, 52, "Data", curses.A_BOLD) if self.data_structs: # Only draw if the dictionary is not empty - self.draw_line(0, 77, 'Parsed values', curses.A_BOLD) + self.draw_line(0, 77, "Parsed values", curses.A_BOLD) def redraw_screen(self): # Trigger a complete redraw self.draw_header() for key in self.ids: - self.draw_can_bus_message(self.ids[key]['msg']) + self.draw_can_bus_message(self.ids[key]["msg"]) # noinspection PyProtectedMember class SmartFormatter(argparse.HelpFormatter): - def _get_default_metavar_for_optional(self, action): return action.dest.upper() def _format_usage(self, usage, actions, groups, prefix): # Use uppercase for "Usage:" text - return super()._format_usage(usage, actions, groups, 'Usage: ') + return super()._format_usage(usage, actions, groups, "Usage: ") def _format_args(self, action, default_metavar): if action.nargs != argparse.REMAINDER and action.nargs != argparse.ONE_OR_MORE: @@ -285,7 +298,7 @@ def _format_args(self, action, default_metavar): # Use the metavar if "REMAINDER" or "ONE_OR_MORE" is set get_metavar = self._metavar_formatter(action, default_metavar) - return '%s' % get_metavar(1) + return "%s" % get_metavar(1) def _format_action_invocation(self, action): if not action.option_strings or action.nargs == 0: @@ -298,21 +311,21 @@ def _format_action_invocation(self, action): args_string = self._format_args(action, default) for i, option_string in enumerate(action.option_strings): if i == len(action.option_strings) - 1: - parts.append('%s %s' % (option_string, args_string)) + parts.append("%s %s" % (option_string, args_string)) else: - parts.append('%s' % option_string) - return ', '.join(parts) + parts.append("%s" % option_string) + return ", ".join(parts) def _split_lines(self, text, width): # Allow to manually split the lines - if text.startswith('R|'): + if text.startswith("R|"): return text[2:].splitlines() return super()._split_lines(text, width) def _fill_text(self, text, width, indent): - if text.startswith('R|'): + if text.startswith("R|"): # noinspection PyTypeChecker - return ''.join(indent + line + '\n' for line in text[2:].splitlines()) + return "".join(indent + line + "\n" for line in text[2:].splitlines()) else: return super()._fill_text(text, width, indent) @@ -321,91 +334,126 @@ def parse_args(args): # Python versions >= 3.5 kwargs = {} if sys.version_info[0] * 10 + sys.version_info[1] >= 35: # pragma: no cover - kwargs = {'allow_abbrev': False} + kwargs = {"allow_abbrev": False} # Parse command line arguments - parser = argparse.ArgumentParser('python -m can.viewer', - description='A simple CAN viewer terminal application written in Python', - epilog='R|Shortcuts: ' - '\n +---------+-------------------------+' - '\n | Key | Description |' - '\n +---------+-------------------------+' - '\n | ESQ/q | Exit the viewer |' - '\n | c | Clear the stored frames |' - '\n | s | Sort the stored frames |' - '\n | SPACE | Pause the viewer |' - '\n | UP/DOWN | Scroll the viewer |' - '\n +---------+-------------------------+', - formatter_class=SmartFormatter, add_help=False, **kwargs) - - optional = parser.add_argument_group('Optional arguments') - - optional.add_argument('-h', '--help', action='help', help='Show this help message and exit') - - optional.add_argument('--version', action='version', help="Show program's version number and exit", - version='%(prog)s (version {version})'.format(version=__version__)) + parser = argparse.ArgumentParser( + "python -m can.viewer", + description="A simple CAN viewer terminal application written in Python", + epilog="R|Shortcuts: " + "\n +---------+-------------------------+" + "\n | Key | Description |" + "\n +---------+-------------------------+" + "\n | ESQ/q | Exit the viewer |" + "\n | c | Clear the stored frames |" + "\n | s | Sort the stored frames |" + "\n | SPACE | Pause the viewer |" + "\n | UP/DOWN | Scroll the viewer |" + "\n +---------+-------------------------+", + formatter_class=SmartFormatter, + add_help=False, + **kwargs + ) + + optional = parser.add_argument_group("Optional arguments") + + optional.add_argument( + "-h", "--help", action="help", help="Show this help message and exit" + ) + + optional.add_argument( + "--version", + action="version", + help="Show program's version number and exit", + version="%(prog)s (version {version})".format(version=__version__), + ) # Copied from: https://github.com/hardbyte/python-can/blob/develop/can/logger.py - optional.add_argument('-b', '--bitrate', type=int, help='''Bitrate to use for the given CAN interface''') - - optional.add_argument('-c', '--channel', help='''Most backend interfaces require some sort of channel. + optional.add_argument( + "-b", + "--bitrate", + type=int, + help="""Bitrate to use for the given CAN interface""", + ) + + optional.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". - (default: use default for the specified interface)''') - - optional.add_argument('-d', '--decode', dest='decode', - help='R|Specify how to convert the raw bytes into real values.' - '\nThe ID of the frame is given as the first argument and the format as the second.' - '\nThe Python struct package is used to unpack the received data' - '\nwhere the format characters have the following meaning:' - '\n < = little-endian, > = big-endian' - '\n x = pad byte' - '\n c = char' - '\n ? = bool' - '\n b = int8_t, B = uint8_t' - '\n h = int16, H = uint16' - '\n l = int32_t, L = uint32_t' - '\n q = int64_t, Q = uint64_t' - '\n f = float (32-bits), d = double (64-bits)' - '\nFx to convert six bytes with ID 0x100 into uint8_t, uint16 and uint32_t:' - '\n $ python -m can.viewer -d "100:: (matches when & mask == can_id & mask)' - '\n ~ (matches when & mask != can_id & mask)' - '\nFx to show only frames with ID 0x100 to 0x103 and 0x200 to 0x20F:' - '\n python -m can.viewer -f 100:7FC 200:7F0' - '\nNote that the ID and mask are alway interpreted as hex values', - metavar='{:,~}', nargs=argparse.ONE_OR_MORE, default='') - - optional.add_argument('-i', '--interface', dest='interface', - help='R|Specify the backend CAN interface to use.', - choices=sorted(can.VALID_INTERFACES)) + (default: use default for the specified interface)""", + ) + + optional.add_argument( + "-d", + "--decode", + dest="decode", + help="R|Specify how to convert the raw bytes into real values." + "\nThe ID of the frame is given as the first argument and the format as the second." + "\nThe Python struct package is used to unpack the received data" + "\nwhere the format characters have the following meaning:" + "\n < = little-endian, > = big-endian" + "\n x = pad byte" + "\n c = char" + "\n ? = bool" + "\n b = int8_t, B = uint8_t" + "\n h = int16, H = uint16" + "\n l = int32_t, L = uint32_t" + "\n q = int64_t, Q = uint64_t" + "\n f = float (32-bits), d = double (64-bits)" + "\nFx to convert six bytes with ID 0x100 into uint8_t, uint16 and uint32_t:" + '\n $ python -m can.viewer -d "100:: (matches when & mask == can_id & mask)" + "\n ~ (matches when & mask != can_id & mask)" + "\nFx to show only frames with ID 0x100 to 0x103 and 0x200 to 0x20F:" + "\n python -m can.viewer -f 100:7FC 200:7F0" + "\nNote that the ID and mask are alway interpreted as hex values", + metavar="{:,~}", + nargs=argparse.ONE_OR_MORE, + default="", + ) + + optional.add_argument( + "-i", + "--interface", + dest="interface", + help="R|Specify the backend CAN interface to use.", + choices=sorted(can.VALID_INTERFACES), + ) # Print help message when no arguments are given if not args: parser.print_help(sys.stderr) import errno + raise SystemExit(errno.EINVAL) parsed_args = parser.parse_args(args) @@ -415,16 +463,16 @@ def parse_args(args): # print('Adding filter/s', parsed_args.filter) for flt in parsed_args.filter: # print(filter) - if ':' in flt: - _ = flt.split(':') + if ":" in flt: + _ = flt.split(":") can_id, can_mask = int(_[0], base=16), int(_[1], base=16) - elif '~' in flt: - can_id, can_mask = flt.split('~') + elif "~" in flt: + can_id, can_mask = flt.split("~") can_id = int(can_id, base=16) | 0x20000000 # CAN_INV_FILTER can_mask = int(can_mask, base=16) & 0x20000000 # socket.CAN_ERR_FLAG else: - raise argparse.ArgumentError(None, 'Invalid filter argument') - can_filters.append({'can_id': can_id, 'can_mask': can_mask}) + raise argparse.ArgumentError(None, "Invalid filter argument") + can_filters.append({"can_id": can_id, "can_mask": can_mask}) # Dictionary used to convert between Python values and C structs represented as Python strings. # If the value is 'None' then the message does not contain any data package. @@ -444,16 +492,18 @@ def parse_args(args): # An optional conversion from real units to integers can be given as additional arguments. # In order to convert from raw integer value the real units are multiplied with the values and similarly the values # are divided by the value in order to convert from real units to raw integer values. - data_structs = {} # type: Dict[Union[int, Tuple[int, ...]], Union[struct.Struct, Tuple, None]] + data_structs = ( + {} + ) # type: Dict[Union[int, Tuple[int, ...]], Union[struct.Struct, Tuple, None]] if parsed_args.decode: if os.path.isfile(parsed_args.decode[0]): - with open(parsed_args.decode[0], 'r') as f: + with open(parsed_args.decode[0], "r") as f: structs = f.readlines() else: structs = parsed_args.decode for s in structs: - tmp = s.rstrip('\n').split(':') + tmp = s.rstrip("\n").split(":") # The ID is given as a hex value, the format needs no conversion key, fmt = int(tmp[0], base=16), tmp[1] @@ -479,13 +529,13 @@ def parse_args(args): def main(): # pragma: no cover parsed_args, can_filters, data_structs = parse_args(sys.argv[1:]) - config = {'single_handle': True} + config = {"single_handle": True} if can_filters: - config['can_filters'] = can_filters + config["can_filters"] = can_filters if parsed_args.interface: - config['interface'] = parsed_args.interface + config["interface"] = parsed_args.interface if parsed_args.bitrate: - config['bitrate'] = parsed_args.bitrate + config["bitrate"] = parsed_args.bitrate # Create a CAN-Bus interface bus = can.Bus(parsed_args.channel, **config) @@ -494,7 +544,7 @@ def main(): # pragma: no cover curses.wrapper(CanViewer, bus, data_structs) -if __name__ == '__main__': +if __name__ == "__main__": # Catch ctrl+c try: main() diff --git a/doc/conf.py b/doc/conf.py index 1c409a06a..b95aa6557 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -10,102 +10,100 @@ import os # General information about the project. -project = u'python-can' +project = "python-can" # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath("..")) import can + # The version info for the project, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = can.__version__.split('-')[0] +version = can.__version__.split("-")[0] release = can.__version__ # -- General configuration ----------------------------------------------------- -primary_domain = 'py' +primary_domain = "py" # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.extlinks', - 'sphinx.ext.todo', - 'sphinx.ext.intersphinx', - 'sphinx.ext.coverage', - 'sphinx.ext.viewcode', - 'sphinx.ext.graphviz', - 'sphinxcontrib.programoutput' - ] +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.extlinks", + "sphinx.ext.todo", + "sphinx.ext.intersphinx", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", + "sphinx.ext.graphviz", + "sphinxcontrib.programoutput", +] # Now, you can use the alias name as a new role, e.g. :issue:`123`. -extlinks = { - 'issue': ('https://github.com/hardbyte/python-can/issues/%s/', 'issue '), -} +extlinks = {"issue": ("https://github.com/hardbyte/python-can/issues/%s/", "issue ")} -intersphinx_mapping = { - 'python': ('https://docs.python.org/3/', None), -} +intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} # If this is True, todo and todolist produce output, else they produce nothing. # The default is False. todo_include_todos = True # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] -graphviz_output_format = 'png' # 'svg' +graphviz_output_format = "png" # 'svg' # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -language = 'en' +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # Include documentation from both the class level and __init__ autoclass_content = "both" # The default autodoc directive flags -autodoc_default_flags = ['members', 'show-inheritance'] +autodoc_default_flags = ["members", "show-inheritance"] # Keep cached intersphinx inventories indefinitely intersphinx_cache_limit = -1 @@ -114,77 +112,77 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'python-can' +htmlhelp_basename = "python-can" diff --git a/examples/asyncio_demo.py b/examples/asyncio_demo.py index 3e71ae6db..534f47184 100644 --- a/examples/asyncio_demo.py +++ b/examples/asyncio_demo.py @@ -1,19 +1,21 @@ import asyncio import can + def print_message(msg): """Regular callback function. Can also be a coroutine.""" print(msg) + async def main(): - can0 = can.Bus('vcan0', bustype='virtual', receive_own_messages=True) + can0 = can.Bus("vcan0", bustype="virtual", receive_own_messages=True) reader = can.AsyncBufferedReader() - logger = can.Logger('logfile.asc') + logger = can.Logger("logfile.asc") listeners = [ print_message, # Callback function - reader, # AsyncBufferedReader() listener - logger # Regular Listener object + reader, # AsyncBufferedReader() listener + logger, # Regular Listener object ] # Create Notifier with an explicit loop to use for scheduling of callbacks loop = asyncio.get_event_loop() @@ -21,7 +23,7 @@ async def main(): # Start sending first message can0.send(can.Message(arbitration_id=0)) - print('Bouncing 10 messages...') + print("Bouncing 10 messages...") for _ in range(10): # Wait for next message from AsyncBufferedReader msg = await reader.get_message() @@ -31,12 +33,13 @@ async def main(): can0.send(msg) # Wait for last message to arrive await reader.get_message() - print('Done!') + print("Done!") # Clean-up notifier.stop() can0.shutdown() + # Get the default event loop loop = asyncio.get_event_loop() # Run until main coroutine finishes diff --git a/examples/cyclic.py b/examples/cyclic.py index 021af14bf..25c215dda 100755 --- a/examples/cyclic.py +++ b/examples/cyclic.py @@ -26,7 +26,9 @@ def simple_periodic_send(bus): Sleeps for 2 seconds then stops the task. """ print("Starting to send a message every 200ms for 2s") - msg = can.Message(arbitration_id=0x123, data=[1, 2, 3, 4, 5, 6], is_extended_id=False) + msg = can.Message( + arbitration_id=0x123, data=[1, 2, 3, 4, 5, 6], is_extended_id=False + ) task = bus.send_periodic(msg, 0.20) assert isinstance(task, can.CyclicSendTaskABC) time.sleep(2) @@ -36,7 +38,9 @@ def simple_periodic_send(bus): def limited_periodic_send(bus): print("Starting to send a message every 200ms for 1s") - msg = can.Message(arbitration_id=0x12345678, data=[0, 0, 0, 0, 0, 0], is_extended_id=True) + msg = can.Message( + arbitration_id=0x12345678, data=[0, 0, 0, 0, 0, 0], is_extended_id=True + ) task = bus.send_periodic(msg, 0.20, 1, store_task=False) if not isinstance(task, can.LimitedDurationCyclicSendTaskABC): print("This interface doesn't seem to support a ") @@ -48,12 +52,12 @@ def limited_periodic_send(bus): # Note the (finished) task will still be tracked by the Bus # unless we pass `store_task=False` to bus.send_periodic # alternatively calling stop removes the task from the bus - #task.stop() + # task.stop() def test_periodic_send_with_modifying_data(bus): print("Starting to send a message every 200ms. Initial data is ones") - msg = can.Message(arbitration_id=0x0cf02200, data=[1, 1, 1, 1]) + msg = can.Message(arbitration_id=0x0CF02200, data=[1, 1, 1, 1]) task = bus.send_periodic(msg, 0.20) if not isinstance(task, can.ModifiableCyclicTaskABC): print("This interface doesn't seem to support modification") @@ -68,7 +72,7 @@ def test_periodic_send_with_modifying_data(bus): task.stop() print("stopped cyclic send") print("Changing data of stopped task to single ff byte") - msg.data = bytearray([0xff]) + msg.data = bytearray([0xFF]) msg.dlc = 1 task.modify_data(msg) time.sleep(1) @@ -107,11 +111,13 @@ def test_periodic_send_with_modifying_data(bus): if __name__ == "__main__": - reset_msg = can.Message(arbitration_id=0x00, data=[0, 0, 0, 0, 0, 0], is_extended_id=False) + reset_msg = can.Message( + arbitration_id=0x00, data=[0, 0, 0, 0, 0, 0], is_extended_id=False + ) for interface, channel in [ - ('socketcan', 'vcan0'), - #('ixxat', 0) + ("socketcan", "vcan0"), + # ('ixxat', 0) ]: print("Carrying out cyclic tests with {} interface".format(interface)) @@ -126,9 +132,9 @@ def test_periodic_send_with_modifying_data(bus): test_periodic_send_with_modifying_data(bus) - #print("Carrying out multirate cyclic test for {} interface".format(interface)) - #can.rc['interface'] = interface - #test_dual_rate_periodic_send() + # print("Carrying out multirate cyclic test for {} interface".format(interface)) + # can.rc['interface'] = interface + # test_dual_rate_periodic_send() bus.shutdown() diff --git a/examples/receive_all.py b/examples/receive_all.py index 44a495de7..ced8841bc 100755 --- a/examples/receive_all.py +++ b/examples/receive_all.py @@ -8,9 +8,9 @@ def receive_all(): - bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) - #bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) - #bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + bus = can.interface.Bus(bustype="pcan", channel="PCAN_USBBUS1", bitrate=250000) + # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) bus.state = BusState.ACTIVE # or BusState.PASSIVE diff --git a/examples/send_one.py b/examples/send_one.py index 2533ca37c..9a3181e35 100755 --- a/examples/send_one.py +++ b/examples/send_one.py @@ -9,6 +9,7 @@ import can + def send_one(): # this uses the default configuration (for example from the config file) @@ -22,9 +23,9 @@ def send_one(): # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) # ... - msg = can.Message(arbitration_id=0xc0ffee, - data=[0, 25, 0, 1, 3, 1, 4, 1], - is_extended_id=True) + msg = can.Message( + arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=True + ) try: bus.send(msg) @@ -32,5 +33,6 @@ def send_one(): except can.CanError: print("Message NOT sent") -if __name__ == '__main__': + +if __name__ == "__main__": send_one() diff --git a/examples/serial_com.py b/examples/serial_com.py index efa0bcdb5..b0dba4fec 100755 --- a/examples/serial_com.py +++ b/examples/serial_com.py @@ -46,17 +46,20 @@ def receive(bus, stop_event): print("rx: {}".format(rx_msg)) print("Stopped receiving messages") + if __name__ == "__main__": - server = can.interface.Bus(bustype='serial', channel='/dev/ttyS10') - client = can.interface.Bus(bustype='serial', channel='/dev/ttyS11') + server = can.interface.Bus(bustype="serial", channel="/dev/ttyS10") + client = can.interface.Bus(bustype="serial", channel="/dev/ttyS11") - tx_msg = can.Message(arbitration_id=0x01, data=[0x11, 0x22, 0x33, 0x44, - 0x55, 0x66, 0x77, 0x88]) + tx_msg = can.Message( + arbitration_id=0x01, data=[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88] + ) # Thread for sending and receiving messages stop_event = threading.Event() - t_send_cyclic = threading.Thread(target=send_cyclic, args=(server, tx_msg, - stop_event)) + t_send_cyclic = threading.Thread( + target=send_cyclic, args=(server, tx_msg, stop_event) + ) t_receive = threading.Thread(target=receive, args=(client, stop_event)) t_receive.start() t_send_cyclic.start() diff --git a/examples/vcan_filtered.py b/examples/vcan_filtered.py index cf7e1f8e3..b351db09b 100755 --- a/examples/vcan_filtered.py +++ b/examples/vcan_filtered.py @@ -9,12 +9,12 @@ import can -if __name__ == '__main__': - bus = can.interface.Bus(bustype='socketcan', - channel='vcan0', - receive_own_messages=True) +if __name__ == "__main__": + bus = can.interface.Bus( + bustype="socketcan", channel="vcan0", receive_own_messages=True + ) - can_filters = [{"can_id": 1, "can_mask": 0xf, "extended": True}] + can_filters = [{"can_id": 1, "can_mask": 0xF, "extended": True}] bus.set_filters(can_filters) notifier = can.Notifier(bus, [can.Printer()]) bus.send(can.Message(arbitration_id=1, is_extended_id=True)) diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index b69fb28da..a4869d51d 100755 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -17,9 +17,11 @@ def producer(id, message_count=16): :param int id: the id of the thread/process """ - with can.Bus(bustype='socketcan', channel='vcan0') as bus: + with can.Bus(bustype="socketcan", channel="vcan0") as bus: for i in range(message_count): - msg = can.Message(arbitration_id=0x0cf02200+id, data=[id, i, 0, 1, 3, 1, 4, 1]) + msg = can.Message( + arbitration_id=0x0CF02200 + id, data=[id, i, 0, 1, 3, 1, 4, 1] + ) bus.send(msg) sleep(1.0) diff --git a/setup.py b/setup.py index 805b582be..354b63c9c 100644 --- a/setup.py +++ b/setup.py @@ -15,29 +15,27 @@ logging.basicConfig(level=logging.WARNING) -with open('can/__init__.py', 'r') as fd: - version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', - fd.read(), re.MULTILINE).group(1) +with open("can/__init__.py", "r") as fd: + version = re.search( + r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE + ).group(1) -with open('README.rst', 'r') as f: +with open("README.rst", "r") as f: long_description = f.read() # Dependencies -extras_require = { - 'serial': ['pyserial~=3.0'], - 'neovi': ['python-ics>=2.12'] -} +extras_require = {"serial": ["pyserial~=3.0"], "neovi": ["python-ics>=2.12"]} tests_require = [ - 'pytest~=4.3', - 'pytest-timeout~=1.3', - 'pytest-cov~=2.6', - 'codecov~=2.0', - 'six', - 'hypothesis' -] + extras_require['serial'] + "pytest~=4.3", + "pytest-timeout~=1.3", + "pytest-cov~=2.6", + "codecov~=2.0", + "six", + "hypothesis", +] + extras_require["serial"] -extras_require['test'] = tests_require +extras_require["test"] = tests_require setup( @@ -69,37 +67,32 @@ "Topic :: System :: Monitoring", "Topic :: System :: Networking", "Topic :: System :: Hardware :: Hardware Drivers", - "Topic :: Utilities" + "Topic :: Utilities", ], - # Code version=version, packages=find_packages(exclude=["test", "doc", "scripts", "examples"]), scripts=list(filter(isfile, (join("scripts/", f) for f in listdir("scripts/")))), - # Author author="Brian Thorne", author_email="brian@thorne.link", - # License license="LGPL v3", - # Package data package_data={ "": ["README.rst", "CONTRIBUTORS.txt", "LICENSE.txt", "CHANGELOG.txt"], "doc": ["*.*"], - "examples": ["*.py"] + "examples": ["*.py"], }, - # Installation # see https://www.python.org/dev/peps/pep-0345/#version-specifiers python_requires=">=3.6", install_requires=[ - 'wrapt~=1.10', - 'aenum', + "wrapt~=1.10", + "aenum", 'windows-curses;platform_system=="Windows"', ], setup_requires=["pytest-runner"], extras_require=extras_require, - tests_require=tests_require + tests_require=tests_require, ) diff --git a/test/back2back_test.py b/test/back2back_test.py index e54ad93e4..b707988ec 100644 --- a/test/back2back_test.py +++ b/test/back2back_test.py @@ -27,30 +27,35 @@ class Back2BackTestCase(unittest.TestCase): BITRATE = 500000 TIMEOUT = 0.1 - INTERFACE_1 = 'virtual' - CHANNEL_1 = 'virtual_channel_0' - INTERFACE_2 = 'virtual' - CHANNEL_2 = 'virtual_channel_0' + INTERFACE_1 = "virtual" + CHANNEL_1 = "virtual_channel_0" + INTERFACE_2 = "virtual" + CHANNEL_2 = "virtual_channel_0" def setUp(self): - self.bus1 = can.Bus(channel=self.CHANNEL_1, - bustype=self.INTERFACE_1, - bitrate=self.BITRATE, - fd=TEST_CAN_FD, - single_handle=True) - self.bus2 = can.Bus(channel=self.CHANNEL_2, - bustype=self.INTERFACE_2, - bitrate=self.BITRATE, - fd=TEST_CAN_FD, - single_handle=True) + self.bus1 = can.Bus( + channel=self.CHANNEL_1, + bustype=self.INTERFACE_1, + bitrate=self.BITRATE, + fd=TEST_CAN_FD, + single_handle=True, + ) + self.bus2 = can.Bus( + channel=self.CHANNEL_2, + bustype=self.INTERFACE_2, + bitrate=self.BITRATE, + fd=TEST_CAN_FD, + single_handle=True, + ) def tearDown(self): self.bus1.shutdown() self.bus2.shutdown() def _check_received_message(self, recv_msg, sent_msg): - self.assertIsNotNone(recv_msg, - "No message was received on %s" % self.INTERFACE_2) + self.assertIsNotNone( + recv_msg, "No message was received on %s" % self.INTERFACE_2 + ) self.assertEqual(recv_msg.arbitration_id, sent_msg.arbitration_id) self.assertEqual(recv_msg.is_extended_id, sent_msg.is_extended_id) self.assertEqual(recv_msg.is_remote_frame, sent_msg.is_remote_frame) @@ -79,7 +84,10 @@ def _send_and_receive(self, msg): def test_no_message(self): self.assertIsNone(self.bus1.recv(0.1)) - @unittest.skipIf(IS_CI, "the timing sensitive behaviour cannot be reproduced reliably on a CI server") + @unittest.skipIf( + IS_CI, + "the timing sensitive behaviour cannot be reproduced reliably on a CI server", + ) def test_timestamp(self): self.bus2.send(can.Message()) recv_msg1 = self.bus1.recv(self.TIMEOUT) @@ -87,98 +95,102 @@ def test_timestamp(self): self.bus2.send(can.Message()) recv_msg2 = self.bus1.recv(self.TIMEOUT) delta_time = recv_msg2.timestamp - recv_msg1.timestamp - self.assertTrue(1.75 <= delta_time <= 2.25, - 'Time difference should have been 2s +/- 250ms.' - 'But measured {}'.format(delta_time)) + self.assertTrue( + 1.75 <= delta_time <= 2.25, + "Time difference should have been 2s +/- 250ms." + "But measured {}".format(delta_time), + ) def test_standard_message(self): - msg = can.Message(is_extended_id=False, - arbitration_id=0x100, - data=[1, 2, 3, 4, 5, 6, 7, 8]) + msg = can.Message( + is_extended_id=False, arbitration_id=0x100, data=[1, 2, 3, 4, 5, 6, 7, 8] + ) self._send_and_receive(msg) def test_extended_message(self): - msg = can.Message(is_extended_id=True, - arbitration_id=0x123456, - data=[10, 11, 12, 13, 14, 15, 16, 17]) + msg = can.Message( + is_extended_id=True, + arbitration_id=0x123456, + data=[10, 11, 12, 13, 14, 15, 16, 17], + ) self._send_and_receive(msg) def test_remote_message(self): - msg = can.Message(is_extended_id=False, - arbitration_id=0x200, - is_remote_frame=True, - dlc=4) + msg = can.Message( + is_extended_id=False, arbitration_id=0x200, is_remote_frame=True, dlc=4 + ) self._send_and_receive(msg) def test_dlc_less_than_eight(self): - msg = can.Message(is_extended_id=False, - arbitration_id=0x300, - data=[4, 5, 6]) + msg = can.Message(is_extended_id=False, arbitration_id=0x300, data=[4, 5, 6]) self._send_and_receive(msg) @unittest.skipUnless(TEST_CAN_FD, "Don't test CAN-FD") def test_fd_message(self): - msg = can.Message(is_fd=True, - is_extended_id=True, - arbitration_id=0x56789, - data=[0xff] * 64) + msg = can.Message( + is_fd=True, is_extended_id=True, arbitration_id=0x56789, data=[0xFF] * 64 + ) self._send_and_receive(msg) @unittest.skipUnless(TEST_CAN_FD, "Don't test CAN-FD") def test_fd_message_with_brs(self): - msg = can.Message(is_fd=True, - bitrate_switch=True, - is_extended_id=True, - arbitration_id=0x98765, - data=[0xff] * 48) + msg = can.Message( + is_fd=True, + bitrate_switch=True, + is_extended_id=True, + arbitration_id=0x98765, + data=[0xFF] * 48, + ) self._send_and_receive(msg) @unittest.skipUnless(TEST_INTERFACE_SOCKETCAN, "skip testing of socketcan") class BasicTestSocketCan(Back2BackTestCase): - INTERFACE_1 = 'socketcan' - CHANNEL_1 = 'vcan0' - INTERFACE_2 = 'socketcan' - CHANNEL_2 = 'vcan0' + INTERFACE_1 = "socketcan" + CHANNEL_1 = "vcan0" + INTERFACE_2 = "socketcan" + CHANNEL_2 = "vcan0" @unittest.skipUnless(TEST_INTERFACE_SOCKETCAN, "skip testing of socketcan") class SocketCanBroadcastChannel(unittest.TestCase): - def setUp(self): - self.broadcast_bus = can.Bus(channel='', bustype='socketcan') - self.regular_bus = can.Bus(channel='vcan0', bustype='socketcan') + self.broadcast_bus = can.Bus(channel="", bustype="socketcan") + self.regular_bus = can.Bus(channel="vcan0", bustype="socketcan") def tearDown(self): self.broadcast_bus.shutdown() self.regular_bus.shutdown() def test_broadcast_channel(self): - self.broadcast_bus.send(can.Message(channel='vcan0')) + self.broadcast_bus.send(can.Message(channel="vcan0")) recv_msg = self.regular_bus.recv(1) self.assertIsNotNone(recv_msg) - self.assertEqual(recv_msg.channel, 'vcan0') + self.assertEqual(recv_msg.channel, "vcan0") self.regular_bus.send(can.Message()) recv_msg = self.broadcast_bus.recv(1) self.assertIsNotNone(recv_msg) - self.assertEqual(recv_msg.channel, 'vcan0') + self.assertEqual(recv_msg.channel, "vcan0") class TestThreadSafeBus(Back2BackTestCase): - def setUp(self): - self.bus1 = can.ThreadSafeBus(channel=self.CHANNEL_1, - bustype=self.INTERFACE_1, - bitrate=self.BITRATE, - fd=TEST_CAN_FD, - single_handle=True) - self.bus2 = can.ThreadSafeBus(channel=self.CHANNEL_2, - bustype=self.INTERFACE_2, - bitrate=self.BITRATE, - fd=TEST_CAN_FD, - single_handle=True) + self.bus1 = can.ThreadSafeBus( + channel=self.CHANNEL_1, + bustype=self.INTERFACE_1, + bitrate=self.BITRATE, + fd=TEST_CAN_FD, + single_handle=True, + ) + self.bus2 = can.ThreadSafeBus( + channel=self.CHANNEL_2, + bustype=self.INTERFACE_2, + bitrate=self.BITRATE, + fd=TEST_CAN_FD, + single_handle=True, + ) @pytest.mark.timeout(5.0) def test_concurrent_writes(self): @@ -190,7 +202,7 @@ def test_concurrent_writes(self): channel=self.CHANNEL_1, is_extended_id=True, timestamp=121334.365, - data=[254, 255, 1, 2] + data=[254, 255, 1, 2], ) workload = 1000 * [message] @@ -221,19 +233,19 @@ def test_filtered_bus(self): channel=self.CHANNEL_1, is_extended_id=True, timestamp=121334.365, - data=[254, 255, 1, 2] + data=[254, 255, 1, 2], ) excluded_message = can.Message( arbitration_id=0x02, channel=self.CHANNEL_1, is_extended_id=True, timestamp=121334.300, - data=[1, 2, 3] + data=[1, 2, 3], ) workload = 500 * [included_message] + 500 * [excluded_message] random.shuffle(workload) - self.bus2.set_filters([{"can_id": 0x123, "can_mask": 0xff, "extended": True}]) + self.bus2.set_filters([{"can_id": 0x123, "can_mask": 0xFF, "extended": True}]) def sender(msg): self.bus1.send(msg) @@ -256,5 +268,5 @@ def receiver(_): receiver_pool.join() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/config.py b/test/config.py index 940ba7cf0..3a9072712 100644 --- a/test/config.py +++ b/test/config.py @@ -12,8 +12,8 @@ from os import environ as environment -def env(name): # type: bool - return environment.get(name, '').lower() in ("yes", "true", "t", "1") +def env(name): # type: bool + return environment.get(name, "").lower() in ("yes", "true", "t", "1") # ############################## Continuos integration @@ -22,13 +22,15 @@ def env(name): # type: bool # - https://docs.travis-ci.com/user/environment-variables/ # - https://www.appveyor.com/docs/environment-variables/ -IS_TRAVIS = env('TRAVIS') -IS_APPVEYOR = env('APPVEYOR') +IS_TRAVIS = env("TRAVIS") +IS_APPVEYOR = env("APPVEYOR") -IS_CI = IS_TRAVIS or IS_APPVEYOR or env('CI') or env('CONTINUOUS_INTEGRATION') +IS_CI = IS_TRAVIS or IS_APPVEYOR or env("CI") or env("CONTINUOUS_INTEGRATION") if IS_APPVEYOR and IS_TRAVIS: - raise EnvironmentError("IS_APPVEYOR and IS_TRAVIS cannot be both True at the same time") + raise EnvironmentError( + "IS_APPVEYOR and IS_TRAVIS cannot be both True at the same time" + ) # ############################## Platforms @@ -41,9 +43,11 @@ def env(name): # type: bool if (IS_WINDOWS and IS_LINUX) or (IS_LINUX and IS_OSX) or (IS_WINDOWS and IS_OSX): raise EnvironmentError( - "only one of IS_WINDOWS ({}), IS_LINUX ({}) and IS_OSX ({}) ".format(IS_WINDOWS, IS_LINUX, IS_OSX) + - "can be True at the same time " + - '(platform.system() == "{}")'.format(platform.system()) + "only one of IS_WINDOWS ({}), IS_LINUX ({}) and IS_OSX ({}) ".format( + IS_WINDOWS, IS_LINUX, IS_OSX + ) + + "can be True at the same time " + + '(platform.system() == "{}")'.format(platform.system()) ) @@ -51,4 +55,4 @@ def env(name): # type: bool TEST_CAN_FD = True -TEST_INTERFACE_SOCKETCAN = IS_LINUX and env('TEST_SOCKETCAN') +TEST_INTERFACE_SOCKETCAN = IS_LINUX and env("TEST_SOCKETCAN") diff --git a/test/contextmanager_test.py b/test/contextmanager_test.py index 35bc045da..95785b128 100644 --- a/test/contextmanager_test.py +++ b/test/contextmanager_test.py @@ -10,13 +10,16 @@ class ContextManagerTest(unittest.TestCase): - def setUp(self): data = [0, 1, 2, 3, 4, 5, 6, 7] - self.msg_send = can.Message(is_extended_id=False, arbitration_id=0x100, data=data) + self.msg_send = can.Message( + is_extended_id=False, arbitration_id=0x100, data=data + ) def test_open_buses(self): - with can.Bus(interface='virtual') as bus_send, can.Bus(interface='virtual') as bus_recv: + with can.Bus(interface="virtual") as bus_send, can.Bus( + interface="virtual" + ) as bus_recv: bus_send.send(self.msg_send) msg_recv = bus_recv.recv() @@ -24,7 +27,9 @@ def test_open_buses(self): self.assertTrue(msg_recv) def test_use_closed_bus(self): - with can.Bus(interface='virtual') as bus_send, can.Bus(interface='virtual') as bus_recv: + with can.Bus(interface="virtual") as bus_send, can.Bus( + interface="virtual" + ) as bus_recv: bus_send.send(self.msg_send) # Receiving a frame after bus has been closed should raise a CanException @@ -32,5 +37,5 @@ def test_use_closed_bus(self): self.assertRaises(can.CanError, bus_send.send, self.msg_send) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/data/example_data.py b/test/data/example_data.py index dd433dc3c..b91fd3bfd 100644 --- a/test/data/example_data.py +++ b/test/data/example_data.py @@ -22,7 +22,7 @@ def sort_messages(messages): :param Iterable[can.Message] messages: a sequence of messages to sort :rtype: list """ - return list(sorted(messages, key=attrgetter('timestamp'))) + return list(sorted(messages, key=attrgetter("timestamp"))) # some random number @@ -30,125 +30,137 @@ def sort_messages(messages): # List of messages of different types that can be used in tests -TEST_MESSAGES_BASE = sort_messages([ - Message( - # empty - ), - Message( - # only data - data=[0x00, 0x42] - ), - Message( - # no data - arbitration_id=0xAB, is_extended_id=False - ), - Message( - # no data - arbitration_id=0x42, is_extended_id=True - ), - Message( - # no data - arbitration_id=0xABCDEF, - ), - Message( - # empty data - data=[] - ), - Message( - # empty data - data=[0xFF, 0xFE, 0xFD], - ), - Message( - # with channel as integer - channel=0, - ), - Message( - # with channel as integer - channel=42, - ), - Message( - # with channel as string - channel="vcan0", - ), - Message( - # with channel as string - channel="awesome_channel", - ), - Message( - arbitration_id=0xABCDEF, is_extended_id=True, - timestamp=TEST_TIME, - data=[1, 2, 3, 4, 5, 6, 7, 8] - ), - Message( - arbitration_id=0x123, is_extended_id=False, - timestamp=TEST_TIME + 42.42, - data=[0xff, 0xff] - ), - Message( - arbitration_id=0xDADADA, is_extended_id=True, - timestamp=TEST_TIME + .165, - data=[1, 2, 3, 4, 5, 6, 7, 8] - ), - Message( - arbitration_id=0x123, is_extended_id=False, - timestamp=TEST_TIME + .365, - data=[254, 255] - ), - Message( - arbitration_id=0x768, is_extended_id=False, - timestamp=TEST_TIME + 3.165 - ), -]) - - -TEST_MESSAGES_REMOTE_FRAMES = sort_messages([ - Message( - arbitration_id=0xDADADA, is_extended_id=True, is_remote_frame=True, - timestamp=TEST_TIME + .165, - ), - Message( - arbitration_id=0x123, is_extended_id=False, is_remote_frame=True, - timestamp=TEST_TIME + .365, - ), - Message( - arbitration_id=0x768, is_extended_id=False, is_remote_frame=True, - timestamp=TEST_TIME + 3.165 - ), - Message( - arbitration_id=0xABCDEF, is_extended_id=True, is_remote_frame=True, - timestamp=TEST_TIME + 7858.67 - ), -]) - - -TEST_MESSAGES_ERROR_FRAMES = sort_messages([ - Message( - is_error_frame=True - ), - Message( - is_error_frame=True, - timestamp=TEST_TIME + 0.170 - ), - Message( - is_error_frame=True, - timestamp=TEST_TIME + 17.157 - ) -]) - - -TEST_ALL_MESSAGES = sort_messages(TEST_MESSAGES_BASE + TEST_MESSAGES_REMOTE_FRAMES + \ - TEST_MESSAGES_ERROR_FRAMES) +TEST_MESSAGES_BASE = sort_messages( + [ + Message( + # empty + ), + Message( + # only data + data=[0x00, 0x42] + ), + Message( + # no data + arbitration_id=0xAB, + is_extended_id=False, + ), + Message( + # no data + arbitration_id=0x42, + is_extended_id=True, + ), + Message( + # no data + arbitration_id=0xABCDEF + ), + Message( + # empty data + data=[] + ), + Message( + # empty data + data=[0xFF, 0xFE, 0xFD] + ), + Message( + # with channel as integer + channel=0 + ), + Message( + # with channel as integer + channel=42 + ), + Message( + # with channel as string + channel="vcan0" + ), + Message( + # with channel as string + channel="awesome_channel" + ), + Message( + arbitration_id=0xABCDEF, + is_extended_id=True, + timestamp=TEST_TIME, + data=[1, 2, 3, 4, 5, 6, 7, 8], + ), + Message( + arbitration_id=0x123, + is_extended_id=False, + timestamp=TEST_TIME + 42.42, + data=[0xFF, 0xFF], + ), + Message( + arbitration_id=0xDADADA, + is_extended_id=True, + timestamp=TEST_TIME + 0.165, + data=[1, 2, 3, 4, 5, 6, 7, 8], + ), + Message( + arbitration_id=0x123, + is_extended_id=False, + timestamp=TEST_TIME + 0.365, + data=[254, 255], + ), + Message( + arbitration_id=0x768, is_extended_id=False, timestamp=TEST_TIME + 3.165 + ), + ] +) + + +TEST_MESSAGES_REMOTE_FRAMES = sort_messages( + [ + Message( + arbitration_id=0xDADADA, + is_extended_id=True, + is_remote_frame=True, + timestamp=TEST_TIME + 0.165, + ), + Message( + arbitration_id=0x123, + is_extended_id=False, + is_remote_frame=True, + timestamp=TEST_TIME + 0.365, + ), + Message( + arbitration_id=0x768, + is_extended_id=False, + is_remote_frame=True, + timestamp=TEST_TIME + 3.165, + ), + Message( + arbitration_id=0xABCDEF, + is_extended_id=True, + is_remote_frame=True, + timestamp=TEST_TIME + 7858.67, + ), + ] +) + + +TEST_MESSAGES_ERROR_FRAMES = sort_messages( + [ + Message(is_error_frame=True), + Message(is_error_frame=True, timestamp=TEST_TIME + 0.170), + Message(is_error_frame=True, timestamp=TEST_TIME + 17.157), + ] +) + + +TEST_ALL_MESSAGES = sort_messages( + TEST_MESSAGES_BASE + TEST_MESSAGES_REMOTE_FRAMES + TEST_MESSAGES_ERROR_FRAMES +) TEST_COMMENTS = [ "This is the first comment", - "", # empty comment + "", # empty comment "This third comment contains some strange characters: 'ä\"§$%&/()=?__::_Öüßêè and ends here.", ( - "This fourth comment is quite long! " \ - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " \ - "Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. " \ - "Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi." \ + "This fourth comment is quite long! " + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " + "Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. " + "Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi." ), ] @@ -159,4 +171,9 @@ def generate_message(arbitration_id): and a non-extended ID. """ data = bytearray([random.randrange(0, 2 ** 8 - 1) for _ in range(8)]) - return Message(arbitration_id=arbitration_id, data=data, is_extended_id=False, timestamp=TEST_TIME) + return Message( + arbitration_id=arbitration_id, + data=data, + is_extended_id=False, + timestamp=TEST_TIME, + ) diff --git a/test/listener_test.py b/test/listener_test.py index 840f687fb..c44acc7a6 100644 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -17,8 +17,8 @@ from .data.example_data import generate_message -channel = 'virtual_channel_0' -can.rc['interface'] = 'virtual' +channel = "virtual_channel_0" +can.rc["interface"] = "virtual" logging.basicConfig(level=logging.DEBUG) @@ -27,37 +27,35 @@ class ListenerImportTest(unittest.TestCase): - def testClassesImportable(self): - self.assertTrue(hasattr(can, 'Listener')) - self.assertTrue(hasattr(can, 'BufferedReader')) - self.assertTrue(hasattr(can, 'Notifier')) - self.assertTrue(hasattr(can, 'Logger')) + self.assertTrue(hasattr(can, "Listener")) + self.assertTrue(hasattr(can, "BufferedReader")) + self.assertTrue(hasattr(can, "Notifier")) + self.assertTrue(hasattr(can, "Logger")) - self.assertTrue(hasattr(can, 'ASCWriter')) - self.assertTrue(hasattr(can, 'ASCReader')) + self.assertTrue(hasattr(can, "ASCWriter")) + self.assertTrue(hasattr(can, "ASCReader")) - self.assertTrue(hasattr(can, 'BLFReader')) - self.assertTrue(hasattr(can, 'BLFWriter')) + self.assertTrue(hasattr(can, "BLFReader")) + self.assertTrue(hasattr(can, "BLFWriter")) - self.assertTrue(hasattr(can, 'CSVReader')) - self.assertTrue(hasattr(can, 'CSVWriter')) + self.assertTrue(hasattr(can, "CSVReader")) + self.assertTrue(hasattr(can, "CSVWriter")) - self.assertTrue(hasattr(can, 'CanutilsLogWriter')) - self.assertTrue(hasattr(can, 'CanutilsLogReader')) + self.assertTrue(hasattr(can, "CanutilsLogWriter")) + self.assertTrue(hasattr(can, "CanutilsLogReader")) - self.assertTrue(hasattr(can, 'SqliteReader')) - self.assertTrue(hasattr(can, 'SqliteWriter')) + self.assertTrue(hasattr(can, "SqliteReader")) + self.assertTrue(hasattr(can, "SqliteWriter")) - self.assertTrue(hasattr(can, 'Printer')) + self.assertTrue(hasattr(can, "Printer")) - self.assertTrue(hasattr(can, 'LogReader')) + self.assertTrue(hasattr(can, "LogReader")) - self.assertTrue(hasattr(can, 'MessageSync')) + self.assertTrue(hasattr(can, "MessageSync")) class BusTest(unittest.TestCase): - def setUp(self): self.bus = can.interface.Bus() @@ -66,7 +64,6 @@ def tearDown(self): class ListenerTest(BusTest): - def testBasicListenerCanBeAddedToNotifier(self): a_listener = can.Printer() notifier = can.Notifier(self.bus, [a_listener], 0.1) @@ -98,7 +95,9 @@ def test_filetype_to_instance(extension, klass): file_handler = open(join(dirname(__file__), "data/logfile.blf")) else: delete = True - file_handler = tempfile.NamedTemporaryFile(suffix=extension, delete=False) + file_handler = tempfile.NamedTemporaryFile( + suffix=extension, delete=False + ) with file_handler as my_file: filename = my_file.name @@ -111,7 +110,7 @@ def test_filetype_to_instance(extension, klass): test_filetype_to_instance(".asc", can.ASCReader) test_filetype_to_instance(".blf", can.BLFReader) test_filetype_to_instance(".csv", can.CSVReader) - test_filetype_to_instance(".db" , can.SqliteReader) + test_filetype_to_instance(".db", can.SqliteReader) test_filetype_to_instance(".log", can.CanutilsLogReader) # test file extensions that are not supported @@ -122,7 +121,9 @@ def testLoggerTypeResolution(self): def test_filetype_to_instance(extension, klass): print("testing: {}".format(extension)) try: - with tempfile.NamedTemporaryFile(suffix=extension, delete=False) as my_file: + with tempfile.NamedTemporaryFile( + suffix=extension, delete=False + ) as my_file: filename = my_file.name with can.Logger(filename) as writer: self.assertIsInstance(writer, klass) @@ -132,7 +133,7 @@ def test_filetype_to_instance(extension, klass): test_filetype_to_instance(".asc", can.ASCWriter) test_filetype_to_instance(".blf", can.BLFWriter) test_filetype_to_instance(".csv", can.CSVWriter) - test_filetype_to_instance(".db" , can.SqliteWriter) + test_filetype_to_instance(".db", can.SqliteWriter) test_filetype_to_instance(".log", can.CanutilsLogWriter) test_filetype_to_instance(".txt", can.Printer) @@ -152,5 +153,5 @@ def testBufferedListenerReceives(self): self.assertIsNotNone(a_listener.get_message(0.1)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/logformats_test.py b/test/logformats_test.py index c393f4ea0..46eded869 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -23,9 +23,13 @@ import can -from .data.example_data import TEST_MESSAGES_BASE, TEST_MESSAGES_REMOTE_FRAMES, \ - TEST_MESSAGES_ERROR_FRAMES, TEST_COMMENTS, \ - sort_messages +from .data.example_data import ( + TEST_MESSAGES_BASE, + TEST_MESSAGES_REMOTE_FRAMES, + TEST_MESSAGES_ERROR_FRAMES, + TEST_COMMENTS, + sort_messages, +) from .message_helper import ComparingMessagesTestCase logging.basicConfig(level=logging.DEBUG) @@ -51,12 +55,20 @@ def _setup_instance(self): """Hook for subclasses.""" raise NotImplementedError() - def _setup_instance_helper(self, - writer_constructor, reader_constructor, binary_file=False, - check_remote_frames=True, check_error_frames=True, check_fd=True, - check_comments=False, test_append=False, - allowed_timestamp_delta=0.0, - preserves_channel=True, adds_default_channel=None): + def _setup_instance_helper( + self, + writer_constructor, + reader_constructor, + binary_file=False, + check_remote_frames=True, + check_error_frames=True, + check_fd=True, + check_comments=False, + test_append=False, + allowed_timestamp_delta=0.0, + preserves_channel=True, + adds_default_channel=None, + ): """ :param Callable writer_constructor: the constructor of the writer class :param Callable reader_constructor: the constructor of the reader class @@ -83,7 +95,7 @@ def _setup_instance_helper(self, if check_error_frames: self.original_messages += TEST_MESSAGES_ERROR_FRAMES if check_fd: - self.original_messages += [] # TODO: add TEST_MESSAGES_CAN_FD + self.original_messages += [] # TODO: add TEST_MESSAGES_CAN_FD # sort them so that for example ASCWriter does not "fix" any messages with timestamp 0.0 self.original_messages = sort_messages(self.original_messages) @@ -92,9 +104,12 @@ def _setup_instance_helper(self, # we check this because of the lack of a common base class # we filter for not starts with '__' so we do not get all the builtin # methods when logging to the console - attrs = [attr for attr in dir(writer_constructor) if not attr.startswith('__')] - assert 'log_event' in attrs, \ - "cannot check comments with this writer: {}".format(writer_constructor) + attrs = [ + attr for attr in dir(writer_constructor) if not attr.startswith("__") + ] + assert ( + "log_event" in attrs + ), "cannot check comments with this writer: {}".format(writer_constructor) # get all test comments self.original_comments = TEST_COMMENTS if check_comments else () @@ -104,13 +119,15 @@ def _setup_instance_helper(self, self.binary_file = binary_file self.test_append_enabled = test_append - ComparingMessagesTestCase.__init__(self, + ComparingMessagesTestCase.__init__( + self, allowed_timestamp_delta=allowed_timestamp_delta, - preserves_channel=preserves_channel) - #adds_default_channel=adds_default_channel # TODO inlcude in tests + preserves_channel=preserves_channel, + ) + # adds_default_channel=adds_default_channel # TODO inlcude in tests def setUp(self): - with tempfile.NamedTemporaryFile('w+', delete=False) as test_file: + with tempfile.NamedTemporaryFile("w+", delete=False) as test_file: self.test_file_name = test_file.name def tearDown(self): @@ -126,7 +143,7 @@ def test_path_like_explicit_stop(self): self._write_all(writer) self._ensure_fsync(writer) writer.stop() - if hasattr(writer.file, 'closed'): + if hasattr(writer.file, "closed"): self.assertTrue(writer.file.closed) print("reading all messages") @@ -134,13 +151,16 @@ def test_path_like_explicit_stop(self): read_messages = list(reader) # redundant, but this checks if stop() can be called multiple times reader.stop() - if hasattr(writer.file, 'closed'): + if hasattr(writer.file, "closed"): self.assertTrue(writer.file.closed) # check if at least the number of messages matches # could use assertCountEqual in later versions of Python and in the other methods - self.assertEqual(len(read_messages), len(self.original_messages), - "the number of written messages does not match the number of read messages") + self.assertEqual( + len(read_messages), + len(self.original_messages), + "the number of written messages does not match the number of read messages", + ) self.assertMessagesEqual(self.original_messages, read_messages) self.assertIncludesComments(self.test_file_name) @@ -154,7 +174,7 @@ def test_path_like_context_manager(self): self._write_all(writer) self._ensure_fsync(writer) w = writer - if hasattr(w.file, 'closed'): + if hasattr(w.file, "closed"): self.assertTrue(w.file.closed) # read all written messages @@ -162,12 +182,15 @@ def test_path_like_context_manager(self): with self.reader_constructor(self.test_file_name) as reader: read_messages = list(reader) r = reader - if hasattr(r.file, 'closed'): + if hasattr(r.file, "closed"): self.assertTrue(r.file.closed) - # check if at least the number of messages matches; - self.assertEqual(len(read_messages), len(self.original_messages), - "the number of written messages does not match the number of read messages") + # check if at least the number of messages matches; + self.assertEqual( + len(read_messages), + len(self.original_messages), + "the number of written messages does not match the number of read messages", + ) self.assertMessagesEqual(self.original_messages, read_messages) self.assertIncludesComments(self.test_file_name) @@ -177,27 +200,30 @@ def test_file_like_explicit_stop(self): # create writer print("writing all messages/comments") - my_file = open(self.test_file_name, 'wb' if self.binary_file else 'w') + my_file = open(self.test_file_name, "wb" if self.binary_file else "w") writer = self.writer_constructor(my_file) self._write_all(writer) self._ensure_fsync(writer) writer.stop() - if hasattr(my_file, 'closed'): + if hasattr(my_file, "closed"): self.assertTrue(my_file.closed) print("reading all messages") - my_file = open(self.test_file_name, 'rb' if self.binary_file else 'r') + my_file = open(self.test_file_name, "rb" if self.binary_file else "r") reader = self.reader_constructor(my_file) read_messages = list(reader) # redundant, but this checks if stop() can be called multiple times reader.stop() - if hasattr(my_file, 'closed'): + if hasattr(my_file, "closed"): self.assertTrue(my_file.closed) # check if at least the number of messages matches # could use assertCountEqual in later versions of Python and in the other methods - self.assertEqual(len(read_messages), len(self.original_messages), - "the number of written messages does not match the number of read messages") + self.assertEqual( + len(read_messages), + len(self.original_messages), + "the number of written messages does not match the number of read messages", + ) self.assertMessagesEqual(self.original_messages, read_messages) self.assertIncludesComments(self.test_file_name) @@ -207,26 +233,29 @@ def test_file_like_context_manager(self): # create writer print("writing all messages/comments") - my_file = open(self.test_file_name, 'wb' if self.binary_file else 'w') + my_file = open(self.test_file_name, "wb" if self.binary_file else "w") with self.writer_constructor(my_file) as writer: self._write_all(writer) self._ensure_fsync(writer) w = writer - if hasattr(my_file, 'closed'): + if hasattr(my_file, "closed"): self.assertTrue(my_file.closed) # read all written messages print("reading all messages") - my_file = open(self.test_file_name, 'rb' if self.binary_file else 'r') + my_file = open(self.test_file_name, "rb" if self.binary_file else "r") with self.reader_constructor(my_file) as reader: read_messages = list(reader) r = reader - if hasattr(my_file, 'closed'): + if hasattr(my_file, "closed"): self.assertTrue(my_file.closed) - # check if at least the number of messages matches; - self.assertEqual(len(read_messages), len(self.original_messages), - "the number of written messages does not match the number of read messages") + # check if at least the number of messages matches; + self.assertEqual( + len(read_messages), + len(self.original_messages), + "the number of written messages does not match the number of read messages", + ) self.assertMessagesEqual(self.original_messages, read_messages) self.assertIncludesComments(self.test_file_name) @@ -239,8 +268,8 @@ def test_append_mode(self): raise unittest.SkipTest("do not test append mode") count = len(self.original_messages) - first_part = self.original_messages[:count // 2] - second_part = self.original_messages[count // 2:] + first_part = self.original_messages[: count // 2] + second_part = self.original_messages[count // 2 :] # write first half with self.writer_constructor(self.test_file_name) as writer: @@ -270,17 +299,19 @@ def test_append_mode(self): def _write_all(self, writer): """Writes messages and insert comments here and there.""" # Note: we make no assumptions about the length of original_messages and original_comments - for msg, comment in zip_longest(self.original_messages, self.original_comments, fillvalue=None): + for msg, comment in zip_longest( + self.original_messages, self.original_comments, fillvalue=None + ): # msg and comment might be None if comment is not None: print("writing comment: ", comment) - writer.log_event(comment) # we already know that this method exists + writer.log_event(comment) # we already know that this method exists if msg is not None: print("writing message: ", msg) writer(msg) def _ensure_fsync(self, io_handler): - if hasattr(io_handler.file, 'fileno'): + if hasattr(io_handler.file, "fileno"): io_handler.file.flush() os.fsync(io_handler.file.fileno()) @@ -292,7 +323,7 @@ def assertIncludesComments(self, filename): """ if self.original_comments: # read the entire outout file - with open(filename, 'rb' if self.binary_file else 'r') as file: + with open(filename, "rb" if self.binary_file else "r") as file: output_contents = file.read() # check each, if they can be found in there literally for comment in self.original_comments: @@ -304,10 +335,12 @@ class TestAscFileFormat(ReaderWriterTest): def _setup_instance(self): super()._setup_instance_helper( - can.ASCWriter, can.ASCReader, + can.ASCWriter, + can.ASCReader, check_fd=False, check_comments=True, - preserves_channel=False, adds_default_channel=0 + preserves_channel=False, + adds_default_channel=0, ) @@ -316,12 +349,14 @@ class TestBlfFileFormat(ReaderWriterTest): def _setup_instance(self): super()._setup_instance_helper( - can.BLFWriter, can.BLFReader, + can.BLFWriter, + can.BLFReader, binary_file=True, check_fd=False, check_comments=False, allowed_timestamp_delta=1.0e-6, - preserves_channel=False, adds_default_channel=0 + preserves_channel=False, + adds_default_channel=0, ) def test_read_known_file(self): @@ -334,12 +369,14 @@ def test_read_known_file(self): timestamp=1.0, is_extended_id=False, arbitration_id=0x64, - data=[0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]), + data=[0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8], + ), can.Message( timestamp=73.0, is_extended_id=True, arbitration_id=0x1FFFFFFF, - is_error_frame=True,) + is_error_frame=True, + ), ] self.assertMessagesEqual(messages, expected) @@ -350,10 +387,13 @@ class TestCanutilsFileFormat(ReaderWriterTest): def _setup_instance(self): super()._setup_instance_helper( - can.CanutilsLogWriter, can.CanutilsLogReader, + can.CanutilsLogWriter, + can.CanutilsLogReader, check_fd=False, - test_append=True, check_comments=False, - preserves_channel=False, adds_default_channel='vcan0' + test_append=True, + check_comments=False, + preserves_channel=False, + adds_default_channel="vcan0", ) @@ -362,10 +402,13 @@ class TestCsvFileFormat(ReaderWriterTest): def _setup_instance(self): super()._setup_instance_helper( - can.CSVWriter, can.CSVReader, + can.CSVWriter, + can.CSVReader, check_fd=False, - test_append=True, check_comments=False, - preserves_channel=False, adds_default_channel=None + test_append=True, + check_comments=False, + preserves_channel=False, + adds_default_channel=None, ) @@ -374,10 +417,13 @@ class TestSqliteDatabaseFormat(ReaderWriterTest): def _setup_instance(self): super()._setup_instance_helper( - can.SqliteWriter, can.SqliteReader, + can.SqliteWriter, + can.SqliteReader, check_fd=False, - test_append=True, check_comments=False, - preserves_channel=False, adds_default_channel=None + test_append=True, + check_comments=False, + preserves_channel=False, + adds_default_channel=None, ) @unittest.skip("not implemented") @@ -402,9 +448,12 @@ def test_read_all(self): with self.reader_constructor(self.test_file_name) as reader: read_messages = list(reader.read_all()) - # check if at least the number of messages matches; - self.assertEqual(len(read_messages), len(self.original_messages), - "the number of written messages does not match the number of read messages") + # check if at least the number of messages matches; + self.assertEqual( + len(read_messages), + len(self.original_messages), + "the number of written messages does not match the number of read messages", + ) self.assertMessagesEqual(self.original_messages, read_messages) @@ -413,7 +462,9 @@ class TestPrinter(unittest.TestCase): """Tests that can.Printer does not crash""" # TODO add CAN FD messages - messages = TEST_MESSAGES_BASE + TEST_MESSAGES_REMOTE_FRAMES + TEST_MESSAGES_ERROR_FRAMES + messages = ( + TEST_MESSAGES_BASE + TEST_MESSAGES_REMOTE_FRAMES + TEST_MESSAGES_ERROR_FRAMES + ) def test_not_crashes_with_stdout(self): with can.Printer() as printer: @@ -421,15 +472,15 @@ def test_not_crashes_with_stdout(self): printer(message) def test_not_crashes_with_file(self): - with tempfile.NamedTemporaryFile('w', delete=False) as temp_file: + with tempfile.NamedTemporaryFile("w", delete=False) as temp_file: with can.Printer(temp_file) as printer: for message in self.messages: printer(message) # this excludes the base class from being executed as a test case itself -del(ReaderWriterTest) +del ReaderWriterTest -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/message_helper.py b/test/message_helper.py index 8fb94f48b..5f437dbde 100644 --- a/test/message_helper.py +++ b/test/message_helper.py @@ -34,24 +34,35 @@ def assertMessageEqual(self, message_1, message_2): elif self.preserves_channel: print("Comparing: message 1: {!r}".format(message_1)) print(" message 2: {!r}".format(message_2)) - self.fail("messages are unequal with allowed timestamp delta {}".format(self.allowed_timestamp_delta)) + self.fail( + "messages are unequal with allowed timestamp delta {}".format( + self.allowed_timestamp_delta + ) + ) else: - message_2 = copy(message_2) # make sure this method is pure + message_2 = copy(message_2) # make sure this method is pure message_2.channel = message_1.channel - if message_1.equals(message_2, timestamp_delta=self.allowed_timestamp_delta): + if message_1.equals( + message_2, timestamp_delta=self.allowed_timestamp_delta + ): return else: print("Comparing: message 1: {!r}".format(message_1)) print(" message 2: {!r}".format(message_2)) - self.fail("messages are unequal with allowed timestamp delta {} even when ignoring channels" \ - .format(self.allowed_timestamp_delta)) + self.fail( + "messages are unequal with allowed timestamp delta {} even when ignoring channels".format( + self.allowed_timestamp_delta + ) + ) def assertMessagesEqual(self, messages_1, messages_2): """ Checks the order and content of the individual messages pairwise. Raises an error if the lengths of the sequences are not equal. """ - self.assertEqual(len(messages_1), len(messages_2), "the number of messages differs") + self.assertEqual( + len(messages_1), len(messages_2), "the number of messages differs" + ) for message_1, message_2 in zip(messages_1, messages_2): self.assertMessageEqual(message_1, message_2) diff --git a/test/network_test.py b/test/network_test.py index 1af102ec3..2ee4795fb 100644 --- a/test/network_test.py +++ b/test/network_test.py @@ -15,11 +15,11 @@ import can -channel = 'vcan0' -can.rc['interface'] = 'virtual' +channel = "vcan0" +can.rc["interface"] = "virtual" -@unittest.skipIf('interface' not in can.rc, "Need a CAN interface") +@unittest.skipIf("interface" not in can.rc, "Need a CAN interface") class ControllerAreaNetworkTestCase(unittest.TestCase): """ This test ensures that what messages go in to the bus is what comes out. @@ -39,9 +39,10 @@ class ControllerAreaNetworkTestCase(unittest.TestCase): extended_flags = [rbool() for _ in range(num_messages)] ids = list(range(num_messages)) - data = list(bytearray([random.randrange(0, 2 ** 8 - 1) - for a in range(random.randrange(9))]) - for b in range(num_messages)) + data = list( + bytearray([random.randrange(0, 2 ** 8 - 1) for a in range(random.randrange(9))]) + for b in range(num_messages) + ) def producer(self, ready_event, msg_read): self.client_bus = can.interface.Bus(channel=channel) @@ -52,9 +53,9 @@ def producer(self, ready_event, msg_read): is_remote_frame=self.remote_flags[i], is_error_frame=self.error_flags[i], is_extended_id=self.extended_flags[i], - data=self.data[i] + data=self.data[i], ) - #logging.debug("writing message: {}".format(m)) + # logging.debug("writing message: {}".format(m)) if msg_read is not None: # Don't send until the other thread is ready msg_read.wait() @@ -95,7 +96,7 @@ def testProducerConsumer(self): msg_read.set() msg = self.server_bus.recv(timeout=0.5) self.assertIsNotNone(msg, "Didn't receive a message") - #logging.debug("Received message {} with data: {}".format(i, msg.data)) + # logging.debug("Received message {} with data: {}".format(i, msg.data)) self.assertEqual(msg.is_extended_id, self.extended_flags[i]) if not msg.is_remote_frame: @@ -111,5 +112,6 @@ def testProducerConsumer(self): self.server_bus.flush_tx_buffer() self.server_bus.shutdown() -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/test/notifier_test.py b/test/notifier_test.py index 71ac7a944..0a60bd25d 100644 --- a/test/notifier_test.py +++ b/test/notifier_test.py @@ -9,9 +9,8 @@ class NotifierTest(unittest.TestCase): - def test_single_bus(self): - bus = can.Bus('test', bustype='virtual', receive_own_messages=True) + bus = can.Bus("test", bustype="virtual", receive_own_messages=True) reader = can.BufferedReader() notifier = can.Notifier(bus, [reader], 0.1) msg = can.Message() @@ -21,8 +20,8 @@ def test_single_bus(self): bus.shutdown() def test_multiple_bus(self): - bus1 = can.Bus(0, bustype='virtual', receive_own_messages=True) - bus2 = can.Bus(1, bustype='virtual', receive_own_messages=True) + bus1 = can.Bus(0, bustype="virtual", receive_own_messages=True) + bus2 = can.Bus(1, bustype="virtual", receive_own_messages=True) reader = can.BufferedReader() notifier = can.Notifier([bus1, bus2], [reader], 0.1) msg = can.Message() @@ -41,10 +40,9 @@ def test_multiple_bus(self): class AsyncNotifierTest(unittest.TestCase): - def test_asyncio_notifier(self): loop = asyncio.get_event_loop() - bus = can.Bus('test', bustype='virtual', receive_own_messages=True) + bus = can.Bus("test", bustype="virtual", receive_own_messages=True) reader = can.AsyncBufferedReader() notifier = can.Notifier(bus, [reader], 0.1, loop=loop) msg = can.Message() @@ -56,5 +54,5 @@ def test_asyncio_notifier(self): bus.shutdown() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/serial_test.py b/test/serial_test.py index a29379511..d0e11f974 100644 --- a/test/serial_test.py +++ b/test/serial_test.py @@ -20,6 +20,7 @@ class SerialDummy: """ Dummy to mock the serial communication """ + msg = None def __init__(self): @@ -43,7 +44,9 @@ class SimpleSerialTestBase(ComparingMessagesTestCase): MAX_TIMESTAMP = 0xFFFFFFFF / 1000 def __init__(self): - ComparingMessagesTestCase.__init__(self, allowed_timestamp_delta=None, preserves_channel=True) + ComparingMessagesTestCase.__init__( + self, allowed_timestamp_delta=None, preserves_channel=True + ) def test_rx_tx_min_max_data(self): """ @@ -109,7 +112,7 @@ def test_rx_tx_max_timestamp_error(self): """ Tests for an exception with an out of range timestamp (max + 1) """ - msg = can.Message(timestamp=self.MAX_TIMESTAMP+1) + msg = can.Message(timestamp=self.MAX_TIMESTAMP + 1) self.assertRaises(ValueError, self.bus.send, msg) def test_rx_tx_min_timestamp(self): @@ -131,36 +134,34 @@ def test_rx_tx_min_timestamp_error(self): class SimpleSerialTest(unittest.TestCase, SimpleSerialTestBase): - def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) SimpleSerialTestBase.__init__(self) def setUp(self): - self.patcher = patch('serial.Serial') + self.patcher = patch("serial.Serial") self.mock_serial = self.patcher.start() self.serial_dummy = SerialDummy() self.mock_serial.return_value.write = self.serial_dummy.write self.mock_serial.return_value.read = self.serial_dummy.read self.addCleanup(self.patcher.stop) - self.bus = SerialBus('bus') + self.bus = SerialBus("bus") def tearDown(self): self.serial_dummy.reset() class SimpleSerialLoopTest(unittest.TestCase, SimpleSerialTestBase): - def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) SimpleSerialTestBase.__init__(self) def setUp(self): - self.bus = SerialBus('loop://') + self.bus = SerialBus("loop://") def tearDown(self): self.bus.shutdown() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/simplecyclic_test.py b/test/simplecyclic_test.py index 06712e921..95bbd0a99 100644 --- a/test/simplecyclic_test.py +++ b/test/simplecyclic_test.py @@ -16,17 +16,23 @@ class SimpleCyclicSendTaskTest(unittest.TestCase, ComparingMessagesTestCase): - def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) - ComparingMessagesTestCase.__init__(self, allowed_timestamp_delta=0.016, preserves_channel=True) - - @unittest.skipIf(IS_CI, "the timing sensitive behaviour cannot be reproduced reliably on a CI server") + ComparingMessagesTestCase.__init__( + self, allowed_timestamp_delta=0.016, preserves_channel=True + ) + + @unittest.skipIf( + IS_CI, + "the timing sensitive behaviour cannot be reproduced reliably on a CI server", + ) def test_cycle_time(self): - msg = can.Message(is_extended_id=False, arbitration_id=0x123, data=[0, 1, 2, 3, 4, 5, 6, 7]) + msg = can.Message( + is_extended_id=False, arbitration_id=0x123, data=[0, 1, 2, 3, 4, 5, 6, 7] + ) - with can.interface.Bus(bustype='virtual') as bus1: - with can.interface.Bus(bustype='virtual') as bus2: + with can.interface.Bus(bustype="virtual") as bus1: + with can.interface.Bus(bustype="virtual") as bus2: # disabling the garbage collector makes the time readings more reliable gc.disable() @@ -37,8 +43,12 @@ def test_cycle_time(self): sleep(2) size = bus2.queue.qsize() # About 100 messages should have been transmitted - self.assertTrue(80 <= size <= 120, - '100 +/- 20 messages should have been transmitted. But queue contained {}'.format(size)) + self.assertTrue( + 80 <= size <= 120, + "100 +/- 20 messages should have been transmitted. But queue contained {}".format( + size + ), + ) last_msg = bus2.recv() next_last_msg = bus2.recv() @@ -57,10 +67,14 @@ def test_cycle_time(self): self.assertMessageEqual(msg, last_msg) def test_removing_bus_tasks(self): - bus = can.interface.Bus(bustype='virtual') + bus = can.interface.Bus(bustype="virtual") tasks = [] for task_i in range(10): - msg = can.Message(is_extended_id=False, arbitration_id=0x123, data=[0, 1, 2, 3, 4, 5, 6, 7]) + msg = can.Message( + is_extended_id=False, + arbitration_id=0x123, + data=[0, 1, 2, 3, 4, 5, 6, 7], + ) msg.arbitration_id = task_i task = bus.send_periodic(msg, 0.1, 1) tasks.append(task) @@ -76,10 +90,14 @@ def test_removing_bus_tasks(self): bus.shutdown() def test_managed_tasks(self): - bus = can.interface.Bus(bustype='virtual', receive_own_messages=True) + bus = can.interface.Bus(bustype="virtual", receive_own_messages=True) tasks = [] for task_i in range(3): - msg = can.Message(is_extended_id=False, arbitration_id=0x123, data=[0, 1, 2, 3, 4, 5, 6, 7]) + msg = can.Message( + is_extended_id=False, + arbitration_id=0x123, + data=[0, 1, 2, 3, 4, 5, 6, 7], + ) msg.arbitration_id = task_i task = bus.send_periodic(msg, 0.1, 10, store_task=False) tasks.append(task) @@ -102,10 +120,14 @@ def test_managed_tasks(self): bus.shutdown() def test_stopping_perodic_tasks(self): - bus = can.interface.Bus(bustype='virtual') + bus = can.interface.Bus(bustype="virtual") tasks = [] for task_i in range(10): - msg = can.Message(is_extended_id=False, arbitration_id=0x123, data=[0, 1, 2, 3, 4, 5, 6, 7]) + msg = can.Message( + is_extended_id=False, + arbitration_id=0x123, + data=[0, 1, 2, 3, 4, 5, 6, 7], + ) msg.arbitration_id = task_i task = bus.send_periodic(msg, 0.1, 1) tasks.append(task) @@ -130,5 +152,5 @@ def test_stopping_perodic_tasks(self): bus.shutdown() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_detect_available_configs.py b/test/test_detect_available_configs.py index 69addafb3..0d94e31f1 100644 --- a/test/test_detect_available_configs.py +++ b/test/test_detect_available_configs.py @@ -15,40 +15,41 @@ class TestDetectAvailableConfigs(unittest.TestCase): - def test_count_returned(self): # At least virtual has to always return at least one interface - self.assertGreaterEqual (len(detect_available_configs() ), 1) - self.assertEqual (len(detect_available_configs(interfaces=[]) ), 0) - self.assertGreaterEqual (len(detect_available_configs(interfaces='virtual') ), 1) - self.assertGreaterEqual (len(detect_available_configs(interfaces=['virtual']) ), 1) - self.assertGreaterEqual (len(detect_available_configs(interfaces=None) ), 1) + self.assertGreaterEqual(len(detect_available_configs()), 1) + self.assertEqual(len(detect_available_configs(interfaces=[])), 0) + self.assertGreaterEqual(len(detect_available_configs(interfaces="virtual")), 1) + self.assertGreaterEqual( + len(detect_available_configs(interfaces=["virtual"])), 1 + ) + self.assertGreaterEqual(len(detect_available_configs(interfaces=None)), 1) def test_general_values(self): configs = detect_available_configs() for config in configs: - self.assertIn('interface', config) - self.assertIn('channel', config) - self.assertIsInstance(config['interface'], str) + self.assertIn("interface", config) + self.assertIn("channel", config) + self.assertIsInstance(config["interface"], str) def test_content_virtual(self): - configs = detect_available_configs(interfaces='virtual') + configs = detect_available_configs(interfaces="virtual") for config in configs: - self.assertEqual(config['interface'], 'virtual') + self.assertEqual(config["interface"], "virtual") def test_content_socketcan(self): - configs = detect_available_configs(interfaces='socketcan') + configs = detect_available_configs(interfaces="socketcan") for config in configs: - self.assertEqual(config['interface'], 'socketcan') + self.assertEqual(config["interface"], "socketcan") @unittest.skipUnless(TEST_INTERFACE_SOCKETCAN, "socketcan is not tested") def test_socketcan_on_ci_server(self): - configs = detect_available_configs(interfaces='socketcan') + configs = detect_available_configs(interfaces="socketcan") self.assertGreaterEqual(len(configs), 1) - self.assertIn('vcan0', [config['channel'] for config in configs]) + self.assertIn("vcan0", [config["channel"] for config in configs]) # see TestSocketCanHelpers.test_find_available_interfaces() too -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_kvaser.py b/test/test_kvaser.py index 106ce7dc5..733bbc367 100644 --- a/test/test_kvaser.py +++ b/test/test_kvaser.py @@ -18,7 +18,6 @@ class KvaserTest(unittest.TestCase): - def setUp(self): canlib.canGetNumberOfChannels = KvaserTest.canGetNumberOfChannels canlib.canOpenChannel = Mock(return_value=0) @@ -40,7 +39,7 @@ def setUp(self): self.msg = {} self.msg_in_cue = None - self.bus = can.Bus(channel=0, bustype='kvaser') + self.bus = can.Bus(channel=0, bustype="kvaser") def tearDown(self): if self.bus: @@ -60,67 +59,58 @@ def test_bus_shutdown(self): 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 + ((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) + 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} - ]) + 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 + ((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) + 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} + {"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 + ((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) + 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], - is_extended_id=True) + arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4], is_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]) + 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], - is_extended_id=False) + msg = can.Message(arbitration_id=0x321, data=[50, 51], is_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]) + 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]) @pytest.mark.timeout(3.0) def test_recv_no_message(self): @@ -128,13 +118,12 @@ def test_recv_no_message(self): def test_recv_extended(self): self.msg_in_cue = can.Message( - arbitration_id=0xc0ffef, - data=[1, 2, 3, 4, 5, 6, 7, 8], - is_extended_id=True) + arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True + ) now = time.time() msg = self.bus.recv() - self.assertEqual(msg.arbitration_id, 0xc0ffef) + self.assertEqual(msg.arbitration_id, 0xC0FFEF) self.assertEqual(msg.dlc, 8) self.assertEqual(msg.is_extended_id, True) self.assertSequenceEqual(msg.data, self.msg_in_cue.data) @@ -142,53 +131,54 @@ def test_recv_extended(self): def test_recv_standard(self): self.msg_in_cue = can.Message( - arbitration_id=0x123, - data=[100, 101], - is_extended_id=False) + arbitration_id=0x123, data=[100, 101], is_extended_id=False + ) msg = self.bus.recv() self.assertEqual(msg.arbitration_id, 0x123) self.assertEqual(msg.dlc, 2) self.assertEqual(msg.is_extended_id, False) self.assertSequenceEqual(msg.data, [100, 101]) - + def test_available_configs(self): configs = canlib.KvaserBus._detect_available_configs() expected = [ - {'interface': 'kvaser', 'channel': 0}, - {'interface': 'kvaser', 'channel': 1} + {"interface": "kvaser", "channel": 0}, + {"interface": "kvaser", "channel": 1}, ] self.assertListEqual(configs, expected) def test_canfd_default_data_bitrate(self): canlib.canSetBusParams.reset_mock() canlib.canSetBusParamsFd.reset_mock() - can.Bus(channel=0, bustype='kvaser', fd=True) + can.Bus(channel=0, bustype="kvaser", fd=True) canlib.canSetBusParams.assert_called_once_with( - 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0) + 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0 + ) canlib.canSetBusParamsFd.assert_called_once_with( - 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0) + 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0 + ) def test_canfd_nondefault_data_bitrate(self): canlib.canSetBusParams.reset_mock() canlib.canSetBusParamsFd.reset_mock() data_bitrate = 2000000 - can.Bus(channel=0, bustype='kvaser', fd=True, data_bitrate=data_bitrate) + can.Bus(channel=0, bustype="kvaser", fd=True, data_bitrate=data_bitrate) bitrate_constant = canlib.BITRATE_FD[data_bitrate] canlib.canSetBusParams.assert_called_once_with( - 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0) - canlib.canSetBusParamsFd.assert_called_once_with( - 0, bitrate_constant, 0, 0, 0) + 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0 + ) + canlib.canSetBusParamsFd.assert_called_once_with(0, bitrate_constant, 0, 0, 0) def test_canfd_custom_data_bitrate(self): canlib.canSetBusParams.reset_mock() canlib.canSetBusParamsFd.reset_mock() data_bitrate = 123456 - can.Bus(channel=0, bustype='kvaser', fd=True, data_bitrate=data_bitrate) + can.Bus(channel=0, bustype="kvaser", fd=True, data_bitrate=data_bitrate) canlib.canSetBusParams.assert_called_once_with( - 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0) - canlib.canSetBusParamsFd.assert_called_once_with( - 0, data_bitrate, 0, 0, 0) + 0, constants.canFD_BITRATE_500K_80P, 0, 0, 0, 0, 0 + ) + canlib.canSetBusParamsFd.assert_called_once_with(0, data_bitrate, 0, 0, 0) def test_bus_get_stats(self): stats = self.bus.get_stats() @@ -201,10 +191,10 @@ def canGetNumberOfChannels(count): count._obj.value = 2 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) + 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: @@ -227,5 +217,6 @@ def canReadWait(self, handle, arb_id, data, dlc, flags, timestamp, timeout): return constants.canOK -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/test/test_load_file_config.py b/test/test_load_file_config.py index dbd7673cb..d2f5fc3f2 100644 --- a/test/test_load_file_config.py +++ b/test/test_load_file_config.py @@ -11,10 +11,10 @@ class LoadFileConfigTest(unittest.TestCase): configuration = { - 'default': {'interface': 'virtual', 'channel': '0'}, - 'one': {'interface': 'virtual', 'channel': '1'}, - 'two': {'channel': '2'}, - 'three': {'extra': 'extra value'}, + "default": {"interface": "virtual", "channel": "0"}, + "one": {"interface": "virtual", "channel": "1"}, + "two": {"channel": "2"}, + "three": {"extra": "extra value"}, } def setUp(self): @@ -26,65 +26,65 @@ def tearDown(self): shutil.rmtree(self.test_dir) def _gen_configration_file(self, sections): - with NamedTemporaryFile(mode='w', dir=self.test_dir, - delete=False) as tmp_config_file: + with NamedTemporaryFile( + mode="w", dir=self.test_dir, delete=False + ) as tmp_config_file: content = [] for section in sections: content.append("[{}]".format(section)) for k, v in self.configuration[section].items(): content.append("{} = {}".format(k, v)) - tmp_config_file.write('\n'.join(content)) + tmp_config_file.write("\n".join(content)) return tmp_config_file.name def test_config_file_with_default(self): - tmp_config = self._gen_configration_file(['default']) + tmp_config = self._gen_configration_file(["default"]) config = can.util.load_file_config(path=tmp_config) - self.assertEqual(config, self.configuration['default']) + self.assertEqual(config, self.configuration["default"]) def test_config_file_with_default_and_section(self): - tmp_config = self._gen_configration_file(['default', 'one']) + tmp_config = self._gen_configration_file(["default", "one"]) default = can.util.load_file_config(path=tmp_config) - self.assertEqual(default, self.configuration['default']) + self.assertEqual(default, self.configuration["default"]) - one = can.util.load_file_config(path=tmp_config, section='one') - self.assertEqual(one, self.configuration['one']) + one = can.util.load_file_config(path=tmp_config, section="one") + self.assertEqual(one, self.configuration["one"]) def test_config_file_with_section_only(self): - tmp_config = self._gen_configration_file(['one']) - config = can.util.load_file_config(path=tmp_config, section='one') - self.assertEqual(config, self.configuration['one']) + tmp_config = self._gen_configration_file(["one"]) + config = can.util.load_file_config(path=tmp_config, section="one") + self.assertEqual(config, self.configuration["one"]) def test_config_file_with_section_and_key_in_default(self): - expected = self.configuration['default'].copy() - expected.update(self.configuration['two']) + expected = self.configuration["default"].copy() + expected.update(self.configuration["two"]) - tmp_config = self._gen_configration_file(['default', 'two']) - config = can.util.load_file_config(path=tmp_config, section='two') + tmp_config = self._gen_configration_file(["default", "two"]) + config = can.util.load_file_config(path=tmp_config, section="two") self.assertEqual(config, expected) def test_config_file_with_section_missing_interface(self): - expected = self.configuration['two'].copy() - tmp_config = self._gen_configration_file(['two']) - config = can.util.load_file_config(path=tmp_config, section='two') + expected = self.configuration["two"].copy() + tmp_config = self._gen_configration_file(["two"]) + config = can.util.load_file_config(path=tmp_config, section="two") self.assertEqual(config, expected) def test_config_file_extra(self): - expected = self.configuration['default'].copy() - expected.update(self.configuration['three']) + expected = self.configuration["default"].copy() + expected.update(self.configuration["three"]) - tmp_config = self._gen_configration_file(['default', 'three']) - config = can.util.load_file_config(path=tmp_config, section='three') + tmp_config = self._gen_configration_file(["default", "three"]) + config = can.util.load_file_config(path=tmp_config, section="three") self.assertEqual(config, expected) def test_config_file_with_non_existing_section(self): expected = {} - tmp_config = self._gen_configration_file([ - 'default', 'one', 'two', 'three']) - config = can.util.load_file_config(path=tmp_config, section='zero') + tmp_config = self._gen_configration_file(["default", "one", "two", "three"]) + config = can.util.load_file_config(path=tmp_config, section="zero") self.assertEqual(config, expected) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_message_class.py b/test/test_message_class.py index 85dbe8560..760a848bd 100644 --- a/test/test_message_class.py +++ b/test/test_message_class.py @@ -30,47 +30,53 @@ class TestMessageClass(unittest.TestCase): data=st.one_of(st.binary(min_size=0, max_size=8), st.none()), is_fd=st.booleans(), bitrate_switch=st.booleans(), - error_state_indicator=st.booleans() + error_state_indicator=st.booleans(), ) @settings(max_examples=2000) def test_methods(self, **kwargs): is_valid = not ( - (not kwargs['is_remote_frame'] and (len(kwargs['data'] or []) != kwargs['dlc'])) or - (kwargs['arbitration_id'] >= 0x800 and not kwargs['is_extended_id']) or - kwargs['arbitration_id'] >= 0x20000000 or - kwargs['arbitration_id'] < 0 or - (kwargs['is_remote_frame'] and kwargs['is_error_frame']) or - (kwargs['is_remote_frame'] and len(kwargs['data'] or []) > 0) or - ((kwargs['bitrate_switch'] or kwargs['error_state_indicator']) and not kwargs['is_fd']) or - isnan(kwargs['timestamp']) or - isinf(kwargs['timestamp']) + ( + not kwargs["is_remote_frame"] + and (len(kwargs["data"] or []) != kwargs["dlc"]) + ) + or (kwargs["arbitration_id"] >= 0x800 and not kwargs["is_extended_id"]) + or kwargs["arbitration_id"] >= 0x20000000 + or kwargs["arbitration_id"] < 0 + or (kwargs["is_remote_frame"] and kwargs["is_error_frame"]) + or (kwargs["is_remote_frame"] and len(kwargs["data"] or []) > 0) + or ( + (kwargs["bitrate_switch"] or kwargs["error_state_indicator"]) + and not kwargs["is_fd"] + ) + or isnan(kwargs["timestamp"]) + or isinf(kwargs["timestamp"]) ) # this should return normally and not throw an exception message = Message(check=is_valid, **kwargs) - if kwargs['data'] is None or kwargs['is_remote_frame']: - kwargs['data'] = bytearray() + if kwargs["data"] is None or kwargs["is_remote_frame"]: + kwargs["data"] = bytearray() - if not is_valid and not kwargs['is_remote_frame']: + if not is_valid and not kwargs["is_remote_frame"]: with self.assertRaises(ValueError): Message(check=True, **kwargs) self.assertGreater(len(str(message)), 0) self.assertGreater(len(message.__repr__()), 0) if is_valid: - self.assertEqual(len(message), kwargs['dlc']) + self.assertEqual(len(message), kwargs["dlc"]) self.assertTrue(bool(message)) self.assertGreater(len("{}".format(message)), 0) _ = "{}".format(message) with self.assertRaises(Exception): _ = "{somespec}".format(message) if sys.version_info.major > 2: - self.assertEqual(bytearray(bytes(message)), kwargs['data']) + self.assertEqual(bytearray(bytes(message)), kwargs["data"]) # check copies and equalities if is_valid: - self.assertEqual(message, message) + self.assertEqual(message, message) normal_copy = copy(message) deep_copy = deepcopy(message) for other in (normal_copy, deep_copy, message): @@ -79,5 +85,5 @@ def test_methods(self, **kwargs): self.assertTrue(message.equals(other, timestamp_delta=0)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_message_filtering.py b/test/test_message_filtering.py index 1b498b95e..18ddf9e19 100644 --- a/test/test_message_filtering.py +++ b/test/test_message_filtering.py @@ -15,23 +15,14 @@ EXAMPLE_MSG = Message(arbitration_id=0x123, is_extended_id=True) HIGHEST_MSG = Message(arbitration_id=0x1FFFFFFF, is_extended_id=True) -MATCH_EXAMPLE = [{ - "can_id": 0x123, - "can_mask": 0x1FFFFFFF, - "extended": True -}] +MATCH_EXAMPLE = [{"can_id": 0x123, "can_mask": 0x1FFFFFFF, "extended": True}] -MATCH_ONLY_HIGHEST = [{ - "can_id": 0xFFFFFFFF, - "can_mask": 0x1FFFFFFF, - "extended": True -}] +MATCH_ONLY_HIGHEST = [{"can_id": 0xFFFFFFFF, "can_mask": 0x1FFFFFFF, "extended": True}] class TestMessageFiltering(unittest.TestCase): - def setUp(self): - self.bus = Bus(bustype='virtual', channel='testy') + self.bus = Bus(bustype="virtual", channel="testy") def tearDown(self): self.bus.shutdown() @@ -58,5 +49,5 @@ def test_match_example_message(self): self.assertTrue(self.bus._matches_filters(HIGHEST_MSG)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_message_sync.py b/test/test_message_sync.py index ba2332776..b7b911bd0 100644 --- a/test/test_message_sync.py +++ b/test/test_message_sync.py @@ -30,10 +30,11 @@ def inc(value): return value -@unittest.skipIf(IS_APPVEYOR or (IS_TRAVIS and IS_OSX), - "this environment's timings are too unpredictable") +@unittest.skipIf( + IS_APPVEYOR or (IS_TRAVIS and IS_OSX), + "this environment's timings are too unpredictable", +) class TestMessageSync(unittest.TestCase, ComparingMessagesTestCase): - def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) ComparingMessagesTestCase.__init__(self) @@ -53,7 +54,7 @@ def test_general(self): Message(timestamp=50.0), Message(timestamp=50.0 + 0.05), Message(timestamp=50.0 + 0.05 + 0.08), - Message(timestamp=50.0) # back in time + Message(timestamp=50.0), # back in time ] sync = MessageSync(messages, gap=0.0) @@ -75,7 +76,7 @@ def test_general(self): self.assertTrue(0.075 <= timings[3] < inc(0.085), str(timings[3])) self.assertTrue(0.0 <= timings[4] < inc(0.005), str(timings[4])) - @pytest.mark.timeout(inc(0.1) * len(TEST_FEWER_MESSAGES)) # very conservative + @pytest.mark.timeout(inc(0.1) * len(TEST_FEWER_MESSAGES)) # very conservative def test_skip(self): messages = copy(TEST_FEWER_MESSAGES) sync = MessageSync(messages, skip=0.005, gap=0.0) @@ -92,19 +93,17 @@ def test_skip(self): self.assertMessagesEqual(messages, collected) -if not IS_APPVEYOR: # this environment's timings are too unpredictable +if not IS_APPVEYOR: # this environment's timings are too unpredictable @pytest.mark.timeout(inc(0.3)) - @pytest.mark.parametrize("timestamp_1,timestamp_2", [ - (0.0, 0.0), - (0.0, 0.01), - (0.01, 0.0), - ]) + @pytest.mark.parametrize( + "timestamp_1,timestamp_2", [(0.0, 0.0), (0.0, 0.01), (0.01, 0.0)] + ) def test_gap(timestamp_1, timestamp_2): """This method is alone so it can be parameterized.""" messages = [ Message(arbitration_id=0x1, timestamp=timestamp_1), - Message(arbitration_id=0x2, timestamp=timestamp_2) + Message(arbitration_id=0x2, timestamp=timestamp_2), ] sync = MessageSync(messages, gap=0.1) @@ -119,5 +118,5 @@ def test_gap(timestamp_1, timestamp_2): assert messages == collected -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_scripts.py b/test/test_scripts.py index 11dc06991..9504cf707 100644 --- a/test/test_scripts.py +++ b/test/test_scripts.py @@ -15,7 +15,6 @@ class CanScriptTest(unittest.TestCase, metaclass=ABCMeta): - @classmethod def setUpClass(cls): # clean up the argument list so the call to the main() functions @@ -38,9 +37,13 @@ def test_do_commands_exist(self): output = "-- NO OUTPUT --" allowed = [0, errno.EINVAL] - self.assertIn(return_code, allowed, - 'Calling "{}" failed (exit code was {} and not SUCCESS/0 or EINVAL/22):\n{}' - .format(command, return_code, output)) + self.assertIn( + return_code, + allowed, + 'Calling "{}" failed (exit code was {} and not SUCCESS/0 or EINVAL/22):\n{}'.format( + command, return_code, output + ), + ) def test_does_not_crash(self): # test import @@ -65,11 +68,10 @@ def _import(self): class TestLoggerScript(CanScriptTest): - def _commands(self): commands = [ "python -m can.logger --help", - "python scripts/can_logger.py --help" + "python scripts/can_logger.py --help", ] if IS_UNIX: commands += ["can_logger.py --help"] @@ -77,15 +79,15 @@ def _commands(self): def _import(self): import can.logger as module + return module class TestPlayerScript(CanScriptTest): - def _commands(self): commands = [ "python -m can.player --help", - "python scripts/can_player.py --help" + "python scripts/can_player.py --help", ] if IS_UNIX: commands += ["can_player.py --help"] @@ -93,6 +95,7 @@ def _commands(self): def _import(self): import can.player as module + return module @@ -100,8 +103,8 @@ def _import(self): # this excludes the base class from being executed as a test case itself -del(CanScriptTest) +del CanScriptTest -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_slcan.py b/test/test_slcan.py index 43703b3f0..c5d0d47e0 100644 --- a/test/test_slcan.py +++ b/test/test_slcan.py @@ -6,9 +6,8 @@ class slcanTestCase(unittest.TestCase): - def setUp(self): - self.bus = can.Bus('loop://', bustype='slcan', sleep_after_open=0) + self.bus = can.Bus("loop://", bustype="slcan", sleep_after_open=0) self.serial = self.bus.serialPortOrig self.serial.read(self.serial.in_waiting) @@ -16,7 +15,7 @@ def tearDown(self): self.bus.shutdown() def test_recv_extended(self): - self.serial.write(b'T12ABCDEF2AA55\r') + self.serial.write(b"T12ABCDEF2AA55\r") msg = self.bus.recv(0) self.assertIsNotNone(msg) self.assertEqual(msg.arbitration_id, 0x12ABCDEF) @@ -26,15 +25,15 @@ def test_recv_extended(self): self.assertSequenceEqual(msg.data, [0xAA, 0x55]) def test_send_extended(self): - msg = can.Message(arbitration_id=0x12ABCDEF, - is_extended_id=True, - data=[0xAA, 0x55]) + msg = can.Message( + arbitration_id=0x12ABCDEF, is_extended_id=True, data=[0xAA, 0x55] + ) self.bus.send(msg) data = self.serial.read(self.serial.in_waiting) - self.assertEqual(data, b'T12ABCDEF2AA55\r') + self.assertEqual(data, b"T12ABCDEF2AA55\r") def test_recv_standard(self): - self.serial.write(b't4563112233\r') + self.serial.write(b"t4563112233\r") msg = self.bus.recv(0) self.assertIsNotNone(msg) self.assertEqual(msg.arbitration_id, 0x456) @@ -44,15 +43,15 @@ def test_recv_standard(self): self.assertSequenceEqual(msg.data, [0x11, 0x22, 0x33]) def test_send_standard(self): - msg = can.Message(arbitration_id=0x456, - is_extended_id=False, - data=[0x11, 0x22, 0x33]) + msg = can.Message( + arbitration_id=0x456, is_extended_id=False, data=[0x11, 0x22, 0x33] + ) self.bus.send(msg) data = self.serial.read(self.serial.in_waiting) - self.assertEqual(data, b't4563112233\r') + self.assertEqual(data, b"t4563112233\r") def test_recv_standard_remote(self): - self.serial.write(b'r1238\r') + self.serial.write(b"r1238\r") msg = self.bus.recv(0) self.assertIsNotNone(msg) self.assertEqual(msg.arbitration_id, 0x123) @@ -61,16 +60,15 @@ def test_recv_standard_remote(self): self.assertEqual(msg.dlc, 8) def test_send_standard_remote(self): - msg = can.Message(arbitration_id=0x123, - is_extended_id=False, - is_remote_frame=True, - dlc=8) + msg = can.Message( + arbitration_id=0x123, is_extended_id=False, is_remote_frame=True, dlc=8 + ) self.bus.send(msg) data = self.serial.read(self.serial.in_waiting) - self.assertEqual(data, b'r1238\r') + self.assertEqual(data, b"r1238\r") def test_recv_extended_remote(self): - self.serial.write(b'R12ABCDEF6\r') + self.serial.write(b"R12ABCDEF6\r") msg = self.bus.recv(0) self.assertIsNotNone(msg) self.assertEqual(msg.arbitration_id, 0x12ABCDEF) @@ -79,20 +77,19 @@ def test_recv_extended_remote(self): self.assertEqual(msg.dlc, 6) def test_send_extended_remote(self): - msg = can.Message(arbitration_id=0x12ABCDEF, - is_extended_id=True, - is_remote_frame=True, - dlc=6) + msg = can.Message( + arbitration_id=0x12ABCDEF, is_extended_id=True, is_remote_frame=True, dlc=6 + ) self.bus.send(msg) data = self.serial.read(self.serial.in_waiting) - self.assertEqual(data, b'R12ABCDEF6\r') + self.assertEqual(data, b"R12ABCDEF6\r") def test_partial_recv(self): - self.serial.write(b'T12ABCDEF') + self.serial.write(b"T12ABCDEF") msg = self.bus.recv(0) self.assertIsNone(msg) - self.serial.write(b'2AA55\rT12') + self.serial.write(b"2AA55\rT12") msg = self.bus.recv(0) self.assertIsNotNone(msg) self.assertEqual(msg.arbitration_id, 0x12ABCDEF) @@ -104,10 +101,10 @@ def test_partial_recv(self): msg = self.bus.recv(0) self.assertIsNone(msg) - self.serial.write(b'ABCDEF2AA55\r') + self.serial.write(b"ABCDEF2AA55\r") msg = self.bus.recv(0) self.assertIsNotNone(msg) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_socketcan_helpers.py b/test/test_socketcan_helpers.py index cc12eb415..311398657 100644 --- a/test/test_socketcan_helpers.py +++ b/test/test_socketcan_helpers.py @@ -7,14 +7,12 @@ import unittest -from can.interfaces.socketcan.utils import \ - find_available_interfaces, error_code_to_str +from can.interfaces.socketcan.utils import find_available_interfaces, error_code_to_str from .config import * class TestSocketCanHelpers(unittest.TestCase): - @unittest.skipUnless(IS_LINUX, "socketcan is only available on Linux") def test_error_code_to_str(self): """ @@ -27,7 +25,7 @@ def test_error_code_to_str(self): for error_code in test_data: string = error_code_to_str(error_code) - self.assertTrue(string) # not None or empty + self.assertTrue(string) # not None or empty @unittest.skipUnless(IS_LINUX, "socketcan is only available on Linux") def test_find_available_interfaces(self): @@ -40,5 +38,5 @@ def test_find_available_interfaces(self): self.assertIn("vcan0", result) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_systec.py b/test/test_systec.py index 0cd38da0d..6910653fc 100644 --- a/test/test_systec.py +++ b/test/test_systec.py @@ -10,10 +10,12 @@ class SystecTest(unittest.TestCase): - def compare_message(self, first, second, msg): - if first.arbitration_id != second.arbitration_id or first.data != second.data or \ - first.is_extended_id != second.is_extended_id: + if ( + first.arbitration_id != second.arbitration_id + or first.data != second.data + or first.is_extended_id != second.is_extended_id + ): raise self.failureException(msg) def setUp(self): @@ -30,12 +32,14 @@ def setUp(self): ucan.UcanDeinitHardware = Mock() ucan.UcanWriteCanMsgEx = Mock() ucan.UcanResetCanEx = Mock() - self.bus = can.Bus(bustype='systec', channel=0, bitrate=125000) + self.bus = can.Bus(bustype="systec", channel=0, bitrate=125000) def test_bus_creation(self): self.assertIsInstance(self.bus, ucanbus.UcanBus) self.assertTrue(ucan.UcanInitHwConnectControlEx.called) - self.assertTrue(ucan.UcanInitHardwareEx.called or ucan.UcanInitHardwareEx2.called) + self.assertTrue( + ucan.UcanInitHardwareEx.called or ucan.UcanInitHardwareEx2.called + ) self.assertTrue(ucan.UcanInitCanEx2.called) self.assertTrue(ucan.UcanGetHardwareInfoEx2.called) self.assertTrue(ucan.UcanSetAcceptanceEx.called) @@ -47,9 +51,7 @@ def test_bus_shutdown(self): def test_filter_setup(self): # no filter in the constructor - expected_args = ( - (self.bus._ucan._handle, 0, AMR_ALL, ACR_ALL), - ) + expected_args = ((self.bus._ucan._handle, 0, AMR_ALL, ACR_ALL),) self.assertEqual(ucan.UcanSetAcceptanceEx.call_args, expected_args) # one filter is handled by the driver @@ -57,33 +59,30 @@ def test_filter_setup(self): can_filter = (True, 0x123, 0x123, False, False) self.bus.set_filters(ucanbus.UcanBus.create_filter(*can_filter)) expected_args = ( - (self.bus._ucan._handle, - 0, - ucan.UcanServer.calculate_amr(*can_filter), - ucan.UcanServer.calculate_acr(*can_filter) - ), + ( + self.bus._ucan._handle, + 0, + ucan.UcanServer.calculate_amr(*can_filter), + ucan.UcanServer.calculate_acr(*can_filter), + ), ) self.assertEqual(ucan.UcanSetAcceptanceEx.call_args, expected_args) # multiple filters are handled by the bus ucan.UcanSetAcceptanceEx.reset_mock() - can_filter = ( - (False, 0x8, 0x8, False, False), - (False, 0x9, 0x9, False, False) - ) - self.bus.set_filters(ucanbus.UcanBus.create_filter(*can_filter[0]) + - ucanbus.UcanBus.create_filter(*can_filter[1])) - expected_args = ( - (self.bus._ucan._handle, 0, AMR_ALL, ACR_ALL), + can_filter = ((False, 0x8, 0x8, False, False), (False, 0x9, 0x9, False, False)) + self.bus.set_filters( + ucanbus.UcanBus.create_filter(*can_filter[0]) + + ucanbus.UcanBus.create_filter(*can_filter[1]) ) + expected_args = ((self.bus._ucan._handle, 0, AMR_ALL, ACR_ALL),) self.assertEqual(ucan.UcanSetAcceptanceEx.call_args, expected_args) - @patch('can.interfaces.systec.ucan.UcanServer.write_can_msg') + @patch("can.interfaces.systec.ucan.UcanServer.write_can_msg") def test_send_extended(self, mock_write_can_msg): msg = can.Message( - arbitration_id=0xc0ffee, - data=[0, 25, 0, 1, 3, 1, 4], - is_extended_id=True) + arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4], is_extended_id=True + ) self.bus.send(msg) expected_args = ( @@ -91,12 +90,9 @@ def test_send_extended(self, mock_write_can_msg): ) self.assertEqual(mock_write_can_msg.call_args, expected_args) - @patch('can.interfaces.systec.ucan.UcanServer.write_can_msg') + @patch("can.interfaces.systec.ucan.UcanServer.write_can_msg") def test_send_standard(self, mock_write_can_msg): - msg = can.Message( - arbitration_id=0x321, - data=[50, 51], - is_extended_id=False) + msg = can.Message(arbitration_id=0x321, data=[50, 51], is_extended_id=False) self.bus.send(msg) expected_args = ( @@ -104,41 +100,43 @@ def test_send_standard(self, mock_write_can_msg): ) self.assertEqual(mock_write_can_msg.call_args, expected_args) - @patch('can.interfaces.systec.ucan.UcanServer.get_msg_pending') + @patch("can.interfaces.systec.ucan.UcanServer.get_msg_pending") def test_recv_no_message(self, mock_get_msg_pending): mock_get_msg_pending.return_value = 0 self.assertEqual(self.bus.recv(timeout=0.5), None) - @patch('can.interfaces.systec.ucan.UcanServer.get_msg_pending') - @patch('can.interfaces.systec.ucan.UcanServer.read_can_msg') + @patch("can.interfaces.systec.ucan.UcanServer.get_msg_pending") + @patch("can.interfaces.systec.ucan.UcanServer.read_can_msg") def test_recv_extended(self, mock_read_can_msg, mock_get_msg_pending): - mock_read_can_msg.return_value = [CanMsg(0xc0ffef, MsgFrameFormat.MSG_FF_EXT, [1, 2, 3, 4, 5, 6, 7, 8])], 0 + mock_read_can_msg.return_value = ( + [CanMsg(0xC0FFEF, MsgFrameFormat.MSG_FF_EXT, [1, 2, 3, 4, 5, 6, 7, 8])], + 0, + ) mock_get_msg_pending.return_value = 1 msg = can.Message( - arbitration_id=0xc0ffef, - data=[1, 2, 3, 4, 5, 6, 7, 8], - is_extended_id=True) + arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True + ) can_msg = self.bus.recv() self.assertEqual(can_msg, msg) - @patch('can.interfaces.systec.ucan.UcanServer.get_msg_pending') - @patch('can.interfaces.systec.ucan.UcanServer.read_can_msg') + @patch("can.interfaces.systec.ucan.UcanServer.get_msg_pending") + @patch("can.interfaces.systec.ucan.UcanServer.read_can_msg") def test_recv_standard(self, mock_read_can_msg, mock_get_msg_pending): - mock_read_can_msg.return_value = [CanMsg(0x321, MsgFrameFormat.MSG_FF_STD, [50, 51])], 0 + mock_read_can_msg.return_value = ( + [CanMsg(0x321, MsgFrameFormat.MSG_FF_STD, [50, 51])], + 0, + ) mock_get_msg_pending.return_value = 1 - msg = can.Message( - arbitration_id=0x321, - data=[50, 51], - is_extended_id=False) + msg = can.Message(arbitration_id=0x321, data=[50, 51], is_extended_id=False) can_msg = self.bus.recv() self.assertEqual(can_msg, msg) @staticmethod def test_bus_defaults(): ucan.UcanInitCanEx2.reset_mock() - bus = can.Bus(bustype='systec', channel=0) + bus = can.Bus(bustype="systec", channel=0) ucan.UcanInitCanEx2.assert_called_once_with( bus._ucan._handle, 0, @@ -150,14 +148,14 @@ def test_bus_defaults(): ACR_ALL, BaudrateEx.BAUDEX_USE_BTR01, DEFAULT_BUFFER_ENTRIES, - DEFAULT_BUFFER_ENTRIES - ) + DEFAULT_BUFFER_ENTRIES, + ), ) @staticmethod def test_bus_channel(): ucan.UcanInitCanEx2.reset_mock() - bus = can.Bus(bustype='systec', channel=1) + bus = can.Bus(bustype="systec", channel=1) ucan.UcanInitCanEx2.assert_called_once_with( bus._ucan._handle, 1, @@ -169,14 +167,14 @@ def test_bus_channel(): ACR_ALL, BaudrateEx.BAUDEX_USE_BTR01, DEFAULT_BUFFER_ENTRIES, - DEFAULT_BUFFER_ENTRIES - ) + DEFAULT_BUFFER_ENTRIES, + ), ) @staticmethod def test_bus_bitrate(): ucan.UcanInitCanEx2.reset_mock() - bus = can.Bus(bustype='systec', channel=0, bitrate=125000) + bus = can.Bus(bustype="systec", channel=0, bitrate=125000) ucan.UcanInitCanEx2.assert_called_once_with( bus._ucan._handle, 0, @@ -188,18 +186,18 @@ def test_bus_bitrate(): ACR_ALL, BaudrateEx.BAUDEX_USE_BTR01, DEFAULT_BUFFER_ENTRIES, - DEFAULT_BUFFER_ENTRIES - ) + DEFAULT_BUFFER_ENTRIES, + ), ) def test_bus_custom_bitrate(self): with self.assertRaises(ValueError): - can.Bus(bustype='systec', channel=0, bitrate=123456) + can.Bus(bustype="systec", channel=0, bitrate=123456) @staticmethod def test_receive_own_messages(): ucan.UcanInitCanEx2.reset_mock() - bus = can.Bus(bustype='systec', channel=0, receive_own_messages=True) + bus = can.Bus(bustype="systec", channel=0, receive_own_messages=True) ucan.UcanInitCanEx2.assert_called_once_with( bus._ucan._handle, 0, @@ -211,14 +209,14 @@ def test_receive_own_messages(): ACR_ALL, BaudrateEx.BAUDEX_USE_BTR01, DEFAULT_BUFFER_ENTRIES, - DEFAULT_BUFFER_ENTRIES - ) + DEFAULT_BUFFER_ENTRIES, + ), ) @staticmethod def test_bus_passive_state(): ucan.UcanInitCanEx2.reset_mock() - bus = can.Bus(bustype='systec', channel=0, state=can.BusState.PASSIVE) + bus = can.Bus(bustype="systec", channel=0, state=can.BusState.PASSIVE) ucan.UcanInitCanEx2.assert_called_once_with( bus._ucan._handle, 0, @@ -230,14 +228,14 @@ def test_bus_passive_state(): ACR_ALL, BaudrateEx.BAUDEX_USE_BTR01, DEFAULT_BUFFER_ENTRIES, - DEFAULT_BUFFER_ENTRIES - ) + DEFAULT_BUFFER_ENTRIES, + ), ) @staticmethod def test_rx_buffer_entries(): ucan.UcanInitCanEx2.reset_mock() - bus = can.Bus(bustype='systec', channel=0, rx_buffer_entries=1024) + bus = can.Bus(bustype="systec", channel=0, rx_buffer_entries=1024) ucan.UcanInitCanEx2.assert_called_once_with( bus._ucan._handle, 0, @@ -249,14 +247,14 @@ def test_rx_buffer_entries(): ACR_ALL, BaudrateEx.BAUDEX_USE_BTR01, 1024, - DEFAULT_BUFFER_ENTRIES - ) + DEFAULT_BUFFER_ENTRIES, + ), ) @staticmethod def test_tx_buffer_entries(): ucan.UcanInitCanEx2.reset_mock() - bus = can.Bus(bustype='systec', channel=0, tx_buffer_entries=1024) + bus = can.Bus(bustype="systec", channel=0, tx_buffer_entries=1024) ucan.UcanInitCanEx2.assert_called_once_with( bus._ucan._handle, 0, @@ -268,14 +266,16 @@ def test_tx_buffer_entries(): ACR_ALL, BaudrateEx.BAUDEX_USE_BTR01, DEFAULT_BUFFER_ENTRIES, - 1024 - ) + 1024, + ), ) def test_flush_tx_buffer(self): self.bus.flush_tx_buffer() - ucan.UcanResetCanEx.assert_called_once_with(self.bus._ucan._handle, 0, ResetFlags.RESET_ONLY_TX_BUFF) + ucan.UcanResetCanEx.assert_called_once_with( + self.bus._ucan._handle, 0, ResetFlags.RESET_ONLY_TX_BUFF + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_viewer.py b/test/test_viewer.py index 9420b6d37..01f3b7c07 100644 --- a/test/test_viewer.py +++ b/test/test_viewer.py @@ -42,7 +42,6 @@ # noinspection SpellCheckingInspection,PyUnusedLocal class StdscrDummy: - def __init__(self): self.key_counter = 0 @@ -65,7 +64,7 @@ def addstr(row, col, txt, *args): assert col >= 0 assert txt is not None # Raise an exception 50 % of the time, so we can make sure the code handles it - if random.random() < .5: + if random.random() < 0.5: raise curses.error @staticmethod @@ -78,13 +77,13 @@ def getch(self): # Send invalid key return -1 elif self.key_counter == 2: - return ord('c') # Clear + return ord("c") # Clear elif self.key_counter == 3: return KEY_SPACE # Pause elif self.key_counter == 4: return KEY_SPACE # Unpause elif self.key_counter == 5: - return ord('s') # Sort + return ord("s") # Sort # Keep scrolling until it exceeds the number of messages elif self.key_counter <= 100: @@ -97,7 +96,6 @@ def getch(self): class CanViewerTest(unittest.TestCase): - @classmethod def setUpClass(cls): # Set seed, so the tests are not affected @@ -105,33 +103,33 @@ def setUpClass(cls): def setUp(self): stdscr = StdscrDummy() - config = {'interface': 'virtual', 'receive_own_messages': True} + config = {"interface": "virtual", "receive_own_messages": True} bus = can.Bus(**config) data_structs = None - patch_curs_set = patch('curses.curs_set') + patch_curs_set = patch("curses.curs_set") patch_curs_set.start() self.addCleanup(patch_curs_set.stop) - patch_use_default_colors = patch('curses.use_default_colors') + patch_use_default_colors = patch("curses.use_default_colors") patch_use_default_colors.start() self.addCleanup(patch_use_default_colors.stop) - patch_init_pair = patch('curses.init_pair') + patch_init_pair = patch("curses.init_pair") patch_init_pair.start() self.addCleanup(patch_init_pair.stop) - patch_color_pair = patch('curses.color_pair') + patch_color_pair = patch("curses.color_pair") patch_color_pair.start() self.addCleanup(patch_color_pair.stop) - patch_is_term_resized = patch('curses.is_term_resized') + patch_is_term_resized = patch("curses.is_term_resized") mock_is_term_resized = patch_is_term_resized.start() - mock_is_term_resized.return_value = True if random.random() < .5 else False + mock_is_term_resized.return_value = True if random.random() < 0.5 else False self.addCleanup(patch_is_term_resized.stop) - if hasattr(curses, 'resizeterm'): - patch_resizeterm = patch('curses.resizeterm') + if hasattr(curses, "resizeterm"): + patch_resizeterm = patch("curses.resizeterm") patch_resizeterm.start() self.addCleanup(patch_resizeterm.stop) @@ -174,7 +172,7 @@ def test_send(self): # self.assertTupleEqual(self.can_viewer.parse_canopen_message(msg), (None, None)) # Send the same message again to make sure that resending works and dt is correct - time.sleep(.1) + time.sleep(0.1) self.can_viewer.bus.send(msg) # Send error message @@ -187,41 +185,46 @@ def test_receive(self): data_structs = { # For converting the EMCY and HEARTBEAT messages - 0x080 + 0x01: struct.Struct('ff'), + 0x123456: struct.Struct(">ff"), } # Receive the messages we just sent in 'test_canopen' while 1: msg = self.can_viewer.bus.recv(timeout=0) if msg is not None: - self.can_viewer.data_structs = data_structs if msg.arbitration_id != 0x101 else None + self.can_viewer.data_structs = ( + data_structs if msg.arbitration_id != 0x101 else None + ) _id = self.can_viewer.draw_can_bus_message(msg) - if _id['msg'].arbitration_id == 0x101: + if _id["msg"].arbitration_id == 0x101: # Check if the counter is reset when the length has changed - self.assertEqual(_id['count'], 1) - elif _id['msg'].arbitration_id == 0x123456: + self.assertEqual(_id["count"], 1) + elif _id["msg"].arbitration_id == 0x123456: # Check if the counter is incremented - if _id['dt'] == 0: - self.assertEqual(_id['count'], 1) + if _id["dt"] == 0: + self.assertEqual(_id["count"], 1) else: - self.assertTrue(pytest.approx(_id['dt'], 0.1)) # dt should be ~0.1 s - self.assertEqual(_id['count'], 2) + self.assertTrue( + pytest.approx(_id["dt"], 0.1) + ) # dt should be ~0.1 s + self.assertEqual(_id["count"], 2) else: # Make sure dt is 0 - if _id['count'] == 1: - self.assertEqual(_id['dt'], 0) + if _id["count"] == 1: + self.assertEqual(_id["dt"], 0) else: break # Convert it into raw integer values and then pack the data @staticmethod - def pack_data(cmd, cmd_to_struct, *args): # type: (int, Dict, Union[*float, *int]) -> bytes + def pack_data( + cmd, cmd_to_struct, *args + ): # type: (int, Dict, Union[*float, *int]) -> bytes if not cmd_to_struct or len(args) == 0: # If no arguments are given, then the message does not contain a data package - return b'' + return b"" for key in cmd_to_struct.keys(): if cmd == key if isinstance(key, int) else cmd in key: @@ -237,12 +240,14 @@ def pack_data(cmd, cmd_to_struct, *args): # type: (int, Dict, Union[*float, *in fmt = six.b(fmt) # Make sure the endian is given as the first argument - assert six.byte2int(fmt) == ord('<') or six.byte2int(fmt) == ord('>') + assert six.byte2int(fmt) == ord("<") or six.byte2int(fmt) == ord( + ">" + ) # Disable rounding if the format is a float data = [] for c, arg, val in zip(six.iterbytes(fmt[1:]), args, value[1:]): - if c == ord('f'): + if c == ord("f"): data.append(arg * val) else: data.append(round(arg * val)) @@ -253,7 +258,7 @@ def pack_data(cmd, cmd_to_struct, *args): # type: (int, Dict, Union[*float, *in return struct_t.pack(*data) else: - raise ValueError('Unknown command: 0x{:02X}'.format(cmd)) + raise ValueError("Unknown command: 0x{:02X}".format(cmd)) def test_pack_unpack(self): CANOPEN_TPDO1 = 0x180 @@ -281,16 +286,18 @@ def test_pack_unpack(self): # are divided by the value in order to convert from real units to raw integer values. data_structs = { # CANopen node 1 - CANOPEN_TPDO1 + 1: struct.Struct('lL'), - (CANOPEN_TPDO3 + 2, CANOPEN_TPDO4 + 2): struct.Struct('>LL'), + CANOPEN_TPDO2 + 2: struct.Struct(">lL"), + (CANOPEN_TPDO3 + 2, CANOPEN_TPDO4 + 2): struct.Struct(">LL"), } # type: Dict[Union[int, Tuple[int, ...]], Union[struct.Struct, Tuple, None]] - raw_data = self.pack_data(CANOPEN_TPDO1 + 1, data_structs, -7, 13, -1024, 2048, 0xFFFF) + raw_data = self.pack_data( + CANOPEN_TPDO1 + 1, data_structs, -7, 13, -1024, 2048, 0xFFFF + ) parsed_data = CanViewer.unpack_data(CANOPEN_TPDO1 + 1, data_structs, raw_data) self.assertListEqual(parsed_data, [-7, 13, -1024, 2048, 0xFFFF]) self.assertTrue(all(isinstance(d, int) for d in parsed_data)) @@ -298,17 +305,22 @@ def test_pack_unpack(self): raw_data = self.pack_data(CANOPEN_TPDO2 + 1, data_structs, 12.34, 4.5, 6) parsed_data = CanViewer.unpack_data(CANOPEN_TPDO2 + 1, data_structs, raw_data) self.assertTrue(pytest.approx(parsed_data, [12.34, 4.5, 6])) - self.assertTrue(isinstance(parsed_data[0], float) and isinstance(parsed_data[1], float) and - isinstance(parsed_data[2], int)) + self.assertTrue( + isinstance(parsed_data[0], float) + and isinstance(parsed_data[1], float) + and isinstance(parsed_data[2], int) + ) raw_data = self.pack_data(CANOPEN_TPDO3 + 1, data_structs, 123.45, 67.89) parsed_data = CanViewer.unpack_data(CANOPEN_TPDO3 + 1, data_structs, raw_data) self.assertTrue(pytest.approx(parsed_data, [123.45, 67.89])) self.assertTrue(all(isinstance(d, float) for d in parsed_data)) - raw_data = self.pack_data(CANOPEN_TPDO4 + 1, data_structs, math.pi / 2., math.pi) + raw_data = self.pack_data( + CANOPEN_TPDO4 + 1, data_structs, math.pi / 2.0, math.pi + ) parsed_data = CanViewer.unpack_data(CANOPEN_TPDO4 + 1, data_structs, raw_data) - self.assertTrue(pytest.approx(parsed_data, [math.pi / 2., math.pi])) + self.assertTrue(pytest.approx(parsed_data, [math.pi / 2.0, math.pi])) self.assertTrue(all(isinstance(d, float) for d in parsed_data)) raw_data = self.pack_data(CANOPEN_TPDO1 + 2, data_structs) @@ -316,7 +328,9 @@ def test_pack_unpack(self): self.assertListEqual(parsed_data, []) self.assertIsInstance(parsed_data, list) - raw_data = self.pack_data(CANOPEN_TPDO2 + 2, data_structs, -2147483648, 0xFFFFFFFF) + raw_data = self.pack_data( + CANOPEN_TPDO2 + 2, data_structs, -2147483648, 0xFFFFFFFF + ) parsed_data = CanViewer.unpack_data(CANOPEN_TPDO2 + 2, data_structs, raw_data) self.assertListEqual(parsed_data, [-2147483648, 0xFFFFFFFF]) @@ -335,50 +349,56 @@ def test_pack_unpack(self): self.pack_data(0x101, data_structs, 1, 2, 3, 4) with self.assertRaises(ValueError): - CanViewer.unpack_data(0x102, data_structs, b'\x01\x02\x03\x04\x05\x06\x07\x08') + CanViewer.unpack_data( + 0x102, data_structs, b"\x01\x02\x03\x04\x05\x06\x07\x08" + ) def test_parse_args(self): - parsed_args, _, _ = parse_args(['-b', '250000']) + parsed_args, _, _ = parse_args(["-b", "250000"]) self.assertEqual(parsed_args.bitrate, 250000) - parsed_args, _, _ = parse_args(['--bitrate', '500000']) + parsed_args, _, _ = parse_args(["--bitrate", "500000"]) self.assertEqual(parsed_args.bitrate, 500000) - parsed_args, _, _ = parse_args(['-c', 'can0']) - self.assertEqual(parsed_args.channel, 'can0') + parsed_args, _, _ = parse_args(["-c", "can0"]) + self.assertEqual(parsed_args.channel, "can0") - parsed_args, _, _ = parse_args(['--channel', 'PCAN_USBBUS1']) - self.assertEqual(parsed_args.channel, 'PCAN_USBBUS1') + parsed_args, _, _ = parse_args(["--channel", "PCAN_USBBUS1"]) + self.assertEqual(parsed_args.channel, "PCAN_USBBUS1") - parsed_args, _, data_structs = parse_args(['-d', '100: Date: Thu, 23 May 2019 22:10:45 -0700 Subject: [PATCH 068/252] Clean up CANalystII interface formatting Clean up formatting of the CANalystII interface after running Black formatter. --- can/interfaces/canalystii.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 11f4acfbd..10de8b170 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -115,12 +115,10 @@ def __init__( logger.error("VCI_OpenDevice Error") for channel in self.channels: - if ( - CANalystII.VCI_InitCAN( - VCI_USBCAN2, self.device, channel, byref(self.init_config) - ) - == STATUS_ERR - ): + status = CANalystII.VCI_InitCAN( + VCI_USBCAN2, self.device, channel, byref(self.init_config) + ) + if status == STATUS_ERR: logger.error("VCI_InitCAN Error") self.shutdown() return @@ -171,17 +169,10 @@ def _recv_internal(self, timeout=None): timeout = -1 if timeout is None else int(timeout * 1000) - if ( - CANalystII.VCI_Receive( - VCI_USBCAN2, - self.device, - self.channels[0], - byref(raw_message), - 1, - timeout, - ) - <= STATUS_ERR - ): + status = CANalystII.VCI_Receive( + VCI_USBCAN2, self.device, self.channels[0], byref(raw_message), 1, timeout + ) + if status <= STATUS_ERR: return None, False else: return ( From c79bba80701954ff36a08dddf17900af4bb6f9bc Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 23 May 2019 22:22:06 -0700 Subject: [PATCH 069/252] Change CI command Switch to using: black --check --verbose . Providing the diff isn't too valuable when the formatting is automatically handled by the formatter. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5c24c1cd8..50909340f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,8 +98,7 @@ jobs: before_install: - travis_retry pip install -r requirements-lint.txt script: - - black --diff . - - black --check . + - black --check --verbose . - stage: deploy name: "PyPi Deployment" python: "3.7" From 4e95890d67ce995647c1826e806034a68d38d8de Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sat, 1 Jun 2019 20:09:47 +0200 Subject: [PATCH 070/252] add set_bitrate_reg to slcanBus --- can/interfaces/slcan.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index b63b7d57a..270f54791 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -103,8 +103,7 @@ def __init__( self.set_bitrate(self, bitrate) if btr is not None: - self.close() - self.write("s" + btr) + self.set_bitrate_reg(self, btr) self.open() @@ -123,6 +122,11 @@ def set_bitrate(self, bitrate): ) self.open() + def set_bitrate_reg(self, btr): + self.close() + self.write("s" + btr) + self.open() + def write(self, string): self.serialPortOrig.write(string.encode() + self.LINE_TERMINATOR) self.serialPortOrig.flush() From a7a9c096134b6fb0b2308c870bb379c6e5d8f4f6 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 2 Jun 2019 04:05:08 +0200 Subject: [PATCH 071/252] Remove dependency on Six (#607) Fixes #600 --- can/viewer.py | 3 ++- setup.py | 1 - test/test_viewer.py | 20 +++++++++----------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/can/viewer.py b/can/viewer.py index 5f8d18858..b6ecbfe36 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -23,12 +23,13 @@ # e-mail : lauszus@gmail.com import argparse +import logging import os import struct import sys import time -import logging from typing import Dict, List, Tuple, Union + import can from can import __version__ diff --git a/setup.py b/setup.py index 354b63c9c..1918b644b 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,6 @@ "pytest-timeout~=1.3", "pytest-cov~=2.6", "codecov~=2.0", - "six", "hypothesis", ] + extras_require["serial"] diff --git a/test/test_viewer.py b/test/test_viewer.py index 01f3b7c07..a0873d02b 100644 --- a/test/test_viewer.py +++ b/test/test_viewer.py @@ -24,19 +24,19 @@ # e-mail : lauszus@gmail.com import argparse -import can import curses import math -import pytest +import os import random import struct import time import unittest -import os -import six from typing import Dict, Tuple, Union -from unittest.mock import Mock, patch +from unittest.mock import patch +import pytest + +import can from can.viewer import KEY_ESC, KEY_SPACE, CanViewer, parse_args @@ -235,18 +235,16 @@ def pack_data( # The conversion from SI-units to raw values are given in the rest of the tuple fmt = struct_t.format - if isinstance(fmt, six.string_types): # pragma: no cover + if isinstance(fmt, str): # pragma: no cover # Needed for Python 3.7 - fmt = six.b(fmt) + fmt = fmt.encode() # Make sure the endian is given as the first argument - assert six.byte2int(fmt) == ord("<") or six.byte2int(fmt) == ord( - ">" - ) + assert fmt[0] == ord("<") or fmt[0] == ord(">") # Disable rounding if the format is a float data = [] - for c, arg, val in zip(six.iterbytes(fmt[1:]), args, value[1:]): + for c, arg, val in zip(fmt[1:], args, value[1:]): if c == ord("f"): data.append(arg * val) else: From dc3ab215e1a55b5ac4b280d2ee2e6edabf1b3c7b Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sun, 2 Jun 2019 09:13:54 +0200 Subject: [PATCH 072/252] unnecessary to specify 1 byte in serial read --- can/interfaces/slcan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 270f54791..7102eb414 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -135,14 +135,14 @@ def read(self, timeout): # first read what is already in receive buffer while self.serialPortOrig.in_waiting: - self._buffer += self.serialPortOrig.read(1) + self._buffer += self.serialPortOrig.read() # if we still don't have a complete message, do a blocking read start = time.time() time_left = timeout while not (self._OK in self._buffer or self._ERROR in self._buffer): self.serialPortOrig.timeout = time_left - byte = self.serialPortOrig.read(1) + byte = self.serialPortOrig.read() if byte: self._buffer += byte @@ -171,7 +171,7 @@ def read(self, timeout): def flush(self): del self._buffer[:] while self.serialPortOrig.in_waiting: - self.serialPortOrig.read(1) + self.serialPortOrig.read() def open(self): self.write("O") From dcc60ac3ea41d9ee060b77e2d4ad0a7f76f6e476 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sun, 2 Jun 2019 09:16:56 +0200 Subject: [PATCH 073/252] fix faulty if in read of slcanBus --- can/interfaces/slcan.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 7102eb414..07e5494c0 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -140,7 +140,10 @@ def read(self, timeout): # if we still don't have a complete message, do a blocking read start = time.time() time_left = timeout - while not (self._OK in self._buffer or self._ERROR in self._buffer): + while not ( + ord(self._OK) in self._buffer or + ord(self._ERROR) in self._buffer + ): self.serialPortOrig.timeout = time_left byte = self.serialPortOrig.read() if byte: @@ -160,8 +163,8 @@ def read(self, timeout): # return first message for i in range(len(self._buffer)): - if ( chr(self._buffer[i]) == self._OK or - chr(self._buffer[i]) == self._ERROR ): + if ( self._buffer[i] == ord(self._OK) or + self._buffer[i] == ord(self._ERROR) ): string = self._buffer[:i+1].decode() del self._buffer[:i+1] break From 807e288622c221e709ce050f42a1ba6c0f21c576 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sun, 2 Jun 2019 09:24:01 +0200 Subject: [PATCH 074/252] rename get_serial to get_serial_number --- can/interfaces/slcan.py | 6 +++--- test/test_slcan.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 07e5494c0..8831b52cb 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -287,7 +287,7 @@ def get_version(self, timeout): else: return None, None - def get_serial(self, timeout): + def get_serial_number(self, timeout): cmd = "N" self.write(cmd) @@ -299,8 +299,8 @@ def get_serial(self, timeout): if not string: pass elif string[0] == cmd and len(string) == 6: - serial = string[1:-1] - return serial + serial_number = string[1:-1] + return serial_number # if timeout is None, try indefinitely if timeout is None: diff --git a/test/test_slcan.py b/test/test_slcan.py index bff016656..178996c93 100644 --- a/test/test_slcan.py +++ b/test/test_slcan.py @@ -115,12 +115,12 @@ def test_version(self): self.assertIsNone(hw_ver) self.assertIsNone(sw_ver) - def test_serial(self): + def test_serial_number(self): self.serial.write(b'NA123\r') - sn = self.bus.get_serial(0) + sn = self.bus.get_serial_number(0) self.assertEqual(sn, "A123") - sn = self.bus.get_serial(0) + sn = self.bus.get_serial_number(0) self.assertIsNone(sn) From ba2a4570ac55eb375c7c40fcf894e383bcda391a Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Sun, 2 Jun 2019 10:16:27 +0200 Subject: [PATCH 075/252] reformat using black --- can/interfaces/slcan.py | 29 +++++------------------------ test/test_slcan.py | 4 ++-- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 8831b52cb..658a8a6fa 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -84,10 +84,8 @@ def __init__( if not channel: # if None or empty raise TypeError("Must specify a serial port.") - if "@" in channel: (channel, ttyBaudrate) = channel.split("@") - self.serialPortOrig = serial.serial_for_url( channel, baudrate=ttyBaudrate, rtscts=rtscts ) @@ -98,13 +96,10 @@ def __init__( if bitrate is not None and btr is not None: raise ValueError("Bitrate and btr mutually exclusive.") - if bitrate is not None: self.set_bitrate(self, bitrate) - if btr is not None: self.set_bitrate_reg(self, btr) - self.open() super().__init__( @@ -117,8 +112,7 @@ def set_bitrate(self, bitrate): self.write(self._BITRATES[bitrate]) else: raise ValueError( - "Invalid bitrate, choose one of " + - (", ".join(self._BITRATES)) + "." + "Invalid bitrate, choose one of " + (", ".join(self._BITRATES)) + "." ) self.open() @@ -136,19 +130,14 @@ def read(self, timeout): # first read what is already in receive buffer while self.serialPortOrig.in_waiting: self._buffer += self.serialPortOrig.read() - # if we still don't have a complete message, do a blocking read start = time.time() time_left = timeout - while not ( - ord(self._OK) in self._buffer or - ord(self._ERROR) in self._buffer - ): + while not (ord(self._OK) in self._buffer or ord(self._ERROR) in self._buffer): self.serialPortOrig.timeout = time_left byte = self.serialPortOrig.read() if byte: self._buffer += byte - # if timeout is None, try indefinitely if timeout is None: continue @@ -160,15 +149,12 @@ def read(self, timeout): continue else: return None - # return first message for i in range(len(self._buffer)): - if ( self._buffer[i] == ord(self._OK) or - self._buffer[i] == ord(self._ERROR) ): - string = self._buffer[:i+1].decode() - del self._buffer[:i+1] + if self._buffer[i] == ord(self._OK) or self._buffer[i] == ord(self._ERROR): + string = self._buffer[: i + 1].decode() + del self._buffer[: i + 1] break - return string def flush(self): @@ -217,7 +203,6 @@ def _recv_internal(self, timeout): dlc = int(string[9]) extended = True remote = True - if canId is not None: msg = Message( arbitration_id=canId, @@ -233,7 +218,6 @@ def _recv_internal(self, timeout): def send(self, msg, timeout=None): if timeout != self.serialPortOrig.write_timeout: self.serialPortOrig.write_timeout = timeout - if msg.is_remote_frame: if msg.is_extended_id: sendStr = "R%08X%d" % (msg.arbitration_id, msg.dlc) @@ -244,7 +228,6 @@ def send(self, msg, timeout=None): sendStr = "T%08X%d" % (msg.arbitration_id, msg.dlc) else: sendStr = "t%03X%d" % (msg.arbitration_id, msg.dlc) - sendStr += "".join(["%02X" % b for b in msg.data]) self.write(sendStr) @@ -274,7 +257,6 @@ def get_version(self, timeout): hw_version = int(string[1:3]) sw_version = int(string[3:5]) return hw_version, sw_version - # if timeout is None, try indefinitely if timeout is None: continue @@ -301,7 +283,6 @@ def get_serial_number(self, timeout): elif string[0] == cmd and len(string) == 6: serial_number = string[1:-1] return serial_number - # if timeout is None, try indefinitely if timeout is None: continue diff --git a/test/test_slcan.py b/test/test_slcan.py index 178996c93..781fa75df 100644 --- a/test/test_slcan.py +++ b/test/test_slcan.py @@ -106,7 +106,7 @@ def test_partial_recv(self): self.assertIsNotNone(msg) def test_version(self): - self.serial.write(b'V1013\r') + self.serial.write(b"V1013\r") hw_ver, sw_ver = self.bus.get_version(0) self.assertEqual(hw_ver, 10) self.assertEqual(sw_ver, 13) @@ -116,7 +116,7 @@ def test_version(self): self.assertIsNone(sw_ver) def test_serial_number(self): - self.serial.write(b'NA123\r') + self.serial.write(b"NA123\r") sn = self.bus.get_serial_number(0) self.assertEqual(sn, "A123") From bca1fa4785e96fd7a55c343244c0570570a8f014 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 3 Jun 2019 10:40:26 +0200 Subject: [PATCH 076/252] Add wrongly removed statement back to codebase (#612) * Add wrongly removed statement back to codebase This was removed in the process of making the linter happy. But it is required since it changes `num_channels`. * remove unused commented out logging --- can/interfaces/kvaser/canlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index f16004c3b..89a05af1e 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -424,7 +424,7 @@ def __init__(self, channel, can_filters=None, **kwargs): self.single_handle = single_handle num_channels = ctypes.c_int(0) - # log.debug("Res: %d", canGetNumberOfChannels(ctypes.byref(num_channels))) + canGetNumberOfChannels(ctypes.byref(num_channels)) num_channels = int(num_channels.value) log.info("Found %d available channels", num_channels) for idx in range(num_channels): From 0c7a18e1ee850d5ffafb74cd0d7fba27c8947478 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Tue, 4 Jun 2019 21:40:58 +0200 Subject: [PATCH 077/252] document public methods that take arguments --- can/interfaces/slcan.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 658a8a6fa..b581a0f13 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -107,6 +107,12 @@ def __init__( ) def set_bitrate(self, bitrate): + """ + :raise ValueError: if both *bitrate* is not among the possible values + + :param int bitrate: + Bitrate in bit/s + """ self.close() if bitrate in self._BITRATES: self.write(self._BITRATES[bitrate]) @@ -117,6 +123,10 @@ def set_bitrate(self, bitrate): self.open() def set_bitrate_reg(self, btr): + """ + :param str btr: + BTR register value to set custom can speed + """ self.close() self.write("s" + btr) self.open() From 1d7701e50619a9ec5d880ef49545db8002dc0949 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Tue, 4 Jun 2019 22:07:25 +0200 Subject: [PATCH 078/252] make read hidden method --- can/interfaces/slcan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index b581a0f13..e1e9a7498 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -135,7 +135,7 @@ def write(self, string): self.serialPortOrig.write(string.encode() + self.LINE_TERMINATOR) self.serialPortOrig.flush() - def read(self, timeout): + def _read(self, timeout): # first read what is already in receive buffer while self.serialPortOrig.in_waiting: @@ -185,7 +185,7 @@ def _recv_internal(self, timeout): extended = False frame = [] - string = self.read(timeout) + string = self._read(timeout) if not string: pass @@ -258,7 +258,7 @@ def get_version(self, timeout): start = time.time() time_left = timeout while True: - string = self.read(time_left) + string = self._read(time_left) if not string: pass @@ -286,7 +286,7 @@ def get_serial_number(self, timeout): start = time.time() time_left = timeout while True: - string = self.read(time_left) + string = self._read(time_left) if not string: pass From f7866d097ae62cfd30164d66d58ce784d47b7d6c Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Tue, 4 Jun 2019 22:12:44 +0200 Subject: [PATCH 079/252] First shot at a BitTiming class --- can/__init__.py | 1 + can/bit_timing.py | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 can/bit_timing.py diff --git a/can/__init__.py b/can/__init__.py index e23f5a9b8..f134ea8af 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -37,6 +37,7 @@ class CanError(IOError): from .interfaces import VALID_INTERFACES from . import interface from .interface import Bus, detect_available_configs +from .bit_timing import BitTiming from .broadcastmanager import ( CyclicSendTaskABC, diff --git a/can/bit_timing.py b/can/bit_timing.py new file mode 100644 index 000000000..b0477227d --- /dev/null +++ b/can/bit_timing.py @@ -0,0 +1,111 @@ + + +class BitTiming: + + # Is this always 1? + sync_seg = 1 + + def __init__( + self, + bitrate=None, + brp=None, + sjw=None, + tseg1=None, + tseg2=None, + nof_samples=1, + f_clock=None, + btr0=None, + btr1=None + ): + if brp is not None and (brp < 1 or brp > 64): + raise ValueError("brp must be 1 - 64") + if sjw is not None and (sjw < 1 or sjw > 4): + raise ValueError("sjw must be 1 - 4") + if tseg1 is not None and (tseg1 < 1 or tseg1 > 16): + raise ValueError("tseg1 must be 1 - 16") + if tseg2 is not None and (tseg2 < 1 or tseg2 > 8): + raise ValueError("tseg2 must be 1 - 8") + if nof_samples is not None and nof_samples not in (1, 3): + raise ValueError("nof_samples must be 1 or 3") + if btr0 is not None and (btr0 < 0 or btr0 > 255): + raise ValueError("btr0 must be 0 - 255") + if btr1 is not None and (btr1 < 0 or btr1 > 255): + raise ValueError("btr1 must be 0 - 255") + + self._bitrate = bitrate + self._brp = brp + self._sjw = sjw + self._tseg1 = tseg1 + self._tseg2 = tseg2 + self._nof_samples = nof_samples + self._f_clock = f_clock + self._btr0 = btr0 + self._btr1 = btr1 + + @property + def nbt(self): + return self.sync_seg + self.tseg1 + self.tseg2 + + @property + def bitrate(self): + if self._bitrate: + return self._bitrate + raise ValueError("bitrate must be specified") + + @property + def brp(self): + if self._brp: + return self._brp + return (2 * self.bitrate * self.nbt) // self.f_clock + + @property + def sjw(self): + if not self._sjw: + raise ValueError("sjw must be specified") + return self._sjw + + @property + def tseg1(self): + if not self._tseg1: + raise ValueError("tseg1 must be specified") + return self._tseg1 + + @property + def tseg2(self): + if not self._tseg2: + raise ValueError("tseg2 must be specified") + return self._tseg2 + + @property + def nof_samples(self): + if not self._nof_samples: + raise ValueError("nof_samples must be specified") + return self._nof_samples + + @property + def f_clock(self): + if not self._f_clock: + raise ValueError("f_clock must be specified") + return self._f_clock + + @property + def btr0(self): + if self._btr0 is not None: + return self._btr0 + btr0 = (self.sjw - 1) << 5 + btr0 |= self.brp - 1 + return btr0 + + @property + def btr1(self): + if self._btr1 is not None: + return self._btr1 + sam = 1 if self.nof_samples == 3 else 0 + btr1 = sam << 7 + btr1 |= (self.tseg2 - 1) << 4 + btr1 |= self.tseg1 - 1 + return btr1 + + @property + def btr(self): + return self.btr0 << 8 | self.btr1 From 86a5f343c03015b89d729963fe5047f95bc91c08 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Tue, 4 Jun 2019 22:29:43 +0200 Subject: [PATCH 080/252] document get_version and get_serial_number --- can/interfaces/slcan.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index e1e9a7498..58a064ad6 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -252,6 +252,17 @@ def fileno(self): return -1 def get_version(self, timeout): + """Get HW and SW version of the slcan interface. + + :type timeout: int or None + :param timeout: + seconds to wait for version or None to wait indefinitely + + :returns: tuple (hw_version, sw_version) + WHERE + int hw_version is the hardware version or None on timeout + int sw_version is the software version or None on timeout + """ cmd = "V" self.write(cmd) @@ -280,6 +291,16 @@ def get_version(self, timeout): return None, None def get_serial_number(self, timeout): + """Get serial number of the slcan interface. + + :type timeout: int or None + :param timeout: + seconds to wait for serial number or None to wait indefinitely + + :rtype str or None + :return: + None on timeout or a str object. + """ cmd = "N" self.write(cmd) From 47d738f7e2dca5be5f3e47c2f1690a4c8309422a Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Wed, 5 Jun 2019 09:19:51 +0200 Subject: [PATCH 081/252] Inverse BRP calculation --- can/bit_timing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/bit_timing.py b/can/bit_timing.py index b0477227d..b6313e910 100644 --- a/can/bit_timing.py +++ b/can/bit_timing.py @@ -56,7 +56,7 @@ def bitrate(self): def brp(self): if self._brp: return self._brp - return (2 * self.bitrate * self.nbt) // self.f_clock + return round(self.f_clock / (2 * self.bitrate * self.nbt)) @property def sjw(self): From bbc647b659d54d681e4251b17aebdc6c7c334110 Mon Sep 17 00:00:00 2001 From: Alberto Scotta Date: Wed, 5 Jun 2019 21:38:30 +0200 Subject: [PATCH 082/252] make write an hidden method --- can/interfaces/slcan.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 58a064ad6..be5d672d3 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -115,7 +115,7 @@ def set_bitrate(self, bitrate): """ self.close() if bitrate in self._BITRATES: - self.write(self._BITRATES[bitrate]) + self._write(self._BITRATES[bitrate]) else: raise ValueError( "Invalid bitrate, choose one of " + (", ".join(self._BITRATES)) + "." @@ -128,10 +128,10 @@ def set_bitrate_reg(self, btr): BTR register value to set custom can speed """ self.close() - self.write("s" + btr) + self._write("s" + btr) self.open() - def write(self, string): + def _write(self, string): self.serialPortOrig.write(string.encode() + self.LINE_TERMINATOR) self.serialPortOrig.flush() @@ -173,10 +173,10 @@ def flush(self): self.serialPortOrig.read() def open(self): - self.write("O") + self._write("O") def close(self): - self.write("C") + self._write("C") def _recv_internal(self, timeout): @@ -239,7 +239,7 @@ def send(self, msg, timeout=None): else: sendStr = "t%03X%d" % (msg.arbitration_id, msg.dlc) sendStr += "".join(["%02X" % b for b in msg.data]) - self.write(sendStr) + self._write(sendStr) def shutdown(self): self.close() @@ -264,7 +264,7 @@ def get_version(self, timeout): int sw_version is the software version or None on timeout """ cmd = "V" - self.write(cmd) + self._write(cmd) start = time.time() time_left = timeout @@ -302,7 +302,7 @@ def get_serial_number(self, timeout): None on timeout or a str object. """ cmd = "N" - self.write(cmd) + self._write(cmd) start = time.time() time_left = timeout From 934a753671d243729e92c9a045bb344e0dab6ee8 Mon Sep 17 00:00:00 2001 From: nick black Date: Sat, 8 Jun 2019 11:32:42 -0400 Subject: [PATCH 083/252] [canalystii] get CANalystII working through init --- can/interfaces/canalystii.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 10de8b170..a28e182d2 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -73,18 +73,18 @@ class VCI_CAN_OBJ(Structure): class CANalystIIBus(BusABC): def __init__( - self, channel, device=0, baud=None, Timing0=None, Timing1=None, can_filters=None + self, channel, device=0, bitrate=None, Timing0=None, Timing1=None, can_filters=None, **kwargs, ): """ :param channel: channel number :param device: device number - :param baud: baud rate - :param Timing0: customize the timing register if baudrate is not specified + :param bitrate: CAN network bandwidth (bytes/s) + :param Timing0: customize the timing register if bitrate is not specified :param Timing1: :param can_filters: filters for packet """ - super().__init__(channel, can_filters) + super().__init__(channel=channel, can_filters=can_filters, **kwargs) if isinstance(channel, (list, tuple)): self.channels = channel @@ -100,11 +100,11 @@ def __init__( self.device, self.channels ) - if baud is not None: + if bitrate is not None: try: - Timing0, Timing1 = TIMING_DICT[baud] + Timing0, Timing1 = TIMING_DICT[bitrate] except KeyError: - raise ValueError("Baudrate is not supported") + raise ValueError("Bitrate is not supported") if Timing0 is None or Timing1 is None: raise ValueError("Timing registers are not set") From f0017ca7b7f8cb29ece23ac2518f4ce2de9ef8c0 Mon Sep 17 00:00:00 2001 From: nick black Date: Sat, 8 Jun 2019 11:54:47 -0400 Subject: [PATCH 084/252] Add canalystii+systec to configuration doc --- doc/configuration.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/configuration.rst b/doc/configuration.rst index dda2ace2a..142e816da 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -126,3 +126,7 @@ Lookup table of interface names: +---------------------+-------------------------------------+ | ``"virtual"`` | :doc:`interfaces/virtual` | +---------------------+-------------------------------------+ +| ``"canalystii"`` | :doc:`interfaces/canalystii` | ++---------------------+-------------------------------------+ +| ``"systec"`` | :doc:`interfaces/systec` | ++---------------------+-------------------------------------+ From c3b18b19e9b07b10853ada7b76d582ae47795ec2 Mon Sep 17 00:00:00 2001 From: nick black Date: Sat, 8 Jun 2019 11:55:21 -0400 Subject: [PATCH 085/252] Add myself to CONTRIBUTORS --- CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 9a37da3b5..b7ac9fbd4 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -27,3 +27,4 @@ Alexander Mueller Jan Goeteyn "ykzheng" Lear Corporation +Nick Black From 07d8fc2c8d62803af7ea9930c795a4552edb7527 Mon Sep 17 00:00:00 2001 From: nick black Date: Sat, 8 Jun 2019 12:02:40 -0400 Subject: [PATCH 086/252] [black] normalize indentation --- can/interfaces/canalystii.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index a28e182d2..cc68ff240 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -73,7 +73,14 @@ class VCI_CAN_OBJ(Structure): class CANalystIIBus(BusABC): def __init__( - self, channel, device=0, bitrate=None, Timing0=None, Timing1=None, can_filters=None, **kwargs, + self, + channel, + device=0, + bitrate=None, + Timing0=None, + Timing1=None, + can_filters=None, + **kwargs, ): """ From 324afd0bdd61f2ab036c200f579f0f1e1a77ae25 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 9 Jun 2019 16:28:06 +1000 Subject: [PATCH 087/252] changing name to seeedstuido and changes for python3 --- can/interfaces/__init__.py | 2 +- can/interfaces/seeedstudio/__init__.py | 6 ++++++ .../{usb_can_analyzer => seeedstudio}/notes | 0 .../seeedstudio.py} | 12 ++++++------ can/interfaces/usb_can_analyzer/__init__.py | 6 ------ 5 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 can/interfaces/seeedstudio/__init__.py rename can/interfaces/{usb_can_analyzer => seeedstudio}/notes (100%) rename can/interfaces/{usb_can_analyzer/usb_can_analyzer.py => seeedstudio/seeedstudio.py} (95%) delete mode 100644 can/interfaces/usb_can_analyzer/__init__.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 671f53cc0..c17d85823 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -24,7 +24,7 @@ 'slcan': ('can.interfaces.slcan', 'slcanBus'), 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus'), 'systec': ('can.interfaces.systec', 'UcanBus'), - 'usb_can_analyzer': ('can.interfaces.usb_can_analyzer.usb_can_analyzer', 'CanAnalyzer') + 'seeedstudio': ('can.interfaces.seeedstudio', 'CanAnalyzer') } BACKENDS.update({ diff --git a/can/interfaces/seeedstudio/__init__.py b/can/interfaces/seeedstudio/__init__.py new file mode 100644 index 000000000..32306df97 --- /dev/null +++ b/can/interfaces/seeedstudio/__init__.py @@ -0,0 +1,6 @@ +# coding: utf-8 + +""" +""" + +from can.interfaces.seeedstudio.seeedstudio import CanAnalyzer diff --git a/can/interfaces/usb_can_analyzer/notes b/can/interfaces/seeedstudio/notes similarity index 100% rename from can/interfaces/usb_can_analyzer/notes rename to can/interfaces/seeedstudio/notes diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/seeedstudio/seeedstudio.py similarity index 95% rename from can/interfaces/usb_can_analyzer/usb_can_analyzer.py rename to can/interfaces/seeedstudio/seeedstudio.py index 73def806d..9bd38bf3d 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -121,7 +121,7 @@ def init_frame(self, timeout=None): byte_msg.append(0x12) # Initialization Message ID byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate - byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) + byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) byte_msg.extend(self.filter_id) @@ -135,11 +135,11 @@ def init_frame(self, timeout=None): byte_msg.append(0x00) crc = Crc8Darc.calc(byte_msg[2:]) - crc_byte = struct.pack('B', crc) +# crc_byte = struct.pack('B', crc) - byte_msg.append(crc_byte) + byte_msg.append(crc) - logger.debug("init_frm:\t" + binascii.hexlify(byte_msg)) + logger.debug("init_frm:\t" + byte_msg.hex()) self.ser.write(byte_msg) def flush_buffer(self): @@ -161,7 +161,7 @@ def status_frame(self, timeout=None): byte_msg.append(crc_byte) - logger.debug("status_frm:\t" + binascii.hexlify(byte_msg)) + logger.debug("status_frm:\t" + byte_msg.hex()) self.ser.write(byte_msg) def send(self, msg, timeout=None): @@ -198,7 +198,7 @@ def send(self, msg, timeout=None): byte_msg.extend(msg.data) byte_msg.append(0x55) - logger.debug("Sending:\t" + binascii.hexlify(byte_msg)) + logger.debug("Sending:\t" + byte_msg.hex()) self.ser.write(byte_msg) def _recv_internal(self, timeout): diff --git a/can/interfaces/usb_can_analyzer/__init__.py b/can/interfaces/usb_can_analyzer/__init__.py deleted file mode 100644 index ec07c4d68..000000000 --- a/can/interfaces/usb_can_analyzer/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# coding: utf-8 - -""" -""" - -from can.interfaces.usb_can_analyzer.usb_can_analyzer import CanAnalyzer as Bus From aad6d6635b411fc17ce924af68e52d7665d76005 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 9 Jun 2019 16:36:17 +1000 Subject: [PATCH 088/252] update option name in setup.py to seeedstudio. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 03055ab18..85947c596 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ extras_require = { 'serial': ['pyserial~=3.0'], 'neovi': ['python-ics>=2.12'], - 'usb_can_analyzer': ['crccheck>=0.6','pyserial>=3.0'] + 'seeedstudio': ['crccheck>=0.6','pyserial>=3.0'] } tests_require = [ From 19287be69448db97ed26962193070e2b617d792b Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 9 Jun 2019 17:09:49 +1000 Subject: [PATCH 089/252] tidy up merge conflicts. --- can/interfaces/__init__.py | 44 +++++++++++++++++++---------------- setup.py | 47 ++++++++++++++++---------------------- 2 files changed, 44 insertions(+), 47 deletions(-) diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index c17d85823..2f8768ed5 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -10,26 +10,30 @@ # interface_name => (module, classname) BACKENDS = { - 'kvaser': ('can.interfaces.kvaser', 'KvaserBus'), - 'socketcan': ('can.interfaces.socketcan', 'SocketcanBus'), - '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.ics_neovi', 'NeoViBus'), - 'vector': ('can.interfaces.vector', 'VectorBus'), - 'slcan': ('can.interfaces.slcan', 'slcanBus'), - 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus'), - 'systec': ('can.interfaces.systec', 'UcanBus'), - 'seeedstudio': ('can.interfaces.seeedstudio', 'CanAnalyzer') + "kvaser": ("can.interfaces.kvaser", "KvaserBus"), + "socketcan": ("can.interfaces.socketcan", "SocketcanBus"), + "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.ics_neovi", "NeoViBus"), + "vector": ("can.interfaces.vector", "VectorBus"), + "slcan": ("can.interfaces.slcan", "slcanBus"), + "canalystii": ("can.interfaces.canalystii", "CANalystIIBus"), + "systec": ("can.interfaces.systec", "UcanBus"), + "seeedstudio": ("can.interfaces.seeedstudio", "CanAnalyzer"), } -BACKENDS.update({ - interface.name: (interface.module_name, interface.attrs[0]) - for interface in iter_entry_points('can.interface') -}) +BACKENDS.update( + { + interface.name: (interface.module_name, interface.attrs[0]) + for interface in iter_entry_points("can.interface") + } +) -VALID_INTERFACES = frozenset(list(BACKENDS.keys()) + ['socketcan_native', 'socketcan_ctypes']) +VALID_INTERFACES = frozenset( + list(BACKENDS.keys()) + ["socketcan_native", "socketcan_ctypes"] +) diff --git a/setup.py b/setup.py index 85947c596..71427bbfa 100644 --- a/setup.py +++ b/setup.py @@ -15,30 +15,28 @@ logging.basicConfig(level=logging.WARNING) -with open('can/__init__.py', 'r') as fd: - version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', - fd.read(), re.MULTILINE).group(1) +with open("can/__init__.py", "r") as fd: + version = re.search( + r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE + ).group(1) -with open('README.rst', 'r') as f: +with open("README.rst", "r") as f: long_description = f.read() # Dependencies -extras_require = { - 'serial': ['pyserial~=3.0'], - 'neovi': ['python-ics>=2.12'], - 'seeedstudio': ['crccheck>=0.6','pyserial>=3.0'] -} +extras_require = {"serial": ["pyserial~=3.0"], + "neovi": ["python-ics>=2.12"], + "seeedstudio": ["crccheck>=0.6","pyserial>=3.0"]} tests_require = [ - 'pytest~=4.3', - 'pytest-timeout~=1.3', - 'pytest-cov~=2.6', - 'codecov~=2.0', - 'six', - 'hypothesis' -] + extras_require['serial'] + "pytest~=4.3", + "pytest-timeout~=1.3", + "pytest-cov~=2.6", + "codecov~=2.0", + "hypothesis", +] + extras_require["serial"] -extras_require['test'] = tests_require +extras_require["test"] = tests_require setup( @@ -70,37 +68,32 @@ "Topic :: System :: Monitoring", "Topic :: System :: Networking", "Topic :: System :: Hardware :: Hardware Drivers", - "Topic :: Utilities" + "Topic :: Utilities", ], - # Code version=version, packages=find_packages(exclude=["test", "doc", "scripts", "examples"]), scripts=list(filter(isfile, (join("scripts/", f) for f in listdir("scripts/")))), - # Author author="Brian Thorne", author_email="brian@thorne.link", - # License license="LGPL v3", - # Package data package_data={ "": ["README.rst", "CONTRIBUTORS.txt", "LICENSE.txt", "CHANGELOG.txt"], "doc": ["*.*"], - "examples": ["*.py"] + "examples": ["*.py"], }, - # Installation # see https://www.python.org/dev/peps/pep-0345/#version-specifiers python_requires=">=3.6", install_requires=[ - 'wrapt~=1.10', - 'aenum', + "wrapt~=1.10", + "aenum", 'windows-curses;platform_system=="Windows"', ], setup_requires=["pytest-runner"], extras_require=extras_require, - tests_require=tests_require + tests_require=tests_require, ) From 16deb5c43b1aa272f9f8d9e60235facfc4dd8d88 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Sun, 9 Jun 2019 20:27:43 +0200 Subject: [PATCH 090/252] Fixes, tweaks, and clarifications. --- can/bit_timing.py | 140 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 37 deletions(-) diff --git a/can/bit_timing.py b/can/bit_timing.py index b6313e910..10348f889 100644 --- a/can/bit_timing.py +++ b/can/bit_timing.py @@ -1,37 +1,66 @@ +class BitTiming: + """Representation of a bit timing configuration. + + The class can be constructed in various ways, depending on the information + available or the capabilities of the interfaces that need to be supported. + The preferred way is using bitrate, CAN clock frequency, TSEG1, TSEG2, SJW:: -class BitTiming: + can.BitTiming(bitrate=1000000, f_clock=8000000, tseg1=5, tseg2=1, sjw=1) + + If the clock frequency is unknown it may be omitted but some interfaces may + require it. + + Alternatively the BRP can be given instead of bitrate and clock frequency but this + will limit the number of supported interfaces. + + It is also possible specify BTR registers directly, + but will not work for all interfaces:: + + can.BitTiming(btr0=0x00, btr1=0x14) + """ - # Is this always 1? sync_seg = 1 def __init__( self, bitrate=None, + f_clock=None, brp=None, - sjw=None, tseg1=None, tseg2=None, + sjw=None, nof_samples=1, - f_clock=None, btr0=None, - btr1=None + btr1=None, ): - if brp is not None and (brp < 1 or brp > 64): - raise ValueError("brp must be 1 - 64") - if sjw is not None and (sjw < 1 or sjw > 4): - raise ValueError("sjw must be 1 - 4") - if tseg1 is not None and (tseg1 < 1 or tseg1 > 16): - raise ValueError("tseg1 must be 1 - 16") - if tseg2 is not None and (tseg2 < 1 or tseg2 > 8): - raise ValueError("tseg2 must be 1 - 8") - if nof_samples is not None and nof_samples not in (1, 3): - raise ValueError("nof_samples must be 1 or 3") - if btr0 is not None and (btr0 < 0 or btr0 > 255): - raise ValueError("btr0 must be 0 - 255") - if btr1 is not None and (btr1 < 0 or btr1 > 255): - raise ValueError("btr1 must be 0 - 255") - + """ + :param int bitrate: + Bitrate in bits/s. + :param int f_clock: + The CAN system clock frequency in Hz. + Usually the oscillator frequency divided by 2. + :param int brp: + Bit Rate Prescaler. Prefer to use bitrate and f_clock instead. + :param int tseg1: + Time segment 1, that is, the number of quanta from (but not including) + the Sync Segment to the sampling point. + :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 Synchronization Jump Width. Decides the maximum number of time quanta + that the controller can resynchronize every bit. + :param int nof_samples: + 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 int btr0: + The BTR0 register value used by many CAN controllers. + :param int btr1: + The BTR1 register value used by many CAN controllers. + """ self._bitrate = bitrate self._brp = brp self._sjw = sjw @@ -39,73 +68,110 @@ def __init__( self._tseg2 = tseg2 self._nof_samples = nof_samples self._f_clock = f_clock - self._btr0 = btr0 - self._btr1 = btr1 + + if btr0 is not None: + self._brp = (btr0 & 0x3F) + 1 + self._sjw = (btr0 >> 6) + 1 + if btr1 is not None: + self._tseg1 = (btr1 & 0xF) + 1 + self._tseg2 = ((btr1 >> 4) & 0x7) + 1 + self._nof_samples = 3 if btr1 & 0x80 else 1 @property def nbt(self): + """Nominal Bit Time.""" return self.sync_seg + self.tseg1 + self.tseg2 @property def bitrate(self): + """Bitrate in bits/s.""" if self._bitrate: return self._bitrate + if self._f_clock and self._brp: + return self._f_clock / (self._brp * self.nbt) raise ValueError("bitrate must be specified") @property def brp(self): + """Bit Rate Prescaler.""" if self._brp: return self._brp - return round(self.f_clock / (2 * self.bitrate * self.nbt)) + if self._f_clock and self._bitrate: + return round(self._f_clock / (self._bitrate * self.nbt)) + raise ValueError("Either bitrate and f_clock or brp must be specified") @property def sjw(self): + """Synchronization Jump Width.""" if not self._sjw: raise ValueError("sjw must be specified") return self._sjw @property def tseg1(self): + """Time segment 1. + + The number of quanta from (but not including) the Sync Segment to the sampling point. + """ if not self._tseg1: raise ValueError("tseg1 must be specified") return self._tseg1 @property def tseg2(self): + """Time segment 2. + + The number of quanta from the sampling point to the end of the bit. + """ if not self._tseg2: raise ValueError("tseg2 must be specified") return self._tseg2 @property def nof_samples(self): + """Number of samples (1 or 3).""" if not self._nof_samples: raise ValueError("nof_samples must be specified") return self._nof_samples @property def f_clock(self): + """The CAN system clock frequency in Hz. + + Usually the oscillator frequency divided by 2. + """ if not self._f_clock: raise ValueError("f_clock must be specified") return self._f_clock + @property + def sample_point(self): + """Sample point in percent.""" + return 100.0 * (self.nbt - self.tseg2) / self.nbt + @property def btr0(self): - if self._btr0 is not None: - return self._btr0 - btr0 = (self.sjw - 1) << 5 - btr0 |= self.brp - 1 - return btr0 + sjw = self.sjw + brp = self.brp + + if brp < 1 or brp > 64: + raise ValueError("brp must be 1 - 64") + if sjw < 1 or sjw > 4: + raise ValueError("sjw must be 1 - 4") + + return (sjw - 1) << 6 | brp - 1 @property def btr1(self): - if self._btr1 is not None: - return self._btr1 sam = 1 if self.nof_samples == 3 else 0 - btr1 = sam << 7 - btr1 |= (self.tseg2 - 1) << 4 - btr1 |= self.tseg1 - 1 - return btr1 + tseg1 = self.tseg1 + tseg2 = self.tseg2 - @property - def btr(self): - return self.btr0 << 8 | self.btr1 + if tseg1 < 1 or tseg1 > 16: + raise ValueError("tseg1 must be 1 - 16") + if tseg2 < 1 or tseg2 > 8: + raise ValueError("tseg2 must be 1 - 8") + if nof_samples not in (1, 3): + raise ValueError("nof_samples must be 1 or 3") + + return sam << 7 | (tseg2 - 1) << 4 | tseg1 - 1 From 80a7945bd07e7bc518fdc3a6133fb6f617f21c38 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Mon, 10 Jun 2019 21:51:12 +0200 Subject: [PATCH 091/252] Add __str__ method --- can/bit_timing.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/can/bit_timing.py b/can/bit_timing.py index 10348f889..58efa2265 100644 --- a/can/bit_timing.py +++ b/can/bit_timing.py @@ -77,6 +77,9 @@ def __init__( self._tseg2 = ((btr1 >> 4) & 0x7) + 1 self._nof_samples = 3 if btr1 & 0x80 else 1 + if nof_samples not in (1, 3): + raise ValueError("nof_samples must be 1 or 3") + @property def nbt(self): """Nominal Bit Time.""" @@ -171,7 +174,37 @@ def btr1(self): raise ValueError("tseg1 must be 1 - 16") if tseg2 < 1 or tseg2 > 8: raise ValueError("tseg2 must be 1 - 8") - if nof_samples not in (1, 3): - raise ValueError("nof_samples must be 1 or 3") return sam << 7 | (tseg2 - 1) << 4 | tseg1 - 1 + + def __str__(self): + segments = [] + try: + segments.append(f"{self.bitrate} bits/s") + except ValueError: + pass + try: + segments.append(f"sample point: {self.sample_point:.2f}%") + except ValueError: + pass + try: + segments.append(f"BRP: {self.brp}") + except ValueError: + pass + try: + segments.append(f"TSEG1: {self.tseg1}") + except ValueError: + pass + try: + segments.append(f"TSEG2: {self.tseg2}") + except ValueError: + pass + try: + segments.append(f"SJW: {self.sjw}") + except ValueError: + pass + try: + segments.append(f"BTR: {self.btr0:02X}{self.btr1:02X}h") + except ValueError: + pass + return ", ".join(segments) From 3e33accd9423745e7f38cc9ba0c748f5d3a4f521 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Mon, 10 Jun 2019 21:51:23 +0200 Subject: [PATCH 092/252] Add unit tests --- test/bit_timing_test.py | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 test/bit_timing_test.py diff --git a/test/bit_timing_test.py b/test/bit_timing_test.py new file mode 100644 index 000000000..ceb0023cc --- /dev/null +++ b/test/bit_timing_test.py @@ -0,0 +1,87 @@ +import can + + +def test_sja1000(): + """Test some values obtained using other bit timing calculators.""" + timing = can.BitTiming(f_clock=8000000, bitrate=125000, tseg1=11, tseg2=4, sjw=2) + assert timing.f_clock == 8000000 + assert timing.bitrate == 125000 + assert timing.brp == 4 + assert timing.nbt == 16 + assert timing.tseg1 == 11 + assert timing.tseg2 == 4 + assert timing.sjw == 2 + assert timing.sample_point == 75 + assert timing.btr0 == 0x43 + assert timing.btr1 == 0x3A + + timing = can.BitTiming(f_clock=8000000, bitrate=500000, tseg1=13, tseg2=2, sjw=1) + assert timing.f_clock == 8000000 + assert timing.bitrate == 500000 + assert timing.brp == 1 + assert timing.nbt == 16 + assert timing.tseg1 == 13 + assert timing.tseg2 == 2 + assert timing.sjw == 1 + assert timing.sample_point == 87.5 + assert timing.btr0 == 0x00 + assert timing.btr1 == 0x1C + + timing = can.BitTiming(f_clock=8000000, bitrate=1000000, tseg1=5, tseg2=2, sjw=1) + assert timing.f_clock == 8000000 + assert timing.bitrate == 1000000 + assert timing.brp == 1 + assert timing.nbt == 8 + assert timing.tseg1 == 5 + assert timing.tseg2 == 2 + assert timing.sjw == 1 + assert timing.sample_point == 75 + assert timing.btr0 == 0x00 + assert timing.btr1 == 0x14 + + +def test_can_fd(): + timing = can.BitTiming( + f_clock=80000000, bitrate=500000, tseg1=119, tseg2=40, sjw=40 + ) + assert timing.f_clock == 80000000 + assert timing.bitrate == 500000 + assert timing.brp == 1 + assert timing.nbt == 160 + assert timing.tseg1 == 119 + assert timing.tseg2 == 40 + assert timing.sjw == 40 + assert timing.sample_point == 75 + + timing = can.BitTiming( + f_clock=80000000, bitrate=2000000, tseg1=29, tseg2=10, sjw=10 + ) + assert timing.f_clock == 80000000 + assert timing.bitrate == 2000000 + assert timing.brp == 1 + assert timing.nbt == 40 + assert timing.tseg1 == 29 + assert timing.tseg2 == 10 + assert timing.sjw == 10 + assert timing.sample_point == 75 + + +def test_from_btr(): + timing = can.BitTiming(f_clock=8000000, btr0=0x00, btr1=0x14) + assert timing.bitrate == 1000000 + assert timing.brp == 1 + assert timing.nbt == 8 + assert timing.tseg1 == 5 + assert timing.tseg2 == 2 + assert timing.sjw == 1 + assert timing.sample_point == 75 + assert timing.btr0 == 0x00 + assert timing.btr1 == 0x14 + + +def test_string_representation(): + timing = can.BitTiming(f_clock=8000000, bitrate=1000000, tseg1=5, tseg2=2, sjw=1) + assert ( + str(timing) + == "1000000 bits/s, sample point: 75.00%, BRP: 1, TSEG1: 5, TSEG2: 2, SJW: 1, BTR: 0014h" + ) From 950a9c6924e682b076d21b95f6e631111452ff67 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Tue, 11 Jun 2019 13:10:24 +0200 Subject: [PATCH 093/252] Rename and add some test --- test/{bit_timing_test.py => test_bit_timing.py} | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) rename test/{bit_timing_test.py => test_bit_timing.py} (91%) diff --git a/test/bit_timing_test.py b/test/test_bit_timing.py similarity index 91% rename from test/bit_timing_test.py rename to test/test_bit_timing.py index ceb0023cc..0b22e308f 100644 --- a/test/bit_timing_test.py +++ b/test/test_bit_timing.py @@ -3,7 +3,9 @@ def test_sja1000(): """Test some values obtained using other bit timing calculators.""" - timing = can.BitTiming(f_clock=8000000, bitrate=125000, tseg1=11, tseg2=4, sjw=2) + timing = can.BitTiming( + f_clock=8000000, bitrate=125000, tseg1=11, tseg2=4, sjw=2, nof_samples=3 + ) assert timing.f_clock == 8000000 assert timing.bitrate == 125000 assert timing.brp == 4 @@ -11,9 +13,10 @@ def test_sja1000(): assert timing.tseg1 == 11 assert timing.tseg2 == 4 assert timing.sjw == 2 + assert timing.nof_samples == 3 assert timing.sample_point == 75 assert timing.btr0 == 0x43 - assert timing.btr1 == 0x3A + assert timing.btr1 == 0xBA timing = can.BitTiming(f_clock=8000000, bitrate=500000, tseg1=13, tseg2=2, sjw=1) assert timing.f_clock == 8000000 @@ -23,6 +26,7 @@ def test_sja1000(): assert timing.tseg1 == 13 assert timing.tseg2 == 2 assert timing.sjw == 1 + assert timing.nof_samples == 1 assert timing.sample_point == 87.5 assert timing.btr0 == 0x00 assert timing.btr1 == 0x1C @@ -35,6 +39,7 @@ def test_sja1000(): assert timing.tseg1 == 5 assert timing.tseg2 == 2 assert timing.sjw == 1 + assert timing.nof_samples == 1 assert timing.sample_point == 75 assert timing.btr0 == 0x00 assert timing.btr1 == 0x14 From efc1cf888d20bc673bf829d0e04b319fb8e4a90e Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Tue, 11 Jun 2019 13:22:17 +0200 Subject: [PATCH 094/252] Add docs --- doc/api.rst | 1 + doc/bit_timing.rst | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 doc/bit_timing.rst diff --git a/doc/api.rst b/doc/api.rst index 640f61e2d..1aef90e9a 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -17,6 +17,7 @@ A form of CAN interface is also required. listeners asyncio bcm + bit_timing internal-api diff --git a/doc/bit_timing.rst b/doc/bit_timing.rst new file mode 100644 index 000000000..1b25adf06 --- /dev/null +++ b/doc/bit_timing.rst @@ -0,0 +1,4 @@ +Bit Timing Configuration +======================== + +.. autoclass:: can.BitTiming From 7b6c4221d4456097828ff49b4b03cf2fb94cfa93 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Tue, 11 Jun 2019 13:38:39 +0200 Subject: [PATCH 095/252] Fix Lint --- can/bit_timing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/can/bit_timing.py b/can/bit_timing.py index 58efa2265..a513b477e 100644 --- a/can/bit_timing.py +++ b/can/bit_timing.py @@ -1,13 +1,13 @@ class BitTiming: """Representation of a bit timing configuration. - + The class can be constructed in various ways, depending on the information available or the capabilities of the interfaces that need to be supported. The preferred way is using bitrate, CAN clock frequency, TSEG1, TSEG2, SJW:: can.BitTiming(bitrate=1000000, f_clock=8000000, tseg1=5, tseg2=1, sjw=1) - + If the clock frequency is unknown it may be omitted but some interfaces may require it. @@ -123,7 +123,7 @@ def tseg1(self): @property def tseg2(self): """Time segment 2. - + The number of quanta from the sampling point to the end of the bit. """ if not self._tseg2: From 6beeecbd0e5fd015ddd6b8f28cfbe2041b880e44 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 11 Jun 2019 14:43:33 +0200 Subject: [PATCH 096/252] Remove OSX + Python 3.7 & nightly It is unsupported and causes problems. See [here](https://github.com/hardbyte/python-can/pull/554#issuecomment-500453610). --- .travis.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 50909340f..2eca95d6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,14 +63,6 @@ jobs: os: osx osx_image: xcode8.3 python: 3.6-dev - - stage: test - os: osx - osx_image: xcode8.3 - python: 3.7-dev - - stage: test - os: osx - osx_image: xcode8.3 - python: nightly - stage: documentation name: "Sphinx Build" From afc6eaf5dd75c97bbb81d20c8902a21edfe264bd Mon Sep 17 00:00:00 2001 From: nick black Date: Tue, 11 Jun 2019 11:11:46 -0400 Subject: [PATCH 097/252] [canalystii] fix param description bytes->bits --- can/interfaces/canalystii.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index cc68ff240..1134ffe51 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -86,7 +86,7 @@ def __init__( :param channel: channel number :param device: device number - :param bitrate: CAN network bandwidth (bytes/s) + :param bitrate: CAN network bandwidth (bits/s) :param Timing0: customize the timing register if bitrate is not specified :param Timing1: :param can_filters: filters for packet From 2a61ed914be543dd0db59e7c7c1c23e0ba0e7a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Tue, 11 Jun 2019 08:14:07 -0400 Subject: [PATCH 098/252] Use inter-process mutex to prevent concurrent neoVI device open When neoVI server is enabled, there is an issue with concurrent device open. --- can/interfaces/ics_neovi/neovi_bus.py | 12 +++++++++++- setup.py | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 3df0ccc39..9f341963e 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -11,9 +11,12 @@ """ import logging +import os +import tempfile from collections import deque from can import Message, CanError, BusABC +from filelock import FileLock logger = logging.getLogger(__name__) @@ -122,7 +125,14 @@ def __init__(self, channel, can_filters=None, **kwargs): type_filter = kwargs.get("type_filter") serial = kwargs.get("serial") self.dev = self._find_device(type_filter, serial) - ics.open_device(self.dev) + + # Use inter-process mutex to prevent concurrent device open. + # When neoVI server is enabled, there is an issue with concurrent + # device open. + open_lock = FileLock( + os.path.join(tempfile.gettempdir(), 'neovi.lock')) + with open_lock: + ics.open_device(self.dev) if "bitrate" in kwargs: for channel in self.channels: diff --git a/setup.py b/setup.py index 1918b644b..576bf1e28 100644 --- a/setup.py +++ b/setup.py @@ -90,6 +90,7 @@ "wrapt~=1.10", "aenum", 'windows-curses;platform_system=="Windows"', + 'filelock' ], setup_requires=["pytest-runner"], extras_require=extras_require, From d1aa80c0040af89f19c7c1e848f937d469b3a3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Tue, 11 Jun 2019 08:51:25 -0400 Subject: [PATCH 099/252] Move neoVI open lock to module level --- can/interfaces/ics_neovi/neovi_bus.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 9f341963e..ad53850b3 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -15,9 +15,10 @@ import tempfile from collections import deque -from can import Message, CanError, BusABC from filelock import FileLock +from can import Message, CanError, BusABC + logger = logging.getLogger(__name__) try: @@ -31,6 +32,11 @@ ics = None +# Use inter-process mutex to prevent concurrent device open. +# When neoVI server is enabled, there is an issue with concurrent device open. +open_lock = FileLock(os.path.join(tempfile.gettempdir(), "neovi.lock")) + + class ICSApiError(CanError): """ Indicates an error with the ICS API. @@ -126,11 +132,6 @@ def __init__(self, channel, can_filters=None, **kwargs): serial = kwargs.get("serial") self.dev = self._find_device(type_filter, serial) - # Use inter-process mutex to prevent concurrent device open. - # When neoVI server is enabled, there is an issue with concurrent - # device open. - open_lock = FileLock( - os.path.join(tempfile.gettempdir(), 'neovi.lock')) with open_lock: ics.open_device(self.dev) From c2e767b35887b5cf4b3994a32fa2c91939c660ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Tue, 11 Jun 2019 10:55:59 -0400 Subject: [PATCH 100/252] Adding filelock to neovi extra requirements --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 576bf1e28..9ea7b78fb 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,10 @@ long_description = f.read() # Dependencies -extras_require = {"serial": ["pyserial~=3.0"], "neovi": ["python-ics>=2.12"]} +extras_require = { + "serial": ["pyserial~=3.0"], + "neovi": ["python-ics>=2.12", "filelock"], +} tests_require = [ "pytest~=4.3", From 3dc6dfc60eb73231af00da596d845cd5bc009fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Tue, 11 Jun 2019 13:37:41 -0400 Subject: [PATCH 101/252] Adding dummy file lock when importing FileLock fails --- can/interfaces/ics_neovi/neovi_bus.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index ad53850b3..55a510283 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -15,8 +15,6 @@ import tempfile from collections import deque -from filelock import FileLock - from can import Message, CanError, BusABC logger = logging.getLogger(__name__) @@ -32,6 +30,30 @@ ics = None +try: + from filelock import FileLock +except ImportError as ie: + + logger.warning( + "Using ICS NeoVi can backend without the " + "filelock module installed may cause some issues!: %s", + ie, + ) + + class FileLock: + """Dummy file lock that do not actually do anything""" + + def __init__(self, lock_file, timeout=-1): + self._lock_file = lock_file + self.timeout = timeout + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return None + + # Use inter-process mutex to prevent concurrent device open. # When neoVI server is enabled, there is an issue with concurrent device open. open_lock = FileLock(os.path.join(tempfile.gettempdir(), "neovi.lock")) From e18c2d51db76bb6cd9f8f4d285a7bba44205f53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Tue, 11 Jun 2019 14:08:59 -0400 Subject: [PATCH 102/252] Using doubles quote since Black refers double quotes --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9ea7b78fb..fb1a4de54 100644 --- a/setup.py +++ b/setup.py @@ -93,7 +93,7 @@ "wrapt~=1.10", "aenum", 'windows-curses;platform_system=="Windows"', - 'filelock' + "filelock", ], setup_requires=["pytest-runner"], extras_require=extras_require, From 33ee636de54039481769ff3603f4ab23ba6822ec Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Tue, 11 Jun 2019 21:17:09 +0200 Subject: [PATCH 103/252] Add typings --- can/bit_timing.py | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/can/bit_timing.py b/can/bit_timing.py index a513b477e..0b892640d 100644 --- a/can/bit_timing.py +++ b/can/bit_timing.py @@ -1,3 +1,6 @@ +from typing import Union + + class BitTiming: """Representation of a bit timing configuration. @@ -24,15 +27,15 @@ class BitTiming: def __init__( self, - bitrate=None, - f_clock=None, - brp=None, - tseg1=None, - tseg2=None, - sjw=None, - nof_samples=1, - btr0=None, - btr1=None, + bitrate: int = None, + f_clock: int = None, + brp: int = None, + tseg1: int = None, + tseg2: int = None, + sjw: int = None, + nof_samples: int = 1, + btr0: int = None, + btr1: int = None, ): """ :param int bitrate: @@ -81,12 +84,12 @@ def __init__( raise ValueError("nof_samples must be 1 or 3") @property - def nbt(self): + def nbt(self) -> int: """Nominal Bit Time.""" return self.sync_seg + self.tseg1 + self.tseg2 @property - def bitrate(self): + def bitrate(self) -> Union[int, float]: """Bitrate in bits/s.""" if self._bitrate: return self._bitrate @@ -95,7 +98,7 @@ def bitrate(self): raise ValueError("bitrate must be specified") @property - def brp(self): + def brp(self) -> int: """Bit Rate Prescaler.""" if self._brp: return self._brp @@ -104,14 +107,14 @@ def brp(self): raise ValueError("Either bitrate and f_clock or brp must be specified") @property - def sjw(self): + def sjw(self) -> int: """Synchronization Jump Width.""" if not self._sjw: raise ValueError("sjw must be specified") return self._sjw @property - def tseg1(self): + def tseg1(self) -> int: """Time segment 1. The number of quanta from (but not including) the Sync Segment to the sampling point. @@ -121,7 +124,7 @@ def tseg1(self): return self._tseg1 @property - def tseg2(self): + def tseg2(self) -> int: """Time segment 2. The number of quanta from the sampling point to the end of the bit. @@ -131,14 +134,14 @@ def tseg2(self): return self._tseg2 @property - def nof_samples(self): + def nof_samples(self) -> int: """Number of samples (1 or 3).""" if not self._nof_samples: raise ValueError("nof_samples must be specified") return self._nof_samples @property - def f_clock(self): + def f_clock(self) -> int: """The CAN system clock frequency in Hz. Usually the oscillator frequency divided by 2. @@ -148,12 +151,12 @@ def f_clock(self): return self._f_clock @property - def sample_point(self): + def sample_point(self) -> float: """Sample point in percent.""" return 100.0 * (self.nbt - self.tseg2) / self.nbt @property - def btr0(self): + def btr0(self) -> int: sjw = self.sjw brp = self.brp @@ -165,7 +168,7 @@ def btr0(self): return (sjw - 1) << 6 | brp - 1 @property - def btr1(self): + def btr1(self) -> int: sam = 1 if self.nof_samples == 3 else 0 tseg1 = self.tseg1 tseg2 = self.tseg2 @@ -177,7 +180,7 @@ def btr1(self): return sam << 7 | (tseg2 - 1) << 4 | tseg1 - 1 - def __str__(self): + def __str__(self) -> str: segments = [] try: segments.append(f"{self.bitrate} bits/s") From 200707f566fce8078510c09473644bc54bb95b20 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 9 Jun 2019 12:29:15 +0200 Subject: [PATCH 104/252] Add badge for black Adds this badge: https://camo.githubusercontent.com/28a51fe3a2c05048d8ca8ecd039d6b1619037326/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64652532307374796c652d626c61636b2d3030303030302e737667 --- README.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5b29f46ad..ee489d06f 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ python-can ========== -|release| |docs| |build_travis| |build_appveyor| |coverage| |downloads| +|release| |docs| |build_travis| |build_appveyor| |coverage| |downloads| |formatter| .. |release| image:: https://img.shields.io/pypi/v/python-can.svg :target: https://pypi.python.org/pypi/python-can/ @@ -27,6 +27,10 @@ python-can :target: https://pepy.tech/project/python-can :alt: Downloads on PePy +.. |formatter| image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/python/black + :alt: This project uses the black formatter. + 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 and reliable deterministic From fa3ab30273574f396104e0b99fed27530b5906d8 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Wed, 12 Jun 2019 12:56:19 +0200 Subject: [PATCH 105/252] Allow bit timing to be set from config file CAN-FD not supported. --- can/bit_timing.py | 19 +++++++++++++++++++ can/util.py | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/can/bit_timing.py b/can/bit_timing.py index 0b892640d..8d9cf5727 100644 --- a/can/bit_timing.py +++ b/can/bit_timing.py @@ -211,3 +211,22 @@ def __str__(self) -> str: except ValueError: pass return ", ".join(segments) + + def __repr__(self) -> str: + kwargs = {} + if self._f_clock: + kwargs["f_clock"] = self._f_clock + if self._bitrate: + kwargs["bitrate"] = self._bitrate + if self._brp: + kwargs["brp"] = self._brp + if self._tseg1: + kwargs["tseg1"] = self._tseg1 + if self._tseg2: + kwargs["tseg2"] = self._tseg2 + if self._sjw: + kwargs["sjw"] = self._sjw + if self._nof_samples != 1: + kwargs["nof_samples"] = self._nof_samples + args = ", ".join(f"{key}={value}" for key, value in kwargs.items()) + return f"can.BitTiming({args})" diff --git a/can/util.py b/can/util.py index c04bb70c9..803fa2388 100644 --- a/can/util.py +++ b/can/util.py @@ -166,6 +166,25 @@ def load_config(path=None, config=None, context=None): if "bitrate" in config: config["bitrate"] = int(config["bitrate"]) + # Create bit timing configuration if given + timing_conf = {} + for key in ( + "f_clock", + "brp", + "tseg1", + "tseg2", + "sjw", + "nof_samples", + "btr0", + "btr1", + ): + if key in config: + timing_conf[key] = int(config[key], base=0) + del config[key] + if timing_conf: + timing_conf["bitrate"] = config.get("bitrate") + config["timing"] = can.BitTiming(**timing_conf) + can.log.debug("can config: {}".format(config)) return config From 587de9b663dee7fded611314aafcfa7779704b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Wed, 12 Jun 2019 07:44:36 -0400 Subject: [PATCH 106/252] Grammar fix --- can/interfaces/ics_neovi/neovi_bus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 55a510283..176747ad2 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -41,7 +41,7 @@ ) class FileLock: - """Dummy file lock that do not actually do anything""" + """Dummy file lock that does not actually do anything""" def __init__(self, lock_file, timeout=-1): self._lock_file = lock_file From 3ded7834c24e9183c87e1757539ab6690535be7d Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:00:10 +1000 Subject: [PATCH 107/252] adding doc and cleaning up comments. Changing to Bus class name SeeedBus. --- can/interfaces/__init__.py | 2 +- can/interfaces/seeedstudio/__init__.py | 2 +- can/interfaces/seeedstudio/notes | 167 ---------------------- can/interfaces/seeedstudio/seeedstudio.py | 81 +++++------ doc/interfaces/seeedstudio.rst | 85 +++++++++++ 5 files changed, 124 insertions(+), 213 deletions(-) delete mode 100644 can/interfaces/seeedstudio/notes create mode 100644 doc/interfaces/seeedstudio.rst diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 2f8768ed5..66e55153d 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -24,7 +24,7 @@ "slcan": ("can.interfaces.slcan", "slcanBus"), "canalystii": ("can.interfaces.canalystii", "CANalystIIBus"), "systec": ("can.interfaces.systec", "UcanBus"), - "seeedstudio": ("can.interfaces.seeedstudio", "CanAnalyzer"), + "seeedstudio": ("can.interfaces.seeedstudio", "SeeedBus"), } BACKENDS.update( diff --git a/can/interfaces/seeedstudio/__init__.py b/can/interfaces/seeedstudio/__init__.py index 32306df97..507ac873e 100644 --- a/can/interfaces/seeedstudio/__init__.py +++ b/can/interfaces/seeedstudio/__init__.py @@ -3,4 +3,4 @@ """ """ -from can.interfaces.seeedstudio.seeedstudio import CanAnalyzer +from can.interfaces.seeedstudio.seeedstudio import SeeedBus diff --git a/can/interfaces/seeedstudio/notes b/can/interfaces/seeedstudio/notes deleted file mode 100644 index 829c85173..000000000 --- a/can/interfaces/seeedstudio/notes +++ /dev/null @@ -1,167 +0,0 @@ -USB-CAN Analyzer - USB to CAN Bus Serial Protocol Definition - -Posted by Wilfried Voss on September 19, 2018 - copperhilltech.com - -CAN Bus To USB Mini Converter - -The USB-CAN Analyzer, in combination with the corresponding Windows software, represents a very economical solution to run an effective CAN Bus Analyzer. It allows you to develop, test, manage, and maintain your own CAN Bus network, as well as receiving, sending, logging, and analyzing CAN Bus data. - -For the following, we need to point out that the device is manufactured in China, and documentation is sparse. And while this is a very affordable solution, there is naturally room for some improvement. In general, there is no application interface, or, to be more precise, the communication between the USB-CAN device and the host system, in this case a PC running Windows, is not documented. Consequently, there is currently no software support for Linux (e.g. SocketCAN), and it would be useful if users could write their own applications, regardless of the operating system they use. - -As a first step in this direction, we invested some efforts to analyze and document the USB communication protocol, which makes it easy developing an application interface. - -There has been a document, USB (Serial port) to CAN protocol defines, that was distributed online, claiming a documentation of the "Send and receive data packet format." However, after analyzing the protocol, we believe that the data frame architecture described therein represents the original intention. - -We believe that the architecture as described caused problems when it came to higher baud rates, specifically at 1 Mbps in combination with high busloads. Consequently, the firmware developers decided to optimize the data frame size. As a result, they cut the frame size from a static 20 bytes to a dynamic 6 to 16 bytes, depending on message ID length (11 or 29 bits) and number of data bytes. - -The following describes the serial protocol as we analyzed it, with a few unknowns remaining. However, the documentation includes all vital information for creating your own CAN Bus application. - -CAN Bus Data Frame - -The CAN Bus data frame format applies to both, send and receive. - -Both message ID lengths, 11-bit standard and 29-bit extended, use the first two bytes in the serial data frame. Depending on the message ID length, the remaining bytes will be filled differently. - -The data frame does not utilize a checksum check, most probably due to keeping the number of transmitted bytes to a minimum and cutting down on processing time. - -Byte Description - -0 0xAA = Packet Start - -1 CAN Bus Data Frame Information - - Bit 7 Always 1 - - Bit 6 Always 1 - - Bit 5 0=STD; 1=EXT - - Bit 4 0=Data; 1=Remote - - Bit 3…0 Data Length Code (DLC); 0…8 - -Standard CAN data frame continues: - -Byte Description - -2 Message ID LSB - -3 Message ID MSB - -x Data, length depending on DLC - -y 0x55 = Packet End - -Extended CAN data frame continues: - -Byte Description - -2 Message ID LSB - -3 Message ID 2ND - -4 Message ID 3RD - -5 Message ID MSB - -x Data, length depending on DLC - -y 0x55 = Packet End - -CAN Bus Data Frame Size - -Standard 11-bit 6 bytes with zero data bytes - - 14 bytes with eight data bytes - -Extended 29-bit 8 bytes with zero data bytes - - 16 bytes with eight data bytes - -CAN Bus Initialization Frame - -The CAN Bus initialization frame has a constant length of 20 bytes. - -Byte(s) Description - -0 0xAA = Frame Start Byte 1 - -1 0x55 = Frame Start Byte 2 - -2 0x12 = Initialization Message ID - -3 CAN Baud Rate (See table below) - -4 0x01=STD; 0x02=EXT; Applies only to transmitting - -5 – 8 Filter ID; LSB first, MSB last - -9 – 12 Mask ID; LSB first, MSB last - -13 Operation Mode (See table below) - -14 0x01; Purpose Unknown - -15 – 18 All 0x00 - -19 Checksum - -Note: The checksum is processed between bytes 2 and 18. - -CAN Bus baud rate - -0x01 1000k - -0x02 800k - -0x03 500k - -0x04 400k - -0x05 250k - -0x06 200k - -0x07 125k - -0x08 100k - -0x09 50k - -0x0A 20k - -0x0B 10k - -0x0C 5k - -Operation Mode - -0x00 Normal - -0x01 Loopback - -0x02 Silent (Listen-Only) - -0x03 Loopback + Silent - -CAN Controller Status Frame - -The format of the status request and status report frames are identical, where the request frame sends all zeroes between the frame header (frame start tokens plus frame ID) and the checksum, i.e. bytes 3 through 18 are 0x00. - -Byte(s) Description - -0 0xAA = Frame Start Byte 1 - -1 0x55 = Frame Start Byte 2 - -2 0x04 = Status Message ID - -3 Receive Error Counter - -4 Transmit Error Counter - -5 – 18 Unknown Purpose - In case of status report, they may be filled with data - -19 Checksum - diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 9bd38bf3d..390ef7e6d 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -2,25 +2,19 @@ """ To Support the Seeed USB-Can analyzer interface. The device will appear -as a serial port, for example "/dev/ttyS1" or "/dev/ttyUSB0" on Linux -machines or "COM1" on Windows. +as a serial port, for example "/dev/ttyUSB0" on Linux machines +or "COM1" on Windows. https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html SKU 114991193 -See protoocl: -https://copperhilltech.com/blog/usbcan-analyzer-usb-to-can-bus-serial-protocol-definition/ - this file uses Crc8Darc checksums. """ -from __future__ import absolute_import, division - import logging import struct -import binascii from time import sleep, time from can import BusABC, Message -logger = logging.getLogger('can.CanAnalyzer') +logger = logging.getLogger(__name__) try: import serial @@ -32,16 +26,11 @@ try: from crccheck.crc import Crc8Darc except ImportError: - logger.warning("The interface requires the install option crccheck.") + logger.warning("The interface requires the install option seeddstudio.") - -class CanAnalyzer(BusABC): +class SeeedBus(BusABC): """ - Enable basic can communication over a serial device. - - .. note:: See :meth:`can.interfaces.serial.CanAnalyzer._recv_internal` - for some special semantics. - + Enable basic can communication over a USB-CAN-Analyzer device. """ BITRATE = { 1000000: 0x01, @@ -70,25 +59,24 @@ class CanAnalyzer(BusABC): "loopback_and_silent":0x03 } - def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, - frame_type='STD', operation_mode='normal', bit_rate=500000, - *args, **kwargs): + def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", + operation_mode="normal", bit_rate=500000, *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). - - .. warning:: - Some serial port implementations don't care about the baudrate. + :param baudrate: + The default matches required baudrate :param float timeout: Timeout for the serial device in seconds (default 0.1). - :param bool rtscts: - turn hardware handshake (RTS/CTS) on and off + :param str frame_type: + STD or EXT, to select standard or extended messages + + :param operation_mode + normal, loopback, silent or loopback_and_silent. """ self.bit_rate = bit_rate @@ -101,9 +89,9 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, self.channel_info = "Serial interface: " + channel self.ser = serial.Serial( - channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) + channel, baudrate=baudrate, timeout=timeout, rtscts=False) - super(CanAnalyzer, self).__init__(channel=channel, *args, **kwargs) + super(SeeedBus, self).__init__(channel=channel, *args, **kwargs) self.init_frame() def shutdown(self): @@ -113,30 +101,29 @@ def shutdown(self): self.ser.close() def init_frame(self, timeout=None): + """ + Send init message to setup the device for comms. this is called during + interface creation. + :param timeout: + This parameter will be ignored. The timeout value of the channel is + used instead. + """ byte_msg = bytearray() byte_msg.append(0xAA) # Frame Start Byte 1 byte_msg.append(0x55) # Frame Start Byte 2 - byte_msg.append(0x12) # Initialization Message ID - - byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate - byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) - + byte_msg.append(SeeedBus.BITRATE[self.bit_rate]) # CAN Baud Rate + byte_msg.append(SeeedBus.FRAMETYPE[self.frame_type]) byte_msg.extend(self.filter_id) - byte_msg.extend(self.mask_id) - - byte_msg.append(CanAnalyzer.OPERATIONMODE[self.op_mode]) - + byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) byte_msg.append(0x01) for i in range(0, 4): byte_msg.append(0x00) crc = Crc8Darc.calc(byte_msg[2:]) -# crc_byte = struct.pack('B', crc) - byte_msg.append(crc) logger.debug("init_frm:\t" + byte_msg.hex()) @@ -146,6 +133,13 @@ def flush_buffer(self): self.ser.flushInput() def status_frame(self, timeout=None): + """ + Send status message over the serial device. + + :param timeout: + This parameter will be ignored. The timeout value of the channel is + used instead. + """ byte_msg = bytearray() byte_msg.append(0xAA) # Frame Start Byte 1 byte_msg.append(0x55) # Frame Start Byte 2 @@ -157,9 +151,7 @@ def status_frame(self, timeout=None): byte_msg.append(0x00) crc = Crc8Darc.calc(byte_msg[2:]) - crc_byte = struct.pack('B', crc) - - byte_msg.append(crc_byte) + byte_msg.append(crc) logger.debug("status_frm:\t" + byte_msg.hex()) self.ser.write(byte_msg) @@ -208,7 +200,8 @@ def _recv_internal(self, timeout): :param timeout: .. warning:: - This parameter will be ignored. The timeout value of the channel is used. + This parameter will be ignored. The timeout value of the + channel is used. :returns: Received message and False (because not filtering as taken place). diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst new file mode 100644 index 000000000..35cc25b93 --- /dev/null +++ b/doc/interfaces/seeedstudio.rst @@ -0,0 +1,85 @@ +.. _seeeddoc: + + +USB-CAN Analyzer +================ +...by Seeed Studio + +SKU: 114991193 + +Links: + +- https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html +- https://github.com/SeeedDocument/USB-CAN_Analyzer +- https://copperhilltech.com/blog/usbcan-analyzer-usb-to-can-bus-serial-protocol-definition/ + +^^^^^^^^^^ + +Installation +------------ +This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install two additional packages if not already available: + - pyserial + - crccheck + + +:: + + pip3 install python-can[seeedstudio] + + +^^^^^^^^^^ + + +Interface +--------- + +:: + + can.interfaces.seeedstudio.SeeedBus + +A bus example:: + + bus = can.interface.Bus(bustype='seeedstudio', channel='/dev/ttyUSB0', bitrate=500000) + + +^^^^^^^^^^ + +Parameters +---------- +:: + + SeeedBus(channel, + timeout=0.1, + frame_type='STD', + operation_mode='normal', + bit_rate=500000) + +ChANNEL + The serial port created by the USB device when connected. + +TIMEOUT + Only used by the underling serial port, it probably should not be changed. The serial port baudrate=2000000 and rtscts=false are also matched to the device so are not added here. + +FRAMETYPE + - "STD" + - "EXT" + +OPERATIONMODE + - "normal" + - "loopback" + - "silent" + - "loopback_and_silent" + +BITRATE + - 1000000 + - 800000 + - 500000 + - 400000 + - 250000 + - 200000 + - 125000 + - 100000 + - 50000 + - 20000 + - 10000 + - 5000 From d3ef672d4a1f287e949a91117aadcfb58c5cf1aa Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:03:17 +1000 Subject: [PATCH 108/252] changing from bit_rate to bitrate to match other interfaces and examples. --- can/interfaces/seeedstudio/seeedstudio.py | 7 +++++-- doc/interfaces/seeedstudio.rst | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 390ef7e6d..33493ca1b 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -60,7 +60,7 @@ class SeeedBus(BusABC): } def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", - operation_mode="normal", bit_rate=500000, *args, **kwargs): + operation_mode="normal", bitrate=500000, *args, **kwargs): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or @@ -78,8 +78,11 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", :param operation_mode normal, loopback, silent or loopback_and_silent. + :param bitrate + CAN bus bit rate, selected from available list. + """ - self.bit_rate = bit_rate + self.bit_rate = bitrate self.frame_type = frame_type self.op_mode = operation_mode self.filter_id = bytearray([0x00, 0x00, 0x00, 0x00]) diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst index 35cc25b93..b018a6643 100644 --- a/doc/interfaces/seeedstudio.rst +++ b/doc/interfaces/seeedstudio.rst @@ -49,10 +49,11 @@ Parameters :: SeeedBus(channel, + baudrate=2000000, timeout=0.1, frame_type='STD', operation_mode='normal', - bit_rate=500000) + bitrate=500000) ChANNEL The serial port created by the USB device when connected. From fde4fdd6be690a0de2845069ec6796a762975b9c Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:06:01 +1000 Subject: [PATCH 109/252] corrected crc calc and removed crccheck package. --- can/interfaces/seeedstudio/seeedstudio.py | 24 ++++++++++------------- setup.py | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 33493ca1b..351ecb93f 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -6,7 +6,6 @@ or "COM1" on Windows. https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html SKU 114991193 -this file uses Crc8Darc checksums. """ import logging @@ -14,7 +13,7 @@ from time import sleep, time from can import BusABC, Message -logger = logging.getLogger(__name__) +logger = logging.getLogger('seeedbus') try: import serial @@ -23,11 +22,6 @@ "the serial module installed!") serial = None -try: - from crccheck.crc import Crc8Darc -except ImportError: - logger.warning("The interface requires the install option seeddstudio.") - class SeeedBus(BusABC): """ Enable basic can communication over a USB-CAN-Analyzer device. @@ -121,12 +115,12 @@ def init_frame(self, timeout=None): byte_msg.extend(self.filter_id) byte_msg.extend(self.mask_id) byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) - byte_msg.append(0x01) + byte_msg.append(0x01) # Follows 'Send once' in windows app. - for i in range(0, 4): + for i in range(0, 4): # Manual bitrate config, details unknown. byte_msg.append(0x00) - crc = Crc8Darc.calc(byte_msg[2:]) + crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) logger.debug("init_frm:\t" + byte_msg.hex()) @@ -137,7 +131,8 @@ def flush_buffer(self): def status_frame(self, timeout=None): """ - Send status message over the serial device. + Send status request message over the serial device. The device will + respond but details of error codes are unknown but are logged - DEBUG. :param timeout: This parameter will be ignored. The timeout value of the channel is @@ -153,7 +148,7 @@ def status_frame(self, timeout=None): for i in range(0, 14): byte_msg.append(0x00) - crc = Crc8Darc.calc(byte_msg[2:]) + crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) logger.debug("status_frm:\t" + byte_msg.hex()) @@ -193,7 +188,7 @@ def send(self, msg, timeout=None): byte_msg.extend(msg.data) byte_msg.append(0x55) - logger.debug("Sending:\t" + byte_msg.hex()) + logger.debug("sending:\t" + byte_msg.hex()) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -224,7 +219,8 @@ def _recv_internal(self, timeout): rx_byte_2 = ord(self.ser.read()) time_stamp = time() if rx_byte_2 == 0x55: - rx_msg_type = self.ser.read() + status = bytearray(self.ser.read(18)) + logger.debug("status resp:\t" + status.hex()) else: length = int(rx_byte_2 & 0x0F) diff --git a/setup.py b/setup.py index 29a50329f..ef3cc3ca0 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ extras_require = { "serial": ["pyserial~=3.0"], "neovi": ["python-ics>=2.12"], - "seeedstudio": ["crccheck>=0.6", "pyserial>=3.0"], + "seeedstudio": ["pyserial>=3.0"], } tests_require = [ From 0c0b9a1b1d18dacf50ad8de293b296f47b96a811 Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:25:29 +1000 Subject: [PATCH 110/252] remove crccheck reference from doco --- doc/interfaces/seeedstudio.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst index b018a6643..17476a498 100644 --- a/doc/interfaces/seeedstudio.rst +++ b/doc/interfaces/seeedstudio.rst @@ -19,7 +19,6 @@ Installation ------------ This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install two additional packages if not already available: - pyserial - - crccheck :: From ed54cc4d1b46e54826dd51b0023fdf74b29e607f Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Thu, 13 Jun 2019 20:54:21 +0200 Subject: [PATCH 111/252] Update docs --- doc/bit_timing.rst | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/doc/bit_timing.rst b/doc/bit_timing.rst index 1b25adf06..e1d2feeeb 100644 --- a/doc/bit_timing.rst +++ b/doc/bit_timing.rst @@ -1,4 +1,49 @@ Bit Timing Configuration ======================== +The CAN protocol allows the bitrate, sample point and number of samples to be +optimized for a given application. You can read more on Wikipedia_, Kvaser_ +and other sources. + +In most cases the recommended settings for a predefined set of common +bitrates will work just fine. In some cases it may however be necessary to specify +custom settings. The :class:`can.BitTiming` class can be used for this purpose to +specify them in a relatively interface agnostic manner. + +It is also possible to specify the same settings for a CAN 2.0 bus +using the config file: + + +.. code-block:: none + + [default] + bitrate=1000000 + f_clock=8000000 + tseg1=5 + tseg2=2 + sjw=1 + nof_samples=1 + + +.. code-block:: none + + [default] + brp=1 + tseg1=5 + tseg2=2 + sjw=1 + nof_samples=1 + + +.. code-block:: none + + [default] + btr0=0x00 + btr1=0x14 + + .. autoclass:: can.BitTiming + + +.. _Wikipedia: https://en.wikipedia.org/wiki/CAN_bus#Bit_timing +.. _Kvaser: https://www.kvaser.com/about-can/the-can-protocol/can-bit-timing/ From a26c209143b003641f4424c455466d1815857de1 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 13 Jun 2019 10:54:43 +0200 Subject: [PATCH 112/252] Update Readme: 4.x is currently developed Make it clearer that 4.x is the version currently developed, maybe more people will stop using the develop branch on Python < 3.6. --- README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index ee489d06f..ef92a6222 100644 --- a/README.rst +++ b/README.rst @@ -44,13 +44,13 @@ messages on a can bus. The library currently supports Python 3.6+ as well as PyPy 3 and runs on Mac, Linux and Windows. -================== =========== -Library Version Python ------------------- ----------- - 2.x 2.6+, 3.4+ - 3.x 2.7+, 3.5+ - 4.x 3.6+ -================== =========== +============================= =========== +Library Version Python +----------------------------- ----------- + 2.x 2.6+, 3.4+ + 3.x 2.7+, 3.5+ + 4.x *(currently on devlop)* 3.6+ +============================= =========== Features From 24e4b3521b2713e5b3e9c79d1c206b8518bfa454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Wed, 19 Jun 2019 11:30:25 -0400 Subject: [PATCH 113/252] Simplifying the configuration context handling. Also allowing to use the context with environment variables. --- can/util.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/can/util.py b/can/util.py index c04bb70c9..b65959ee5 100644 --- a/can/util.py +++ b/can/util.py @@ -30,7 +30,7 @@ CONFIG_FILES.extend(["can.ini", os.path.join(os.getenv("APPDATA", ""), "can.ini")]) -def load_file_config(path=None, section=None): +def load_file_config(path=None, section="default"): """ Loads configuration from file with following content:: @@ -52,16 +52,13 @@ def load_file_config(path=None, section=None): _config = {} - section = section if section is not None else "default" if config.has_section(section): - if config.has_section("default"): - _config.update(dict((key, val) for key, val in config.items("default"))) _config.update(dict((key, val) for key, val in config.items(section))) return _config -def load_environment_config(): +def load_environment_config(context=None): """ Loads config dict from environmental variables (if set): @@ -69,15 +66,27 @@ def load_environment_config(): * CAN_CHANNEL * CAN_BITRATE + if context is supplied, "_{context}" is appended to the environment + variable name we will look at. For example if context="ABC": + + * CAN_INTERFACE_ABC + * CAN_CHANNEL_ABC + * CAN_BITRATE_ABC + """ 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 - ) + config = dict() + for key, val in mapper.items(): + if context is not None: + val = val + "_{}".format(context) + if val in os.environ: + config[key] = os.environ.get(val) + + return config def load_config(path=None, config=None, context=None): @@ -135,8 +144,10 @@ def load_config(path=None, config=None, context=None): config_sources = [ given_config, can.rc, - lambda _context: load_environment_config(), # context is not supported + lambda _context: load_environment_config(_context), + lambda _context: load_environment_config(), lambda _context: load_file_config(path, _context), + lambda _context: load_file_config(path), ] # Slightly complex here to only search for the file config if required From 2e37856f468009f7c0852b4606c959ee43440566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Wed, 19 Jun 2019 11:57:34 -0400 Subject: [PATCH 114/252] Updating tests --- test/test_load_file_config.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/test_load_file_config.py b/test/test_load_file_config.py index d2f5fc3f2..79b2e6c4b 100644 --- a/test/test_load_file_config.py +++ b/test/test_load_file_config.py @@ -45,15 +45,16 @@ def test_config_file_with_default(self): def test_config_file_with_default_and_section(self): tmp_config = self._gen_configration_file(["default", "one"]) - default = can.util.load_file_config(path=tmp_config) - self.assertEqual(default, self.configuration["default"]) + config = can.util.load_file_config(path=tmp_config) + self.assertEqual(config, self.configuration["default"]) - one = can.util.load_file_config(path=tmp_config, section="one") - self.assertEqual(one, self.configuration["one"]) + config.update(can.util.load_file_config(path=tmp_config, section="one")) + self.assertEqual(config, self.configuration["one"]) def test_config_file_with_section_only(self): tmp_config = self._gen_configration_file(["one"]) - config = can.util.load_file_config(path=tmp_config, section="one") + config = can.util.load_file_config(path=tmp_config) + config.update(can.util.load_file_config(path=tmp_config, section="one")) self.assertEqual(config, self.configuration["one"]) def test_config_file_with_section_and_key_in_default(self): @@ -61,13 +62,15 @@ def test_config_file_with_section_and_key_in_default(self): expected.update(self.configuration["two"]) tmp_config = self._gen_configration_file(["default", "two"]) - config = can.util.load_file_config(path=tmp_config, section="two") + config = can.util.load_file_config(path=tmp_config) + config.update(can.util.load_file_config(path=tmp_config, section="two")) self.assertEqual(config, expected) def test_config_file_with_section_missing_interface(self): expected = self.configuration["two"].copy() tmp_config = self._gen_configration_file(["two"]) - config = can.util.load_file_config(path=tmp_config, section="two") + config = can.util.load_file_config(path=tmp_config) + config.update(can.util.load_file_config(path=tmp_config, section="two")) self.assertEqual(config, expected) def test_config_file_extra(self): @@ -75,14 +78,16 @@ def test_config_file_extra(self): expected.update(self.configuration["three"]) tmp_config = self._gen_configration_file(["default", "three"]) - config = can.util.load_file_config(path=tmp_config, section="three") + config = can.util.load_file_config(path=tmp_config) + config.update(can.util.load_file_config(path=tmp_config, section="three")) self.assertEqual(config, expected) def test_config_file_with_non_existing_section(self): - expected = {} + expected = self.configuration["default"].copy() tmp_config = self._gen_configration_file(["default", "one", "two", "three"]) - config = can.util.load_file_config(path=tmp_config, section="zero") + config = can.util.load_file_config(path=tmp_config) + config.update(can.util.load_file_config(path=tmp_config, section="zero")) self.assertEqual(config, expected) From 62ff42ee1613f4af87fada1776ff2d3abaa33a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Wed, 19 Jun 2019 12:06:55 -0400 Subject: [PATCH 115/252] Allow to set bus config argument via CAN_CONFIG environment variable --- can/util.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/can/util.py b/can/util.py index b65959ee5..e892fdcde 100644 --- a/can/util.py +++ b/can/util.py @@ -4,6 +4,7 @@ Utilities and configuration file parsing. """ +import json import os import os.path import platform @@ -65,6 +66,7 @@ def load_environment_config(context=None): * CAN_INTERFACE * CAN_CHANNEL * CAN_BITRATE + * CAN_CONFIG if context is supplied, "_{context}" is appended to the environment variable name we will look at. For example if context="ABC": @@ -72,6 +74,7 @@ def load_environment_config(context=None): * CAN_INTERFACE_ABC * CAN_CHANNEL_ABC * CAN_BITRATE_ABC + * CAN_CONFIG_ABC """ mapper = { @@ -79,12 +82,18 @@ def load_environment_config(context=None): "channel": "CAN_CHANNEL", "bitrate": "CAN_BITRATE", } - config = dict() + + context_suffix = "_{}".format(context) if context else "" + + config = {} + + can_config_key = "CAN_CONFIG" + context_suffix + if can_config_key in os.environ: + config = json.loads(os.environ.get(can_config_key)) + for key, val in mapper.items(): - if context is not None: - val = val + "_{}".format(context) if val in os.environ: - config[key] = os.environ.get(val) + config[key] = os.environ.get(val + context_suffix) return config From f7afe9930ea5689841d13e8a2fd18ca67defda97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Wed, 19 Jun 2019 13:20:50 -0400 Subject: [PATCH 116/252] Disable unnecessary lambda pylint warning. This line is more readable and consistent with the other lines with the use of lambda --- can/util.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/can/util.py b/can/util.py index e892fdcde..5bd1ce52c 100644 --- a/can/util.py +++ b/can/util.py @@ -153,7 +153,9 @@ def load_config(path=None, config=None, context=None): config_sources = [ given_config, can.rc, - lambda _context: load_environment_config(_context), + lambda _context: load_environment_config( # pylint: disable=unnecessary-lambda + _context + ), lambda _context: load_environment_config(), lambda _context: load_file_config(path, _context), lambda _context: load_file_config(path), From dce74315c3001684f4c24178ea98ba4293e11a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Sch=C3=A4rlund?= Date: Sat, 22 Jun 2019 10:14:05 +0200 Subject: [PATCH 117/252] Avoid padding CAN_FD_MESSAGE_64 objects to 4 bytes --- can/io/blf.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/can/io/blf.py b/can/io/blf.py index 06afd6552..94ed2b51b 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -206,8 +206,13 @@ def __iter__(self): raise BLFParseError() obj_size = header[3] + obj_type = header[4] # Calculate position of next object - next_pos = pos + obj_size + (obj_size % 4) + if obj_size % 4 and obj_type != CAN_FD_MESSAGE_64: + next_pos = pos + obj_size + (obj_size % 4) + else: + # CAN_FD_MESSAGE_64 objects are not padded to 4 bytes. + next_pos = pos + obj_size if next_pos > len(data): # Object continues in next log container break @@ -239,7 +244,6 @@ def __iter__(self): factor = 1e-9 timestamp = timestamp * factor + self.start_timestamp - obj_type = header[4] # Both CAN message types have the same starting content if obj_type in (CAN_MESSAGE, CAN_MESSAGE2): ( From fee245411c5e185ab9a4421f1bc9f3762dd87c59 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 23 Jun 2019 14:32:57 +1000 Subject: [PATCH 118/252] fixing pylint errors, Unused variable 'i', unused import sleep. --- can/interfaces/seeedstudio/seeedstudio.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 351ecb93f..f4f6867ba 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -10,7 +10,7 @@ import logging import struct -from time import sleep, time +from time import time from can import BusABC, Message logger = logging.getLogger('seeedbus') @@ -117,7 +117,7 @@ def init_frame(self, timeout=None): byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) byte_msg.append(0x01) # Follows 'Send once' in windows app. - for i in range(0, 4): # Manual bitrate config, details unknown. + for _ in range(0, 4): # Manual bitrate config, details unknown. byte_msg.append(0x00) crc = sum(byte_msg[2:]) & 0xFF @@ -145,7 +145,7 @@ def status_frame(self, timeout=None): byte_msg.append(0x00) # In response packet - Rx error count byte_msg.append(0x00) # In response packet - Tx error count - for i in range(0, 14): + for _ in range(0, 14): byte_msg.append(0x00) crc = sum(byte_msg[2:]) & 0xFF From cf1fcb4db6c62ce27ff4ef4f5611ce67adbcefe8 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 23 Jun 2019 15:54:02 +1000 Subject: [PATCH 119/252] fixing pylint errors, logging string arguments --- can/interfaces/seeedstudio/seeedstudio.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index f4f6867ba..328d67649 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -123,7 +123,7 @@ def init_frame(self, timeout=None): crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) - logger.debug("init_frm:\t" + byte_msg.hex()) + logger.debug("init_frm:\t%s", byte_msg.hex()) self.ser.write(byte_msg) def flush_buffer(self): @@ -151,7 +151,7 @@ def status_frame(self, timeout=None): crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) - logger.debug("status_frm:\t" + byte_msg.hex()) + logger.debug("status_frm:\t%s", byte_msg.hex()) self.ser.write(byte_msg) def send(self, msg, timeout=None): @@ -188,7 +188,7 @@ def send(self, msg, timeout=None): byte_msg.extend(msg.data) byte_msg.append(0x55) - logger.debug("sending:\t" + byte_msg.hex()) + logger.debug("sending:\t%s", byte_msg.hex()) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -219,8 +219,9 @@ def _recv_internal(self, timeout): rx_byte_2 = ord(self.ser.read()) time_stamp = time() if rx_byte_2 == 0x55: - status = bytearray(self.ser.read(18)) - logger.debug("status resp:\t" + status.hex()) + status = bytearray([0xAA, 0x55]) + status += bytearray(self.ser.read(18)) + logger.debug("status resp:\t%s", status.hex()) else: length = int(rx_byte_2 & 0x0F) @@ -243,7 +244,7 @@ def _recv_internal(self, timeout): is_remote_frame=is_remote, dlc=length, data=data) - logger.debug("recv message: " + str(msg)) + logger.debug("recv message: %s", str(msg)) return msg, False else: From 876e12cb79b39dd6beec9d66cb9077ba75ecfec3 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 23 Jun 2019 16:08:40 +1000 Subject: [PATCH 120/252] fixing format errors, ran back format tool. --- can/interfaces/seeedstudio/seeedstudio.py | 119 ++++++++++++---------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 328d67649..7e93dfeb0 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -13,48 +13,58 @@ from time import time from can import BusABC, Message -logger = logging.getLogger('seeedbus') +logger = logging.getLogger("seeedbus") try: import serial except ImportError: - logger.warning("You won't be able to use the serial can backend without " - "the serial module installed!") + logger.warning( + "You won't be able to use the serial can backend without " + "the serial module installed!" + ) serial = None + class SeeedBus(BusABC): """ Enable basic can communication over a USB-CAN-Analyzer device. """ + BITRATE = { - 1000000: 0x01, - 800000: 0x02, - 500000: 0x03, - 400000: 0x04, - 250000: 0x05, - 200000: 0x06, - 125000: 0x07, - 100000: 0x08, - 50000: 0x09, - 20000: 0x0A, - 10000: 0x0B, - 5000: 0x0C - } - - FRAMETYPE = { - "STD":0x01, - "EXT":0x02 - } + 1000000: 0x01, + 800000: 0x02, + 500000: 0x03, + 400000: 0x04, + 250000: 0x05, + 200000: 0x06, + 125000: 0x07, + 100000: 0x08, + 50000: 0x09, + 20000: 0x0A, + 10000: 0x0B, + 5000: 0x0C, + } + + FRAMETYPE = {"STD": 0x01, "EXT": 0x02} OPERATIONMODE = { - "normal":0x00, - "loopback":0x01, - "silent":0x02, - "loopback_and_silent":0x03 - } - - def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", - operation_mode="normal", bitrate=500000, *args, **kwargs): + "normal": 0x00, + "loopback": 0x01, + "silent": 0x02, + "loopback_and_silent": 0x03, + } + + def __init__( + self, + channel, + baudrate=2000000, + timeout=0.1, + frame_type="STD", + operation_mode="normal", + bitrate=500000, + *args, + **kwargs + ): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or @@ -86,7 +96,8 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", self.channel_info = "Serial interface: " + channel self.ser = serial.Serial( - channel, baudrate=baudrate, timeout=timeout, rtscts=False) + channel, baudrate=baudrate, timeout=timeout, rtscts=False + ) super(SeeedBus, self).__init__(channel=channel, *args, **kwargs) self.init_frame() @@ -107,17 +118,17 @@ def init_frame(self, timeout=None): used instead. """ byte_msg = bytearray() - byte_msg.append(0xAA) # Frame Start Byte 1 - byte_msg.append(0x55) # Frame Start Byte 2 - byte_msg.append(0x12) # Initialization Message ID + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + byte_msg.append(0x12) # Initialization Message ID byte_msg.append(SeeedBus.BITRATE[self.bit_rate]) # CAN Baud Rate byte_msg.append(SeeedBus.FRAMETYPE[self.frame_type]) byte_msg.extend(self.filter_id) byte_msg.extend(self.mask_id) byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) - byte_msg.append(0x01) # Follows 'Send once' in windows app. + byte_msg.append(0x01) # Follows 'Send once' in windows app. - for _ in range(0, 4): # Manual bitrate config, details unknown. + for _ in range(0, 4): # Manual bitrate config, details unknown. byte_msg.append(0x00) crc = sum(byte_msg[2:]) & 0xFF @@ -139,11 +150,11 @@ def status_frame(self, timeout=None): used instead. """ byte_msg = bytearray() - byte_msg.append(0xAA) # Frame Start Byte 1 - byte_msg.append(0x55) # Frame Start Byte 2 - byte_msg.append(0x04) # Status Message ID - byte_msg.append(0x00) # In response packet - Rx error count - byte_msg.append(0x00) # In response packet - Tx error count + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + byte_msg.append(0x04) # Status Message ID + byte_msg.append(0x00) # In response packet - Rx error count + byte_msg.append(0x00) # In response packet - Tx error count for _ in range(0, 14): byte_msg.append(0x00) @@ -169,7 +180,7 @@ def send(self, msg, timeout=None): byte_msg = bytearray() byte_msg.append(0xAA) - m_type = 0xc0 + m_type = 0xC0 if msg.is_extended_id: m_type += 1 << 5 @@ -180,9 +191,9 @@ def send(self, msg, timeout=None): byte_msg.append(m_type) if msg.is_extended_id: - a_id = struct.pack(' Date: Sun, 23 Jun 2019 16:22:28 +1000 Subject: [PATCH 121/252] minor tidy-up --- can/interfaces/seeedstudio/seeedstudio.py | 6 ++---- doc/interfaces/seeedstudio.rst | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 7e93dfeb0..eebd07753 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -128,8 +128,7 @@ def init_frame(self, timeout=None): byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) byte_msg.append(0x01) # Follows 'Send once' in windows app. - for _ in range(0, 4): # Manual bitrate config, details unknown. - byte_msg.append(0x00) + byte_msg.extend([0x00] * 4) # Manual bitrate config, details unknown. crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) @@ -156,8 +155,7 @@ def status_frame(self, timeout=None): byte_msg.append(0x00) # In response packet - Rx error count byte_msg.append(0x00) # In response packet - Tx error count - for _ in range(0, 14): - byte_msg.append(0x00) + byte_msg.extend([0x00] * 14) crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst index 17476a498..5c86fa688 100644 --- a/doc/interfaces/seeedstudio.rst +++ b/doc/interfaces/seeedstudio.rst @@ -17,7 +17,7 @@ Links: Installation ------------ -This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install two additional packages if not already available: +This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install an additional packages if not already available: - pyserial From 37dc484bf425a268271276eca1bbb809412f1eae Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 23 Jun 2019 12:36:12 +0200 Subject: [PATCH 122/252] reformat asc.py --- can/io/asc.py | 124 ++++++++++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index a8a50ed38..1371d8049 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -56,7 +56,6 @@ def __iter__(self): temp = line.strip() if not temp or not temp[0].isdigit(): continue - try: timestamp, channel, dummy = temp.split( None, 2 @@ -64,27 +63,30 @@ def __iter__(self): except ValueError: # we parsed an empty comment continue - timestamp = float(timestamp) try: # See ASCWriter channel = int(channel) - 1 except ValueError: pass - if dummy.strip()[0:10].lower() == "errorframe": - msg = Message(timestamp=timestamp, is_error_frame=True, channel=channel) + msg = Message( + timestamp=timestamp, + is_error_frame=True, + channel=channel, + ) yield msg - elif ( not isinstance(channel, int) - or dummy.strip()[0:10].lower() == "statistic:" + or dummy.strip()[0:10].lower() + == "statistic:" ): pass - elif dummy[-1:].lower() == "r": can_id_str, _ = dummy.split(None, 1) - can_id_num, is_extended_id = self._extract_can_id(can_id_str) + can_id_num, is_extended_id = self._extract_can_id( + can_id_str + ) msg = Message( timestamp=timestamp, arbitration_id=can_id_num & CAN_ID_MASK, @@ -93,24 +95,27 @@ def __iter__(self): channel=channel, ) yield msg - else: try: # this only works if dlc > 0 and thus data is availabe - can_id_str, _, _, dlc, data = dummy.split(None, 4) + can_id_str, _, _, dlc, data = dummy.split( + None, 4 + ) except ValueError: # but if not, we only want to get the stuff up to the dlc - can_id_str, _, _, dlc = dummy.split(None, 3) + can_id_str, _, _, dlc = dummy.split( + None, 3 + ) # and we set data to an empty sequence manually data = "" - dlc = int(dlc) frame = bytearray() data = data.split() for byte in data[0:dlc]: frame.append(int(byte, 16)) - - can_id_num, is_extended_id = self._extract_can_id(can_id_str) + can_id_num, is_extended_id = self._extract_can_id( + can_id_str + ) yield Message( timestamp=timestamp, @@ -121,7 +126,6 @@ def __iter__(self): data=frame, channel=channel, ) - self.stop() @@ -134,26 +138,30 @@ class ASCWriter(BaseIOHandler, Listener): It the first message does not have a timestamp, it is set to zero. """ - FORMAT_MESSAGE = "{channel} {id:<15} Rx {dtype} {data}" - FORMAT_MESSAGE_FD = " ".join([ - "CANFD", - "{channel:>3}", - "{dir:<4}", - "{id:>8} {symbolic_name:>32}", - "{brs}", - "{esi}", - "{dlc}", - "{data_length:>2}", - "{data}", - "{message_duration:>8}", - "{message_length:>4}", - "{flags:>8X}", - "{crc:>8}", - "{bit_timing_conf_arb:>8}", - "{bit_timing_conf_data:>8}", - "{bit_timing_conf_ext_arb:>8}", - "{bit_timing_conf_ext_data:>8}" - ]) + FORMAT_MESSAGE = ( + "{channel} {id:<15} Rx {dtype} {data}" + ) + FORMAT_MESSAGE_FD = " ".join( + [ + "CANFD", + "{channel:>3}", + "{dir:<4}", + "{id:>8} {symbolic_name:>32}", + "{brs}", + "{esi}", + "{dlc}", + "{data_length:>2}", + "{data}", + "{message_duration:>8}", + "{message_length:>4}", + "{flags:>8X}", + "{crc:>8}", + "{bit_timing_conf_arb:>8}", + "{bit_timing_conf_data:>8}", + "{bit_timing_conf_ext_arb:>8}", + "{bit_timing_conf_ext_data:>8}", + ] + ) FORMAT_DATE = "%a %b %m %I:%M:%S.{} %p %Y" FORMAT_EVENT = "{timestamp: 9.6f} {message}\n" @@ -169,7 +177,9 @@ def __init__(self, file, channel=1): self.channel = channel # write start of file header - now = datetime.now().strftime("%a %b %m %I:%M:%S.%f %p %Y") + now = datetime.now().strftime( + "%a %b %m %I:%M:%S.%f %p %Y" + ) self.file.write("date %s\n" % now) self.file.write("base hex timestamps absolute\n") self.file.write("internal events logged\n") @@ -192,56 +202,64 @@ def log_event(self, message, timestamp=None): """ if not message: # if empty or None - logger.debug("ASCWriter: ignoring empty message") + logger.debug( + "ASCWriter: ignoring empty message" + ) return - # this is the case for the very first message: if not self.header_written: self.last_timestamp = timestamp or 0.0 self.started = self.last_timestamp - mlsec = repr(self.last_timestamp).split(".")[1][:3] + mlsec = repr(self.last_timestamp).split(".")[1][ + :3 + ] formatted_date = time.strftime( - self.FORMAT_DATE.format(mlsec), time.localtime(self.last_timestamp) + self.FORMAT_DATE.format(mlsec), + time.localtime(self.last_timestamp), + ) + self.file.write( + "Begin Triggerblock %s\n" % formatted_date ) - self.file.write("Begin Triggerblock %s\n" % formatted_date) self.header_written = True - self.log_event("Start of measurement") # caution: this is a recursive call! - + self.log_event( + "Start of measurement" + ) # caution: this is a recursive call! # Use last known timestamp if unknown if timestamp is None: timestamp = self.last_timestamp - # turn into relative timestamps if necessary if timestamp >= self.started: timestamp -= self.started - - line = self.FORMAT_EVENT.format(timestamp=timestamp, message=message) + line = self.FORMAT_EVENT.format( + timestamp=timestamp, message=message + ) self.file.write(line) def on_message_received(self, msg): if msg.is_error_frame: - self.log_event("{} ErrorFrame".format(self.channel), msg.timestamp) + 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] - + data = [ + "{:02X}".format(byte) for byte in msg.data + ] arb_id = "{:X}".format(msg.arbitration_id) if msg.is_extended_id: arb_id += "x" - channel = channel2int(msg.channel) if channel is None: channel = self.channel else: # Many interfaces start channel numbering at 0 which is invalid channel += 1 - if msg.is_fd: flags = 0 flags |= 1 << 12 @@ -249,7 +267,6 @@ def on_message_received(self, msg): flags |= 1 << 13 if msg.error_state_indicator: flags |= 1 << 14 - serialized = self.FORMAT_MESSAGE_FD.format( channel=channel, id=arb_id, @@ -276,5 +293,4 @@ def on_message_received(self, msg): dtype=dtype, data=" ".join(data), ) - self.log_event(serialized, msg.timestamp) From ccab928c0d09fa7ef8241ae58f68f2f112b6b5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Sch=C3=A4rlund?= Date: Sun, 23 Jun 2019 13:55:21 +0200 Subject: [PATCH 123/252] Refactor to save calculations --- can/io/blf.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/can/io/blf.py b/can/io/blf.py index 94ed2b51b..2ac553717 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -208,11 +208,9 @@ def __iter__(self): obj_size = header[3] obj_type = header[4] # Calculate position of next object - if obj_size % 4 and obj_type != CAN_FD_MESSAGE_64: - next_pos = pos + obj_size + (obj_size % 4) - else: - # CAN_FD_MESSAGE_64 objects are not padded to 4 bytes. - next_pos = pos + obj_size + next_pos = pos + obj_size + if obj_type != CAN_FD_MESSAGE_64: + next_pos += obj_size % 4 if next_pos > len(data): # Object continues in next log container break From 1753dc81f8344892d93ac28b9a5807f23b485093 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 24 Jun 2019 10:25:56 +0200 Subject: [PATCH 124/252] hope to make black happier --- can/io/asc.py | 70 +++++++++++++-------------------------------------- 1 file changed, 17 insertions(+), 53 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index 1371d8049..656ac4fbe 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -70,23 +70,16 @@ def __iter__(self): except ValueError: pass if dummy.strip()[0:10].lower() == "errorframe": - msg = Message( - timestamp=timestamp, - is_error_frame=True, - channel=channel, - ) + msg = Message(timestamp=timestamp, is_error_frame=True, channel=channel) yield msg elif ( not isinstance(channel, int) - or dummy.strip()[0:10].lower() - == "statistic:" + or dummy.strip()[0:10].lower() == "statistic:" ): pass elif dummy[-1:].lower() == "r": can_id_str, _ = dummy.split(None, 1) - can_id_num, is_extended_id = self._extract_can_id( - can_id_str - ) + can_id_num, is_extended_id = self._extract_can_id(can_id_str) msg = Message( timestamp=timestamp, arbitration_id=can_id_num & CAN_ID_MASK, @@ -98,14 +91,10 @@ def __iter__(self): else: try: # this only works if dlc > 0 and thus data is availabe - can_id_str, _, _, dlc, data = dummy.split( - None, 4 - ) + can_id_str, _, _, dlc, data = dummy.split(None, 4) except ValueError: # but if not, we only want to get the stuff up to the dlc - can_id_str, _, _, dlc = dummy.split( - None, 3 - ) + can_id_str, _, _, dlc = dummy.split(None, 3) # and we set data to an empty sequence manually data = "" dlc = int(dlc) @@ -113,9 +102,7 @@ def __iter__(self): data = data.split() for byte in data[0:dlc]: frame.append(int(byte, 16)) - can_id_num, is_extended_id = self._extract_can_id( - can_id_str - ) + can_id_num, is_extended_id = self._extract_can_id(can_id_str) yield Message( timestamp=timestamp, @@ -138,9 +125,7 @@ class ASCWriter(BaseIOHandler, Listener): It the first message does not have a timestamp, it is set to zero. """ - FORMAT_MESSAGE = ( - "{channel} {id:<15} Rx {dtype} {data}" - ) + FORMAT_MESSAGE = "{channel} {id:<15} Rx {dtype} {data}" FORMAT_MESSAGE_FD = " ".join( [ "CANFD", @@ -177,9 +162,7 @@ def __init__(self, file, channel=1): self.channel = channel # write start of file header - now = datetime.now().strftime( - "%a %b %m %I:%M:%S.%f %p %Y" - ) + now = datetime.now().strftime("%a %b %m %I:%M:%S.%f %p %Y") self.file.write("date %s\n" % now) self.file.write("base hex timestamps absolute\n") self.file.write("internal events logged\n") @@ -202,55 +185,39 @@ def log_event(self, message, timestamp=None): """ if not message: # if empty or None - logger.debug( - "ASCWriter: ignoring empty message" - ) + logger.debug("ASCWriter: ignoring empty message") return # this is the case for the very first message: if not self.header_written: self.last_timestamp = timestamp or 0.0 self.started = self.last_timestamp - mlsec = repr(self.last_timestamp).split(".")[1][ - :3 - ] + mlsec = repr(self.last_timestamp).split(".")[1][:3] formatted_date = time.strftime( - self.FORMAT_DATE.format(mlsec), - time.localtime(self.last_timestamp), - ) - self.file.write( - "Begin Triggerblock %s\n" % formatted_date + self.FORMAT_DATE.format(mlsec), time.localtime(self.last_timestamp) ) + self.file.write("Begin Triggerblock %s\n" % formatted_date) self.header_written = True - self.log_event( - "Start of measurement" - ) # caution: this is a recursive call! + self.log_event("Start of measurement") # caution: this is a recursive call! # Use last known timestamp if unknown if timestamp is None: timestamp = self.last_timestamp # turn into relative timestamps if necessary if timestamp >= self.started: timestamp -= self.started - line = self.FORMAT_EVENT.format( - timestamp=timestamp, message=message - ) + line = self.FORMAT_EVENT.format(timestamp=timestamp, message=message) self.file.write(line) def on_message_received(self, msg): if msg.is_error_frame: - self.log_event( - "{} ErrorFrame".format(self.channel), - msg.timestamp, - ) + 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 - ] + data = ["{:02X}".format(byte) for byte in msg.data] arb_id = "{:X}".format(msg.arbitration_id) if msg.is_extended_id: arb_id += "x" @@ -288,9 +255,6 @@ def on_message_received(self, msg): ) else: serialized = self.FORMAT_MESSAGE.format( - channel=channel, - id=arb_id, - dtype=dtype, - data=" ".join(data), + channel=channel, id=arb_id, dtype=dtype, data=" ".join(data) ) self.log_event(serialized, msg.timestamp) From b1c7c131e63df182cf63c375c1cff713187cdd8a Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Wed, 26 Jun 2019 12:59:02 +0200 Subject: [PATCH 125/252] Add CAN-FD support to CLI's and config file --- can/logger.py | 12 ++++++++++++ can/player.py | 12 ++++++++++++ can/util.py | 5 +++++ can/viewer.py | 12 ++++++++++++ 4 files changed, 41 insertions(+) diff --git a/can/logger.py b/can/logger.py index 9b326946c..00f667979 100644 --- a/can/logger.py +++ b/can/logger.py @@ -79,6 +79,14 @@ def main(): "-b", "--bitrate", type=int, help="""Bitrate to use for the CAN bus.""" ) + parser.add_argument("--fd", help="Activate CAN-FD support", action="store_true") + + parser.add_argument( + "--data_bitrate", + type=int, + help="""Bitrate to use for the data phase in case of CAN-FD.""", + ) + state_group = parser.add_mutually_exclusive_group(required=False) state_group.add_argument( "--active", @@ -123,6 +131,10 @@ def main(): config["interface"] = results.interface if results.bitrate: config["bitrate"] = results.bitrate + if results.fd: + config["fd"] = True + if results.data_bitrate: + config["data_bitrate"] = results.data_bitrate bus = Bus(results.channel, **config) if results.active: diff --git a/can/player.py b/can/player.py index b279579f7..34be48670 100644 --- a/can/player.py +++ b/can/player.py @@ -58,6 +58,14 @@ def main(): "-b", "--bitrate", type=int, help="""Bitrate to use for the CAN bus.""" ) + parser.add_argument("--fd", help="Activate CAN-FD support", action="store_true") + + parser.add_argument( + "--data_bitrate", + type=int, + help="""Bitrate to use for the data phase in case of CAN-FD.""", + ) + parser.add_argument( "--ignore-timestamps", dest="timestamps", @@ -108,6 +116,10 @@ def main(): config["interface"] = results.interface if results.bitrate: config["bitrate"] = results.bitrate + if results.fd: + config["fd"] = True + if results.data_bitrate: + config["data_bitrate"] = results.data_bitrate bus = Bus(results.channel, **config) reader = LogReader(results.infile) diff --git a/can/util.py b/can/util.py index 803fa2388..5ecc028c1 100644 --- a/can/util.py +++ b/can/util.py @@ -165,6 +165,10 @@ def load_config(path=None, config=None, context=None): if "bitrate" in config: config["bitrate"] = int(config["bitrate"]) + if "fd" in config: + config["fd"] = config["fd"] not in ("0", "False", "false") + if "data_bitrate" in config: + config["data_bitrate"] = int(config["data_bitrate"]) # Create bit timing configuration if given timing_conf = {} @@ -186,6 +190,7 @@ def load_config(path=None, config=None, context=None): config["timing"] = can.BitTiming(**timing_conf) can.log.debug("can config: {}".format(config)) + print(config) return config diff --git a/can/viewer.py b/can/viewer.py index b6ecbfe36..21980e361 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -377,6 +377,14 @@ def parse_args(args): help="""Bitrate to use for the given CAN interface""", ) + optional.add_argument("--fd", help="Activate CAN-FD support", action="store_true") + + optional.add_argument( + "--data_bitrate", + type=int, + help="""Bitrate to use for the data phase in case of CAN-FD.""", + ) + optional.add_argument( "-c", "--channel", @@ -537,6 +545,10 @@ def main(): # pragma: no cover config["interface"] = parsed_args.interface if parsed_args.bitrate: config["bitrate"] = parsed_args.bitrate + if parsed_args.fd: + config["fd"] = True + if parsed_args.data_bitrate: + config["data_bitrate"] = parsed_args.data_bitrate # Create a CAN-Bus interface bus = can.Bus(parsed_args.channel, **config) From 161eb0a463679aaf4a52a477ac793f7b954b1b48 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Wed, 26 Jun 2019 13:35:02 +0200 Subject: [PATCH 126/252] Fix viewer when data does not fit --- can/viewer.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/can/viewer.py b/can/viewer.py index 21980e361..707192526 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -212,11 +212,6 @@ def draw_can_bus_message(self, msg, sorting=False): msg.arbitration_id, 8 if msg.is_extended_id else 3 ) - # Generate data string - data_string = "" - if msg.dlc > 0: - data_string = " ".join("{:02X}".format(x) for x in msg.data) - # Use red for error frames if msg.is_error_frame: color = curses.color_pair(1) @@ -236,7 +231,14 @@ def draw_can_bus_message(self, msg, sorting=False): ) self.draw_line(self.ids[key]["row"], 35, arbitration_id_string, color) self.draw_line(self.ids[key]["row"], 47, str(msg.dlc), color) - self.draw_line(self.ids[key]["row"], 52, data_string, color) + for i, b in enumerate(msg.data): + col = 52 + i * 3 + if col > self.x - 2: + # Data does not fit + self.draw_line(self.ids[key]["row"], col - 4, "...", color) + break + text = "{:02X}".format(b) + self.draw_line(self.ids[key]["row"], col, text, color) if self.data_structs: try: From c3f4c82a3535636ef172d37aa267342e72ca0e8c Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Wed, 26 Jun 2019 13:37:31 +0200 Subject: [PATCH 127/252] Remove debug print --- can/util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/can/util.py b/can/util.py index 5ecc028c1..9d0145dd7 100644 --- a/can/util.py +++ b/can/util.py @@ -190,7 +190,6 @@ def load_config(path=None, config=None, context=None): config["timing"] = can.BitTiming(**timing_conf) can.log.debug("can config: {}".format(config)) - print(config) return config From b59b684c7dfb096e1f63bf35564b190c493191ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Luc=20Tessier=20Gagn=C3=A9?= Date: Thu, 27 Jun 2019 13:27:44 -0400 Subject: [PATCH 128/252] Adding doc --- doc/configuration.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/configuration.rst b/doc/configuration.rst index 142e816da..0ce8f85a7 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -92,6 +92,13 @@ Configuration can be pulled from these environmental variables: * CAN_INTERFACE * CAN_CHANNEL * CAN_BITRATE + * CAN_CONFIG + +The ``CAN_CONFIG`` environment variable allows to set any bus configuration using JSON. + +For example: + +``CAN_INTERFACE=socketcan CAN_CONFIG={"receive_own_messages": true, "fd": true}`` Interface Names From 1892dc5bf19be2fd873cd809aa22e2810ca5a7c3 Mon Sep 17 00:00:00 2001 From: Pierre-jean Texier Date: Thu, 4 Jul 2019 13:31:33 +0200 Subject: [PATCH 129/252] setup.py: require pytest-runner only when necessary (#633) This optimizes setup.py for cases when pytest-runner is not needed, using the approach that is suggested upstream: https://pypi.python.org/pypi/pytest-runner#conditional-requirement --- setup.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 221c0301d..ded73f458 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,7 @@ from os.path import isfile, join import re import logging +import sys from setuptools import setup, find_packages logging.basicConfig(level=logging.WARNING) @@ -40,6 +41,13 @@ extras_require["test"] = tests_require +# Check for 'pytest-runner' only if setup.py was invoked with 'test'. +# This optimizes setup.py for cases when pytest-runner is not needed, +# using the approach that is suggested upstream. +# +# See https://pypi.org/project/pytest-runner/#conditional-requirement +needs_pytest = {"pytest", "test", "ptr"}.intersection(sys.argv) +pytest_runner = ["pytest-runner"] if needs_pytest else [] setup( # Description @@ -96,7 +104,7 @@ 'windows-curses;platform_system=="Windows"', "filelock", ], - setup_requires=["pytest-runner"], + setup_requires=pytest_runner, extras_require=extras_require, tests_require=tests_require, ) From b2e1d758c86873d8591c9b901cd8dfa374733195 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Sat, 6 Jul 2019 15:33:42 +0200 Subject: [PATCH 130/252] added xlCanRequestChipState and some constants --- can/interfaces/vector/vxlapi.py | 67 +++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index 5669490ac..d1af9693b 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -28,36 +28,90 @@ XL_ERR_QUEUE_IS_EMPTY = 10 XL_ERR_HW_NOT_PRESENT = 129 +# XLeventTag +# Common and CAN events +XL_NO_COMMAND = 0 XL_RECEIVE_MSG = 1 -XL_CAN_EV_TAG_RX_OK = 1024 -XL_CAN_EV_TAG_TX_OK = 1028 +XL_CHIP_STATE = 4 +XL_TRANSCEIVER = 6 +XL_TIMER = 8 XL_TRANSMIT_MSG = 10 -XL_CAN_EV_TAG_TX_MSG = 1088 - +XL_SYNC_PULSE = 11 +XL_APPLICATION_NOTIFICATION = 15 + +# CAN/CAN-FD event tags +# Rx +XL_CAN_EV_TAG_RX_OK = 0x0400 +XL_CAN_EV_TAG_RX_ERROR = 0x0401 +XL_CAN_EV_TAG_TX_ERROR = 0x0402 +XL_CAN_EV_TAG_TX_REQUEST = 0x0403 +XL_CAN_EV_TAG_TX_OK = 0x0404 +XL_CAN_EV_TAG_CHIP_STATE = 0x0409 +# Tx +XL_CAN_EV_TAG_TX_MSG = 0x0440 + +# s_xl_can_msg : id XL_CAN_EXT_MSG_ID = 0x80000000 + +# s_xl_can_msg : flags XL_CAN_MSG_FLAG_ERROR_FRAME = 0x01 XL_CAN_MSG_FLAG_REMOTE_FRAME = 0x10 XL_CAN_MSG_FLAG_TX_COMPLETED = 0x40 +# to be used with +# XLcanTxEvent::XL_CAN_TX_MSG::msgFlags XL_CAN_TXMSG_FLAG_EDL = 0x0001 XL_CAN_TXMSG_FLAG_BRS = 0x0002 XL_CAN_TXMSG_FLAG_RTR = 0x0010 +XL_CAN_TXMSG_FLAG_HIGHPRIO = 0x0080 +XL_CAN_TXMSG_FLAG_WAKEUP = 0x0200 + +# to be used with +# XLcanRxEvent::XL_CAN_EV_RX_MSG::msgFlags +# XLcanRxEvent::XL_CAN_EV_TX_REQUEST::msgFlags +# XLcanRxEvent::XL_CAN_EV_RX_MSG::msgFlags +# XLcanRxEvent::XL_CAN_EV_TX_REMOVED::msgFlags +# XLcanRxEvent::XL_CAN_EV_ERROR::msgFlags XL_CAN_RXMSG_FLAG_EDL = 0x0001 XL_CAN_RXMSG_FLAG_BRS = 0x0002 XL_CAN_RXMSG_FLAG_ESI = 0x0004 XL_CAN_RXMSG_FLAG_RTR = 0x0010 XL_CAN_RXMSG_FLAG_EF = 0x0200 +XL_CAN_RXMSG_FLAG_ARB_LOST = 0x0400 +XL_CAN_RXMSG_FLAG_WAKEUP = 0x2000 +XL_CAN_RXMSG_FLAG_TE = 0x4000 +# acceptance filter XL_CAN_STD = 1 XL_CAN_EXT = 2 +# s_xl_chip_state : busStatus +XL_CHIPSTAT_BUSOFF = 0x01 +XL_CHIPSTAT_ERROR_PASSIVE = 0x02 +XL_CHIPSTAT_ERROR_WARNING = 0x04 +XL_CHIPSTAT_ERROR_ACTIVE = 0x08 + +# s_xl_can_ev_error : errorCode +XL_CAN_ERRC_BIT_ERROR = 1 +XL_CAN_ERRC_FORM_ERROR = 2 +XL_CAN_ERRC_STUFF_ERROR = 3 +XL_CAN_ERRC_OTHER_ERROR = 4 +XL_CAN_ERRC_CRC_ERROR = 5 +XL_CAN_ERRC_ACK_ERROR = 6 +XL_CAN_ERRC_NACK_ERROR = 7 +XL_CAN_ERRC_OVLD_ERROR = 8 +XL_CAN_ERRC_EXCPT_ERROR = 9 + XLuint64 = ctypes.c_int64 XLaccess = XLuint64 XLhandle = ctypes.c_void_p MAX_MSG_LEN = 8 +# CAN / CAN-FD types and definitions XL_CAN_MAX_DATA_LEN = 64 +XL_CANFD_RX_EVENT_HEADER_SIZE = 32 +XL_CANFD_MAX_EVENT_SIZE = 128 # current version XL_INTERFACE_VERSION = 3 @@ -424,3 +478,8 @@ def check_status(result, function, arguments): xlCanResetAcceptance.argtypes = [XLportHandle, XLaccess, ctypes.c_uint] xlCanResetAcceptance.restype = XLstatus xlCanResetAcceptance.errcheck = check_status + +xlCanRequestChipState = _xlapi_dll.xlCanRequestChipState +xlCanRequestChipState.argtypes = [XLportHandle, XLaccess] +xlCanRequestChipState.restype = XLstatus +xlCanRequestChipState.errcheck = check_status From 56f153f9ccd8c79388f8642640b4c3d4101a9ac7 Mon Sep 17 00:00:00 2001 From: karl ding Date: Wed, 10 Jul 2019 14:27:45 -0700 Subject: [PATCH 131/252] Add support for multiple Cyclic Messages in Task (#610) This adds support for multiple Cyclic Messages in a Cyclic Task. The default implementation is also changed to provide support for this, by iterating over the list of Cyclic Messages. In order to maintain backwards compatibility, the periodic APIs now take a CAN Message as before, in addition to a Sequence of CAN Messages. The SocketCAN interface takes advantage of the Linux BCM APIs to do so, while the IXXAT interface maintains its original behaviour. This also introduces a new example that illustrates how to use Cyclic Messages, backed by the SocketCAN interface. We previously tracked the can_id and arbitration_id class members due to the ongoing deprecation of the can_id Message attribute. Now that can_id is replaced by arbitration_id, we no longer need this in CyclicSendTaskABC either. As such, this removes the deprecated can_id member from the Cyclic Task. Fixes #606 --- can/broadcastmanager.py | 127 +++++-- can/bus.py | 44 ++- can/interfaces/ixxat/canlib.py | 19 +- can/interfaces/socketcan/socketcan.py | 136 ++++--- examples/cyclic_multiple.py | 147 +++++++ test/test_socketcan_cyclic_multiple.py | 507 +++++++++++++++++++++++++ 6 files changed, 879 insertions(+), 101 deletions(-) create mode 100644 examples/cyclic_multiple.py create mode 100644 test/test_socketcan_cyclic_multiple.py diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index ae5d126fd..edb5da2a3 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -12,6 +12,8 @@ import threading import time +import can + log = logging.getLogger("can.bcm") @@ -34,28 +36,65 @@ class CyclicSendTaskABC(CyclicTask): Message send task with defined period """ - def __init__(self, message, period): + def __init__(self, messages, period): """ - :param can.Message message: The message to be sent periodically. - :param float period: The rate in seconds at which to send the message. + :param Union[Sequence[can.Message], can.Message] messages: + The messages to be sent periodically. + :param float period: The rate in seconds at which to send the messages. """ - self.message = message - self.can_id = message.arbitration_id - self.arbitration_id = message.arbitration_id + messages = self._check_and_convert_messages(messages) + + # Take the Arbitration ID of the first element + self.arbitration_id = messages[0].arbitration_id self.period = period - super().__init__() + self.messages = messages + + @staticmethod + def _check_and_convert_messages(messages): + """Helper function to convert a Message or Sequence of messages into a + tuple, and raises an error when the given value is invalid. + + Performs error checking to ensure that all Messages have the same + arbitration ID and channel. + + Should be called when the cyclic task is initialized + """ + if not isinstance(messages, (list, tuple)): + if isinstance(messages, can.Message): + messages = [messages] + else: + raise ValueError("Must be either a list, tuple, or a Message") + if not messages: + raise ValueError("Must be at least a list or tuple of length 1") + messages = tuple(messages) + + all_same_id = all( + message.arbitration_id == messages[0].arbitration_id for message in messages + ) + if not all_same_id: + raise ValueError("All Arbitration IDs should be the same") + + all_same_channel = all( + message.channel == messages[0].channel for message in messages + ) + if not all_same_channel: + raise ValueError("All Channel IDs should be the same") + + return messages class LimitedDurationCyclicSendTaskABC(CyclicSendTaskABC): - def __init__(self, message, period, duration): + def __init__(self, messages, period, duration): """Message send task with a defined duration and period. - :param can.Message message: The message to be sent periodically. - :param float period: The rate in seconds at which to send the message. + :param Union[Sequence[can.Message], can.Message] messages: + The messages to be sent periodically. + :param float period: The rate in seconds at which to send the messages. :param float duration: - The duration to keep sending this message at given rate. + Approximate duration in seconds to continue sending messages. If + no duration is provided, the task will continue indefinitely. """ - super().__init__(message, period) + super().__init__(messages, period) self.duration = duration @@ -71,33 +110,61 @@ def start(self): 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. + def _check_modified_messages(self, messages): + """Helper function to perform error checking when modifying the data in + the cyclic task. + + Performs error checking to ensure the arbitration ID and the number of + cyclic messages hasn't changed. - :param can.Message message: - The message with the new :attr:`can.Message.data`. - Note: The arbitration ID cannot be changed. + Should be called when modify_data is called in the cyclic task. """ - self.message = message + if len(self.messages) != len(messages): + raise ValueError( + "The number of new cyclic messages to be sent must be equal to " + "the number of messages originally specified for this task" + ) + if self.arbitration_id != messages[0].arbitration_id: + raise ValueError( + "The arbitration ID of new cyclic messages cannot be changed " + "from when the task was created" + ) + + def modify_data(self, messages): + """Update the contents of the periodically sent messages, without + altering the timing. + + :param Union[Sequence[can.Message], can.Message] messages: + The messages with the new :attr:`can.Message.data`. + + Note: The arbitration ID cannot be changed. + + Note: The number of new cyclic messages to be sent must be equal + to the original number of messages originally specified for this + task. + """ + messages = self._check_and_convert_messages(messages) + self._check_modified_messages(messages) + + self.messages = messages class MultiRateCyclicSendTaskABC(CyclicSendTaskABC): """A Cyclic send task that supports switches send frequency after a set time. """ - def __init__(self, channel, message, count, initial_period, subsequent_period): + def __init__(self, channel, messages, count, initial_period, subsequent_period): """ Transmits a message `count` times at `initial_period` then continues to - transmit message at `subsequent_period`. + transmit messages at `subsequent_period`. :param channel: See interface specific documentation. - :param can.Message message: + :param Union[Sequence[can.Message], can.Message] messages: :param int count: :param float initial_period: :param float subsequent_period: """ - super().__init__(channel, message, subsequent_period) + super().__init__(channel, messages, subsequent_period) class ThreadBasedCyclicSendTask( @@ -105,10 +172,10 @@ class ThreadBasedCyclicSendTask( ): """Fallback cyclic send task using thread.""" - def __init__(self, bus, lock, message, period, duration=None): - super().__init__(message, period, duration) + def __init__(self, bus, lock, messages, period, duration=None): + super().__init__(messages, period, duration) self.bus = bus - self.lock = lock + self.send_lock = lock self.stopped = True self.thread = None self.end_time = time.time() + duration if duration else None @@ -120,23 +187,25 @@ def stop(self): 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) + name = "Cyclic send task for 0x%X" % (self.messages[0].arbitration_id) self.thread = threading.Thread(target=self._run, name=name) self.thread.daemon = True self.thread.start() def _run(self): + msg_index = 0 while not self.stopped: # Prevent calling bus.send from multiple threads - with self.lock: + with self.send_lock: started = time.time() try: - self.bus.send(self.message) + self.bus.send(self.messages[msg_index]) except Exception as exc: log.exception(exc) break if self.end_time is not None and time.time() >= self.end_time: break + msg_index = (msg_index + 1) % len(self.messages) # Compensate for the time it takes to send the message delay = self.period - (time.time() - started) time.sleep(max(0.0, delay)) diff --git a/can/bus.py b/can/bus.py index 7fcf910b0..9682b31af 100644 --- a/can/bus.py +++ b/can/bus.py @@ -5,6 +5,7 @@ """ from abc import ABCMeta, abstractmethod +import can import logging import threading from time import time @@ -163,8 +164,8 @@ def send(self, msg, timeout=None): """ raise NotImplementedError("Trying to write to a readonly bus?") - def send_periodic(self, msg, period, duration=None, store_task=True): - """Start sending a message at a given period on this bus. + def send_periodic(self, msgs, period, duration=None, store_task=True): + """Start sending messages at a given period on this bus. The task will be active until one of the following conditions are met: @@ -174,12 +175,12 @@ def send_periodic(self, msg, period, duration=None, store_task=True): - :meth:`BusABC.stop_all_periodic_tasks()` is called - the task's :meth:`CyclicTask.stop()` method is called. - :param can.Message msg: - Message to transmit + :param Union[Sequence[can.Message], can.Message] msgs: + Messages 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 + Approximate duration in seconds to continue sending messages. If no duration is provided, the task will continue indefinitely. :param bool store_task: If True (the default) the task will be attached to this Bus instance. @@ -191,18 +192,26 @@ def send_periodic(self, msg, period, duration=None, store_task=True): .. note:: - Note the duration before the message stops being sent may not + Note the duration before the messages stop 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. .. note:: - For extremely long running Bus instances with many short lived tasks the default - api with ``store_task==True`` may not be appropriate as the stopped tasks are - still taking up memory as they are associated with the Bus instance. + For extremely long running Bus instances with many short lived + tasks the default api with ``store_task==True`` may not be + appropriate as the stopped tasks are still taking up memory as they + are associated with the Bus instance. """ - task = self._send_periodic_internal(msg, period, duration) + if not isinstance(msgs, (list, tuple)): + if isinstance(msgs, can.Message): + msgs = [msgs] + else: + raise ValueError("Must be either a list, tuple, or a Message") + if not msgs: + raise ValueError("Must be at least a list or tuple of length 1") + task = self._send_periodic_internal(msgs, period, duration) # we wrap the task's stop method to also remove it from the Bus's list of tasks original_stop_method = task.stop @@ -221,21 +230,22 @@ def wrapped_stop_method(remove_task=True): return task - def _send_periodic_internal(self, msg, period, duration=None): + def _send_periodic_internal(self, msgs, period, duration=None): """Default implementation of periodic message sending using threading. Override this method to enable a more efficient backend specific approach. - :param can.Message msg: - Message to transmit + :param Union[Sequence[can.Message], can.Message] msgs: + Messages 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 + The duration between sending each message at the given rate. If no duration is provided, the task will continue indefinitely. :return: - A started task instance. Note the task can be stopped (and depending on - the backend modified) by calling the :meth:`stop` method. + A started task instance. Note the task can be stopped (and + depending on the backend modified) by calling the :meth:`stop` + method. :rtype: can.broadcastmanager.CyclicSendTaskABC """ if not hasattr(self, "_lock_send_periodic"): @@ -244,7 +254,7 @@ def _send_periodic_internal(self, msg, period, duration=None): threading.Lock() ) # pylint: disable=attribute-defined-outside-init task = ThreadBasedCyclicSendTask( - self, self._lock_send_periodic, msg, period, duration + self, self._lock_send_periodic, msgs, period, duration ) return task diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 0dae6a513..13c8ef779 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -716,20 +716,25 @@ def shutdown(self): class CyclicSendTask(LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC): """A message in the cyclic transmit list.""" - def __init__(self, scheduler, msg, period, duration, resolution): - super().__init__(msg, period, duration) + def __init__(self, scheduler, msgs, period, duration, resolution): + super().__init__(msgs, period, duration) + if len(self.messages) != 1: + raise ValueError( + "IXXAT Interface only supports periodic transmission of 1 element" + ) + self._scheduler = scheduler self._index = None self._count = int(duration / period) if duration else 0 self._msg = structures.CANCYCLICTXMSG() self._msg.wCycleTime = int(round(period * resolution)) - self._msg.dwMsgId = msg.arbitration_id + self._msg.dwMsgId = self.messages[0].arbitration_id self._msg.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA - self._msg.uMsgInfo.Bits.ext = 1 if msg.is_extended_id 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.uMsgInfo.Bits.ext = 1 if self.messages[0].is_extended_id else 0 + self._msg.uMsgInfo.Bits.rtr = 1 if self.messages[0].is_remote_frame else 0 + self._msg.uMsgInfo.Bits.dlc = self.messages[0].dlc + for i, b in enumerate(self.messages[0].data): self._msg.abData[i] = b self.start() diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index 81315c8dc..f504d1cfc 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -191,7 +191,7 @@ def build_bcm_tx_delete_header(can_id, flags): def build_bcm_transmit_header( - can_id, count, initial_period, subsequent_period, msg_flags + can_id, count, initial_period, subsequent_period, msg_flags, nframes=1 ): opcode = CAN_BCM_TX_SETUP @@ -209,7 +209,6 @@ def split_time(value): ival1_seconds, ival1_usec = split_time(initial_period) ival2_seconds, ival2_usec = split_time(subsequent_period) - nframes = 1 return build_bcm_header( opcode, @@ -224,8 +223,8 @@ def split_time(value): ) -def build_bcm_update_header(can_id, msg_flags): - return build_bcm_header(CAN_BCM_TX_SETUP, msg_flags, 0, 0, 0, 0, 0, can_id, 1) +def build_bcm_update_header(can_id, msg_flags, nframes=1): + return build_bcm_header(CAN_BCM_TX_SETUP, msg_flags, 0, 0, 0, 0, 0, can_id, nframes) def dissect_can_frame(frame): @@ -288,7 +287,7 @@ class CyclicSendTask( LimitedDurationCyclicSendTaskABC, ModifiableCyclicTaskABC, RestartableCyclicTaskABC ): """ - A socketcan cyclic send task supports: + A SocketCAN cyclic send task supports: - setting of a task duration - modifying the data @@ -296,24 +295,32 @@ class CyclicSendTask( """ - def __init__(self, bcm_socket, message, period, duration=None): + def __init__(self, bcm_socket, messages, period, duration=None): """ - :param bcm_socket: An open bcm socket on the desired CAN channel. - :param can.Message message: The message to be sent periodically. - :param float period: The rate in seconds at which to send the message. - :param float duration: Approximate duration in seconds to send the message. + :param bcm_socket: An open BCM socket on the desired CAN channel. + :param Union[Sequence[can.Message], can.Message] messages: + The messages to be sent periodically. + :param float period: + The rate in seconds at which to send the messages. + :param float duration: + Approximate duration in seconds to send the messages for. """ - super().__init__(message, period, duration) - self.bcm_socket = bcm_socket - self.duration = duration - self._tx_setup(message) - self.message = message + # The following are assigned by LimitedDurationCyclicSendTaskABC: + # - self.messages + # - self.period + # - self.duration + super().__init__(messages, period, duration) - def _tx_setup(self, message): + self.bcm_socket = bcm_socket + self._tx_setup(self.messages) + def _tx_setup(self, messages): # 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 + header = bytearray() + body = bytearray() + self.can_id_with_flags = _add_flags_to_can_id(messages[0]) + self.flags = CAN_FD_FRAME if messages[0].is_fd else 0 + if self.duration: count = int(self.duration / self.period) ival1 = self.period @@ -322,12 +329,19 @@ def _tx_setup(self, message): count = 0 ival1 = 0 ival2 = self.period + header = build_bcm_transmit_header( - self.can_id_with_flags, count, ival1, ival2, self.flags + self.can_id_with_flags, + count, + ival1, + ival2, + self.flags, + nframes=len(messages), ) - frame = build_can_frame(message) + for message in messages: + body += build_can_frame(message) log.debug("Sending BCM command") - send_bcm(self.bcm_socket, header + frame) + send_bcm(self.bcm_socket, header + body) def stop(self): """Send a TX_DELETE message to cancel this task. @@ -341,22 +355,35 @@ def stop(self): stopframe = build_bcm_tx_delete_header(self.can_id_with_flags, self.flags) send_bcm(self.bcm_socket, stopframe) - def modify_data(self, message): - """Update the contents of this periodically sent message. + def modify_data(self, messages): + """Update the contents of the periodically sent messages. + + Note: The messages must all have the same + :attr:`~can.Message.arbitration_id` like the first message. - Note the Message must have the same :attr:`~can.Message.arbitration_id` - like the first message. + Note: The number of new cyclic messages to be sent must be equal to the + original number of messages originally specified for this task. + + :param Union[Sequence[can.Message], can.Message] messages: + The messages with the new :attr:`can.Message.data`. """ - assert ( - message.arbitration_id == self.can_id - ), "You cannot modify the can identifier" - self.message = message - header = build_bcm_update_header(self.can_id_with_flags, self.flags) - frame = build_can_frame(message) - send_bcm(self.bcm_socket, header + frame) + messages = self._check_and_convert_messages(messages) + self._check_modified_messages(messages) + + self.messages = messages + + header = bytearray() + body = bytearray() + header = build_bcm_update_header( + can_id=self.can_id_with_flags, msg_flags=self.flags, nframes=len(messages) + ) + for message in messages: + body += build_can_frame(message) + log.debug("Sending BCM command") + send_bcm(self.bcm_socket, header + body) def start(self): - self._tx_setup(self.message) + self._tx_setup(self.messages) class MultiRateCyclicSendTask(CyclicSendTask): @@ -365,17 +392,25 @@ class MultiRateCyclicSendTask(CyclicSendTask): """ - def __init__(self, channel, message, count, initial_period, subsequent_period): - super().__init__(channel, message, subsequent_period) + def __init__(self, channel, messages, count, initial_period, subsequent_period): + super().__init__(channel, messages, subsequent_period) # Create a low level packed frame to pass to the kernel - frame = build_can_frame(message) header = build_bcm_transmit_header( - self.can_id_with_flags, count, initial_period, subsequent_period, self.flags + self.can_id_with_flags, + count, + initial_period, + subsequent_period, + self.flags, + nframes=len(messages), ) + body = bytearray() + for message in messages: + body += build_can_frame(message) + log.info("Sending BCM TX_SETUP command") - send_bcm(self.bcm_socket, header + frame) + send_bcm(self.bcm_socket, header + body) def create_socket(): @@ -599,17 +634,17 @@ def _send_once(self, data, channel=None): raise can.CanError("Failed to transmit: %s" % exc) return sent - def _send_periodic_internal(self, msg, period, duration=None): - """Start sending a message at a given period on this bus. + def _send_periodic_internal(self, msgs, period, duration=None): + """Start sending messages at a given period on this bus. - The kernel's broadcast manager will be used. + The kernel's Broadcast Manager SocketCAN API will be used. - :param can.Message msg: - Message to transmit + :param Union[Sequence[can.Message], can.Message] messages: + The messages to be sent periodically :param float period: - Period in seconds between each message + The rate in seconds at which to send the messages. :param float duration: - The duration to keep sending this message at given rate. If + Approximate duration in seconds to continue sending messages. If no duration is provided, the task will continue indefinitely. :return: @@ -619,14 +654,19 @@ def _send_periodic_internal(self, msg, period, duration=None): .. note:: - Note the duration before the message stops being sent may not + Note the duration before the messages stop 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. """ - bcm_socket = self._get_bcm_socket(msg.channel or self.channel) - task = CyclicSendTask(bcm_socket, msg, period, duration) + msgs = LimitedDurationCyclicSendTaskABC._check_and_convert_messages(msgs) + + bcm_socket = self._get_bcm_socket(msgs[0].channel or self.channel) + # TODO: The SocketCAN BCM interface treats all cyclic tasks sharing an + # Arbitration ID as the same Cyclic group. We should probably warn the + # user instead of overwriting the old group? + task = CyclicSendTask(bcm_socket, msgs, period, duration) return task def _get_bcm_socket(self, channel): diff --git a/examples/cyclic_multiple.py b/examples/cyclic_multiple.py new file mode 100644 index 000000000..ae32e7416 --- /dev/null +++ b/examples/cyclic_multiple.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This example exercises the periodic task's multiple message sending capabilities + +Expects a vcan0 interface: + + python3 -m examples.cyclic_multiple + +""" + +from __future__ import print_function + +import logging +import time + +import can + +logging.basicConfig(level=logging.INFO) + + +def cyclic_multiple_send(bus): + """ + Sends periodic messages every 1 s with no explicit timeout + Sleeps for 10 seconds then stops the task. + """ + print("Starting to send a message every 1 s for 10 s") + messages = [] + + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x33, 0x33, 0x33, 0x33, 0x33, 0x33], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x44, 0x44, 0x44, 0x44, 0x44, 0x44], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x55, 0x55, 0x55, 0x55, 0x55, 0x55], + is_extended_id=False, + ) + ) + task = bus.send_periodic(messages, 1) + assert isinstance(task, can.CyclicSendTaskABC) + time.sleep(10) + task.stop() + print("stopped cyclic send") + + +def cyclic_multiple_send_modify(bus): + """ + Sends initial set of 3 Messages containing Odd data sent every 1 s with + no explicit timeout. Sleeps for 8 s. + + Then the set is updated to 3 Messages containing Even data. + Sleeps for 10 s. + """ + messages_odd = [] + messages_odd.append( + can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + ) + messages_odd.append( + can.Message( + arbitration_id=0x401, + data=[0x33, 0x33, 0x33, 0x33, 0x33, 0x33], + is_extended_id=False, + ) + ) + messages_odd.append( + can.Message( + arbitration_id=0x401, + data=[0x55, 0x55, 0x55, 0x55, 0x55, 0x55], + is_extended_id=False, + ) + ) + messages_even = [] + messages_even.append( + can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + ) + messages_even.append( + can.Message( + arbitration_id=0x401, + data=[0x44, 0x44, 0x44, 0x44, 0x44, 0x44], + is_extended_id=False, + ) + ) + messages_even.append( + can.Message( + arbitration_id=0x401, + data=[0x66, 0x66, 0x66, 0x66, 0x66, 0x66], + is_extended_id=False, + ) + ) + print("Starting to send a message with odd every 1 s for 8 s with odd data") + task = bus.send_periodic(messages_odd, 1) + assert isinstance(task, can.CyclicSendTaskABC) + time.sleep(8) + print("Starting to send a message with even data every 1 s for 10 s with even data") + task.modify_data(messages_even) + time.sleep(10) + print("stopped cyclic modify send") + + +if __name__ == "__main__": + for interface, channel in [("socketcan", "vcan0")]: + print("Carrying out cyclic multiple tests with {} interface".format(interface)) + + bus = can.Bus(interface=interface, channel=channel, bitrate=500000) + + cyclic_multiple_send(bus) + + cyclic_multiple_send_modify(bus) + + bus.shutdown() + + time.sleep(2) diff --git a/test/test_socketcan_cyclic_multiple.py b/test/test_socketcan_cyclic_multiple.py new file mode 100644 index 000000000..ee2f0adaf --- /dev/null +++ b/test/test_socketcan_cyclic_multiple.py @@ -0,0 +1,507 @@ +""" +This module tests multiple message cyclic send tasks. +""" +import unittest + +import time +import can + +from .config import TEST_INTERFACE_SOCKETCAN + + +@unittest.skipUnless(TEST_INTERFACE_SOCKETCAN, "skip testing of socketcan") +class SocketCanCyclicMultiple(unittest.TestCase): + BITRATE = 500000 + TIMEOUT = 0.1 + + INTERFACE_1 = "socketcan" + CHANNEL_1 = "vcan0" + INTERFACE_2 = "socketcan" + CHANNEL_2 = "vcan0" + + PERIOD = 1.0 + + DELTA = 0.01 + + def _find_start_index(self, tx_messages, message): + """ + :param tx_messages: + The list of messages that were passed to the periodic backend + :param message: + The message whose data we wish to match and align to + + :returns: start index in the tx_messages + """ + start_index = -1 + for index, tx_message in enumerate(tx_messages): + if tx_message.data == message.data: + start_index = index + break + return start_index + + def setUp(self): + self._send_bus = can.Bus( + interface=self.INTERFACE_1, channel=self.CHANNEL_1, bitrate=self.BITRATE + ) + self._recv_bus = can.Bus( + interface=self.INTERFACE_2, channel=self.CHANNEL_2, bitrate=self.BITRATE + ) + + def tearDown(self): + self._send_bus.shutdown() + self._recv_bus.shutdown() + + def test_cyclic_initializer_list(self): + messages = [] + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x33, 0x33, 0x33, 0x33, 0x33, 0x33], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x44, 0x44, 0x44, 0x44, 0x44, 0x44], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x55, 0x55, 0x55, 0x55, 0x55, 0x55], + is_extended_id=False, + ) + ) + + task = self._send_bus.send_periodic(messages, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.CyclicSendTaskABC) + + results = [] + for _ in range(len(messages) * 2): + result = self._recv_bus.recv(self.PERIOD * 2) + if result: + results.append(result) + + task.stop() + + # Find starting index for each + start_index = self._find_start_index(messages, results[0]) + self.assertTrue(start_index != -1) + + # Now go through the partitioned results and assert that they're equal + for rx_index, rx_message in enumerate(results): + tx_message = messages[start_index] + + self.assertIsNotNone(rx_message) + self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id) + self.assertEqual(tx_message.dlc, rx_message.dlc) + self.assertEqual(tx_message.data, rx_message.data) + self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id) + self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame) + self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame) + self.assertEqual(tx_message.is_fd, rx_message.is_fd) + + start_index = (start_index + 1) % len(messages) + + def test_cyclic_initializer_tuple(self): + messages = [] + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x33, 0x33, 0x33, 0x33, 0x33, 0x33], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x44, 0x44, 0x44, 0x44, 0x44, 0x44], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x55, 0x55, 0x55, 0x55, 0x55, 0x55], + is_extended_id=False, + ) + ) + messages = tuple(messages) + + self.assertIsInstance(messages, tuple) + + task = self._send_bus.send_periodic(messages, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.CyclicSendTaskABC) + + results = [] + for _ in range(len(messages) * 2): + result = self._recv_bus.recv(self.PERIOD * 2) + if result: + results.append(result) + + task.stop() + + # Find starting index for each + start_index = self._find_start_index(messages, results[0]) + self.assertTrue(start_index != -1) + + # Now go through the partitioned results and assert that they're equal + for rx_index, rx_message in enumerate(results): + tx_message = messages[start_index] + + self.assertIsNotNone(rx_message) + self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id) + self.assertEqual(tx_message.dlc, rx_message.dlc) + self.assertEqual(tx_message.data, rx_message.data) + self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id) + self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame) + self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame) + self.assertEqual(tx_message.is_fd, rx_message.is_fd) + + start_index = (start_index + 1) % len(messages) + + def test_cyclic_initializer_message(self): + message = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + + task = self._send_bus.send_periodic(message, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.CyclicSendTaskABC) + + # Take advantage of kernel's queueing mechanisms + time.sleep(4 * self.PERIOD) + task.stop() + + for _ in range(4): + tx_message = message + rx_message = self._recv_bus.recv(self.TIMEOUT) + + self.assertIsNotNone(rx_message) + self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id) + self.assertEqual(tx_message.dlc, rx_message.dlc) + self.assertEqual(tx_message.data, rx_message.data) + self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id) + self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame) + self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame) + self.assertEqual(tx_message.is_fd, rx_message.is_fd) + + def test_cyclic_initializer_invalid_none(self): + with self.assertRaises(ValueError): + task = self._send_bus.send_periodic(None, self.PERIOD) + + def test_cyclic_initializer_invalid_empty_list(self): + with self.assertRaises(ValueError): + task = self._send_bus.send_periodic([], self.PERIOD) + + def test_cyclic_initializer_different_arbitration_ids(self): + messages = [] + messages.append( + can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + ) + messages.append( + can.Message( + arbitration_id=0x3E1, + data=[0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE], + is_extended_id=False, + ) + ) + with self.assertRaises(ValueError): + task = self._send_bus.send_periodic(messages, self.PERIOD) + + def test_modify_data_list(self): + messages_odd = [] + messages_odd.append( + can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + ) + messages_odd.append( + can.Message( + arbitration_id=0x401, + data=[0x33, 0x33, 0x33, 0x33, 0x33, 0x33], + is_extended_id=False, + ) + ) + messages_odd.append( + can.Message( + arbitration_id=0x401, + data=[0x55, 0x55, 0x55, 0x55, 0x55, 0x55], + is_extended_id=False, + ) + ) + messages_even = [] + messages_even.append( + can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + ) + messages_even.append( + can.Message( + arbitration_id=0x401, + data=[0x44, 0x44, 0x44, 0x44, 0x44, 0x44], + is_extended_id=False, + ) + ) + messages_even.append( + can.Message( + arbitration_id=0x401, + data=[0x66, 0x66, 0x66, 0x66, 0x66, 0x66], + is_extended_id=False, + ) + ) + + task = self._send_bus.send_periodic(messages_odd, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.ModifiableCyclicTaskABC) + + results_odd = [] + results_even = [] + for _ in range(len(messages_odd) * 2): + result = self._recv_bus.recv(self.PERIOD * 2) + if result: + results_odd.append(result) + + task.modify_data(messages_even) + for _ in range(len(messages_even) * 2): + result = self._recv_bus.recv(self.PERIOD * 2) + if result: + results_even.append(result) + + task.stop() + + # Make sure we received some messages + self.assertTrue(len(results_even) != 0) + self.assertTrue(len(results_odd) != 0) + + # Find starting index for each + start_index_even = self._find_start_index(messages_even, results_even[0]) + self.assertTrue(start_index_even != -1) + + start_index_odd = self._find_start_index(messages_odd, results_odd[0]) + self.assertTrue(start_index_odd != -1) + + # Now go through the partitioned results and assert that they're equal + for rx_index, rx_message in enumerate(results_even): + tx_message = messages_even[start_index_even] + + self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id) + self.assertEqual(tx_message.dlc, rx_message.dlc) + self.assertEqual(tx_message.data, rx_message.data) + self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id) + self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame) + self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame) + self.assertEqual(tx_message.is_fd, rx_message.is_fd) + + start_index_even = (start_index_even + 1) % len(messages_even) + + if rx_index != 0: + prev_rx_message = results_even[rx_index - 1] + # Assert timestamps are within the expected period + self.assertTrue( + abs( + (rx_message.timestamp - prev_rx_message.timestamp) - self.PERIOD + ) + <= self.DELTA + ) + + for rx_index, rx_message in enumerate(results_odd): + tx_message = messages_odd[start_index_odd] + + self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id) + self.assertEqual(tx_message.dlc, rx_message.dlc) + self.assertEqual(tx_message.data, rx_message.data) + self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id) + self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame) + self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame) + self.assertEqual(tx_message.is_fd, rx_message.is_fd) + + start_index_odd = (start_index_odd + 1) % len(messages_odd) + + if rx_index != 0: + prev_rx_message = results_odd[rx_index - 1] + # Assert timestamps are within the expected period + self.assertTrue( + abs( + (rx_message.timestamp - prev_rx_message.timestamp) - self.PERIOD + ) + <= self.DELTA + ) + + def test_modify_data_message(self): + message_odd = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + message_even = can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + task = self._send_bus.send_periodic(message_odd, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.ModifiableCyclicTaskABC) + + results_odd = [] + results_even = [] + for _ in range(1 * 4): + result = self._recv_bus.recv(self.PERIOD * 2) + if result: + results_odd.append(result) + + task.modify_data(message_even) + for _ in range(1 * 4): + result = self._recv_bus.recv(self.PERIOD * 2) + if result: + results_even.append(result) + + task.stop() + + # Now go through the partitioned results and assert that they're equal + for rx_index, rx_message in enumerate(results_even): + tx_message = message_even + + self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id) + self.assertEqual(tx_message.dlc, rx_message.dlc) + self.assertEqual(tx_message.data, rx_message.data) + self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id) + self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame) + self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame) + self.assertEqual(tx_message.is_fd, rx_message.is_fd) + + if rx_index != 0: + prev_rx_message = results_even[rx_index - 1] + # Assert timestamps are within the expected period + self.assertTrue( + abs( + (rx_message.timestamp - prev_rx_message.timestamp) - self.PERIOD + ) + <= self.DELTA + ) + + for rx_index, rx_message in enumerate(results_odd): + tx_message = message_odd + + self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id) + self.assertEqual(tx_message.dlc, rx_message.dlc) + self.assertEqual(tx_message.data, rx_message.data) + self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id) + self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame) + self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame) + self.assertEqual(tx_message.is_fd, rx_message.is_fd) + + if rx_index != 0: + prev_rx_message = results_odd[rx_index - 1] + # Assert timestamps are within the expected period + self.assertTrue( + abs( + (rx_message.timestamp - prev_rx_message.timestamp) - self.PERIOD + ) + <= self.DELTA + ) + + def test_modify_data_invalid(self): + message = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + task = self._send_bus.send_periodic(message, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.ModifiableCyclicTaskABC) + + time.sleep(2 * self.PERIOD) + + with self.assertRaises(ValueError): + task.modify_data(None) + + def test_modify_data_unequal_lengths(self): + message = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + new_messages = [] + new_messages.append( + can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + ) + new_messages.append( + can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + ) + + task = self._send_bus.send_periodic(message, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.ModifiableCyclicTaskABC) + + time.sleep(2 * self.PERIOD) + + with self.assertRaises(ValueError): + task.modify_data(new_messages) + + def test_modify_data_different_arbitration_id_than_original(self): + old_message = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + new_message = can.Message( + arbitration_id=0x3E1, + data=[0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE], + is_extended_id=False, + ) + + task = self._send_bus.send_periodic(old_message, self.PERIOD) + self.assertIsInstance(task, can.broadcastmanager.ModifiableCyclicTaskABC) + + time.sleep(2 * self.PERIOD) + + with self.assertRaises(ValueError): + task.modify_data(new_message) + + +if __name__ == "__main__": + unittest.main() From 36a8d63cbad2fc590ea66954af5c22c50350aa91 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 11 Jul 2019 07:25:49 +0200 Subject: [PATCH 132/252] Fix iteration in Bus.stop_all_periodic_tasks Modifying a list while iterating over it results in unspecified order. This is now fixed in `Bus.stop_all_periodic_tasks`. What happens when an exception is thrown while stopping a task? Do we want to catch it, log it and continue? Or do we want to leave the note on unspecified behaviour in there? Fixes #634 --- can/bus.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/can/bus.py b/can/bus.py index 9682b31af..59ddbd1d2 100644 --- a/can/bus.py +++ b/can/bus.py @@ -259,13 +259,21 @@ def _send_periodic_internal(self, msgs, period, duration=None): return task def stop_all_periodic_tasks(self, remove_tasks=True): - """Stop sending any messages that were started using bus.send_periodic + """Stop sending any messages that were started using **bus.send_periodic**. + + .. note:: + The result is undefined if a single task throws an exception while being stopped. :param bool remove_tasks: Stop tracking the stopped tasks. """ for task in self._periodic_tasks: - task.stop(remove_task=remove_tasks) + # we cannot let `task.stop()` modify `self._periodic_tasks` while we are + # iterating over it (#634) + task.stop(remove_task=False) + + if remove_tasks: + self._periodic_tasks = [] def __iter__(self): """Allow iteration on messages as they are received. From 948f141adc512a52bfa7de1647698ad0a0a0c474 Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 11 Jul 2019 10:00:11 -0700 Subject: [PATCH 133/252] Read the BCM status before creating a Task Currently bus.send_periodic blindly wraps the BCM socket API, and sends a BCM operation with the TX_SETUP opcode. However, the semantics that the kernel driver provides are an "upsert". As such, when one attempts to create two Tasks with the same CAN Arbitration ID, it ends up silently modifying the periodic messages, which is contrary to what our API implies. This fixes the problem by first sending a TX_READ opcode with the CAN Arbitration ID that we wish to create. The kernel driver will return -EINVAL if a periodic transmission with the given Arbitration ID does not exist. If this is the case, then we can continue creating the Task, otherwise we error and notify the user. Fixes #605 --- can/interfaces/socketcan/constants.py | 1 + can/interfaces/socketcan/socketcan.py | 25 +++++++++++++++++++ ...c_multiple.py => test_cyclic_socketcan.py} | 23 ++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) rename test/{test_socketcan_cyclic_multiple.py => test_cyclic_socketcan.py} (95%) diff --git a/can/interfaces/socketcan/constants.py b/can/interfaces/socketcan/constants.py index ba5100403..98bbc39d1 100644 --- a/can/interfaces/socketcan/constants.py +++ b/can/interfaces/socketcan/constants.py @@ -11,6 +11,7 @@ # BCM opcodes CAN_BCM_TX_SETUP = 1 CAN_BCM_TX_DELETE = 2 +CAN_BCM_TX_READ = 3 # BCM flags SETTIMER = 0x0001 diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index f504d1cfc..a4886722f 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -330,6 +330,31 @@ def _tx_setup(self, messages): ival1 = 0 ival2 = self.period + # First do a TX_READ before creating a new task, and check if we get + # EINVAL. If so, then we are referring to a CAN message with the same + # ID + check_header = build_bcm_header( + opcode=CAN_BCM_TX_READ, + flags=0, + count=0, + ival1_seconds=0, + ival1_usec=0, + ival2_seconds=0, + ival2_usec=0, + can_id=self.can_id_with_flags, + nframes=0, + ) + try: + self.bcm_socket.send(check_header) + except OSError as e: + assert e.errno == errno.EINVAL + else: + raise ValueError( + "A periodic Task for Arbitration ID {} has already been created".format( + messages[0].arbitration_id + ) + ) + header = build_bcm_transmit_header( self.can_id_with_flags, count, diff --git a/test/test_socketcan_cyclic_multiple.py b/test/test_cyclic_socketcan.py similarity index 95% rename from test/test_socketcan_cyclic_multiple.py rename to test/test_cyclic_socketcan.py index ee2f0adaf..bd3042b38 100644 --- a/test/test_socketcan_cyclic_multiple.py +++ b/test/test_cyclic_socketcan.py @@ -10,7 +10,7 @@ @unittest.skipUnless(TEST_INTERFACE_SOCKETCAN, "skip testing of socketcan") -class SocketCanCyclicMultiple(unittest.TestCase): +class CyclicSocketCan(unittest.TestCase): BITRATE = 500000 TIMEOUT = 0.1 @@ -244,6 +244,27 @@ def test_cyclic_initializer_different_arbitration_ids(self): with self.assertRaises(ValueError): task = self._send_bus.send_periodic(messages, self.PERIOD) + def test_create_same_id_raises_exception(self): + messages_a = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + + messages_b = can.Message( + arbitration_id=0x401, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + + task_a = self._send_bus.send_periodic(messages_a, 1) + self.assertIsInstance(task_a, can.broadcastmanager.CyclicSendTaskABC) + + # The second one raises a ValueError when we attempt to create a new + # Task, since it has the same arbitration ID. + with self.assertRaises(ValueError): + task_b = self._send_bus.send_periodic(messages_b, 1) + def test_modify_data_list(self): messages_odd = [] messages_odd.append( From 6a4da474265c4ca6a81731ee649f8da40aa816e1 Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 13 Jul 2019 14:38:59 -0700 Subject: [PATCH 134/252] Raise Exception instead of using assert on EINVAL --- can/interfaces/socketcan/socketcan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index a4886722f..b99124719 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -347,7 +347,8 @@ def _tx_setup(self, messages): try: self.bcm_socket.send(check_header) except OSError as e: - assert e.errno == errno.EINVAL + if e.errno != errno.EINVAL: + raise e else: raise ValueError( "A periodic Task for Arbitration ID {} has already been created".format( From b05d94673c8f865cdb186a8c980fdb2103a0c9d2 Mon Sep 17 00:00:00 2001 From: "Seemann Jochen (CC-DA/ECU2)" Date: Sun, 14 Jul 2019 21:38:12 +0200 Subject: [PATCH 135/252] add support for query available PCAN channels --- can/interfaces/pcan/pcan.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 80b6054d0..1e19d0e76 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -477,6 +477,27 @@ def state(self, new_state): ) + @staticmethod + def _detect_available_configs(): + libraryHandle = PCANBasic() + channels = [] + interfaces = [] + for i in range(16): + interfaces.append({'id': TPCANHandle(PCAN_PCIBUS1.value + i), 'name': 'PCAN_PCIBUS'+str(i+1)}) + for i in range(16): + interfaces.append({'id': TPCANHandle(PCAN_USBBUS1.value + i), 'name': 'PCAN_USBBUS'+str(i+1)}) + for i in range(2): + interfaces.append({'id': TPCANHandle(PCAN_PCCBUS1.value + i), 'name': 'PCAN_PCCBUS'+str(i+1)}) + for i in range(16): + interfaces.append({'id': TPCANHandle(PCAN_LANBUS1.value + i), 'name': 'PCAN_LANBUS'+str(i+1)}) + status = TPCANStatus(0) + for i in interfaces: + error, value = libraryHandle.GetValue(i['id'], PCAN_CHANNEL_CONDITION) + if error != PCAN_ERROR_OK or value != PCAN_CHANNEL_AVAILABLE: + continue + channels.append({'interface': 'pcan', 'channel': i['name']}) + return channels + class PcanError(CanError): """ A generic error on a PCAN bus. From 4ecf02e94f5d0af50b93414e5ba527475fd85b8c Mon Sep 17 00:00:00 2001 From: "Seemann Jochen (CC-DA/ECU2)" Date: Sun, 14 Jul 2019 21:49:33 +0200 Subject: [PATCH 136/252] add CAN FD support of channel in config --- can/interfaces/pcan/pcan.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 1e19d0e76..69f5b7214 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -495,7 +495,11 @@ def _detect_available_configs(): error, value = libraryHandle.GetValue(i['id'], PCAN_CHANNEL_CONDITION) if error != PCAN_ERROR_OK or value != PCAN_CHANNEL_AVAILABLE: continue - channels.append({'interface': 'pcan', 'channel': i['name']}) + hasFd = False + error, value = libraryHandle.GetValue(i['id'], PCAN_CHANNEL_FEATURES) + if error == PCAN_ERROR_OK: + hasFd = bool(value & FEATURE_FD_CAPABLE) + channels.append({'interface': 'pcan', 'channel': i['name'], 'supportsFd': hasFd}) return channels class PcanError(CanError): From dab03b71f5c9622183ef014e5c5b4595a6acd093 Mon Sep 17 00:00:00 2001 From: "Seemann Jochen (CC-DA/ECU2)" Date: Sun, 14 Jul 2019 22:21:14 +0200 Subject: [PATCH 137/252] provide CAN FD support information in config detection --- can/interfaces/vector/canlib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index a34ebe853..579336c31 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -484,6 +484,7 @@ def _detect_available_configs(): "interface": "vector", "app_name": None, "channel": channel_config.channelIndex, + 'supportsFd': bool(channel_config.channelBusCapabilities & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT)} } ) return configs From b4cfda287ffa8b6c5f97fff64663ff46c1d4e804 Mon Sep 17 00:00:00 2001 From: "Seemann Jochen (CC-DA/ECU2)" Date: Sun, 14 Jul 2019 22:23:09 +0200 Subject: [PATCH 138/252] skip channels without CAN support --- can/interfaces/vector/canlib.py | 2 ++ can/interfaces/vector/vxlapi.py | 1 + 2 files changed, 3 insertions(+) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 579336c31..215b57a05 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -474,6 +474,8 @@ def _detect_available_configs(): channel_configs = get_channel_configs() LOG.info("Found %d channels", len(channel_configs)) for channel_config in channel_configs: + if not channel_config.channelBusCapabilities & vxlapi.XL_BUS_ACTIVE_CAP_CAN: + continue LOG.info( "Channel index %d: %s", channel_config.channelIndex, diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index 5669490ac..83e9cf337 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -63,6 +63,7 @@ XL_INTERFACE_VERSION = 3 XL_INTERFACE_VERSION_V4 = 4 +XL_BUS_ACTIVE_CAP_CAN = (XL_BUS_TYPE_CAN << 16) XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT = 0x80000000 # structure for XL_RECEIVE_MSG, XL_TRANSMIT_MSG From 2868b6670ea340bfb6c3d05cb0d720f2161c0265 Mon Sep 17 00:00:00 2001 From: jsee23 Date: Mon, 15 Jul 2019 10:53:01 +0200 Subject: [PATCH 139/252] use consistent naming scheme Co-Authored-By: Felix Divo --- can/interfaces/pcan/pcan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 69f5b7214..f7da6e21a 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -499,7 +499,7 @@ def _detect_available_configs(): error, value = libraryHandle.GetValue(i['id'], PCAN_CHANNEL_FEATURES) if error == PCAN_ERROR_OK: hasFd = bool(value & FEATURE_FD_CAPABLE) - channels.append({'interface': 'pcan', 'channel': i['name'], 'supportsFd': hasFd}) + channels.append({'interface': 'pcan', 'channel': i['name'], 'supports_fd': hasFd}) return channels class PcanError(CanError): From 2c03af4ca574b0bc4566b2019cb598937943ceda Mon Sep 17 00:00:00 2001 From: jsee23 Date: Mon, 15 Jul 2019 10:55:13 +0200 Subject: [PATCH 140/252] use consistent naming scheme Co-Authored-By: Felix Divo --- can/interfaces/vector/canlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 215b57a05..3e1dd1df9 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -486,7 +486,7 @@ def _detect_available_configs(): "interface": "vector", "app_name": None, "channel": channel_config.channelIndex, - 'supportsFd': bool(channel_config.channelBusCapabilities & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT)} + 'supports_fd': bool(channel_config.channelBusCapabilities & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT)} } ) return configs From b1839f80a21e4aa4836b3fd86da19f5a30018413 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 15 Jul 2019 12:24:47 +0200 Subject: [PATCH 141/252] Document socketcan Adds some internal comments for the socketcan module. Removes a TODO that was placed there previously. --- can/interfaces/socketcan/socketcan.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index b99124719..a0ee5c595 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -1,5 +1,12 @@ # coding: utf-8 +""" +The main module of the socketcan interface containing most user-facing classes and methods +along some internal methods. + +At the end of the file the usage of the internal methods is shown. +""" + import logging import ctypes import ctypes.util @@ -726,15 +733,14 @@ def _detect_available_configs(): if __name__ == "__main__": - # TODO move below to examples? - - # Create two sockets on vcan0 to test send and receive + # This example demonstrates how to use the internal methods of this module. + # It creates two sockets on vcan0 to test sending and receiving. # # If you want to try it out you can do the following (possibly using sudo): # # modprobe vcan # ip link add dev vcan0 type vcan - # ifconfig vcan0 up + # ip link set vcan0 up # log.setLevel(logging.DEBUG) From 9f170be15ea49365bcdd6d442a7c4f84796ac301 Mon Sep 17 00:00:00 2001 From: "Seemann Jochen (CC-DA/ECU2)" Date: Mon, 15 Jul 2019 12:43:55 +0200 Subject: [PATCH 142/252] fix unvalid syntax --- can/interfaces/vector/canlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 3e1dd1df9..65b8b76eb 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -486,7 +486,7 @@ def _detect_available_configs(): "interface": "vector", "app_name": None, "channel": channel_config.channelIndex, - 'supports_fd': bool(channel_config.channelBusCapabilities & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT)} + 'supports_fd': bool(channel_config.channelBusCapabilities & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT) } ) return configs From a0362145ff6781d11aaa753779808a3bb45b076a Mon Sep 17 00:00:00 2001 From: Jochen Seemann Date: Mon, 15 Jul 2019 20:13:03 +0200 Subject: [PATCH 143/252] handle exception if library cannot be loaded --- can/interfaces/pcan/pcan.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index f7da6e21a..bbeba2e9b 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -479,8 +479,11 @@ def state(self, new_state): @staticmethod def _detect_available_configs(): - libraryHandle = PCANBasic() channels = [] + try: + libraryHandle = PCANBasic() + except: + return channels interfaces = [] for i in range(16): interfaces.append({'id': TPCANHandle(PCAN_PCIBUS1.value + i), 'name': 'PCAN_PCIBUS'+str(i+1)}) From 15264beb47a49c732c55afdbbb60bd2f1717bd59 Mon Sep 17 00:00:00 2001 From: Jochen Seemann Date: Mon, 15 Jul 2019 20:37:41 +0200 Subject: [PATCH 144/252] fix linter and formatting warnings --- can/interfaces/pcan/pcan.py | 47 +++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index bbeba2e9b..4bddfc3b1 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -476,35 +476,56 @@ def state(self, new_state): self.m_PcanHandle, PCAN_LISTEN_ONLY, PCAN_PARAMETER_ON ) - @staticmethod def _detect_available_configs(): channels = [] try: - libraryHandle = PCANBasic() - except: + library_handle = PCANBasic() + except OSError: return channels interfaces = [] for i in range(16): - interfaces.append({'id': TPCANHandle(PCAN_PCIBUS1.value + i), 'name': 'PCAN_PCIBUS'+str(i+1)}) + interfaces.append( + { + "id": TPCANHandle(PCAN_PCIBUS1.value + i), + "name": "PCAN_PCIBUS" + str(i + 1), + } + ) for i in range(16): - interfaces.append({'id': TPCANHandle(PCAN_USBBUS1.value + i), 'name': 'PCAN_USBBUS'+str(i+1)}) + interfaces.append( + { + "id": TPCANHandle(PCAN_USBBUS1.value + i), + "name": "PCAN_USBBUS" + str(i + 1), + } + ) for i in range(2): - interfaces.append({'id': TPCANHandle(PCAN_PCCBUS1.value + i), 'name': 'PCAN_PCCBUS'+str(i+1)}) + interfaces.append( + { + "id": TPCANHandle(PCAN_PCCBUS1.value + i), + "name": "PCAN_PCCBUS" + str(i + 1), + } + ) for i in range(16): - interfaces.append({'id': TPCANHandle(PCAN_LANBUS1.value + i), 'name': 'PCAN_LANBUS'+str(i+1)}) - status = TPCANStatus(0) + interfaces.append( + { + "id": TPCANHandle(PCAN_LANBUS1.value + i), + "name": "PCAN_LANBUS" + str(i + 1), + } + ) for i in interfaces: - error, value = libraryHandle.GetValue(i['id'], PCAN_CHANNEL_CONDITION) + error, value = library_handle.GetValue(i["id"], PCAN_CHANNEL_CONDITION) if error != PCAN_ERROR_OK or value != PCAN_CHANNEL_AVAILABLE: continue - hasFd = False - error, value = libraryHandle.GetValue(i['id'], PCAN_CHANNEL_FEATURES) + has_fd = False + error, value = library_handle.GetValue(i["id"], PCAN_CHANNEL_FEATURES) if error == PCAN_ERROR_OK: - hasFd = bool(value & FEATURE_FD_CAPABLE) - channels.append({'interface': 'pcan', 'channel': i['name'], 'supports_fd': hasFd}) + has_fd = bool(value & FEATURE_FD_CAPABLE) + channels.append( + {"interface": "pcan", "channel": i["name"], "supports_fd": has_fd} + ) return channels + class PcanError(CanError): """ A generic error on a PCAN bus. From 1aa0bc64033b5fff6bc88474e7d1afb52e81ee7a Mon Sep 17 00:00:00 2001 From: karl ding Date: Wed, 17 Jul 2019 08:52:49 -0700 Subject: [PATCH 145/252] Add test for stopping Cyclic Tasks with removal (#645) This increases test coverage in order to verify that Cyclic Tasks are correctly stopped when multiple tasks are active on a particular bus. The test is carried out via the SocketCAN interface. Addresses #634 --- test/test_cyclic_socketcan.py | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/test_cyclic_socketcan.py b/test/test_cyclic_socketcan.py index bd3042b38..bb5411be9 100644 --- a/test/test_cyclic_socketcan.py +++ b/test/test_cyclic_socketcan.py @@ -523,6 +523,56 @@ def test_modify_data_different_arbitration_id_than_original(self): with self.assertRaises(ValueError): task.modify_data(new_message) + def test_stop_all_periodic_tasks_and_remove_task(self): + message_a = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + message_b = can.Message( + arbitration_id=0x402, + data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22], + is_extended_id=False, + ) + message_c = can.Message( + arbitration_id=0x403, + data=[0x33, 0x33, 0x33, 0x33, 0x33, 0x33], + is_extended_id=False, + ) + + # Start Tasks + task_a = self._send_bus.send_periodic(message_a, self.PERIOD) + task_b = self._send_bus.send_periodic(message_b, self.PERIOD) + task_c = self._send_bus.send_periodic(message_c, self.PERIOD) + + self.assertIsInstance(task_a, can.broadcastmanager.ModifiableCyclicTaskABC) + self.assertIsInstance(task_b, can.broadcastmanager.ModifiableCyclicTaskABC) + self.assertIsInstance(task_c, can.broadcastmanager.ModifiableCyclicTaskABC) + + for _ in range(6): + _ = self._recv_bus.recv(self.PERIOD) + + # Stop all tasks and delete + self._send_bus.stop_all_periodic_tasks(remove_tasks=True) + + # Now wait for a few periods, after which we should definitely not + # receive any CAN messages + time.sleep(4 * self.PERIOD) + + # If we successfully deleted everything, then we will eventually read + # 0 messages. + successfully_stopped = False + for _ in range(6): + rx_message = self._recv_bus.recv(self.PERIOD) + + if rx_message is None: + successfully_stopped = True + break + self.assertTrue(successfully_stopped, "Still received messages after stopping") + + # None of the tasks should still be associated with the bus + self.assertEqual(0, len(self._send_bus._periodic_tasks)) + if __name__ == "__main__": unittest.main() From ba633096253d8feb9cd8763acb87b2458387fbf2 Mon Sep 17 00:00:00 2001 From: Jochen Seemann Date: Thu, 18 Jul 2019 21:06:26 +0200 Subject: [PATCH 146/252] fix formatter warnings --- can/interfaces/vector/canlib.py | 5 ++++- can/interfaces/vector/vxlapi.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 65b8b76eb..970994c56 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -486,7 +486,10 @@ def _detect_available_configs(): "interface": "vector", "app_name": None, "channel": channel_config.channelIndex, - 'supports_fd': bool(channel_config.channelBusCapabilities & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT) + "supports_fd": bool( + channel_config.channelBusCapabilities + & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT + ), } ) return configs diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index 01fed87e3..b631923f2 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -117,7 +117,7 @@ XL_INTERFACE_VERSION = 3 XL_INTERFACE_VERSION_V4 = 4 -XL_BUS_ACTIVE_CAP_CAN = (XL_BUS_TYPE_CAN << 16) +XL_BUS_ACTIVE_CAP_CAN = XL_BUS_TYPE_CAN << 16 XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT = 0x80000000 # structure for XL_RECEIVE_MSG, XL_TRANSMIT_MSG From 8fef6997b55ea9802c78cf8fbf86fc0f9d13568a Mon Sep 17 00:00:00 2001 From: Karl Date: Tue, 16 Jul 2019 22:46:26 -0700 Subject: [PATCH 147/252] Add mypy type-checking to Travis CI builds Currently just get a few files running under mypy without any errors. The files can incrementally be converted and guarded by CI builds, until python-can is completely PEP 561 compliant. --- .travis.yml | 2 ++ can/__init__.py | 4 +++- can/bit_timing.py | 18 +++++++++--------- can/listener.py | 2 +- can/thread_safe_bus.py | 4 ++-- requirements-lint.txt | 2 ++ 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2eca95d6d..f35a148aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,6 +84,8 @@ jobs: # warnings to the .pylintrc-wip file to prevent them from being # re-introduced - pylint --rcfile=.pylintrc-wip can/ + # mypy checking + - mypy --python-version=3.7 --ignore-missing-imports --no-implicit-optional can/bit_timing.py can/broadcastmanager.py can/bus.py can/interface.py can/listener.py can/logger.py can/message.py can/notifier.py can/player.py can/thread_safe_bus.py can/util.py - stage: linter name: "Formatting Checks" python: "3.7" diff --git a/can/__init__.py b/can/__init__.py index f134ea8af..3c1ac8d75 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -6,11 +6,13 @@ import logging +from typing import Dict, Any + __version__ = "3.2.0" log = logging.getLogger("can") -rc = dict() +rc: Dict[str, Any] = dict() class CanError(IOError): diff --git a/can/bit_timing.py b/can/bit_timing.py index 8d9cf5727..b0ad762fb 100644 --- a/can/bit_timing.py +++ b/can/bit_timing.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Optional, Union class BitTiming: @@ -27,15 +27,15 @@ class BitTiming: def __init__( self, - bitrate: int = None, - f_clock: int = None, - brp: int = None, - tseg1: int = None, - tseg2: int = None, - sjw: int = None, + bitrate: Optional[int] = None, + f_clock: Optional[int] = None, + brp: Optional[int] = None, + tseg1: Optional[int] = None, + tseg2: Optional[int] = None, + sjw: Optional[int] = None, nof_samples: int = 1, - btr0: int = None, - btr1: int = None, + btr0: Optional[int] = None, + btr1: Optional[int] = None, ): """ :param int bitrate: diff --git a/can/listener.py b/can/listener.py index 2f773fcf0..ac0f1aa64 100644 --- a/can/listener.py +++ b/can/listener.py @@ -11,7 +11,7 @@ from queue import SimpleQueue, Empty except ImportError: # Python 3.0 - 3.6 - from queue import Queue as SimpleQueue, Empty + from queue import Queue as SimpleQueue, Empty # type: ignore import asyncio diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 91f7e6d2c..9f6346fb8 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -16,11 +16,11 @@ try: - from contextlib import nullcontext + from contextlib import nullcontext # type: ignore except ImportError: - class nullcontext: + class nullcontext: # type: ignore """A context manager that does nothing at all. A fallback for Python 3.7's :class:`contextlib.nullcontext` manager. """ diff --git a/requirements-lint.txt b/requirements-lint.txt index 6a81fe2eb..ce953e68b 100644 --- a/requirements-lint.txt +++ b/requirements-lint.txt @@ -1,2 +1,4 @@ pylint==2.3.1 black==19.3b0 +mypy==0.720 +mypy-extensions==0.4.1 From acda99212bb373e0ca70e9d969db788c19bee5e0 Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Tue, 23 Jul 2019 01:11:21 +0200 Subject: [PATCH 148/252] refactoring and tests for vector interface (#646) * add tests for vector interface * fixed WaitForSingleObject with Mock, formatted with black * catch ctypes.windll exception to read ctypes structures add side_effects to mocks * add xlSetNotification mock for windows testing * Rename vxlapi.py to XLDriver.py * refactored vxlapi to XLDefine (enums of constants), XLClass (data types and structures) and XLDriver (api functions) * renamed modules for PEP8 conformity, removed unnecessary lines from test_vector.py --- can/interfaces/vector/canlib.py | 193 +++++++----- can/interfaces/vector/vxlapi.py | 486 ------------------------------ can/interfaces/vector/xlclass.py | 226 ++++++++++++++ can/interfaces/vector/xldefine.py | 172 +++++++++++ can/interfaces/vector/xldriver.py | 224 ++++++++++++++ test/test_vector.py | 301 ++++++++++++++++++ 6 files changed, 1041 insertions(+), 561 deletions(-) delete mode 100644 can/interfaces/vector/vxlapi.py create mode 100644 can/interfaces/vector/xlclass.py create mode 100644 can/interfaces/vector/xldefine.py create mode 100644 can/interfaces/vector/xldriver.py create mode 100644 test/test_vector.py diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 970994c56..5dc44c4e0 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -37,10 +37,14 @@ # ==================== LOG = logging.getLogger(__name__) +# Import Vector API module +# ======================== +from . import xldefine, xlclass + # Import safely Vector API module for Travis tests -vxlapi = None +xldriver = None try: - from . import vxlapi + from . import xldriver except Exception as exc: LOG.warning("Could not import vxlapi: %s", exc) @@ -93,7 +97,7 @@ def __init__( Which bitrate to use for data phase in CAN FD. Defaults to arbitration bitrate. """ - if vxlapi is None: + if xldriver is None: raise ImportError("The Vector API has not been loaded") self.poll_interval = poll_interval if isinstance(channel, (list, tuple)): @@ -129,8 +133,8 @@ def __init__( "None of the configured channels could be found on the specified hardware." ) - vxlapi.xlOpenDriver() - self.port_handle = vxlapi.XLportHandle(vxlapi.XL_INVALID_PORTHANDLE) + xldriver.xlOpenDriver() + self.port_handle = xlclass.XLportHandle(xldefine.XL_INVALID_PORTHANDLE) self.mask = 0 self.fd = fd # Get channels masks @@ -143,16 +147,16 @@ def __init__( hw_type = ctypes.c_uint(0) hw_index = ctypes.c_uint(0) hw_channel = ctypes.c_uint(0) - vxlapi.xlGetApplConfig( + xldriver.xlGetApplConfig( self._app_name, channel, hw_type, hw_index, hw_channel, - vxlapi.XL_BUS_TYPE_CAN, + xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value, ) LOG.debug("Channel index %d found", channel) - idx = vxlapi.xlGetChannelIndex( + idx = xldriver.xlGetChannelIndex( hw_type.value, hw_index.value, hw_channel.value ) if idx < 0: @@ -161,7 +165,7 @@ def __init__( # Raise an exception as if the driver # would have signalled XL_ERR_HW_NOT_PRESENT. raise VectorError( - vxlapi.XL_ERR_HW_NOT_PRESENT, + xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT.value, "XL_ERR_HW_NOT_PRESENT", "xlGetChannelIndex", ) @@ -173,29 +177,29 @@ def __init__( self.index_to_channel[idx] = channel self.mask |= mask - permission_mask = vxlapi.XLaccess() + permission_mask = xlclass.XLaccess() # Set mask to request channel init permission if needed if bitrate or fd: permission_mask.value = self.mask if fd: - vxlapi.xlOpenPort( + xldriver.xlOpenPort( self.port_handle, self._app_name, self.mask, permission_mask, rx_queue_size, - vxlapi.XL_INTERFACE_VERSION_V4, - vxlapi.XL_BUS_TYPE_CAN, + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4.value, + xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value, ) else: - vxlapi.xlOpenPort( + xldriver.xlOpenPort( self.port_handle, self._app_name, self.mask, permission_mask, rx_queue_size, - vxlapi.XL_INTERFACE_VERSION, - vxlapi.XL_BUS_TYPE_CAN, + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION.value, + xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value, ) LOG.debug( "Open Port: PortHandle: %d, PermissionMask: 0x%X", @@ -205,7 +209,7 @@ def __init__( if permission_mask.value == self.mask: if fd: - self.canFdConf = vxlapi.XLcanFdConf() + self.canFdConf = xlclass.XLcanFdConf() if bitrate: self.canFdConf.arbitrationBitRate = ctypes.c_uint(bitrate) else: @@ -221,7 +225,7 @@ def __init__( self.canFdConf.tseg1Dbr = ctypes.c_uint(tseg1Dbr) self.canFdConf.tseg2Dbr = ctypes.c_uint(tseg2Dbr) - vxlapi.xlCanFdSetConfiguration( + xldriver.xlCanFdSetConfiguration( self.port_handle, self.mask, self.canFdConf ) LOG.info( @@ -243,7 +247,7 @@ def __init__( ) else: if bitrate: - vxlapi.xlCanSetChannelBitrate( + xldriver.xlCanSetChannelBitrate( self.port_handle, permission_mask, bitrate ) LOG.info("SetChannelBitrate: baudr.=%u", bitrate) @@ -252,25 +256,28 @@ def __init__( # Enable/disable TX receipts tx_receipts = 1 if receive_own_messages else 0 - vxlapi.xlCanSetChannelMode(self.port_handle, self.mask, tx_receipts, 0) + xldriver.xlCanSetChannelMode(self.port_handle, self.mask, tx_receipts, 0) if HAS_EVENTS: - self.event_handle = vxlapi.XLhandle() - vxlapi.xlSetNotification(self.port_handle, self.event_handle, 1) + self.event_handle = xlclass.XLhandle() + xldriver.xlSetNotification(self.port_handle, self.event_handle, 1) else: LOG.info("Install pywin32 to avoid polling") try: - vxlapi.xlActivateChannel( - self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0 + xldriver.xlActivateChannel( + self.port_handle, + self.mask, + xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value, + 0, ) except VectorError: self.shutdown() raise # Calculate time offset for absolute timestamps - offset = vxlapi.XLuint64() - vxlapi.xlGetSyncTime(self.port_handle, offset) + offset = xlclass.XLuint64() + xldriver.xlGetSyncTime(self.port_handle, offset) self._time_offset = time.time() - offset.value * 1e-9 self._is_filtered = False @@ -285,14 +292,14 @@ def _apply_filters(self, filters): ): try: for can_filter in filters: - vxlapi.xlCanSetChannelAcceptance( + xldriver.xlCanSetChannelAcceptance( self.port_handle, self.mask, can_filter["can_id"], can_filter["can_mask"], - vxlapi.XL_CAN_EXT + xldefine.XL_AcceptanceFilter.XL_CAN_EXT.value if can_filter.get("extended") - else vxlapi.XL_CAN_STD, + else xldefine.XL_AcceptanceFilter.XL_CAN_STD.value, ) except VectorError as exc: LOG.warning("Could not set filters: %s", exc) @@ -307,11 +314,19 @@ def _apply_filters(self, filters): # fallback: reset filters self._is_filtered = False try: - vxlapi.xlCanSetChannelAcceptance( - self.port_handle, self.mask, 0x0, 0x0, vxlapi.XL_CAN_EXT + xldriver.xlCanSetChannelAcceptance( + self.port_handle, + self.mask, + 0x0, + 0x0, + xldefine.XL_AcceptanceFilter.XL_CAN_EXT.value, ) - vxlapi.xlCanSetChannelAcceptance( - self.port_handle, self.mask, 0x0, 0x0, vxlapi.XL_CAN_STD + xldriver.xlCanSetChannelAcceptance( + self.port_handle, + self.mask, + 0x0, + 0x0, + xldefine.XL_AcceptanceFilter.XL_CAN_STD.value, ) except VectorError as exc: LOG.warning("Could not reset filters: %s", exc) @@ -320,22 +335,24 @@ def _recv_internal(self, timeout): end_time = time.time() + timeout if timeout is not None else None if self.fd: - event = vxlapi.XLcanRxEvent() + event = xlclass.XLcanRxEvent() else: - event = vxlapi.XLevent() + event = xlclass.XLevent() event_count = ctypes.c_uint() while True: if self.fd: try: - vxlapi.xlCanReceive(self.port_handle, event) + xldriver.xlCanReceive(self.port_handle, event) except VectorError as exc: - if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY: + if exc.error_code != xldefine.XL_Status.XL_ERR_QUEUE_IS_EMPTY.value: raise else: if ( - event.tag == vxlapi.XL_CAN_EV_TAG_RX_OK - or event.tag == vxlapi.XL_CAN_EV_TAG_TX_OK + event.tag + == xldefine.XL_CANFD_RX_EventTags.XL_CAN_EV_TAG_RX_OK.value + or event.tag + == xldefine.XL_CANFD_RX_EventTags.XL_CAN_EV_TAG_TX_OK.value ): msg_id = event.tagData.canRxOkMsg.canId dlc = dlc2len(event.tagData.canRxOkMsg.dlc) @@ -345,14 +362,30 @@ def _recv_internal(self, timeout): msg = Message( timestamp=timestamp + self._time_offset, arbitration_id=msg_id & 0x1FFFFFFF, - is_extended_id=bool(msg_id & vxlapi.XL_CAN_EXT_MSG_ID), - is_remote_frame=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_RTR), - is_error_frame=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_EF), - is_fd=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_EDL), + is_extended_id=bool( + msg_id + & xldefine.XL_MessageFlagsExtended.XL_CAN_EXT_MSG_ID.value + ), + is_remote_frame=bool( + flags + & xldefine.XL_CANFD_RX_MessageFlags.XL_CAN_RXMSG_FLAG_RTR.value + ), + is_error_frame=bool( + flags + & xldefine.XL_CANFD_RX_MessageFlags.XL_CAN_RXMSG_FLAG_EF.value + ), + is_fd=bool( + flags + & xldefine.XL_CANFD_RX_MessageFlags.XL_CAN_RXMSG_FLAG_EDL.value + ), error_state_indicator=bool( - flags & vxlapi.XL_CAN_RXMSG_FLAG_ESI + flags + & xldefine.XL_CANFD_RX_MessageFlags.XL_CAN_RXMSG_FLAG_ESI.value + ), + bitrate_switch=bool( + flags + & xldefine.XL_CANFD_RX_MessageFlags.XL_CAN_RXMSG_FLAG_BRS.value ), - bitrate_switch=bool(flags & vxlapi.XL_CAN_RXMSG_FLAG_BRS), dlc=dlc, data=event.tagData.canRxOkMsg.data[:dlc], channel=channel, @@ -361,12 +394,12 @@ def _recv_internal(self, timeout): else: event_count.value = 1 try: - vxlapi.xlReceive(self.port_handle, event_count, event) + xldriver.xlReceive(self.port_handle, event_count, event) except VectorError as exc: - if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY: + if exc.error_code != xldefine.XL_Status.XL_ERR_QUEUE_IS_EMPTY.value: raise else: - if event.tag == vxlapi.XL_RECEIVE_MSG: + if event.tag == xldefine.XL_EventTags.XL_RECEIVE_MSG.value: msg_id = event.tagData.msg.id dlc = event.tagData.msg.dlc flags = event.tagData.msg.flags @@ -375,12 +408,17 @@ def _recv_internal(self, timeout): msg = Message( timestamp=timestamp + self._time_offset, arbitration_id=msg_id & 0x1FFFFFFF, - is_extended_id=bool(msg_id & vxlapi.XL_CAN_EXT_MSG_ID), + is_extended_id=bool( + msg_id + & xldefine.XL_MessageFlagsExtended.XL_CAN_EXT_MSG_ID.value + ), is_remote_frame=bool( - flags & vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME + flags + & xldefine.XL_MessageFlags.XL_CAN_MSG_FLAG_REMOTE_FRAME.value ), is_error_frame=bool( - flags & vxlapi.XL_CAN_MSG_FLAG_ERROR_FRAME + flags + & xldefine.XL_MessageFlags.XL_CAN_MSG_FLAG_ERROR_FRAME.value ), is_fd=False, dlc=dlc, @@ -408,7 +446,7 @@ def send(self, msg, timeout=None): msg_id = msg.arbitration_id if msg.is_extended_id: - msg_id |= vxlapi.XL_CAN_EXT_MSG_ID + msg_id |= xldefine.XL_MessageFlagsExtended.XL_CAN_EXT_MSG_ID.value flags = 0 @@ -418,17 +456,17 @@ def send(self, msg, timeout=None): if self.fd: if msg.is_fd: - flags |= vxlapi.XL_CAN_TXMSG_FLAG_EDL + flags |= xldefine.XL_CANFD_TX_MessageFlags.XL_CAN_TXMSG_FLAG_EDL.value if msg.bitrate_switch: - flags |= vxlapi.XL_CAN_TXMSG_FLAG_BRS + flags |= xldefine.XL_CANFD_TX_MessageFlags.XL_CAN_TXMSG_FLAG_BRS.value if msg.is_remote_frame: - flags |= vxlapi.XL_CAN_TXMSG_FLAG_RTR + flags |= xldefine.XL_CANFD_TX_MessageFlags.XL_CAN_TXMSG_FLAG_RTR.value message_count = 1 MsgCntSent = ctypes.c_uint(1) - XLcanTxEvent = vxlapi.XLcanTxEvent() - XLcanTxEvent.tag = vxlapi.XL_CAN_EV_TAG_TX_MSG + XLcanTxEvent = xlclass.XLcanTxEvent() + XLcanTxEvent.tag = xldefine.XL_CANFD_TX_EventTags.XL_CAN_EV_TAG_TX_MSG.value XLcanTxEvent.transId = 0xFFFF XLcanTxEvent.tagData.canMsg.canId = msg_id @@ -436,37 +474,39 @@ def send(self, msg, timeout=None): XLcanTxEvent.tagData.canMsg.dlc = len2dlc(msg.dlc) for idx, value in enumerate(msg.data): XLcanTxEvent.tagData.canMsg.data[idx] = value - vxlapi.xlCanTransmitEx( + xldriver.xlCanTransmitEx( self.port_handle, mask, message_count, MsgCntSent, XLcanTxEvent ) else: if msg.is_remote_frame: - flags |= vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME + flags |= xldefine.XL_MessageFlags.XL_CAN_MSG_FLAG_REMOTE_FRAME.value message_count = ctypes.c_uint(1) - xl_event = vxlapi.XLevent() - xl_event.tag = vxlapi.XL_TRANSMIT_MSG + xl_event = xlclass.XLevent() + xl_event.tag = xldefine.XL_EventTags.XL_TRANSMIT_MSG.value 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, mask, message_count, xl_event) + xldriver.xlCanTransmit(self.port_handle, mask, message_count, xl_event) def flush_tx_buffer(self): - vxlapi.xlCanFlushTransmitQueue(self.port_handle, self.mask) + xldriver.xlCanFlushTransmitQueue(self.port_handle, self.mask) def shutdown(self): - vxlapi.xlDeactivateChannel(self.port_handle, self.mask) - vxlapi.xlClosePort(self.port_handle) - vxlapi.xlCloseDriver() + xldriver.xlDeactivateChannel(self.port_handle, self.mask) + xldriver.xlClosePort(self.port_handle) + xldriver.xlCloseDriver() def reset(self): - vxlapi.xlDeactivateChannel(self.port_handle, self.mask) - vxlapi.xlActivateChannel(self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0) + xldriver.xlDeactivateChannel(self.port_handle, self.mask) + xldriver.xlActivateChannel( + self.port_handle, self.mask, xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value, 0 + ) @staticmethod def _detect_available_configs(): @@ -474,7 +514,10 @@ def _detect_available_configs(): channel_configs = get_channel_configs() LOG.info("Found %d channels", len(channel_configs)) for channel_config in channel_configs: - if not channel_config.channelBusCapabilities & vxlapi.XL_BUS_ACTIVE_CAP_CAN: + if ( + not channel_config.channelBusCapabilities + & xldefine.XL_BusCapabilities.XL_BUS_ACTIVE_CAP_CAN.value + ): continue LOG.info( "Channel index %d: %s", @@ -488,7 +531,7 @@ def _detect_available_configs(): "channel": channel_config.channelIndex, "supports_fd": bool( channel_config.channelBusCapabilities - & vxlapi.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT + & xldefine.XL_ChannelCapabilities.XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT.value ), } ) @@ -496,13 +539,13 @@ def _detect_available_configs(): def get_channel_configs(): - if vxlapi is None: + if xldriver is None: return [] - driver_config = vxlapi.XLdriverConfig() + driver_config = xlclass.XLdriverConfig() try: - vxlapi.xlOpenDriver() - vxlapi.xlGetDriverConfig(driver_config) - vxlapi.xlCloseDriver() + xldriver.xlOpenDriver() + xldriver.xlGetDriverConfig(driver_config) + xldriver.xlCloseDriver() except Exception: pass return [driver_config.channel[i] for i in range(driver_config.channelCount)] diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py deleted file mode 100644 index b631923f2..000000000 --- a/can/interfaces/vector/vxlapi.py +++ /dev/null @@ -1,486 +0,0 @@ -# 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_ERR_HW_NOT_PRESENT = 129 - -# XLeventTag -# Common and CAN events -XL_NO_COMMAND = 0 -XL_RECEIVE_MSG = 1 -XL_CHIP_STATE = 4 -XL_TRANSCEIVER = 6 -XL_TIMER = 8 -XL_TRANSMIT_MSG = 10 -XL_SYNC_PULSE = 11 -XL_APPLICATION_NOTIFICATION = 15 - -# CAN/CAN-FD event tags -# Rx -XL_CAN_EV_TAG_RX_OK = 0x0400 -XL_CAN_EV_TAG_RX_ERROR = 0x0401 -XL_CAN_EV_TAG_TX_ERROR = 0x0402 -XL_CAN_EV_TAG_TX_REQUEST = 0x0403 -XL_CAN_EV_TAG_TX_OK = 0x0404 -XL_CAN_EV_TAG_CHIP_STATE = 0x0409 -# Tx -XL_CAN_EV_TAG_TX_MSG = 0x0440 - -# s_xl_can_msg : id -XL_CAN_EXT_MSG_ID = 0x80000000 - -# s_xl_can_msg : flags -XL_CAN_MSG_FLAG_ERROR_FRAME = 0x01 -XL_CAN_MSG_FLAG_REMOTE_FRAME = 0x10 -XL_CAN_MSG_FLAG_TX_COMPLETED = 0x40 - -# to be used with -# XLcanTxEvent::XL_CAN_TX_MSG::msgFlags -XL_CAN_TXMSG_FLAG_EDL = 0x0001 -XL_CAN_TXMSG_FLAG_BRS = 0x0002 -XL_CAN_TXMSG_FLAG_RTR = 0x0010 -XL_CAN_TXMSG_FLAG_HIGHPRIO = 0x0080 -XL_CAN_TXMSG_FLAG_WAKEUP = 0x0200 - -# to be used with -# XLcanRxEvent::XL_CAN_EV_RX_MSG::msgFlags -# XLcanRxEvent::XL_CAN_EV_TX_REQUEST::msgFlags -# XLcanRxEvent::XL_CAN_EV_RX_MSG::msgFlags -# XLcanRxEvent::XL_CAN_EV_TX_REMOVED::msgFlags -# XLcanRxEvent::XL_CAN_EV_ERROR::msgFlags -XL_CAN_RXMSG_FLAG_EDL = 0x0001 -XL_CAN_RXMSG_FLAG_BRS = 0x0002 -XL_CAN_RXMSG_FLAG_ESI = 0x0004 -XL_CAN_RXMSG_FLAG_RTR = 0x0010 -XL_CAN_RXMSG_FLAG_EF = 0x0200 -XL_CAN_RXMSG_FLAG_ARB_LOST = 0x0400 -XL_CAN_RXMSG_FLAG_WAKEUP = 0x2000 -XL_CAN_RXMSG_FLAG_TE = 0x4000 - -# acceptance filter -XL_CAN_STD = 1 -XL_CAN_EXT = 2 - -# s_xl_chip_state : busStatus -XL_CHIPSTAT_BUSOFF = 0x01 -XL_CHIPSTAT_ERROR_PASSIVE = 0x02 -XL_CHIPSTAT_ERROR_WARNING = 0x04 -XL_CHIPSTAT_ERROR_ACTIVE = 0x08 - -# s_xl_can_ev_error : errorCode -XL_CAN_ERRC_BIT_ERROR = 1 -XL_CAN_ERRC_FORM_ERROR = 2 -XL_CAN_ERRC_STUFF_ERROR = 3 -XL_CAN_ERRC_OTHER_ERROR = 4 -XL_CAN_ERRC_CRC_ERROR = 5 -XL_CAN_ERRC_ACK_ERROR = 6 -XL_CAN_ERRC_NACK_ERROR = 7 -XL_CAN_ERRC_OVLD_ERROR = 8 -XL_CAN_ERRC_EXCPT_ERROR = 9 - -XLuint64 = ctypes.c_int64 -XLaccess = XLuint64 -XLhandle = ctypes.c_void_p - -MAX_MSG_LEN = 8 - -# CAN / CAN-FD types and definitions -XL_CAN_MAX_DATA_LEN = 64 -XL_CANFD_RX_EVENT_HEADER_SIZE = 32 -XL_CANFD_MAX_EVENT_SIZE = 128 - -# current version -XL_INTERFACE_VERSION = 3 -XL_INTERFACE_VERSION_V4 = 4 - -XL_BUS_ACTIVE_CAP_CAN = XL_BUS_TYPE_CAN << 16 -XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT = 0x80000000 - -# 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), - ] - - -class s_xl_can_ev_error(ctypes.Structure): - _fields_ = [("errorCode", ctypes.c_ubyte), ("reserved", ctypes.c_ubyte * 95)] - - -class s_xl_can_ev_chip_state(ctypes.Structure): - _fields_ = [ - ("busStatus", ctypes.c_ubyte), - ("txErrorCounter", ctypes.c_ubyte), - ("rxErrorCounter", ctypes.c_ubyte), - ("reserved", ctypes.c_ubyte), - ("reserved0", ctypes.c_uint), - ] - - -class s_xl_can_ev_sync_pulse(ctypes.Structure): - _fields_ = [ - ("triggerSource", ctypes.c_uint), - ("reserved", ctypes.c_uint), - ("time", XLuint64), - ] - - -# BASIC bus message structure -class s_xl_tag_data(ctypes.Union): - _fields_ = [("msg", s_xl_can_msg)] - - -# CAN FD messages -class s_xl_can_ev_rx_msg(ctypes.Structure): - _fields_ = [ - ("canId", ctypes.c_uint), - ("msgFlags", ctypes.c_uint), - ("crc", ctypes.c_uint), - ("reserved1", ctypes.c_ubyte * 12), - ("totalBitCnt", ctypes.c_ushort), - ("dlc", ctypes.c_ubyte), - ("reserved", ctypes.c_ubyte * 5), - ("data", ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN), - ] - - -class s_xl_can_ev_tx_request(ctypes.Structure): - _fields_ = [ - ("canId", ctypes.c_uint), - ("msgFlags", ctypes.c_uint), - ("dlc", ctypes.c_ubyte), - ("txAttemptConf", ctypes.c_ubyte), - ("reserved", ctypes.c_ushort), - ("data", ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN), - ] - - -class s_xl_can_tx_msg(ctypes.Structure): - _fields_ = [ - ("canId", ctypes.c_uint), - ("msgFlags", ctypes.c_uint), - ("dlc", ctypes.c_ubyte), - ("reserved", ctypes.c_ubyte * 7), - ("data", ctypes.c_ubyte * XL_CAN_MAX_DATA_LEN), - ] - - -class s_rxTagData(ctypes.Union): - _fields_ = [ - ("canRxOkMsg", s_xl_can_ev_rx_msg), - ("canTxOkMsg", s_xl_can_ev_rx_msg), - ("canTxRequest", s_xl_can_ev_tx_request), - ("canError", s_xl_can_ev_error), - ("canChipState", s_xl_can_ev_chip_state), - ("canSyncPulse", s_xl_can_ev_sync_pulse), - ] - - -class s_txTagData(ctypes.Union): - _fields_ = [("canMsg", s_xl_can_tx_msg)] - - -# BASIC events -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), - ] - - -# CAN FD events -class XLcanRxEvent(ctypes.Structure): - _fields_ = [ - ("size", ctypes.c_int), - ("tag", ctypes.c_ushort), - ("chanIndex", ctypes.c_ubyte), - ("reserved", ctypes.c_ubyte), - ("userHandle", ctypes.c_int), - ("flagsChip", ctypes.c_ushort), - ("reserved0", ctypes.c_ushort), - ("reserved1", XLuint64), - ("timeStamp", XLuint64), - ("tagData", s_rxTagData), - ] - - -class XLcanTxEvent(ctypes.Structure): - _fields_ = [ - ("tag", ctypes.c_ushort), - ("transId", ctypes.c_ushort), - ("chanIndex", ctypes.c_ubyte), - ("reserved", ctypes.c_ubyte * 3), - ("tagData", s_txTagData), - ] - - -# CAN FD configuration structure -class XLcanFdConf(ctypes.Structure): - _fields_ = [ - ("arbitrationBitRate", ctypes.c_uint), - ("sjwAbr", ctypes.c_uint), - ("tseg1Abr", ctypes.c_uint), - ("tseg2Abr", ctypes.c_uint), - ("dataBitRate", ctypes.c_uint), - ("sjwDbr", ctypes.c_uint), - ("tseg1Dbr", ctypes.c_uint), - ("tseg2Dbr", ctypes.c_uint), - ("reserved", ctypes.c_uint * 2), - ] - - -class XLchannelConfig(ctypes.Structure): - _pack_ = 1 - _fields_ = [ - ("name", ctypes.c_char * 32), - ("hwType", ctypes.c_ubyte), - ("hwIndex", ctypes.c_ubyte), - ("hwChannel", ctypes.c_ubyte), - ("transceiverType", ctypes.c_ushort), - ("transceiverState", ctypes.c_ushort), - ("configError", ctypes.c_ushort), - ("channelIndex", ctypes.c_ubyte), - ("channelMask", XLuint64), - ("channelCapabilities", ctypes.c_uint), - ("channelBusCapabilities", ctypes.c_uint), - ("isOnBus", ctypes.c_ubyte), - ("connectedBusType", ctypes.c_uint), - ("busParams", ctypes.c_ubyte * 32), - ("_doNotUse", ctypes.c_uint), - ("driverVersion", ctypes.c_uint), - ("interfaceVersion", ctypes.c_uint), - ("raw_data", ctypes.c_uint * 10), - ("serialNumber", ctypes.c_uint), - ("articleNumber", ctypes.c_uint), - ("transceiverName", ctypes.c_char * 32), - ("specialCabFlags", ctypes.c_uint), - ("dominantTimeout", ctypes.c_uint), - ("dominantRecessiveDelay", ctypes.c_ubyte), - ("recessiveDominantDelay", ctypes.c_ubyte), - ("connectionInfo", ctypes.c_ubyte), - ("currentlyAvailableTimestamps", ctypes.c_ubyte), - ("minimalSupplyVoltage", ctypes.c_ushort), - ("maximalSupplyVoltage", ctypes.c_ushort), - ("maximalBaudrate", ctypes.c_uint), - ("fpgaCoreCapabilities", ctypes.c_ubyte), - ("specialDeviceStatus", ctypes.c_ubyte), - ("channelBusActiveCapabilities", ctypes.c_ushort), - ("breakOffset", ctypes.c_ushort), - ("delimiterOffset", ctypes.c_ushort), - ("reserved", ctypes.c_uint * 3), - ] - - -class XLdriverConfig(ctypes.Structure): - _fields_ = [ - ("dllVersion", ctypes.c_uint), - ("channelCount", ctypes.c_uint), - ("reserved", ctypes.c_uint * 10), - ("channel", XLchannelConfig * 64), - ] - - -# 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(), function.__name__) - return result - - -xlGetDriverConfig = _xlapi_dll.xlGetDriverConfig -xlGetDriverConfig.argtypes = [ctypes.POINTER(XLdriverConfig)] -xlGetDriverConfig.restype = XLstatus -xlGetDriverConfig.errcheck = check_status - -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 - -xlSetNotification = _xlapi_dll.xlSetNotification -xlSetNotification.argtypes = [XLportHandle, ctypes.POINTER(XLhandle), ctypes.c_int] -xlSetNotification.restype = XLstatus -xlSetNotification.errcheck = check_status - -xlCanSetChannelMode = _xlapi_dll.xlCanSetChannelMode -xlCanSetChannelMode.argtypes = [XLportHandle, XLaccess, ctypes.c_int, ctypes.c_int] -xlCanSetChannelMode.restype = XLstatus -xlCanSetChannelMode.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 - -xlCanFdSetConfiguration = _xlapi_dll.xlCanFdSetConfiguration -xlCanFdSetConfiguration.argtypes = [XLportHandle, XLaccess, ctypes.POINTER(XLcanFdConf)] -xlCanFdSetConfiguration.restype = XLstatus -xlCanFdSetConfiguration.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 - -xlCanReceive = _xlapi_dll.xlCanReceive -xlCanReceive.argtypes = [XLportHandle, ctypes.POINTER(XLcanRxEvent)] -xlCanReceive.restype = XLstatus -xlCanReceive.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 - -xlCanTransmitEx = _xlapi_dll.xlCanTransmitEx -xlCanTransmitEx.argtypes = [ - XLportHandle, - XLaccess, - ctypes.c_uint, - ctypes.POINTER(ctypes.c_uint), - ctypes.POINTER(XLcanTxEvent), -] -xlCanTransmitEx.restype = XLstatus -xlCanTransmitEx.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 - -xlCanRequestChipState = _xlapi_dll.xlCanRequestChipState -xlCanRequestChipState.argtypes = [XLportHandle, XLaccess] -xlCanRequestChipState.restype = XLstatus -xlCanRequestChipState.errcheck = check_status diff --git a/can/interfaces/vector/xlclass.py b/can/interfaces/vector/xlclass.py new file mode 100644 index 000000000..90fc611bd --- /dev/null +++ b/can/interfaces/vector/xlclass.py @@ -0,0 +1,226 @@ +# coding: utf-8 + +""" +Definition of data types and structures for vxlapi. + +Authors: Julien Grave , Christian Sandberg +""" + +# Import Standard Python Modules +# ============================== +import ctypes + +# Vector XL API Definitions +# ========================= +from . import xldefine + +XLuint64 = ctypes.c_int64 +XLaccess = XLuint64 +XLhandle = ctypes.c_void_p +XLstatus = ctypes.c_short +XLportHandle = ctypes.c_long +XLeventTag = ctypes.c_ubyte + +# 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 * xldefine.MAX_MSG_LEN), + ("res2", XLuint64), + ] + + +class s_xl_can_ev_error(ctypes.Structure): + _fields_ = [("errorCode", ctypes.c_ubyte), ("reserved", ctypes.c_ubyte * 95)] + + +class s_xl_can_ev_chip_state(ctypes.Structure): + _fields_ = [ + ("busStatus", ctypes.c_ubyte), + ("txErrorCounter", ctypes.c_ubyte), + ("rxErrorCounter", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte), + ("reserved0", ctypes.c_uint), + ] + + +class s_xl_can_ev_sync_pulse(ctypes.Structure): + _fields_ = [ + ("triggerSource", ctypes.c_uint), + ("reserved", ctypes.c_uint), + ("time", XLuint64), + ] + + +# BASIC bus message structure +class s_xl_tag_data(ctypes.Union): + _fields_ = [("msg", s_xl_can_msg)] + + +# CAN FD messages +class s_xl_can_ev_rx_msg(ctypes.Structure): + _fields_ = [ + ("canId", ctypes.c_uint), + ("msgFlags", ctypes.c_uint), + ("crc", ctypes.c_uint), + ("reserved1", ctypes.c_ubyte * 12), + ("totalBitCnt", ctypes.c_ushort), + ("dlc", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte * 5), + ("data", ctypes.c_ubyte * xldefine.XL_CAN_MAX_DATA_LEN), + ] + + +class s_xl_can_ev_tx_request(ctypes.Structure): + _fields_ = [ + ("canId", ctypes.c_uint), + ("msgFlags", ctypes.c_uint), + ("dlc", ctypes.c_ubyte), + ("txAttemptConf", ctypes.c_ubyte), + ("reserved", ctypes.c_ushort), + ("data", ctypes.c_ubyte * xldefine.XL_CAN_MAX_DATA_LEN), + ] + + +class s_xl_can_tx_msg(ctypes.Structure): + _fields_ = [ + ("canId", ctypes.c_uint), + ("msgFlags", ctypes.c_uint), + ("dlc", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte * 7), + ("data", ctypes.c_ubyte * xldefine.XL_CAN_MAX_DATA_LEN), + ] + + +class s_rxTagData(ctypes.Union): + _fields_ = [ + ("canRxOkMsg", s_xl_can_ev_rx_msg), + ("canTxOkMsg", s_xl_can_ev_rx_msg), + ("canTxRequest", s_xl_can_ev_tx_request), + ("canError", s_xl_can_ev_error), + ("canChipState", s_xl_can_ev_chip_state), + ("canSyncPulse", s_xl_can_ev_sync_pulse), + ] + + +class s_txTagData(ctypes.Union): + _fields_ = [("canMsg", s_xl_can_tx_msg)] + + +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), + ] + + +# CAN FD events +class XLcanRxEvent(ctypes.Structure): + _fields_ = [ + ("size", ctypes.c_int), + ("tag", ctypes.c_ushort), + ("chanIndex", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte), + ("userHandle", ctypes.c_int), + ("flagsChip", ctypes.c_ushort), + ("reserved0", ctypes.c_ushort), + ("reserved1", XLuint64), + ("timeStamp", XLuint64), + ("tagData", s_rxTagData), + ] + + +class XLcanTxEvent(ctypes.Structure): + _fields_ = [ + ("tag", ctypes.c_ushort), + ("transId", ctypes.c_ushort), + ("chanIndex", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte * 3), + ("tagData", s_txTagData), + ] + + +# CAN configuration structure +class XLchipParams(ctypes.Structure): + _fields_ = [ + ("bitRate", ctypes.c_ulong), + ("sjw", ctypes.c_ubyte), + ("tseg1", ctypes.c_ubyte), + ("tseg2", ctypes.c_ubyte), + ("sam", ctypes.c_ubyte), + ] + + +# CAN FD configuration structure +class XLcanFdConf(ctypes.Structure): + _fields_ = [ + ("arbitrationBitRate", ctypes.c_uint), + ("sjwAbr", ctypes.c_uint), + ("tseg1Abr", ctypes.c_uint), + ("tseg2Abr", ctypes.c_uint), + ("dataBitRate", ctypes.c_uint), + ("sjwDbr", ctypes.c_uint), + ("tseg1Dbr", ctypes.c_uint), + ("tseg2Dbr", ctypes.c_uint), + ("reserved", ctypes.c_uint * 2), + ] + + +class XLchannelConfig(ctypes.Structure): + _pack_ = 1 + _fields_ = [ + ("name", ctypes.c_char * 32), + ("hwType", ctypes.c_ubyte), + ("hwIndex", ctypes.c_ubyte), + ("hwChannel", ctypes.c_ubyte), + ("transceiverType", ctypes.c_ushort), + ("transceiverState", ctypes.c_ushort), + ("configError", ctypes.c_ushort), + ("channelIndex", ctypes.c_ubyte), + ("channelMask", XLuint64), + ("channelCapabilities", ctypes.c_uint), + ("channelBusCapabilities", ctypes.c_uint), + ("isOnBus", ctypes.c_ubyte), + ("connectedBusType", ctypes.c_uint), + ("busParams", ctypes.c_ubyte * 32), + ("_doNotUse", ctypes.c_uint), + ("driverVersion", ctypes.c_uint), + ("interfaceVersion", ctypes.c_uint), + ("raw_data", ctypes.c_uint * 10), + ("serialNumber", ctypes.c_uint), + ("articleNumber", ctypes.c_uint), + ("transceiverName", ctypes.c_char * 32), + ("specialCabFlags", ctypes.c_uint), + ("dominantTimeout", ctypes.c_uint), + ("dominantRecessiveDelay", ctypes.c_ubyte), + ("recessiveDominantDelay", ctypes.c_ubyte), + ("connectionInfo", ctypes.c_ubyte), + ("currentlyAvailableTimestamps", ctypes.c_ubyte), + ("minimalSupplyVoltage", ctypes.c_ushort), + ("maximalSupplyVoltage", ctypes.c_ushort), + ("maximalBaudrate", ctypes.c_uint), + ("fpgaCoreCapabilities", ctypes.c_ubyte), + ("specialDeviceStatus", ctypes.c_ubyte), + ("channelBusActiveCapabilities", ctypes.c_ushort), + ("breakOffset", ctypes.c_ushort), + ("delimiterOffset", ctypes.c_ushort), + ("reserved", ctypes.c_uint * 3), + ] + + +class XLdriverConfig(ctypes.Structure): + _fields_ = [ + ("dllVersion", ctypes.c_uint), + ("channelCount", ctypes.c_uint), + ("reserved", ctypes.c_uint * 10), + ("channel", XLchannelConfig * 64), + ] diff --git a/can/interfaces/vector/xldefine.py b/can/interfaces/vector/xldefine.py new file mode 100644 index 000000000..fcf041683 --- /dev/null +++ b/can/interfaces/vector/xldefine.py @@ -0,0 +1,172 @@ +# coding: utf-8 + +""" +Definition of constants for vxlapi. +""" + +# Import Python Modules +# ============================== +from enum import Enum + + +MAX_MSG_LEN = 8 +XL_CAN_MAX_DATA_LEN = 64 +XL_INVALID_PORTHANDLE = -1 + + +class XL_AC_Flags(Enum): + XL_ACTIVATE_NONE = 0 + XL_ACTIVATE_RESET_CLOCK = 8 + + +class XL_AcceptanceFilter(Enum): + XL_CAN_STD = 1 + XL_CAN_EXT = 2 + + +class XL_BusCapabilities(Enum): + XL_BUS_COMPATIBLE_CAN = 1 + XL_BUS_ACTIVE_CAP_CAN = 65536 + + +class XL_BusStatus(Enum): + XL_CHIPSTAT_BUSOFF = 1 + XL_CHIPSTAT_ERROR_PASSIVE = 2 + XL_CHIPSTAT_ERROR_WARNING = 4 + XL_CHIPSTAT_ERROR_ACTIVE = 8 + + +class XL_BusTypes(Enum): + XL_BUS_TYPE_NONE = 0 + XL_BUS_TYPE_CAN = 1 + + +class XL_CANFD_BusParams_CanOpMode(Enum): + XL_BUS_PARAMS_CANOPMODE_CAN20 = 1 + XL_BUS_PARAMS_CANOPMODE_CANFD = 2 + XL_BUS_PARAMS_CANOPMODE_CANFD_NO_ISO = 8 + + +class XL_CANFD_ConfigOptions(Enum): + CANFD_CONFOPT_NO_ISO = 8 + + +class XL_CANFD_RX_EV_ERROR_errorCode(Enum): + XL_CAN_ERRC_BIT_ERROR = 1 + XL_CAN_ERRC_FORM_ERROR = 2 + XL_CAN_ERRC_STUFF_ERROR = 3 + XL_CAN_ERRC_OTHER_ERROR = 4 + XL_CAN_ERRC_CRC_ERROR = 5 + XL_CAN_ERRC_ACK_ERROR = 6 + XL_CAN_ERRC_NACK_ERROR = 7 + XL_CAN_ERRC_OVLD_ERROR = 8 + XL_CAN_ERRC_EXCPT_ERROR = 9 + + +class XL_CANFD_RX_EventTags(Enum): + XL_SYNC_PULSE = 11 + XL_CAN_EV_TAG_RX_OK = 1024 + XL_CAN_EV_TAG_RX_ERROR = 1025 + XL_CAN_EV_TAG_TX_ERROR = 1026 + XL_CAN_EV_TAG_TX_REQUEST = 1027 + XL_CAN_EV_TAG_TX_OK = 1028 + XL_CAN_EV_TAG_CHIP_STATE = 1033 + + +class XL_CANFD_RX_MessageFlags(Enum): + XL_CAN_RXMSG_FLAG_NONE = 0 + XL_CAN_RXMSG_FLAG_EDL = 1 + XL_CAN_RXMSG_FLAG_BRS = 2 + XL_CAN_RXMSG_FLAG_ESI = 4 + XL_CAN_RXMSG_FLAG_RTR = 16 + XL_CAN_RXMSG_FLAG_EF = 512 + XL_CAN_RXMSG_FLAG_ARB_LOST = 1024 + XL_CAN_RXMSG_FLAG_WAKEUP = 8192 + XL_CAN_RXMSG_FLAG_TE = 16384 + + +class XL_CANFD_TX_EventTags(Enum): + XL_CAN_EV_TAG_TX_MSG = 1088 + + +class XL_CANFD_TX_MessageFlags(Enum): + XL_CAN_TXMSG_FLAG_NONE = 0 + XL_CAN_TXMSG_FLAG_EDL = 1 + XL_CAN_TXMSG_FLAG_BRS = 2 + XL_CAN_TXMSG_FLAG_RTR = 16 + XL_CAN_TXMSG_FLAG_HIGHPRIO = 128 + XL_CAN_TXMSG_FLAG_WAKEUP = 512 + + +class XL_ChannelCapabilities(Enum): + XL_CHANNEL_FLAG_TIME_SYNC_RUNNING = 1 + XL_CHANNEL_FLAG_NO_HWSYNC_SUPPORT = 1024 + XL_CHANNEL_FLAG_SPDIF_CAPABLE = 16384 + XL_CHANNEL_FLAG_CANFD_BOSCH_SUPPORT = 536870912 + XL_CHANNEL_FLAG_CMACTLICENSE_SUPPORT = 1073741824 + XL_CHANNEL_FLAG_CANFD_ISO_SUPPORT = 2147483648 + + +class XL_EventTags(Enum): + XL_NO_COMMAND = 0 + XL_RECEIVE_MSG = 1 + XL_CHIP_STATE = 4 + XL_TRANSCEIVER = 6 + XL_TIMER = 8 + XL_TRANSMIT_MSG = 10 + XL_SYNC_PULSE = 11 + XL_APPLICATION_NOTIFICATION = 15 + + +class XL_InterfaceVersion(Enum): + XL_INTERFACE_VERSION_V2 = 2 + XL_INTERFACE_VERSION_V3 = 3 + XL_INTERFACE_VERSION = XL_INTERFACE_VERSION_V3 + XL_INTERFACE_VERSION_V4 = 4 + + +class XL_MessageFlags(Enum): + XL_CAN_MSG_FLAG_NONE = 0 + XL_CAN_MSG_FLAG_ERROR_FRAME = 1 + XL_CAN_MSG_FLAG_OVERRUN = 2 + XL_CAN_MSG_FLAG_NERR = 4 + XL_CAN_MSG_FLAG_WAKEUP = 8 + XL_CAN_MSG_FLAG_REMOTE_FRAME = 16 + XL_CAN_MSG_FLAG_RESERVED_1 = 32 + XL_CAN_MSG_FLAG_TX_COMPLETED = 64 + XL_CAN_MSG_FLAG_TX_REQUEST = 128 + XL_CAN_MSG_FLAG_SRR_BIT_DOM = 512 + XL_EVENT_FLAG_OVERRUN = 1 + + +class XL_MessageFlagsExtended(Enum): + XL_CAN_EXT_MSG_ID = 2147483648 + + +class XL_OutputMode(Enum): + XL_OUTPUT_MODE_SILENT = 0 + XL_OUTPUT_MODE_NORMAL = 1 + XL_OUTPUT_MODE_TX_OFF = 2 + XL_OUTPUT_MODE_SJA_1000_SILENT = 3 + + +class XL_Sizes(Enum): + XL_MAX_LENGTH = 31 + XL_MAX_APPNAME = 32 + XL_MAX_NAME_LENGTH = 48 + XLEVENT_SIZE = 48 + XL_CONFIG_MAX_CHANNELS = 64 + XL_APPLCONFIG_MAX_CHANNELS = 256 + + +class XL_Status(Enum): + XL_SUCCESS = 0 + XL_PENDING = 1 + XL_ERR_QUEUE_IS_EMPTY = 10 + XL_ERR_HW_NOT_PRESENT = 129 + + +class XL_TimeSyncNewValue(Enum): + XL_SET_TIMESYNC_NO_CHANGE = 0 + XL_SET_TIMESYNC_ON = 1 + XL_SET_TIMESYNC_OFF = 2 diff --git a/can/interfaces/vector/xldriver.py b/can/interfaces/vector/xldriver.py new file mode 100644 index 000000000..b413e47fb --- /dev/null +++ b/can/interfaces/vector/xldriver.py @@ -0,0 +1,224 @@ +# 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 +# ========================= +from . import xlclass + +# Load Windows DLL +DLL_NAME = "vxlapi64" if platform.architecture()[0] == "64bit" else "vxlapi" +_xlapi_dll = ctypes.windll.LoadLibrary(DLL_NAME) + + +# ctypes wrapping for API functions +xlGetErrorString = _xlapi_dll.xlGetErrorString +xlGetErrorString.argtypes = [xlclass.XLstatus] +xlGetErrorString.restype = ctypes.c_char_p + + +def check_status(result, function, arguments): + if result > 0: + raise VectorError(result, xlGetErrorString(result).decode(), function.__name__) + return result + + +xlGetDriverConfig = _xlapi_dll.xlGetDriverConfig +xlGetDriverConfig.argtypes = [ctypes.POINTER(xlclass.XLdriverConfig)] +xlGetDriverConfig.restype = xlclass.XLstatus +xlGetDriverConfig.errcheck = check_status + +xlOpenDriver = _xlapi_dll.xlOpenDriver +xlOpenDriver.argtypes = [] +xlOpenDriver.restype = xlclass.XLstatus +xlOpenDriver.errcheck = check_status + +xlCloseDriver = _xlapi_dll.xlCloseDriver +xlCloseDriver.argtypes = [] +xlCloseDriver.restype = xlclass.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 = xlclass.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 = xlclass.XLaccess + +xlOpenPort = _xlapi_dll.xlOpenPort +xlOpenPort.argtypes = [ + ctypes.POINTER(xlclass.XLportHandle), + ctypes.c_char_p, + xlclass.XLaccess, + ctypes.POINTER(xlclass.XLaccess), + ctypes.c_uint, + ctypes.c_uint, + ctypes.c_uint, +] +xlOpenPort.restype = xlclass.XLstatus +xlOpenPort.errcheck = check_status + +xlGetSyncTime = _xlapi_dll.xlGetSyncTime +xlGetSyncTime.argtypes = [xlclass.XLportHandle, ctypes.POINTER(xlclass.XLuint64)] +xlGetSyncTime.restype = xlclass.XLstatus +xlGetSyncTime.errcheck = check_status + +xlClosePort = _xlapi_dll.xlClosePort +xlClosePort.argtypes = [xlclass.XLportHandle] +xlClosePort.restype = xlclass.XLstatus +xlClosePort.errcheck = check_status + +xlSetNotification = _xlapi_dll.xlSetNotification +xlSetNotification.argtypes = [ + xlclass.XLportHandle, + ctypes.POINTER(xlclass.XLhandle), + ctypes.c_int, +] +xlSetNotification.restype = xlclass.XLstatus +xlSetNotification.errcheck = check_status + +xlCanSetChannelMode = _xlapi_dll.xlCanSetChannelMode +xlCanSetChannelMode.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.c_int, + ctypes.c_int, +] +xlCanSetChannelMode.restype = xlclass.XLstatus +xlCanSetChannelMode.errcheck = check_status + +xlActivateChannel = _xlapi_dll.xlActivateChannel +xlActivateChannel.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.c_uint, + ctypes.c_uint, +] +xlActivateChannel.restype = xlclass.XLstatus +xlActivateChannel.errcheck = check_status + +xlDeactivateChannel = _xlapi_dll.xlDeactivateChannel +xlDeactivateChannel.argtypes = [xlclass.XLportHandle, xlclass.XLaccess] +xlDeactivateChannel.restype = xlclass.XLstatus +xlDeactivateChannel.errcheck = check_status + +xlCanFdSetConfiguration = _xlapi_dll.xlCanFdSetConfiguration +xlCanFdSetConfiguration.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.POINTER(xlclass.XLcanFdConf), +] +xlCanFdSetConfiguration.restype = xlclass.XLstatus +xlCanFdSetConfiguration.errcheck = check_status + +xlReceive = _xlapi_dll.xlReceive +xlReceive.argtypes = [ + xlclass.XLportHandle, + ctypes.POINTER(ctypes.c_uint), + ctypes.POINTER(xlclass.XLevent), +] +xlReceive.restype = xlclass.XLstatus +xlReceive.errcheck = check_status + +xlCanReceive = _xlapi_dll.xlCanReceive +xlCanReceive.argtypes = [xlclass.XLportHandle, ctypes.POINTER(xlclass.XLcanRxEvent)] +xlCanReceive.restype = xlclass.XLstatus +xlCanReceive.errcheck = check_status + +xlCanSetChannelBitrate = _xlapi_dll.xlCanSetChannelBitrate +xlCanSetChannelBitrate.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.c_ulong, +] +xlCanSetChannelBitrate.restype = xlclass.XLstatus +xlCanSetChannelBitrate.errcheck = check_status + +xlCanSetChannelParams = _xlapi_dll.xlCanSetChannelParams +xlCanSetChannelParams.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.POINTER(xlclass.XLchipParams), +] +xlCanSetChannelParams.restype = xlclass.XLstatus +xlCanSetChannelParams.errcheck = check_status + +xlCanTransmit = _xlapi_dll.xlCanTransmit +xlCanTransmit.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.POINTER(ctypes.c_uint), + ctypes.POINTER(xlclass.XLevent), +] +xlCanTransmit.restype = xlclass.XLstatus +xlCanTransmit.errcheck = check_status + +xlCanTransmitEx = _xlapi_dll.xlCanTransmitEx +xlCanTransmitEx.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.c_uint, + ctypes.POINTER(ctypes.c_uint), + ctypes.POINTER(xlclass.XLcanTxEvent), +] +xlCanTransmitEx.restype = xlclass.XLstatus +xlCanTransmitEx.errcheck = check_status + +xlCanFlushTransmitQueue = _xlapi_dll.xlCanFlushTransmitQueue +xlCanFlushTransmitQueue.argtypes = [xlclass.XLportHandle, xlclass.XLaccess] +xlCanFlushTransmitQueue.restype = xlclass.XLstatus +xlCanFlushTransmitQueue.errcheck = check_status + +xlCanSetChannelAcceptance = _xlapi_dll.xlCanSetChannelAcceptance +xlCanSetChannelAcceptance.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_uint, +] +xlCanSetChannelAcceptance.restype = xlclass.XLstatus +xlCanSetChannelAcceptance.errcheck = check_status + +xlCanResetAcceptance = _xlapi_dll.xlCanResetAcceptance +xlCanResetAcceptance.argtypes = [xlclass.XLportHandle, xlclass.XLaccess, ctypes.c_uint] +xlCanResetAcceptance.restype = xlclass.XLstatus +xlCanResetAcceptance.errcheck = check_status + +xlCanRequestChipState = _xlapi_dll.xlCanRequestChipState +xlCanRequestChipState.argtypes = [xlclass.XLportHandle, xlclass.XLaccess] +xlCanRequestChipState.restype = xlclass.XLstatus +xlCanRequestChipState.errcheck = check_status + +xlCanSetChannelOutput = _xlapi_dll.xlCanSetChannelOutput +xlCanSetChannelOutput.argtypes = [xlclass.XLportHandle, xlclass.XLaccess, ctypes.c_char] +xlCanSetChannelOutput.restype = xlclass.XLstatus +xlCanSetChannelOutput.errcheck = check_status diff --git a/test/test_vector.py b/test/test_vector.py new file mode 100644 index 000000000..69fb42133 --- /dev/null +++ b/test/test_vector.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +Test for Vector Interface +""" + +import ctypes +import time +import logging +import unittest +from unittest.mock import Mock + +import pytest + +import can +from can.interfaces.vector import canlib, xldefine, xlclass + + +class TestVectorBus(unittest.TestCase): + def setUp(self) -> None: + # basic mock for XLDriver + can.interfaces.vector.canlib.xldriver = Mock() + + # bus creation functions + can.interfaces.vector.canlib.xldriver.xlOpenDriver = Mock() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig = Mock( + side_effect=xlGetApplConfig + ) + can.interfaces.vector.canlib.xldriver.xlGetChannelIndex = Mock( + side_effect=xlGetChannelIndex + ) + can.interfaces.vector.canlib.xldriver.xlOpenPort = Mock(side_effect=xlOpenPort) + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration = Mock( + return_value=0 + ) + can.interfaces.vector.canlib.xldriver.xlCanSetChannelMode = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlActivateChannel = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlGetSyncTime = Mock( + side_effect=xlGetSyncTime + ) + can.interfaces.vector.canlib.xldriver.xlCanSetChannelAcceptance = Mock( + return_value=0 + ) + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate = Mock( + return_value=0 + ) + can.interfaces.vector.canlib.xldriver.xlSetNotification = Mock( + side_effect=xlSetNotification + ) + + # bus deactivation functions + can.interfaces.vector.canlib.xldriver.xlDeactivateChannel = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlClosePort = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlCloseDriver = Mock() + + # receiver functions + can.interfaces.vector.canlib.xldriver.xlReceive = Mock(side_effect=xlReceive) + can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock( + side_effect=xlCanReceive + ) + + # sender functions + can.interfaces.vector.canlib.xldriver.xlCanTransmit = Mock(return_value=0) + can.interfaces.vector.canlib.xldriver.xlCanTransmitEx = Mock(return_value=0) + + # various functions + can.interfaces.vector.canlib.xldriver.xlCanFlushTransmitQueue = Mock() + can.interfaces.vector.canlib.WaitForSingleObject = Mock() + + self.bus = None + + def tearDown(self) -> None: + if self.bus: + self.bus.shutdown() + self.bus = None + + def test_bus_creation(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector") + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION.value + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() + + def test_bus_creation_bitrate(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", bitrate=200000) + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION.value + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_called() + xlCanSetChannelBitrate_args = can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.call_args[ + 0 + ] + self.assertEqual(xlCanSetChannelBitrate_args[2], 200000) + + def test_bus_creation_fd(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", fd=True) + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4.value, + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() + + def test_bus_creation_fd_bitrate_timings(self) -> None: + self.bus = can.Bus( + channel=0, + bustype="vector", + fd=True, + bitrate=500000, + data_bitrate=2000000, + sjwAbr=10, + tseg1Abr=11, + tseg2Abr=12, + sjwDbr=13, + tseg1Dbr=14, + tseg2Dbr=15, + ) + self.assertIsInstance(self.bus, canlib.VectorBus) + can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() + can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() + + can.interfaces.vector.canlib.xldriver.xlOpenPort.assert_called() + xlOpenPort_args = can.interfaces.vector.canlib.xldriver.xlOpenPort.call_args[0] + self.assertEqual( + xlOpenPort_args[5], + xldefine.XL_InterfaceVersion.XL_INTERFACE_VERSION_V4.value, + ) + self.assertEqual(xlOpenPort_args[6], xldefine.XL_BusTypes.XL_BUS_TYPE_CAN.value) + + can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() + + xlCanFdSetConfiguration_args = can.interfaces.vector.canlib.xldriver.xlCanFdSetConfiguration.call_args[ + 0 + ] + canFdConf = xlCanFdSetConfiguration_args[2] + self.assertEqual(canFdConf.arbitrationBitRate, 500000) + self.assertEqual(canFdConf.dataBitRate, 2000000) + self.assertEqual(canFdConf.sjwAbr, 10) + self.assertEqual(canFdConf.tseg1Abr, 11) + self.assertEqual(canFdConf.tseg2Abr, 12) + self.assertEqual(canFdConf.sjwDbr, 13) + self.assertEqual(canFdConf.tseg1Dbr, 14) + self.assertEqual(canFdConf.tseg2Dbr, 15) + + def test_receive(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector") + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() + + def test_receive_fd(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", fd=True) + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() + + def test_send(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector") + msg = can.Message( + arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True + ) + self.bus.send(msg) + can.interfaces.vector.canlib.xldriver.xlCanTransmit.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_not_called() + + def test_send_fd(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector", fd=True) + msg = can.Message( + arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True + ) + self.bus.send(msg) + can.interfaces.vector.canlib.xldriver.xlCanTransmit.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_called() + + def test_flush_tx_buffer(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector") + self.bus.flush_tx_buffer() + can.interfaces.vector.canlib.xldriver.xlCanFlushTransmitQueue.assert_called() + + def test_shutdown(self) -> None: + self.bus = can.Bus(channel=0, bustype="vector") + self.bus.shutdown() + can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() + can.interfaces.vector.canlib.xldriver.xlClosePort.assert_called() + can.interfaces.vector.canlib.xldriver.xlCloseDriver.assert_called() + + def test_reset(self): + self.bus = can.Bus(channel=0, bustype="vector") + self.bus.reset() + can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() + can.interfaces.vector.canlib.xldriver.xlActivateChannel.assert_called() + + +def xlGetApplConfig( + app_name_p: ctypes.c_char_p, + app_channel: ctypes.c_uint, + hw_type: ctypes.POINTER(ctypes.c_uint), + hw_index: ctypes.POINTER(ctypes.c_uint), + hw_channel: ctypes.POINTER(ctypes.c_uint), + bus_type: ctypes.c_uint, +) -> int: + hw_type.value = 1 + hw_channel.value = app_channel + return 0 + + +def xlGetChannelIndex( + hw_type: ctypes.c_int, hw_index: ctypes.c_int, hw_channel: ctypes.c_int +) -> int: + return hw_channel + + +def xlOpenPort( + port_handle_p: ctypes.POINTER(xlclass.XLportHandle), + app_name_p: ctypes.c_char_p, + access_mask: xlclass.XLaccess, + permission_mask_p: ctypes.POINTER(xlclass.XLaccess), + rx_queue_size: ctypes.c_uint, + xl_interface_version: ctypes.c_uint, + bus_type: ctypes.c_uint, +) -> int: + port_handle_p.value = 0 + return 0 + + +def xlGetSyncTime( + port_handle: xlclass.XLportHandle, time_p: ctypes.POINTER(xlclass.XLuint64) +) -> int: + time_p.value = 544219859027581 + return 0 + + +def xlSetNotification( + port_handle: xlclass.XLportHandle, + event_handle: ctypes.POINTER(xlclass.XLhandle), + queue_level: ctypes.c_int, +) -> int: + event_handle.value = 520 + return 0 + + +def xlReceive( + port_handle: xlclass.XLportHandle, + event_count_p: ctypes.POINTER(ctypes.c_uint), + event: ctypes.POINTER(xlclass.XLevent), +) -> int: + event.tag = xldefine.XL_EventTags.XL_RECEIVE_MSG.value + event.tagData.msg.id = 0x123 + event.tagData.msg.dlc = 8 + event.tagData.msg.flags = 0 + event.timeStamp = 0 + event.chanIndex = 0 + for idx, value in enumerate([1, 2, 3, 4, 5, 6, 7, 8]): + event.tagData.msg.data[idx] = value + return 0 + + +def xlCanReceive( + port_handle: xlclass.XLportHandle, event: ctypes.POINTER(xlclass.XLcanRxEvent) +) -> int: + event.tag = xldefine.XL_CANFD_RX_EventTags.XL_CAN_EV_TAG_RX_OK.value + event.tagData.canRxOkMsg.canId = 0x123 + event.tagData.canRxOkMsg.dlc = 8 + event.tagData.canRxOkMsg.msgFlags = 0 + event.timeStamp = 0 + event.chanIndex = 0 + for idx, value in enumerate([1, 2, 3, 4, 5, 6, 7, 8]): + event.tagData.canRxOkMsg.data[idx] = value + return 0 + + +if __name__ == "__main__": + unittest.main() From d0a9b8fe6717c45e750a89203c2bb9c7283f95ac Mon Sep 17 00:00:00 2001 From: Karl Date: Tue, 23 Jul 2019 07:11:14 -0700 Subject: [PATCH 149/252] Add sphinx-autodoc-typehints to sphinx docs Add the sphinx-autodoc-typehints extension to the Sphinx documentation generation pipeline, which allows us to automatically generate the appropriate :type argname: and :rtype: directives in the docstring. --- doc/conf.py | 1 + doc/doc-requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/conf.py b/doc/conf.py index b95aa6557..62fd649b6 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -43,6 +43,7 @@ "sphinx.ext.viewcode", "sphinx.ext.graphviz", "sphinxcontrib.programoutput", + "sphinx_autodoc_typehints", ] # Now, you can use the alias name as a new role, e.g. :issue:`123`. diff --git a/doc/doc-requirements.txt b/doc/doc-requirements.txt index 45026701b..a63beee71 100644 --- a/doc/doc-requirements.txt +++ b/doc/doc-requirements.txt @@ -1,2 +1,3 @@ sphinx>=1.8.1 sphinxcontrib-programoutput +sphinx-autodoc-typehints==1.6.0 From 6384baa946ab9a993236a88e03234a07fee507a3 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Wed, 24 Jul 2019 21:06:59 +0200 Subject: [PATCH 150/252] add event based cyclic send --- can/broadcastmanager.py | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index edb5da2a3..a3987f4ae 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -14,6 +14,16 @@ import can +# try to import win32event for event-based cyclic send task(needs pywin32 package) +try: + import win32event + + HAS_EVENTS = True +except ImportError as e: + print(str(e)) + HAS_EVENTS = False + + log = logging.getLogger("can.bcm") @@ -178,10 +188,19 @@ def __init__(self, bus, lock, messages, period, duration=None): self.send_lock = lock self.stopped = True self.thread = None - self.end_time = time.time() + duration if duration else None + self.end_time = time.perf_counter() + duration if duration else None + + if HAS_EVENTS: + self.period_ms: int = int(round(period * 1000, 0)) + self.event = win32event.CreateWaitableTimer( + None, False, "TIMER_" + str(self.period_ms) + ) + self.start() def stop(self): + if HAS_EVENTS: + win32event.CancelWaitableTimer(self.event.handle) self.stopped = True def start(self): @@ -190,6 +209,12 @@ def start(self): name = "Cyclic send task for 0x%X" % (self.messages[0].arbitration_id) self.thread = threading.Thread(target=self._run, name=name) self.thread.daemon = True + + if HAS_EVENTS: + win32event.SetWaitableTimer( + self.event.handle, 0, self.period_ms, None, None, False + ) + self.thread.start() def _run(self): @@ -197,15 +222,19 @@ def _run(self): while not self.stopped: # Prevent calling bus.send from multiple threads with self.send_lock: - started = time.time() + started = time.perf_counter() try: self.bus.send(self.messages[msg_index]) except Exception as exc: log.exception(exc) break - if self.end_time is not None and time.time() >= self.end_time: + if self.end_time is not None and time.perf_counter() >= self.end_time: break msg_index = (msg_index + 1) % len(self.messages) - # Compensate for the time it takes to send the message - delay = self.period - (time.time() - started) - time.sleep(max(0.0, delay)) + + if HAS_EVENTS: + win32event.WaitForSingleObject(self.event.handle, self.period_ms) + else: + # Compensate for the time it takes to send the message + delay = self.period - (time.perf_counter() - started) + time.sleep(max(0.0, delay)) From 8e5a3098a4abe01dd607208be7ae8a2cfc3ccea6 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Wed, 24 Jul 2019 21:35:59 +0200 Subject: [PATCH 151/252] little fix --- can/broadcastmanager.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index a3987f4ae..6c44c5272 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -23,7 +23,6 @@ print(str(e)) HAS_EVENTS = False - log = logging.getLogger("can.bcm") @@ -192,9 +191,7 @@ def __init__(self, bus, lock, messages, period, duration=None): if HAS_EVENTS: self.period_ms: int = int(round(period * 1000, 0)) - self.event = win32event.CreateWaitableTimer( - None, False, "TIMER_" + str(self.period_ms) - ) + self.event = win32event.CreateWaitableTimer(None, False, None) self.start() From 8b66f0955838c9f9079660ea809a510781a03392 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Wed, 24 Jul 2019 21:41:33 +0200 Subject: [PATCH 152/252] little fix --- can/broadcastmanager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 6c44c5272..bd550e5db 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -19,8 +19,7 @@ import win32event HAS_EVENTS = True -except ImportError as e: - print(str(e)) +except ImportError: HAS_EVENTS = False log = logging.getLogger("can.bcm") From 0019438d984af0f80c9b66a900aa032b486b7888 Mon Sep 17 00:00:00 2001 From: karl ding Date: Sun, 28 Jul 2019 17:30:26 -0700 Subject: [PATCH 153/252] Add typing annotations for functions in can.bus (#652) This adds typing annotations for use via mypy for all functions under can.bus. This works towards PEP 561 compatibility. * Add file for type-checking specific code. Add a Type Alias for CAN Filters used by can.bus * Fix pylint unused import error * Switch CAN Filter to TypedDict * Add mypy_extensions dependency to install_requires * Remove types generated by sphinx-autodoc-typehints With the introduction of the sphinx-autodoc-typehints extension, we don't need to duplicate typing information in the docstring as well as the function signature. --- can/bus.py | 90 +++++++++++++++++++++++++-------------------- can/typechecking.py | 10 +++++ setup.py | 1 + 3 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 can/typechecking.py diff --git a/can/bus.py b/can/bus.py index 59ddbd1d2..b6e7935b7 100644 --- a/can/bus.py +++ b/can/bus.py @@ -4,6 +4,10 @@ Contains the ABC bus implementation and its documentation. """ +from typing import Any, Iterator, List, Optional, Sequence, Tuple, Union + +import can.typechecking + from abc import ABCMeta, abstractmethod import can import logging @@ -38,7 +42,12 @@ class BusABC(metaclass=ABCMeta): RECV_LOGGING_LEVEL = 9 @abstractmethod - def __init__(self, channel, can_filters=None, **kwargs): + def __init__( + self, + channel: Any, + can_filters: Optional[can.typechecking.CanFilters] = None, + **kwargs: object + ): """Construct and open a CAN bus instance of the specified type. Subclasses should call though this method with all given parameters @@ -47,26 +56,24 @@ def __init__(self, channel, can_filters=None, **kwargs): :param channel: The can interface identifier. Expected type is backend dependent. - :param list can_filters: + :param can_filters: See :meth:`~can.BusABC.set_filters` for details. :param dict kwargs: Any backend dependent configurations are passed in this dictionary """ - self._periodic_tasks = [] + self._periodic_tasks: List[can.broadcastmanager.CyclicSendTaskABC] = [] self.set_filters(can_filters) - def __str__(self): + def __str__(self) -> str: return self.channel_info - def recv(self, timeout=None): + def recv(self, timeout: Optional[float] = None) -> Optional[can.Message]: """Block waiting for a message from the Bus. - :type timeout: float or None :param timeout: seconds to wait for a message or None to wait indefinitely - :rtype: can.Message or None :return: None on timeout or a :class:`can.Message` object. :raises can.CanError: @@ -100,7 +107,9 @@ def recv(self, timeout=None): else: return None - def _recv_internal(self, timeout): + def _recv_internal( + self, timeout: Optional[float] + ) -> Tuple[Optional[can.Message], bool]: """ Read a message from the bus and tell whether it was filtered. This methods may be called by :meth:`~can.BusABC.recv` @@ -128,7 +137,6 @@ def _recv_internal(self, timeout): :param float timeout: seconds to wait for a message, see :meth:`~can.BusABC.send` - :rtype: tuple[can.Message, bool] or tuple[None, bool] :return: 1. a message that was read or None on timeout 2. a bool that is True if message filtering has already @@ -144,14 +152,13 @@ def _recv_internal(self, timeout): raise NotImplementedError("Trying to read from a write only bus?") @abstractmethod - def send(self, msg, timeout=None): + def send(self, msg: can.Message, timeout: Optional[float] = None): """Transmit a message to the CAN bus. Override this method to enable the transmit path. :param can.Message msg: A message object. - :type timeout: float or None :param 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. @@ -164,7 +171,13 @@ def send(self, msg, timeout=None): """ raise NotImplementedError("Trying to write to a readonly bus?") - def send_periodic(self, msgs, period, duration=None, store_task=True): + def send_periodic( + self, + msgs: Union[Sequence[can.Message], can.Message], + period: float, + duration: Optional[float] = None, + store_task: bool = True, + ) -> can.broadcastmanager.CyclicSendTaskABC: """Start sending messages at a given period on this bus. The task will be active until one of the following conditions are met: @@ -175,20 +188,19 @@ def send_periodic(self, msgs, period, duration=None, store_task=True): - :meth:`BusABC.stop_all_periodic_tasks()` is called - the task's :meth:`CyclicTask.stop()` method is called. - :param Union[Sequence[can.Message], can.Message] msgs: + :param msgs: Messages to transmit - :param float period: + :param period: Period in seconds between each message - :param float duration: + :param duration: Approximate duration in seconds to continue sending messages. If no duration is provided, the task will continue indefinitely. - :param bool store_task: + :param store_task: If True (the default) the task will be attached to this Bus instance. Disable to instead manage tasks manually. :return: A started task instance. Note the task can be stopped (and depending on the backend modified) by calling the :meth:`stop` method. - :rtype: can.broadcastmanager.CyclicSendTaskABC .. note:: @@ -223,30 +235,34 @@ def wrapped_stop_method(remove_task=True): pass original_stop_method() - task.stop = wrapped_stop_method + setattr(task, "stop", wrapped_stop_method) if store_task: self._periodic_tasks.append(task) return task - def _send_periodic_internal(self, msgs, period, duration=None): + def _send_periodic_internal( + self, + msgs: Union[Sequence[can.Message], can.Message], + period: float, + duration: Optional[float] = None, + ) -> can.broadcastmanager.CyclicSendTaskABC: """Default implementation of periodic message sending using threading. Override this method to enable a more efficient backend specific approach. - :param Union[Sequence[can.Message], can.Message] msgs: + :param msgs: Messages to transmit - :param float period: + :param period: Period in seconds between each message - :param float duration: + :param duration: The duration between sending each message at the given rate. If no duration is provided, the task will continue indefinitely. :return: A started task instance. Note the task can be stopped (and depending on the backend modified) by calling the :meth:`stop` method. - :rtype: can.broadcastmanager.CyclicSendTaskABC """ if not hasattr(self, "_lock_send_periodic"): # Create a send lock for this bus, but not for buses which override this method @@ -275,7 +291,7 @@ def stop_all_periodic_tasks(self, remove_tasks=True): if remove_tasks: self._periodic_tasks = [] - def __iter__(self): + def __iter__(self) -> Iterator[can.Message]: """Allow iteration on messages as they are received. >>> for msg in bus: @@ -291,7 +307,7 @@ def __iter__(self): yield msg @property - def filters(self): + def filters(self) -> Optional[can.typechecking.CanFilters]: """ Modify the filters of this bus. See :meth:`~can.BusABC.set_filters` for details. @@ -299,10 +315,10 @@ def filters(self): return self._filters @filters.setter - def filters(self, filters): + def filters(self, filters: Optional[can.typechecking.CanFilters]): self.set_filters(filters) - def set_filters(self, filters=None): + def set_filters(self, filters: Optional[can.typechecking.CanFilters] = None): """Apply filtering to all messages received by this Bus. All messages that match at least one filter are returned. @@ -327,25 +343,24 @@ def set_filters(self, filters=None): self._filters = filters or None self._apply_filters(self._filters) - def _apply_filters(self, filters): + def _apply_filters(self, filters: Optional[can.typechecking.CanFilters]): """ Hook for applying the filters to the underlying kernel or hardware if supported/implemented by the interface. - :param Iterator[dict] filters: + :param filters: See :meth:`~can.BusABC.set_filters` for details. """ - def _matches_filters(self, msg): + def _matches_filters(self, msg: can.Message) -> bool: """Checks whether the given message matches at least one of the current filters. See :meth:`~can.BusABC.set_filters` for details on how the filters work. This method should not be overridden. - :param can.Message msg: + :param msg: the message to check if matching - :rtype: bool :return: whether the given message matches at least one filter """ @@ -388,25 +403,21 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.shutdown() @property - def state(self): + def state(self) -> BusState: """ Return the current state of the hardware - - :type: can.BusState """ return BusState.ACTIVE @state.setter - def state(self, new_state): + def state(self, new_state: BusState): """ Set the new state of the hardware - - :type: can.BusState """ raise NotImplementedError("Property is not implemented.") @staticmethod - def _detect_available_configs(): + def _detect_available_configs() -> Iterator[dict]: """Detect all configurations/channels that this interface could currently connect with. @@ -414,7 +425,6 @@ def _detect_available_configs(): May not to be implemented by every interface on every platform. - :rtype: Iterator[dict] :return: an iterable of dicts, each being a configuration suitable for usage in the interface's bus constructor. """ diff --git a/can/typechecking.py b/can/typechecking.py new file mode 100644 index 000000000..29ceded62 --- /dev/null +++ b/can/typechecking.py @@ -0,0 +1,10 @@ +"""Types for mypy type-checking +""" +import typing + +import mypy_extensions + +CanFilter = mypy_extensions.TypedDict( + "CanFilter", {"can_id": int, "can_mask": int, "extended": bool} +) +CanFilters = typing.Iterable[CanFilter] diff --git a/setup.py b/setup.py index ded73f458..a68e76721 100644 --- a/setup.py +++ b/setup.py @@ -103,6 +103,7 @@ "aenum", 'windows-curses;platform_system=="Windows"', "filelock", + "mypy_extensions >= 0.4.0, < 0.5.0", ], setup_requires=pytest_runner, extras_require=extras_require, From 02d5032332b7a6a8675db85b18cfb23851f9beae Mon Sep 17 00:00:00 2001 From: Colin Rafferty Date: Mon, 29 Jul 2019 10:19:19 -0400 Subject: [PATCH 154/252] Do not incorrectly reset CANMsg.MSGTYPE on remote frame. In `PcanBus.send()`, we initially set `msgType` based on all the flags of `msg`, including RTR. In the if/else for `self.fd`, we are incorrectly resetting it if rtr. We should not, and so we are no longer doing it. --- can/interfaces/pcan/pcan.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 4bddfc3b1..26da9ded9 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -426,9 +426,7 @@ def send(self, msg, timeout=None): 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: + if not msg.is_remote_frame: # copy data for i in range(CANMsg.LEN): CANMsg.DATA[i] = msg.data[i] From 80cc665fabb1b828c42398272a6f713d3dfb1e1f Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 28 Jul 2019 20:00:17 -0700 Subject: [PATCH 155/252] Add typing annotations for can.broadcastmanager This adds typing annotations for functions in can.broadcastmanager. A Channel definition is introduced for use in can.bus for the generic case. In addition, this remove the redundant typing information that was previously in the docstring, since we now have sphinx-autodoc-typehints to generate the types for the docs from the annotations in the function signature. This works towards PEP 561 compatibility. --- .travis.yml | 16 ++++++++++- can/broadcastmanager.py | 64 +++++++++++++++++++++++++++++------------ can/bus.py | 4 +-- can/typechecking.py | 3 ++ 4 files changed, 66 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index f35a148aa..4d765d43a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -85,7 +85,21 @@ jobs: # re-introduced - pylint --rcfile=.pylintrc-wip can/ # mypy checking - - mypy --python-version=3.7 --ignore-missing-imports --no-implicit-optional can/bit_timing.py can/broadcastmanager.py can/bus.py can/interface.py can/listener.py can/logger.py can/message.py can/notifier.py can/player.py can/thread_safe_bus.py can/util.py + - mypy + --python-version=3.7 + --ignore-missing-imports + --no-implicit-optional + can/bit_timing.py + can/broadcastmanager.py + can/bus.py + can/interface.py + can/listener.py + can/logger.py + can/message.py + can/notifier.py + can/player.py + can/thread_safe_bus.py + can/util.py - stage: linter name: "Formatting Checks" python: "3.7" diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index edb5da2a3..460154e50 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -7,6 +7,10 @@ :meth:`can.BusABC.send_periodic`. """ +from typing import Optional, Sequence, Tuple, Union + +import can.typechecking + import abc import logging import threading @@ -36,11 +40,13 @@ class CyclicSendTaskABC(CyclicTask): Message send task with defined period """ - def __init__(self, messages, period): + def __init__( + self, messages: Union[Sequence[can.Message], can.Message], period: float + ): """ - :param Union[Sequence[can.Message], can.Message] messages: + :param messages: The messages to be sent periodically. - :param float period: The rate in seconds at which to send the messages. + :param period: The rate in seconds at which to send the messages. """ messages = self._check_and_convert_messages(messages) @@ -50,7 +56,9 @@ def __init__(self, messages, period): self.messages = messages @staticmethod - def _check_and_convert_messages(messages): + def _check_and_convert_messages( + messages: Union[Sequence[can.Message], can.Message] + ) -> Tuple[can.Message, ...]: """Helper function to convert a Message or Sequence of messages into a tuple, and raises an error when the given value is invalid. @@ -84,13 +92,18 @@ def _check_and_convert_messages(messages): class LimitedDurationCyclicSendTaskABC(CyclicSendTaskABC): - def __init__(self, messages, period, duration): + def __init__( + self, + messages: Union[Sequence[can.Message], can.Message], + period: float, + duration: Optional[float], + ): """Message send task with a defined duration and period. - :param Union[Sequence[can.Message], can.Message] messages: + :param messages: The messages to be sent periodically. - :param float period: The rate in seconds at which to send the messages. - :param float duration: + :param period: The rate in seconds at which to send the messages. + :param duration: Approximate duration in seconds to continue sending messages. If no duration is provided, the task will continue indefinitely. """ @@ -110,7 +123,7 @@ def start(self): class ModifiableCyclicTaskABC(CyclicSendTaskABC): """Adds support for modifying a periodic message""" - def _check_modified_messages(self, messages): + def _check_modified_messages(self, messages: Tuple[can.Message, ...]): """Helper function to perform error checking when modifying the data in the cyclic task. @@ -130,11 +143,11 @@ def _check_modified_messages(self, messages): "from when the task was created" ) - def modify_data(self, messages): + def modify_data(self, messages: Union[Sequence[can.Message], can.Message]): """Update the contents of the periodically sent messages, without altering the timing. - :param Union[Sequence[can.Message], can.Message] messages: + :param messages: The messages with the new :attr:`can.Message.data`. Note: The arbitration ID cannot be changed. @@ -153,18 +166,26 @@ class MultiRateCyclicSendTaskABC(CyclicSendTaskABC): """A Cyclic send task that supports switches send frequency after a set time. """ - def __init__(self, channel, messages, count, initial_period, subsequent_period): + def __init__( + self, + channel: can.typechecking.Channel, + messages: Union[Sequence[can.Message], can.Message], + count: int, + initial_period: float, + subsequent_period: float, + ): """ Transmits a message `count` times at `initial_period` then continues to transmit messages at `subsequent_period`. :param channel: See interface specific documentation. - :param Union[Sequence[can.Message], can.Message] messages: - :param int count: - :param float initial_period: - :param float subsequent_period: + :param messages: + :param count: + :param initial_period: + :param subsequent_period: """ - super().__init__(channel, messages, subsequent_period) + super().__init__(messages, subsequent_period) + self._channel = channel class ThreadBasedCyclicSendTask( @@ -172,7 +193,14 @@ class ThreadBasedCyclicSendTask( ): """Fallback cyclic send task using thread.""" - def __init__(self, bus, lock, messages, period, duration=None): + def __init__( + self, + bus: "can.bus.BusABC", + lock: threading.Lock, + messages: Union[Sequence[can.Message], can.Message], + period: float, + duration: Optional[float] = None, + ): super().__init__(messages, period, duration) self.bus = bus self.send_lock = lock diff --git a/can/bus.py b/can/bus.py index b6e7935b7..c188939a9 100644 --- a/can/bus.py +++ b/can/bus.py @@ -4,7 +4,7 @@ Contains the ABC bus implementation and its documentation. """ -from typing import Any, Iterator, List, Optional, Sequence, Tuple, Union +from typing import Iterator, List, Optional, Sequence, Tuple, Union import can.typechecking @@ -44,7 +44,7 @@ class BusABC(metaclass=ABCMeta): @abstractmethod def __init__( self, - channel: Any, + channel: can.typechecking.Channel, can_filters: Optional[can.typechecking.CanFilters] = None, **kwargs: object ): diff --git a/can/typechecking.py b/can/typechecking.py index 29ceded62..d592d0d4a 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -8,3 +8,6 @@ "CanFilter", {"can_id": int, "can_mask": int, "extended": bool} ) CanFilters = typing.Iterable[CanFilter] + +# Used for the Abstract Base Class +Channel = typing.Union[int, str] From 41e592c4e776294f9d7b42467b99824ff5bfa09b Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 1 Aug 2019 01:34:06 -0700 Subject: [PATCH 156/252] Add typing annotations for can.message This adds typing annotations for functions in can.message. Currently the typing annotation for the Message data is incomplete due to upstream not implementing the buffer protocol. Since can.Message attempts to cast the data to a bytearray via bytearray(), the Message data can be any type that bytearray() supports. In addition, this remove the redundant typing information that was previously in the docstring, since we now have sphinx-autodoc-typehints to generate the types for the docs from the annotations in the function signature. This works towards PEP 561 compatibility. --- can/message.py | 61 ++++++++++++++++++++++++--------------------- can/typechecking.py | 7 ++++++ 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/can/message.py b/can/message.py index b3fd38139..d1c7c9366 100644 --- a/can/message.py +++ b/can/message.py @@ -8,6 +8,9 @@ starting with Python 3.7. """ +from typing import Optional, Union + +from . import typechecking from copy import deepcopy from math import isinf, isnan @@ -48,28 +51,28 @@ class Message: def __init__( self, - timestamp=0.0, - arbitration_id=0, - is_extended_id=True, - is_remote_frame=False, - is_error_frame=False, - channel=None, - dlc=None, - data=None, - is_fd=False, - bitrate_switch=False, - error_state_indicator=False, - check=False, + timestamp: float = 0.0, + arbitration_id: int = 0, + is_extended_id: bool = True, + is_remote_frame: bool = False, + is_error_frame: bool = False, + channel: Optional[typechecking.Channel] = None, + dlc: Optional[int] = None, + data: Optional[typechecking.CanData] = None, + is_fd: bool = False, + bitrate_switch: bool = False, + error_state_indicator: bool = False, + check: bool = False, ): """ To create a message object, simply provide any of the below attributes together with additional parameters as keyword arguments to the constructor. - :param bool check: By default, the constructor of this class does not strictly check the input. - Thus, the caller must prevent the creation of invalid messages or - set this parameter to `True`, to raise an Error on invalid inputs. - Possible problems include the `dlc` field not matching the length of `data` - or creating a message with both `is_remote_frame` and `is_error_frame` set to `True`. + :param check: By default, the constructor of this class does not strictly check the input. + Thus, the caller must prevent the creation of invalid messages or + set this parameter to `True`, to raise an Error on invalid inputs. + Possible problems include the `dlc` field not matching the length of `data` + or creating a message with both `is_remote_frame` and `is_error_frame` set to `True`. :raises ValueError: iff `check` is set to `True` and one or more arguments were invalid """ @@ -102,7 +105,7 @@ def __init__( if check: self._check() - def __str__(self): + def __str__(self) -> str: field_strings = ["Timestamp: {0:>15.6f}".format(self.timestamp)] if self.is_extended_id: arbitration_id_string = "ID: {0:08x}".format(self.arbitration_id) @@ -144,14 +147,14 @@ def __str__(self): return " ".join(field_strings).strip() - def __len__(self): + def __len__(self) -> int: # return the dlc such that it also works on remote frames return self.dlc - def __bool__(self): + def __bool__(self) -> bool: return True - def __repr__(self): + def __repr__(self) -> str: args = [ "timestamp={}".format(self.timestamp), "arbitration_id={:#x}".format(self.arbitration_id), @@ -177,16 +180,16 @@ def __repr__(self): return "can.Message({})".format(", ".join(args)) - def __format__(self, format_spec): + def __format__(self, format_spec: Optional[str]) -> str: if not format_spec: return self.__str__() else: raise ValueError("non empty format_specs are not supported") - def __bytes__(self): + def __bytes__(self) -> bytes: return bytes(self.data) - def __copy__(self): + def __copy__(self) -> "Message": new = Message( timestamp=self.timestamp, arbitration_id=self.arbitration_id, @@ -202,7 +205,7 @@ def __copy__(self): ) return new - def __deepcopy__(self, memo): + def __deepcopy__(self, memo: dict) -> "Message": new = Message( timestamp=self.timestamp, arbitration_id=self.arbitration_id, @@ -278,17 +281,17 @@ def _check(self): "error state indicator is only allowed for CAN FD frames" ) - def equals(self, other, timestamp_delta=1.0e-6): + def equals( + self, other: "Message", timestamp_delta: Optional[Union[float, int]] = 1.0e-6 + ) -> bool: """ Compares a given message with this one. - :param can.Message other: the message to compare with + :param other: the message to compare with - :type timestamp_delta: float or int or None :param timestamp_delta: the maximum difference at which two timestamps are still considered equal or None to not compare timestamps - :rtype: bool :return: True iff the given message equals this one """ # see https://github.com/hardbyte/python-can/pull/413 for a discussion diff --git a/can/typechecking.py b/can/typechecking.py index d592d0d4a..9ca9edfd0 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -9,5 +9,12 @@ ) CanFilters = typing.Iterable[CanFilter] +# TODO: Once buffer protocol support lands in typing, we should switch to that, +# since can.message.Message attempts to call bytearray() on the given data, so +# this should have the same typing info. +# +# See: https://github.com/python/typing/issues/593 +CanData = typing.Union[bytes, bytearray, int, typing.Iterable[int]] + # Used for the Abstract Base Class Channel = typing.Union[int, str] From d58aac2d76175d4e40ec4087ca16bd809f65240d Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 3 Aug 2019 00:04:47 -0700 Subject: [PATCH 157/252] Add RedirectReader to the generated Listener docs This makes Sphinx aware of the RedirectReader class, which allows CAN Messages to be gatewayed from one Bus to another Bus. --- doc/listeners.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/listeners.rst b/doc/listeners.rst index 975de6fd1..fcdc32f52 100644 --- a/doc/listeners.rst +++ b/doc/listeners.rst @@ -41,6 +41,13 @@ BufferedReader :members: +RedirectReader +-------------- + +.. autoclass:: can.RedirectReader + :members: + + Logger ------ From 089e29cd1a0339043970a49172a3cffdfbe6f604 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 3 Aug 2019 12:32:51 +0200 Subject: [PATCH 158/252] State "windows only" in Vector docs Fix #463 --- doc/interfaces/vector.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/interfaces/vector.rst b/doc/interfaces/vector.rst index a936e693e..dcd45f1bf 100644 --- a/doc/interfaces/vector.rst +++ b/doc/interfaces/vector.rst @@ -1,7 +1,7 @@ Vector ====== -This interface adds support for CAN controllers by `Vector`_. +This interface adds support for CAN controllers by `Vector`_. Only Windows is supported. By default this library uses the channel configuration for CANalyzer. To use a different application, open Vector Hardware Config program and create From 35402f2f775278e520e35eb4607972b34c5f74c3 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 3 Aug 2019 12:39:45 +0200 Subject: [PATCH 159/252] raise more helpful exception if using vector on non-windows platforms --- can/interfaces/vector/canlib.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 5dc44c4e0..6e65561f0 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -11,6 +11,7 @@ import ctypes import logging import time +import os try: # Try builtin Python 3 Windows API @@ -97,8 +98,14 @@ def __init__( Which bitrate to use for data phase in CAN FD. Defaults to arbitration bitrate. """ + if os.name != "nt": + raise OSError( + f'The Vector interface is only supported on Windows, but you are running "{os.name}"' + ) + if xldriver is None: raise ImportError("The Vector API has not been loaded") + self.poll_interval = poll_interval if isinstance(channel, (list, tuple)): self.channels = channel From 451ac0b1b61c020043002ae87dc0d05327541ba0 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 3 Aug 2019 13:44:06 +0200 Subject: [PATCH 160/252] add testing flag to vector --- can/interfaces/vector/canlib.py | 2 +- test/test_vector.py | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 6e65561f0..8ba1afbde 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -98,7 +98,7 @@ def __init__( Which bitrate to use for data phase in CAN FD. Defaults to arbitration bitrate. """ - if os.name != "nt": + if os.name != "nt" and not kwargs.get("testing", d=False): raise OSError( f'The Vector interface is only supported on Windows, but you are running "{os.name}"' ) diff --git a/test/test_vector.py b/test/test_vector.py index 69fb42133..558a2ea4a 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -18,6 +18,7 @@ class TestVectorBus(unittest.TestCase): + def setUp(self) -> None: # basic mock for XLDriver can.interfaces.vector.canlib.xldriver = Mock() @@ -76,7 +77,7 @@ def tearDown(self) -> None: self.bus = None def test_bus_creation(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector") + self.bus = can.Bus(channel=0, bustype="vector", testing=True) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() @@ -92,7 +93,7 @@ def test_bus_creation(self) -> None: can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() def test_bus_creation_bitrate(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", bitrate=200000) + self.bus = can.Bus(channel=0, bustype="vector", bitrate=200000, testing=True) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() @@ -112,7 +113,7 @@ def test_bus_creation_bitrate(self) -> None: self.assertEqual(xlCanSetChannelBitrate_args[2], 200000) def test_bus_creation_fd(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", fd=True) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, testing=True) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() @@ -141,6 +142,7 @@ def test_bus_creation_fd_bitrate_timings(self) -> None: sjwDbr=13, tseg1Dbr=14, tseg2Dbr=15, + testing=True, ) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() @@ -171,19 +173,19 @@ def test_bus_creation_fd_bitrate_timings(self) -> None: self.assertEqual(canFdConf.tseg2Dbr, 15) def test_receive(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector") + self.bus = can.Bus(channel=0, bustype="vector", testing=True) self.bus.recv(timeout=0.05) can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() def test_receive_fd(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", fd=True) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, testing=True) self.bus.recv(timeout=0.05) can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() def test_send(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector") + self.bus = can.Bus(channel=0, bustype="vector", testing=True) msg = can.Message( arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True ) @@ -192,7 +194,7 @@ def test_send(self) -> None: can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_not_called() def test_send_fd(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", fd=True) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, testing=True) msg = can.Message( arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True ) @@ -201,19 +203,19 @@ def test_send_fd(self) -> None: can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_called() def test_flush_tx_buffer(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector") + self.bus = can.Bus(channel=0, bustype="vector", testing=True) self.bus.flush_tx_buffer() can.interfaces.vector.canlib.xldriver.xlCanFlushTransmitQueue.assert_called() def test_shutdown(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector") + self.bus = can.Bus(channel=0, bustype="vector", testing=True) self.bus.shutdown() can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() can.interfaces.vector.canlib.xldriver.xlClosePort.assert_called() can.interfaces.vector.canlib.xldriver.xlCloseDriver.assert_called() def test_reset(self): - self.bus = can.Bus(channel=0, bustype="vector") + self.bus = can.Bus(channel=0, bustype="vector", testing=True) self.bus.reset() can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() can.interfaces.vector.canlib.xldriver.xlActivateChannel.assert_called() From 39af904d188080c1656db5621efc627c7eac89b0 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 3 Aug 2019 14:06:27 +0200 Subject: [PATCH 161/252] syntax fixed --- can/interfaces/vector/canlib.py | 4 ++-- test/test_vector.py | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 8ba1afbde..7c8d9c7c3 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -71,7 +71,7 @@ def __init__( sjwDbr=2, tseg1Dbr=6, tseg2Dbr=3, - **kwargs + **kwargs, ): """ :param list channel: @@ -98,7 +98,7 @@ def __init__( Which bitrate to use for data phase in CAN FD. Defaults to arbitration bitrate. """ - if os.name != "nt" and not kwargs.get("testing", d=False): + if os.name != "nt" and not kwargs.get("_testing", False): raise OSError( f'The Vector interface is only supported on Windows, but you are running "{os.name}"' ) diff --git a/test/test_vector.py b/test/test_vector.py index 558a2ea4a..e5a634a50 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -18,7 +18,6 @@ class TestVectorBus(unittest.TestCase): - def setUp(self) -> None: # basic mock for XLDriver can.interfaces.vector.canlib.xldriver = Mock() @@ -77,7 +76,7 @@ def tearDown(self) -> None: self.bus = None def test_bus_creation(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", testing=True) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() @@ -93,7 +92,7 @@ def test_bus_creation(self) -> None: can.interfaces.vector.canlib.xldriver.xlCanSetChannelBitrate.assert_not_called() def test_bus_creation_bitrate(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", bitrate=200000, testing=True) + self.bus = can.Bus(channel=0, bustype="vector", bitrate=200000, _testing=True) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() @@ -113,7 +112,7 @@ def test_bus_creation_bitrate(self) -> None: self.assertEqual(xlCanSetChannelBitrate_args[2], 200000) def test_bus_creation_fd(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", fd=True, testing=True) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() can.interfaces.vector.canlib.xldriver.xlGetApplConfig.assert_called() @@ -142,7 +141,7 @@ def test_bus_creation_fd_bitrate_timings(self) -> None: sjwDbr=13, tseg1Dbr=14, tseg2Dbr=15, - testing=True, + _testing=True, ) self.assertIsInstance(self.bus, canlib.VectorBus) can.interfaces.vector.canlib.xldriver.xlOpenDriver.assert_called() @@ -173,19 +172,19 @@ def test_bus_creation_fd_bitrate_timings(self) -> None: self.assertEqual(canFdConf.tseg2Dbr, 15) def test_receive(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", testing=True) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) self.bus.recv(timeout=0.05) can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() def test_receive_fd(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", fd=True, testing=True) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) self.bus.recv(timeout=0.05) can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() def test_send(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", testing=True) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) msg = can.Message( arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True ) @@ -194,7 +193,7 @@ def test_send(self) -> None: can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_not_called() def test_send_fd(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", fd=True, testing=True) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) msg = can.Message( arbitration_id=0xC0FFEF, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=True ) @@ -203,19 +202,19 @@ def test_send_fd(self) -> None: can.interfaces.vector.canlib.xldriver.xlCanTransmitEx.assert_called() def test_flush_tx_buffer(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", testing=True) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) self.bus.flush_tx_buffer() can.interfaces.vector.canlib.xldriver.xlCanFlushTransmitQueue.assert_called() def test_shutdown(self) -> None: - self.bus = can.Bus(channel=0, bustype="vector", testing=True) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) self.bus.shutdown() can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() can.interfaces.vector.canlib.xldriver.xlClosePort.assert_called() can.interfaces.vector.canlib.xldriver.xlCloseDriver.assert_called() def test_reset(self): - self.bus = can.Bus(channel=0, bustype="vector", testing=True) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) self.bus.reset() can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() can.interfaces.vector.canlib.xldriver.xlActivateChannel.assert_called() From 4de88d205b82bb8dcdecf695b8ddf9b514e9233a Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 3 Aug 2019 14:44:14 +0200 Subject: [PATCH 162/252] add tiny test for new exception being thrown --- test/test_vector.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/test_vector.py b/test/test_vector.py index e5a634a50..639b28de9 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -8,6 +8,7 @@ import ctypes import time import logging +import os import unittest from unittest.mock import Mock @@ -213,12 +214,19 @@ def test_shutdown(self) -> None: can.interfaces.vector.canlib.xldriver.xlClosePort.assert_called() can.interfaces.vector.canlib.xldriver.xlCloseDriver.assert_called() - def test_reset(self): + def test_reset(self) -> None: self.bus = can.Bus(channel=0, bustype="vector", _testing=True) self.bus.reset() can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() can.interfaces.vector.canlib.xldriver.xlActivateChannel.assert_called() + def test_called_without_testing_argument(self) -> None: + """This tests if an exception is thrown when we are not running on Windows.""" + if os.name != "nt": + with self.assertRaises(OSError): + # do not set the _testing argument, since it supresses the exception + can.Bus(channel=0, bustype="vector") + def xlGetApplConfig( app_name_p: ctypes.c_char_p, From d035cab46e035a4873d279e3e74cd536ca777fb1 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Sun, 4 Aug 2019 14:15:28 +0200 Subject: [PATCH 163/252] install pywin32 on appveyor CI --- .appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index 500c71320..4bd178a8f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,6 +14,9 @@ install: # Prepend Python installation and scripts (e.g. pytest) to PATH - set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH% + # Install pywin32 for Windows Events + - "python -m pip install pywin32" + # We need to install the python-can library itself including the dependencies - "python -m pip install .[test,neovi]" From 88b4a2115b70789ebe9db591c06bcf2a5d51fe4a Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Tue, 6 Aug 2019 13:31:46 +0200 Subject: [PATCH 164/252] Add Vector dependencies to docs (#670) --- doc/installation.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/installation.rst b/doc/installation.rst index 147b27b74..a70f7d5ea 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -78,6 +78,17 @@ neoVI See :doc:`interfaces/neovi`. +Vector +~~~~~~ + +To install ``python-can`` using the XL Driver Library as the backend: + +1. Install the `latest drivers `__ for your Vector hardware interface. + +2. Install the `XL Driver Library `__ or copy the ``vxlapi.dll`` and/or + ``vxlapi64.dll`` into your working directory. + +3. Use Vector Hardware Configuration to assign a channel to your application. Installing python-can in development mode ----------------------------------------- From 93062bdd63ccc85695f9d154d18d4e3e5aeceecf Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Tue, 6 Aug 2019 19:01:04 +0200 Subject: [PATCH 165/252] Revert "install pywin32 on appveyor CI" This reverts commit d035cab4 --- .appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 4bd178a8f..500c71320 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,9 +14,6 @@ install: # Prepend Python installation and scripts (e.g. pytest) to PATH - set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH% - # Install pywin32 for Windows Events - - "python -m pip install pywin32" - # We need to install the python-can library itself including the dependencies - "python -m pip install .[test,neovi]" From 3c5edf80a3d70dd090abe8d646f6090701f39b93 Mon Sep 17 00:00:00 2001 From: zariiii9003 Date: Tue, 6 Aug 2019 19:02:20 +0200 Subject: [PATCH 166/252] add pywin32 to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index a68e76721..2b1bf2296 100644 --- a/setup.py +++ b/setup.py @@ -104,6 +104,7 @@ 'windows-curses;platform_system=="Windows"', "filelock", "mypy_extensions >= 0.4.0, < 0.5.0", + 'pywin32;platform_system=="Windows"', ], setup_requires=pytest_runner, extras_require=extras_require, From 78585da6ed3dab1db2fa2710b33d1b0ca99fab3c Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 3 Aug 2019 12:57:48 -0700 Subject: [PATCH 167/252] Add the seeedstudio interface to docs Previously the seeedstudio interface wasn't being included in the generated docs. This silences the warning: WARNING: document isn't included in any toctree --- doc/interfaces.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/interfaces.rst b/doc/interfaces.rst index 7c8253f9e..a19dc7e84 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -25,6 +25,7 @@ The available interfaces are: interfaces/virtual interfaces/canalystii interfaces/systec + interfaces/seeedstudio Additional interfaces can be added via a plugin interface. An external package can register a new interface by using the ``can.interface`` entry point in its setup.py. From 3594d6bcf120364cce85b3625b6b9546f56fa4cb Mon Sep 17 00:00:00 2001 From: Karl Date: Sat, 3 Aug 2019 12:19:17 -0700 Subject: [PATCH 168/252] Fix the docs for can.detect_available_configs This fixes the Sphinx documentation for can.detect_available_configs so that it appears in the generated build output. --- doc/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 1aef90e9a..193d1c707 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -11,7 +11,7 @@ A form of CAN interface is also required. .. toctree:: :maxdepth: 1 - + bus message listeners @@ -25,7 +25,7 @@ Utilities --------- -.. automethod:: can.detect_available_configs +.. autofunction:: can.detect_available_configs .. _notifier: From 7252e865a89992ed8648a9d20e9efd648ec3c581 Mon Sep 17 00:00:00 2001 From: Karl Date: Fri, 2 Aug 2019 23:43:48 -0700 Subject: [PATCH 169/252] Add typing annotations for can.listener This adds typing annotations for functions in can.listener. In addition, this remove the redundant typing information that was previously in the docstring, since we now have sphinx-autodoc-typehints to generate the types for the docs from the annotations in the function signature. This works towards PEP 561 compatibility. --- can/broadcastmanager.py | 37 ++++++++++++++++++----------------- can/bus.py | 25 ++++++++++++------------ can/listener.py | 43 ++++++++++++++++++++++------------------- 3 files changed, 55 insertions(+), 50 deletions(-) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 1b616cf34..733693802 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -7,17 +7,20 @@ :meth:`can.BusABC.send_periodic`. """ -from typing import Optional, Sequence, Tuple, Union +from typing import Optional, Sequence, Tuple, Union, TYPE_CHECKING -import can.typechecking +from can import typechecking + +if TYPE_CHECKING: + from can.bus import BusABC + +from can.message import Message import abc import logging import threading import time -import can - # try to import win32event for event-based cyclic send task(needs pywin32 package) try: import win32event @@ -48,9 +51,7 @@ class CyclicSendTaskABC(CyclicTask): Message send task with defined period """ - def __init__( - self, messages: Union[Sequence[can.Message], can.Message], period: float - ): + def __init__(self, messages: Union[Sequence[Message], Message], period: float): """ :param messages: The messages to be sent periodically. @@ -65,8 +66,8 @@ def __init__( @staticmethod def _check_and_convert_messages( - messages: Union[Sequence[can.Message], can.Message] - ) -> Tuple[can.Message, ...]: + messages: Union[Sequence[Message], Message] + ) -> Tuple[Message, ...]: """Helper function to convert a Message or Sequence of messages into a tuple, and raises an error when the given value is invalid. @@ -76,7 +77,7 @@ def _check_and_convert_messages( Should be called when the cyclic task is initialized """ if not isinstance(messages, (list, tuple)): - if isinstance(messages, can.Message): + if isinstance(messages, Message): messages = [messages] else: raise ValueError("Must be either a list, tuple, or a Message") @@ -102,7 +103,7 @@ def _check_and_convert_messages( class LimitedDurationCyclicSendTaskABC(CyclicSendTaskABC): def __init__( self, - messages: Union[Sequence[can.Message], can.Message], + messages: Union[Sequence[Message], Message], period: float, duration: Optional[float], ): @@ -131,7 +132,7 @@ def start(self): class ModifiableCyclicTaskABC(CyclicSendTaskABC): """Adds support for modifying a periodic message""" - def _check_modified_messages(self, messages: Tuple[can.Message, ...]): + def _check_modified_messages(self, messages: Tuple[Message, ...]): """Helper function to perform error checking when modifying the data in the cyclic task. @@ -151,12 +152,12 @@ def _check_modified_messages(self, messages: Tuple[can.Message, ...]): "from when the task was created" ) - def modify_data(self, messages: Union[Sequence[can.Message], can.Message]): + def modify_data(self, messages: Union[Sequence[Message], Message]): """Update the contents of the periodically sent messages, without altering the timing. :param messages: - The messages with the new :attr:`can.Message.data`. + The messages with the new :attr:`Message.data`. Note: The arbitration ID cannot be changed. @@ -176,8 +177,8 @@ class MultiRateCyclicSendTaskABC(CyclicSendTaskABC): def __init__( self, - channel: can.typechecking.Channel, - messages: Union[Sequence[can.Message], can.Message], + channel: typechecking.Channel, + messages: Union[Sequence[Message], Message], count: int, initial_period: float, subsequent_period: float, @@ -203,9 +204,9 @@ class ThreadBasedCyclicSendTask( def __init__( self, - bus: "can.bus.BusABC", + bus: "BusABC", lock: threading.Lock, - messages: Union[Sequence[can.Message], can.Message], + messages: Union[Sequence[Message], Message], period: float, duration: Optional[float] = None, ): diff --git a/can/bus.py b/can/bus.py index c188939a9..e22bf6157 100644 --- a/can/bus.py +++ b/can/bus.py @@ -15,7 +15,8 @@ from time import time from aenum import Enum, auto -from .broadcastmanager import ThreadBasedCyclicSendTask +from can.broadcastmanager import ThreadBasedCyclicSendTask +from can.message import Message LOG = logging.getLogger(__name__) @@ -68,14 +69,14 @@ def __init__( def __str__(self) -> str: return self.channel_info - def recv(self, timeout: Optional[float] = None) -> Optional[can.Message]: + def recv(self, timeout: Optional[float] = None) -> Optional[Message]: """Block waiting for a message from the Bus. :param timeout: seconds to wait for a message or None to wait indefinitely :return: - None on timeout or a :class:`can.Message` object. + None on timeout or a :class:`Message` object. :raises can.CanError: if an error occurred while reading """ @@ -109,7 +110,7 @@ def recv(self, timeout: Optional[float] = None) -> Optional[can.Message]: def _recv_internal( self, timeout: Optional[float] - ) -> Tuple[Optional[can.Message], bool]: + ) -> Tuple[Optional[Message], bool]: """ Read a message from the bus and tell whether it was filtered. This methods may be called by :meth:`~can.BusABC.recv` @@ -152,12 +153,12 @@ def _recv_internal( raise NotImplementedError("Trying to read from a write only bus?") @abstractmethod - def send(self, msg: can.Message, timeout: Optional[float] = None): + def send(self, msg: Message, timeout: Optional[float] = None): """Transmit a message to the CAN bus. Override this method to enable the transmit path. - :param can.Message msg: A message object. + :param Message msg: A message object. :param timeout: If > 0, wait up to this many seconds for message to be ACK'ed or @@ -173,7 +174,7 @@ def send(self, msg: can.Message, timeout: Optional[float] = None): def send_periodic( self, - msgs: Union[Sequence[can.Message], can.Message], + msgs: Union[Sequence[Message], Message], period: float, duration: Optional[float] = None, store_task: bool = True, @@ -217,7 +218,7 @@ def send_periodic( are associated with the Bus instance. """ if not isinstance(msgs, (list, tuple)): - if isinstance(msgs, can.Message): + if isinstance(msgs, Message): msgs = [msgs] else: raise ValueError("Must be either a list, tuple, or a Message") @@ -244,7 +245,7 @@ def wrapped_stop_method(remove_task=True): def _send_periodic_internal( self, - msgs: Union[Sequence[can.Message], can.Message], + msgs: Union[Sequence[Message], Message], period: float, duration: Optional[float] = None, ) -> can.broadcastmanager.CyclicSendTaskABC: @@ -291,7 +292,7 @@ def stop_all_periodic_tasks(self, remove_tasks=True): if remove_tasks: self._periodic_tasks = [] - def __iter__(self) -> Iterator[can.Message]: + def __iter__(self) -> Iterator[Message]: """Allow iteration on messages as they are received. >>> for msg in bus: @@ -299,7 +300,7 @@ def __iter__(self) -> Iterator[can.Message]: :yields: - :class:`can.Message` msg objects. + :class:`Message` msg objects. """ while True: msg = self.recv(timeout=1.0) @@ -352,7 +353,7 @@ def _apply_filters(self, filters: Optional[can.typechecking.CanFilters]): See :meth:`~can.BusABC.set_filters` for details. """ - def _matches_filters(self, msg: can.Message) -> bool: + def _matches_filters(self, msg: Message) -> bool: """Checks whether the given message matches at least one of the current filters. See :meth:`~can.BusABC.set_filters` for details on how the filters work. diff --git a/can/listener.py b/can/listener.py index ac0f1aa64..c33fec70a 100644 --- a/can/listener.py +++ b/can/listener.py @@ -4,6 +4,11 @@ This module contains the implementation of `can.Listener` and some readers. """ +from typing import AsyncIterator, Awaitable, Optional + +from can.message import Message +from can.bus import BusABC + from abc import ABCMeta, abstractmethod try: @@ -33,20 +38,20 @@ class Listener(metaclass=ABCMeta): """ @abstractmethod - def on_message_received(self, msg): + def on_message_received(self, msg: Message): """This method is called to handle the given message. - :param can.Message msg: the delivered message + :param msg: the delivered message """ - def __call__(self, msg): - return self.on_message_received(msg) + def __call__(self, msg: Message): + self.on_message_received(msg) - def on_error(self, exc): + def on_error(self, exc: Exception): """This method is called to handle any exception in the receive thread. - :param Exception exc: The exception causing the thread to stop + :param exc: The exception causing the thread to stop """ def stop(self): @@ -64,10 +69,10 @@ class RedirectReader(Listener): """ - def __init__(self, bus): + def __init__(self, bus: BusABC): self.bus = bus - def on_message_received(self, msg): + def on_message_received(self, msg: Message): self.bus.send(msg) @@ -90,7 +95,7 @@ def __init__(self): self.buffer = SimpleQueue() self.is_stopped = False - def on_message_received(self, msg): + def on_message_received(self, msg: Message): """Append a message to the buffer. :raises: BufferError @@ -101,16 +106,15 @@ def on_message_received(self, msg): else: self.buffer.put(msg) - def get_message(self, timeout=0.5): + def get_message(self, timeout: float = 0.5) -> Optional[Message]: """ 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, or else returns None (whichever is shorter). This method does not block after :meth:`can.BufferedReader.stop` has been called. - :param float timeout: The number of seconds to wait for a new message. - :rytpe: can.Message or None - :return: the message if there is one, or None if there is not. + :param timeout: The number of seconds to wait for a new message. + :return: the Message if there is one, or None if there is not. """ try: return self.buffer.get(block=not self.is_stopped, timeout=timeout) @@ -134,30 +138,29 @@ class AsyncBufferedReader(Listener): print(msg) """ - def __init__(self, loop=None): + def __init__(self, loop: Optional[asyncio.events.AbstractEventLoop] = None): # set to "infinite" size - self.buffer = asyncio.Queue(loop=loop) + self.buffer: "asyncio.Queue[Message]" = asyncio.Queue(loop=loop) - def on_message_received(self, msg): + def on_message_received(self, msg: Message): """Append a message to the buffer. Must only be called inside an event loop! """ self.buffer.put_nowait(msg) - async def get_message(self): + async def get_message(self) -> Message: """ Retrieve the latest message when awaited for:: msg = await reader.get_message() - :rtype: can.Message :return: The CAN message. """ return await self.buffer.get() - def __aiter__(self): + def __aiter__(self) -> AsyncIterator[Message]: return self - def __anext__(self): + def __anext__(self) -> Awaitable[Message]: return self.buffer.get() From 29e8e1fecca1aead3b2ab3ed09833289fbc5947b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 7 Aug 2019 01:41:28 +0200 Subject: [PATCH 170/252] Add .mypy_cache to gitignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6b813427e..5c4962ea5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ test/__tempdir__/ .pytest_cache/ +.mypy_cache/ # ------------------------- # below: https://github.com/github/gitignore/blob/da00310ccba9de9a988cc973ef5238ad2c1460e9/Python.gitignore From a7527a4eb4c1d2ac93cfe53a3d7c363ef8ef157b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 8 Aug 2019 21:05:20 +0200 Subject: [PATCH 171/252] add mypy deamon config to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5c4962ea5..258ca73ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ test/__tempdir__/ .pytest_cache/ .mypy_cache/ +.dmypy.json +dmypy.json # ------------------------- # below: https://github.com/github/gitignore/blob/da00310ccba9de9a988cc973ef5238ad2c1460e9/Python.gitignore From ec4862f760434b26e7e6b9b98de73e88f1d228d3 Mon Sep 17 00:00:00 2001 From: Karl Date: Fri, 9 Aug 2019 15:54:26 -0700 Subject: [PATCH 172/252] Remove deprecated SocketCAN interfaces from list The SocketCAN interface used to be split into socketcan_native and socketcan_ctypes interfaces. However, these have now been deprecated, with a deprecation window throughout 3.*.* releases. The code relevant to these interfaces has already been cleaned up, but it seems like these references were missed in the process. --- can/interfaces/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 66e55153d..174b7f7ab 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -34,6 +34,4 @@ } ) -VALID_INTERFACES = frozenset( - list(BACKENDS.keys()) + ["socketcan_native", "socketcan_ctypes"] -) +VALID_INTERFACES = frozenset(list(BACKENDS.keys())) From 807fc54da96e4f3e203f9532e0d1f2646c0ebc8c Mon Sep 17 00:00:00 2001 From: Benny Meisels Date: Mon, 12 Aug 2019 12:15:29 +0300 Subject: [PATCH 173/252] resolves #680 --- can/interfaces/pcan/basic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index 7557f036c..eb29f0e16 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -780,7 +780,7 @@ def GetValue(self, Channel, Parameter): A touple with 2 values """ try: - if Parameter in { + if Parameter in ( PCAN_API_VERSION, PCAN_HARDWARE_NAME, PCAN_CHANNEL_VERSION, @@ -788,7 +788,7 @@ def GetValue(self, Channel, Parameter): PCAN_TRACE_LOCATION, PCAN_BITRATE_INFO_FD, PCAN_IP_ADDRESS, - }: + ): mybuffer = create_string_buffer(256) else: mybuffer = c_int(0) @@ -822,7 +822,7 @@ def SetValue(self, Channel, Parameter, Buffer): A TPCANStatus error code """ try: - if Parameter in {PCAN_LOG_LOCATION, PCAN_LOG_TEXT, PCAN_TRACE_LOCATION}: + if Parameter in (PCAN_LOG_LOCATION, PCAN_LOG_TEXT, PCAN_TRACE_LOCATION): mybuffer = create_string_buffer(256) else: mybuffer = c_int(0) From e6f3453e0abf3665b7c932d5ffb549ce73630a59 Mon Sep 17 00:00:00 2001 From: Karl Date: Wed, 7 Aug 2019 21:16:47 -0700 Subject: [PATCH 174/252] Add typing annotations for can.notifier This adds typing annotations for functions in can.notifier. In addition, this remove the redundant typing information that was previously in the docstring, since we now have sphinx-autodoc-typehints to generate the types for the docs from the annotations in the function signature. This works towards PEP 561 compatibility. --- can/notifier.py | 65 +++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/can/notifier.py b/can/notifier.py index 5d0642ee6..679af384d 100644 --- a/can/notifier.py +++ b/can/notifier.py @@ -4,6 +4,12 @@ This module contains the implementation of :class:`~can.Notifier`. """ +from typing import Iterable, List, Optional, Union + +from can.bus import BusABC +from can.listener import Listener +from can.message import Message + import threading import logging import time @@ -13,7 +19,13 @@ class Notifier: - def __init__(self, bus, listeners, timeout=1.0, loop=None): + def __init__( + self, + bus: BusABC, + listeners: Iterable[Listener], + timeout: float = 1.0, + loop: Optional[asyncio.AbstractEventLoop] = None, + ): """Manages the distribution of :class:`can.Message` instances to listeners. Supports multiple buses and listeners. @@ -24,37 +36,40 @@ def __init__(self, bus, listeners, timeout=1.0, loop=None): many listeners carry out flush operations to persist data. - :param can.BusABC bus: A :ref:`bus` or a list of buses to listen to. - :param list listeners: An iterable of :class:`~can.Listener` - :param float timeout: An optional maximum number of seconds to wait for any message. - :param asyncio.AbstractEventLoop loop: - An :mod:`asyncio` event loop to schedule listeners in. + :param bus: A :ref:`bus` or a list of buses to listen to. + :param listeners: An iterable of :class:`~can.Listener` + :param timeout: An optional maximum number of seconds to wait for any message. + :param loop: An :mod:`asyncio` event loop to schedule listeners in. """ - self.listeners = listeners + self.listeners = list(listeners) self.bus = bus self.timeout = timeout self._loop = loop #: Exception raised in thread - self.exception = None + self.exception: Optional[Exception] = None self._running = True self._lock = threading.Lock() - self._readers = [] + self._readers: List[Union[int, threading.Thread]] = [] buses = self.bus if isinstance(self.bus, list) else [self.bus] for bus in buses: self.add_bus(bus) - def add_bus(self, bus): + def add_bus(self, bus: BusABC): """Add a bus for notification. - :param can.BusABC bus: + :param bus: CAN bus instance. """ - if self._loop is not None and hasattr(bus, "fileno") and bus.fileno() >= 0: + if ( + self._loop is not None + and hasattr(bus, "fileno") + and bus.fileno() >= 0 # type: ignore + ): # Use file descriptor to watch for messages - reader = bus.fileno() + reader = bus.fileno() # type: ignore self._loop.add_reader(reader, self._on_message_available, bus) else: reader = threading.Thread( @@ -66,11 +81,11 @@ def add_bus(self, bus): reader.start() self._readers.append(reader) - def stop(self, timeout=5): + def stop(self, timeout: float = 5): """Stop notifying Listeners when new :class:`~can.Message` objects arrive and call :meth:`~can.Listener.stop` on each Listener. - :param float timeout: + :param timeout: Max time in seconds to wait for receive threads to finish. Should be longer than timeout given at instantiation. """ @@ -81,14 +96,14 @@ def stop(self, timeout=5): now = time.time() if now < end_time: reader.join(end_time - now) - else: + elif self._loop: # reader is a file descriptor self._loop.remove_reader(reader) for listener in self.listeners: if hasattr(listener, "stop"): listener.stop() - def _rx_thread(self, bus): + def _rx_thread(self, bus: BusABC): msg = None try: while self._running: @@ -109,40 +124,38 @@ def _rx_thread(self, bus): self._on_error(exc) raise - def _on_message_available(self, bus): + def _on_message_available(self, bus: BusABC): msg = bus.recv(0) if msg is not None: self._on_message_received(msg) - def _on_message_received(self, msg): + def _on_message_received(self, msg: Message): for callback in self.listeners: res = callback(msg) if self._loop is not None and asyncio.iscoroutine(res): # Schedule coroutine self._loop.create_task(res) - def _on_error(self, exc): + def _on_error(self, exc: Exception): for listener in self.listeners: if hasattr(listener, "on_error"): listener.on_error(exc) - def add_listener(self, listener): + def add_listener(self, listener: Listener): """Add new Listener to the notification list. If it is already present, it will be called two times each time a message arrives. - :param can.Listener listener: Listener to be added to - the list to be notified + :param listener: Listener to be added to the list to be notified """ self.listeners.append(listener) - def remove_listener(self, listener): + def remove_listener(self, listener: Listener): """Remove a listener from the notification list. This method trows an exception if the given listener is not part of the stored listeners. - :param can.Listener listener: Listener to be removed from - the list to be notified + :param listener: Listener to be removed from the list to be notified :raises ValueError: if `listener` was never added to this notifier """ self.listeners.remove(listener) From cd3f81bedda963eba2f492631999d6101561f55c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 7 Aug 2019 01:43:22 +0200 Subject: [PATCH 175/252] add typing to the generic IO module --- .travis.yml | 2 ++ can/io/generic.py | 16 ++++++++++------ can/typechecking.py | 6 ++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4d765d43a..fc226944c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -99,7 +99,9 @@ jobs: can/notifier.py can/player.py can/thread_safe_bus.py + can/typechecking.py can/util.py + can/io/generic.py - stage: linter name: "Formatting Checks" python: "3.7" diff --git a/can/io/generic.py b/can/io/generic.py index 62bae18d4..8c86a1fd3 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -5,6 +5,9 @@ """ from abc import ABCMeta +from typing import Optional, Union, cast + +from can.typechecking import FileLike, PathLike class BaseIOHandler(metaclass=ABCMeta): @@ -12,24 +15,24 @@ class BaseIOHandler(metaclass=ABCMeta): Can be used as a context manager. - :attr file-like file: + :attr Optional[FileLike] file: the file-like object that is kept internally, or None if none was opened """ - def __init__(self, file, mode="rt"): + def __init__(self, file: Optional[Union[FileLike, PathLike]], mode: str = "rt"): """ :param file: a path-like object to open a file, a file-like object to be used as a file or `None` to not use a file at all - :param str mode: the mode that should be used to open the file, see - :func:`open`, ignored if *file* is `None` + :param mode: the mode that should be used to open the file, see + :func:`open`, ignored if *file* is `None` """ if file is None or (hasattr(file, "read") and hasattr(file, "write")): # file is None or some file-like object - self.file = file + self.file = cast(Optional[FileLike], file) else: # file is some path-like object - self.file = open(file, mode) + self.file = open(cast(PathLike, file), mode) # for multiple inheritance super().__init__() @@ -41,6 +44,7 @@ def __exit__(self, *args): self.stop() def stop(self): + """Closes the undelying file-like object and flushes it, if it was opened in write mode.""" if self.file is not None: # this also implies a flush() self.file.close() diff --git a/can/typechecking.py b/can/typechecking.py index 9ca9edfd0..bf02e5c85 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -1,5 +1,7 @@ """Types for mypy type-checking """ + +import os import typing import mypy_extensions @@ -18,3 +20,7 @@ # Used for the Abstract Base Class Channel = typing.Union[int, str] + +# Used by the IO module +FileLike = typing.IO[typing.Any] +PathLike = typing.Union[str, bytes, os.PathLike] From fb4e5298ee978f55ed2188ee61eff18230440968 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 7 Aug 2019 02:06:41 +0200 Subject: [PATCH 176/252] add io/logger.py --- .travis.yml | 1 + can/io/generic.py | 4 ++-- can/io/logger.py | 50 ++++++++++++++++++++++++--------------------- can/typechecking.py | 3 ++- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc226944c..51a5e67d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -102,6 +102,7 @@ jobs: can/typechecking.py can/util.py can/io/generic.py + can/io/logger.py - stage: linter name: "Formatting Checks" python: "3.7" diff --git a/can/io/generic.py b/can/io/generic.py index 8c86a1fd3..cb543c94d 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -7,7 +7,7 @@ from abc import ABCMeta from typing import Optional, Union, cast -from can.typechecking import FileLike, PathLike +from can.typechecking import FileLike, PathLike, AcceptedIOType class BaseIOHandler(metaclass=ABCMeta): @@ -20,7 +20,7 @@ class BaseIOHandler(metaclass=ABCMeta): was opened """ - def __init__(self, file: Optional[Union[FileLike, PathLike]], mode: str = "rt"): + def __init__(self, file: AcceptedIOType, mode: str = "rt"): """ :param file: a path-like object to open a file, a file-like object to be used as a file or `None` to not use a file at all diff --git a/can/io/logger.py b/can/io/logger.py index edffe1c78..b0c7fc4da 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -5,6 +5,8 @@ """ import logging +import pathlib +import typing from ..listener import Listener from .generic import BaseIOHandler @@ -15,6 +17,8 @@ from .sqlite import SqliteWriter from .printer import Printer +from can.typechecking import PathLike + log = logging.getLogger("can.io.logger") @@ -28,36 +32,36 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method * .csv: :class:`can.CSVWriter` * .db: :class:`can.SqliteWriter` * .log :class:`can.CanutilsLogWriter` - * other: :class:`can.Printer` + + The **filename** may also be *None*, to fall back to :class:`can.Printer`. The log files may be incomplete until `stop()` is called due to buffering. .. note:: - This class itself is just a dispatcher, and any positional an keyword + This class itself is just a dispatcher, and any positional and keyword arguments are passed on to the returned instance. """ @staticmethod - def __new__(cls, filename, *args, **kwargs): + def __new__(cls, filename: typing.Optional[PathLike], *args, **kwargs): """ - :type filename: str or None or path-like - :param filename: the filename/path the file to write to, - may be a path-like object if the target logger supports - it, and may be None to instantiate a :class:`~can.Printer` - + :param filename: the filename/path of the file to write to, + may be a path-like object or None to + instantiate a :class:`~can.Printer` """ - if filename: - if filename.endswith(".asc"): - return ASCWriter(filename, *args, **kwargs) - elif filename.endswith(".blf"): - return BLFWriter(filename, *args, **kwargs) - elif filename.endswith(".csv"): - return CSVWriter(filename, *args, **kwargs) - elif filename.endswith(".db"): - return SqliteWriter(filename, *args, **kwargs) - elif filename.endswith(".log"): - return CanutilsLogWriter(filename, *args, **kwargs) - - # else: - log.warning('unknown file type "%s", falling pack to can.Printer', filename) - return Printer(filename, *args, **kwargs) + if filename is None: + return Printer(*args, **kwargs) + + suffix = pathlib.PurePath(filename).suffix + if suffix == ".asc": + return ASCWriter(filename, *args, **kwargs) + if suffix == ".blf": + return BLFWriter(filename, *args, **kwargs) + if suffix == ".csv": + return CSVWriter(filename, *args, **kwargs) + if suffix == ".db": + return SqliteWriter(filename, *args, **kwargs) + if suffix == ".log": + return CanutilsLogWriter(filename, *args, **kwargs) + + raise ValueError(f'unknown file type "{filename}"') diff --git a/can/typechecking.py b/can/typechecking.py index bf02e5c85..773e38cfa 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -23,4 +23,5 @@ # Used by the IO module FileLike = typing.IO[typing.Any] -PathLike = typing.Union[str, bytes, os.PathLike] +PathLike = typing.Union[str, os.PathLike[str]] +AcceptedIOType = typing.Optional[typing.Union[FileLike, PathLike]] From 3200713d722a1088f994268247446c44ccb89681 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 7 Aug 2019 02:09:33 +0200 Subject: [PATCH 177/252] fix problem with shpinx-build and typing --- can/io/generic.py | 8 ++++---- can/io/logger.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/can/io/generic.py b/can/io/generic.py index cb543c94d..07703074d 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -7,7 +7,7 @@ from abc import ABCMeta from typing import Optional, Union, cast -from can.typechecking import FileLike, PathLike, AcceptedIOType +import can.typechecking class BaseIOHandler(metaclass=ABCMeta): @@ -20,7 +20,7 @@ class BaseIOHandler(metaclass=ABCMeta): was opened """ - def __init__(self, file: AcceptedIOType, mode: str = "rt"): + def __init__(self, file: can.typechecking.AcceptedIOType, mode: str = "rt"): """ :param file: a path-like object to open a file, a file-like object to be used as a file or `None` to not use a file at all @@ -29,10 +29,10 @@ def __init__(self, file: AcceptedIOType, mode: str = "rt"): """ if file is None or (hasattr(file, "read") and hasattr(file, "write")): # file is None or some file-like object - self.file = cast(Optional[FileLike], file) + self.file = cast(Optional[can.typechecking.FileLike], file) else: # file is some path-like object - self.file = open(cast(PathLike, file), mode) + self.file = open(cast(can.typechecking.PathLike, file), mode) # for multiple inheritance super().__init__() diff --git a/can/io/logger.py b/can/io/logger.py index b0c7fc4da..a39834342 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -17,7 +17,7 @@ from .sqlite import SqliteWriter from .printer import Printer -from can.typechecking import PathLike +import can.typechecking log = logging.getLogger("can.io.logger") @@ -43,7 +43,7 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method """ @staticmethod - def __new__(cls, filename: typing.Optional[PathLike], *args, **kwargs): + def __new__(cls, filename: typing.Optional[can.typechecking.PathLike], *args, **kwargs): """ :param filename: the filename/path of the file to write to, may be a path-like object or None to From dfa264a02213dc4674ef841e1db1eef98a95d4ff Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 7 Aug 2019 02:18:17 +0200 Subject: [PATCH 178/252] fix formatting --- can/io/generic.py | 10 +++++----- can/io/logger.py | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/can/io/generic.py b/can/io/generic.py index 07703074d..67588008d 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -5,7 +5,7 @@ """ from abc import ABCMeta -from typing import Optional, Union, cast +from typing import Optional, cast import can.typechecking @@ -20,7 +20,7 @@ class BaseIOHandler(metaclass=ABCMeta): was opened """ - def __init__(self, file: can.typechecking.AcceptedIOType, mode: str = "rt"): + def __init__(self, file: can.typechecking.AcceptedIOType, mode: str = "rt") -> None: """ :param file: a path-like object to open a file, a file-like object to be used as a file or `None` to not use a file at all @@ -37,13 +37,13 @@ def __init__(self, file: can.typechecking.AcceptedIOType, mode: str = "rt"): # for multiple inheritance super().__init__() - def __enter__(self): + def __enter__(self) -> "BaseIOHandler": return self - def __exit__(self, *args): + def __exit__(self, *args) -> None: self.stop() - def stop(self): + def stop(self) -> None: """Closes the undelying file-like object and flushes it, if it was opened in write mode.""" if self.file is not None: # this also implies a flush() diff --git a/can/io/logger.py b/can/io/logger.py index a39834342..678bbf02e 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -8,6 +8,8 @@ import pathlib import typing +import can.typechecking + from ..listener import Listener from .generic import BaseIOHandler from .asc import ASCWriter @@ -17,8 +19,6 @@ from .sqlite import SqliteWriter from .printer import Printer -import can.typechecking - log = logging.getLogger("can.io.logger") @@ -43,7 +43,9 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method """ @staticmethod - def __new__(cls, filename: typing.Optional[can.typechecking.PathLike], *args, **kwargs): + def __new__( + cls, filename: typing.Optional[can.typechecking.PathLike], *args, **kwargs + ): """ :param filename: the filename/path of the file to write to, may be a path-like object or None to From 8f8c5e7c8cfae2d4ef019011e226a281a18e02cb Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 8 Aug 2019 01:14:08 +0200 Subject: [PATCH 179/252] finalize IO typechecking, some import erros remain --- .travis.yml | 3 +-- can/io/generic.py | 13 +++++++++++ can/io/logger.py | 3 --- can/io/player.py | 57 +++++++++++++++++++++++++-------------------- can/typechecking.py | 2 +- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51a5e67d3..8ecb4abd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -101,8 +101,7 @@ jobs: can/thread_safe_bus.py can/typechecking.py can/util.py - can/io/generic.py - can/io/logger.py + can/io/**.py - stage: linter name: "Formatting Checks" python: "3.7" diff --git a/can/io/generic.py b/can/io/generic.py index 67588008d..59157e762 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -7,6 +7,7 @@ from abc import ABCMeta from typing import Optional, cast +import can import can.typechecking @@ -48,3 +49,15 @@ def stop(self) -> None: if self.file is not None: # this also implies a flush() self.file.close() + + +# pylint: disable=abstract-method,too-few-public-methods +class MessageWriter( + BaseIOHandler, can.Listener, metaclass=ABCMeta +): + """The base class for all writers.""" + + +# pylint: disable=too-few-public-methods +class MessageReader(BaseIOHandler, metaclass=ABCMeta): + """The base class for all readers.""" diff --git a/can/io/logger.py b/can/io/logger.py index 678bbf02e..1cd5ce579 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -4,7 +4,6 @@ See the :class:`Logger` class. """ -import logging import pathlib import typing @@ -19,8 +18,6 @@ from .sqlite import SqliteWriter from .printer import Printer -log = logging.getLogger("can.io.logger") - class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method """ diff --git a/can/io/player.py b/can/io/player.py index 3455fe97a..39a525ac0 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -6,8 +6,11 @@ in the recorded order an time intervals. """ +import pathlib from time import time, sleep -import logging +import typing + +import can.typechecking from .generic import BaseIOHandler from .asc import ASCReader @@ -16,8 +19,6 @@ from .csv import CSVReader from .sqlite import SqliteReader -log = logging.getLogger("can.io.player") - class LogReader(BaseIOHandler): """ @@ -45,45 +46,51 @@ class LogReader(BaseIOHandler): """ @staticmethod - def __new__(cls, filename, *args, **kwargs): + def __new__(cls, filename: can.typechecking.PathLike, *args, **kwargs): """ - :param str filename: the filename/path the file to read from + :param filename: the filename/path the file to read from """ - if filename.endswith(".asc"): - return ASCReader(filename, *args, **kwargs) - elif filename.endswith(".blf"): - return BLFReader(filename, *args, **kwargs) - elif filename.endswith(".csv"): - return CSVReader(filename, *args, **kwargs) - elif filename.endswith(".db"): + suffix = pathlib.PurePath(filename).suffix + + if suffix == ".asc": + return ASCReader(filename) + if suffix == ".blf": + return BLFReader(filename) + if suffix == ".csv": + return CSVReader(filename) + if suffix == ".db": return SqliteReader(filename, *args, **kwargs) - elif filename.endswith(".log"): - return CanutilsLogReader(filename, *args, **kwargs) - else: - raise NotImplementedError( - "No read support for this log format: {}".format(filename) - ) + if suffix == ".log": + return CanutilsLogReader(filename) + + raise NotImplementedError(f"No read support for this log format: {filename}") -class MessageSync: +class MessageSync: # pylint: disable=too-few-public-methods """ Used to iterate over some given messages in the recorded time. """ - def __init__(self, messages, timestamps=True, gap=0.0001, skip=60): + def __init__( + self, + messages: typing.Iterable[can.Message], + timestamps: bool = True, + gap: float = 0.0001, + skip: float = 60.0, + ) -> None: """Creates an new **MessageSync** instance. - :param Iterable[can.Message] messages: An iterable of :class:`can.Message` instances. - :param bool timestamps: Use the messages' timestamps. If False, uses the *gap* parameter as the time between messages. - :param float gap: Minimum time between sent messages in seconds - :param float skip: Skip periods of inactivity greater than this (in seconds). + :param messages: An iterable of :class:`can.Message` instances. + :param timestamps: Use the messages' timestamps. If False, uses the *gap* parameter as the time between messages. + :param gap: Minimum time between sent messages in seconds + :param skip: Skip periods of inactivity greater than this (in seconds). """ self.raw_messages = messages self.timestamps = timestamps self.gap = gap self.skip = skip - def __iter__(self): + def __iter__(self) -> typing.Generator[can.Message, None, None]: playback_start_time = time() recorded_start_time = None diff --git a/can/typechecking.py b/can/typechecking.py index 773e38cfa..1375a7a07 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -23,5 +23,5 @@ # Used by the IO module FileLike = typing.IO[typing.Any] -PathLike = typing.Union[str, os.PathLike[str]] +PathLike = typing.Union[str, "os.PathLike[str]"] AcceptedIOType = typing.Optional[typing.Union[FileLike, PathLike]] From b8a1130793378a6aab1703454addee89920376ef Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 8 Aug 2019 01:54:36 +0200 Subject: [PATCH 180/252] cleanups --- can/io/generic.py | 4 +--- can/io/logger.py | 3 +++ can/io/player.py | 8 ++++---- can/io/printer.py | 7 +++++-- can/typechecking.py | 4 +++- test/listener_test.py | 13 +++++++------ test/logformats_test.py | 5 ++++- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/can/io/generic.py b/can/io/generic.py index 59157e762..70d5e92d9 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -52,9 +52,7 @@ def stop(self) -> None: # pylint: disable=abstract-method,too-few-public-methods -class MessageWriter( - BaseIOHandler, can.Listener, metaclass=ABCMeta -): +class MessageWriter(BaseIOHandler, can.Listener, metaclass=ABCMeta): """The base class for all writers.""" diff --git a/can/io/logger.py b/can/io/logger.py index 1cd5ce579..33c2460c5 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -29,6 +29,7 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method * .csv: :class:`can.CSVWriter` * .db: :class:`can.SqliteWriter` * .log :class:`can.CanutilsLogWriter` + * .txt :class:`can.Printer` The **filename** may also be *None*, to fall back to :class:`can.Printer`. @@ -62,5 +63,7 @@ def __new__( return SqliteWriter(filename, *args, **kwargs) if suffix == ".log": return CanutilsLogWriter(filename, *args, **kwargs) + if suffix == ".txt": + return Printer(filename, *args, **kwargs) raise ValueError(f'unknown file type "{filename}"') diff --git a/can/io/player.py b/can/io/player.py index 39a525ac0..baee3cc4c 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -10,7 +10,7 @@ from time import time, sleep import typing -import can.typechecking +import can from .generic import BaseIOHandler from .asc import ASCReader @@ -46,7 +46,7 @@ class LogReader(BaseIOHandler): """ @staticmethod - def __new__(cls, filename: can.typechecking.PathLike, *args, **kwargs): + def __new__(cls, filename: "can.typechecking.PathLike", *args, **kwargs): """ :param filename: the filename/path the file to read from """ @@ -73,7 +73,7 @@ class MessageSync: # pylint: disable=too-few-public-methods def __init__( self, - messages: typing.Iterable[can.Message], + messages: typing.Iterable["can.Message"], timestamps: bool = True, gap: float = 0.0001, skip: float = 60.0, @@ -90,7 +90,7 @@ def __init__( self.gap = gap self.skip = skip - def __iter__(self) -> typing.Generator[can.Message, None, None]: + def __iter__(self) -> typing.Generator["can.Message", None, None]: playback_start_time = time() recorded_start_time = None diff --git a/can/io/printer.py b/can/io/printer.py index f6a4b28e0..d363e6917 100644 --- a/can/io/printer.py +++ b/can/io/printer.py @@ -22,15 +22,18 @@ class Printer(BaseIOHandler, Listener): standard out """ - def __init__(self, file=None): + def __init__(self, file=None, append=False): """ :param file: an optional path-like object or as file-like object to "print" to instead of writing to standard out (stdout) If this is a file-like object, is has to opened in text write mode, not binary write mode. + :param bool append: if set to `True` messages are appended to + the file, else the file is truncated """ self.write_to_file = file is not None - super().__init__(file, mode="w") + mode = "a" if append else "w" + super().__init__(file, mode=mode) def on_message_received(self, msg): if self.write_to_file: diff --git a/can/typechecking.py b/can/typechecking.py index 1375a7a07..53396c883 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -1,9 +1,11 @@ """Types for mypy type-checking """ -import os import typing +if typing.TYPE_CHECKING: + import os + import mypy_extensions CanFilter = mypy_extensions.TypedDict( diff --git a/test/listener_test.py b/test/listener_test.py index c44acc7a6..be820892c 100644 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -4,12 +4,10 @@ """ """ -from time import sleep import unittest import random import logging import tempfile -import sqlite3 import os from os.path import join, dirname @@ -137,13 +135,16 @@ def test_filetype_to_instance(extension, klass): test_filetype_to_instance(".log", can.CanutilsLogWriter) test_filetype_to_instance(".txt", can.Printer) - # test file extensions that should use a fallback - test_filetype_to_instance("", can.Printer) - test_filetype_to_instance(".", can.Printer) - test_filetype_to_instance(".some_unknown_extention_42", can.Printer) with can.Logger(None) as logger: self.assertIsInstance(logger, can.Printer) + # test file extensions that should use a fallback + should_fail_with = ["", ".", ".some_unknown_extention_42"] + for supposed_fail in should_fail_with: + with self.assertRaises(ValueError): + with can.Logger(supposed_fail): # make sure we close it anyways + pass + def testBufferedListenerReceives(self): a_listener = can.BufferedReader() a_listener(generate_message(0xDADADA)) diff --git a/test/logformats_test.py b/test/logformats_test.py index 46eded869..9983b0ecb 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -459,7 +459,10 @@ def test_read_all(self): class TestPrinter(unittest.TestCase): - """Tests that can.Printer does not crash""" + """Tests that can.Printer does not crash + + TODO test append mode + """ # TODO add CAN FD messages messages = ( From 32d1ffcff4a7302320205ffd7998c2557d8466ee Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 8 Aug 2019 02:02:09 +0200 Subject: [PATCH 181/252] fix problem in sphinx-build doc generation --- can/typechecking.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/can/typechecking.py b/can/typechecking.py index 53396c883..1583dd197 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -3,8 +3,7 @@ import typing -if typing.TYPE_CHECKING: - import os +import os import mypy_extensions From c01f49ab0c8739924dda681293995e1e062b0048 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 8 Aug 2019 02:09:16 +0200 Subject: [PATCH 182/252] make linter happier --- can/io/logger.py | 1 + can/io/player.py | 2 +- can/typechecking.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 33c2460c5..1e0ef5830 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -40,6 +40,7 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method arguments are passed on to the returned instance. """ + # pylint: disable=too-many-return-statements @staticmethod def __new__( cls, filename: typing.Optional[can.typechecking.PathLike], *args, **kwargs diff --git a/can/io/player.py b/can/io/player.py index baee3cc4c..556abee44 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -10,7 +10,7 @@ from time import time, sleep import typing -import can +import can # pylint: disable=unused-import from .generic import BaseIOHandler from .asc import ASCReader diff --git a/can/typechecking.py b/can/typechecking.py index 1583dd197..df2a68d56 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -3,7 +3,7 @@ import typing -import os +import os # pylint: disable=unused-import import mypy_extensions From 219f482885d5d49979c98ebc0876104cde989e5b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 8 Aug 2019 21:08:20 +0200 Subject: [PATCH 183/252] fix grammar in doc string Co-Authored-By: karl ding --- can/io/player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/player.py b/can/io/player.py index 556abee44..4f07639ed 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -48,7 +48,7 @@ class LogReader(BaseIOHandler): @staticmethod def __new__(cls, filename: "can.typechecking.PathLike", *args, **kwargs): """ - :param filename: the filename/path the file to read from + :param filename: the filename/path of the file to read from """ suffix = pathlib.PurePath(filename).suffix From 56c6adb21f7c2833f0ec4015e070df183687fc95 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Aug 2019 10:37:39 +0200 Subject: [PATCH 184/252] address review --- can/io/logger.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 1e0ef5830..f00bdb27f 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -53,18 +53,16 @@ def __new__( if filename is None: return Printer(*args, **kwargs) + lookup = { + ".asc": ASCWriter, + ".blf": BLFWriter, + ".csv": CSVWriter, + ".db": SqliteWriter, + ".log": CanutilsLogWriter, + ".txt": Printer, + } suffix = pathlib.PurePath(filename).suffix - if suffix == ".asc": - return ASCWriter(filename, *args, **kwargs) - if suffix == ".blf": - return BLFWriter(filename, *args, **kwargs) - if suffix == ".csv": - return CSVWriter(filename, *args, **kwargs) - if suffix == ".db": - return SqliteWriter(filename, *args, **kwargs) - if suffix == ".log": - return CanutilsLogWriter(filename, *args, **kwargs) - if suffix == ".txt": - return Printer(filename, *args, **kwargs) - - raise ValueError(f'unknown file type "{filename}"') + try: + return lookup[suffix](filename, *args, **kwargs) + except KeyError: + raise ValueError(f'unknown file type "{suffix}"') From 329f5bccfc463bdf20341d0363a591595163b144 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Aug 2019 10:47:44 +0200 Subject: [PATCH 185/252] address review --- can/io/logger.py | 1 - test/listener_test.py | 1 - 2 files changed, 2 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index f00bdb27f..4213edfc0 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -40,7 +40,6 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method arguments are passed on to the returned instance. """ - # pylint: disable=too-many-return-statements @staticmethod def __new__( cls, filename: typing.Optional[can.typechecking.PathLike], *args, **kwargs diff --git a/test/listener_test.py b/test/listener_test.py index be820892c..8014443d4 100644 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -138,7 +138,6 @@ def test_filetype_to_instance(extension, klass): with can.Logger(None) as logger: self.assertIsInstance(logger, can.Printer) - # test file extensions that should use a fallback should_fail_with = ["", ".", ".some_unknown_extention_42"] for supposed_fail in should_fail_with: with self.assertRaises(ValueError): From 2ae51001634373c07477d46ee8ad9c01d5290e81 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Aug 2019 10:54:17 +0200 Subject: [PATCH 186/252] address review on typing of PathLike --- can/typechecking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/typechecking.py b/can/typechecking.py index df2a68d56..e34b6b515 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -24,5 +24,5 @@ # Used by the IO module FileLike = typing.IO[typing.Any] -PathLike = typing.Union[str, "os.PathLike[str]"] -AcceptedIOType = typing.Optional[typing.Union[FileLike, PathLike]] +StringPathLike = typing.Union[str, "os.PathLike[str]"] +AcceptedIOType = typing.Optional[typing.Union[FileLike, StringPathLike]] From 2dda6238d6b1a9455e29d417eaea50300ddea68d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 12 Aug 2019 00:02:06 +0200 Subject: [PATCH 187/252] address review comments, cleanup tests --- can/io/generic.py | 2 +- can/io/logger.py | 5 +++-- can/io/player.py | 31 +++++++++++++++++-------------- can/typechecking.py | 3 ++- test/listener_test.py | 14 ++++++++------ 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/can/io/generic.py b/can/io/generic.py index 70d5e92d9..c64051b75 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -33,7 +33,7 @@ def __init__(self, file: can.typechecking.AcceptedIOType, mode: str = "rt") -> N self.file = cast(Optional[can.typechecking.FileLike], file) else: # file is some path-like object - self.file = open(cast(can.typechecking.PathLike, file), mode) + self.file = open(cast(can.typechecking.StringPathLike, file), mode) # for multiple inheritance super().__init__() diff --git a/can/io/logger.py b/can/io/logger.py index 4213edfc0..6336fc917 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -42,12 +42,13 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method @staticmethod def __new__( - cls, filename: typing.Optional[can.typechecking.PathLike], *args, **kwargs + cls, filename: typing.Optional[can.typechecking.StringPathLike], *args, **kwargs ): """ :param filename: the filename/path of the file to write to, may be a path-like object or None to instantiate a :class:`~can.Printer` + :raises ValueError: if the filename's suffix is of an unknown file type """ if filename is None: return Printer(*args, **kwargs) @@ -64,4 +65,4 @@ def __new__( try: return lookup[suffix](filename, *args, **kwargs) except KeyError: - raise ValueError(f'unknown file type "{suffix}"') + raise ValueError(f'No write support for this unknown log format "{suffix}"') diff --git a/can/io/player.py b/can/io/player.py index 4f07639ed..6e45e50ca 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -10,7 +10,8 @@ from time import time, sleep import typing -import can # pylint: disable=unused-import +if typing.TYPE_CHECKING: + import can from .generic import BaseIOHandler from .asc import ASCReader @@ -49,21 +50,22 @@ class LogReader(BaseIOHandler): def __new__(cls, filename: "can.typechecking.PathLike", *args, **kwargs): """ :param filename: the filename/path of the file to read from + :raises ValueError: if the filename's suffix is of an unknown file type """ suffix = pathlib.PurePath(filename).suffix - if suffix == ".asc": - return ASCReader(filename) - if suffix == ".blf": - return BLFReader(filename) - if suffix == ".csv": - return CSVReader(filename) - if suffix == ".db": - return SqliteReader(filename, *args, **kwargs) - if suffix == ".log": - return CanutilsLogReader(filename) - - raise NotImplementedError(f"No read support for this log format: {filename}") + lookup = { + ".asc": ASCReader, + ".blf": BLFReader, + ".csv": CSVReader, + ".db": SqliteReader, + ".log": CanutilsLogReader, + } + suffix = pathlib.PurePath(filename).suffix + try: + return lookup[suffix](filename, *args, **kwargs) + except KeyError: + raise ValueError(f'No read support for this unknown log format "{suffix}"') class MessageSync: # pylint: disable=too-few-public-methods @@ -81,7 +83,8 @@ def __init__( """Creates an new **MessageSync** instance. :param messages: An iterable of :class:`can.Message` instances. - :param timestamps: Use the messages' timestamps. If False, uses the *gap* parameter as the time between messages. + :param timestamps: Use the messages' timestamps. If False, uses the *gap* parameter + as the time between messages. :param gap: Minimum time between sent messages in seconds :param skip: Skip periods of inactivity greater than this (in seconds). """ diff --git a/can/typechecking.py b/can/typechecking.py index e34b6b515..b5b79d500 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -3,7 +3,8 @@ import typing -import os # pylint: disable=unused-import +if typing.TYPE_CHECKING: + import os import mypy_extensions diff --git a/test/listener_test.py b/test/listener_test.py index 8014443d4..00dad1b0a 100644 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -111,9 +111,11 @@ def test_filetype_to_instance(extension, klass): test_filetype_to_instance(".db", can.SqliteReader) test_filetype_to_instance(".log", can.CanutilsLogReader) - # test file extensions that are not supported - with self.assertRaisesRegex(NotImplementedError, ".xyz_42"): - test_filetype_to_instance(".xyz_42", can.Printer) + def testPlayerTypeResolutionUnsupportedFileTypes(self): + for should_fail_with in ["", ".", ".some_unknown_extention_42"]: + with self.assertRaises(ValueError): + with can.LogReader(should_fail_with): # make sure we close it anyways + pass def testLoggerTypeResolution(self): def test_filetype_to_instance(extension, klass): @@ -138,10 +140,10 @@ def test_filetype_to_instance(extension, klass): with can.Logger(None) as logger: self.assertIsInstance(logger, can.Printer) - should_fail_with = ["", ".", ".some_unknown_extention_42"] - for supposed_fail in should_fail_with: + def testLoggerTypeResolutionUnsupportedFileTypes(self): + for should_fail_with in ["", ".", ".some_unknown_extention_42"]: with self.assertRaises(ValueError): - with can.Logger(supposed_fail): # make sure we close it anyways + with can.Logger(should_fail_with): # make sure we close it anyways pass def testBufferedListenerReceives(self): From a4154a49c75e5e5e8955584f36c6a12139e0320d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 16 Aug 2019 01:51:46 +0200 Subject: [PATCH 188/252] fix exception message if filetype is unknown --- can/io/logger.py | 4 +++- can/io/player.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 6336fc917..bc9d2c5f1 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -65,4 +65,6 @@ def __new__( try: return lookup[suffix](filename, *args, **kwargs) except KeyError: - raise ValueError(f'No write support for this unknown log format "{suffix}"') + raise ValueError( + f'No write support for this unknown log format "{suffix}"' + ) from None diff --git a/can/io/player.py b/can/io/player.py index 6e45e50ca..a4089fc32 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -65,7 +65,9 @@ def __new__(cls, filename: "can.typechecking.PathLike", *args, **kwargs): try: return lookup[suffix](filename, *args, **kwargs) except KeyError: - raise ValueError(f'No read support for this unknown log format "{suffix}"') + raise ValueError( + f'No read support for this unknown log format "{suffix}"' + ) from None class MessageSync: # pylint: disable=too-few-public-methods From 31c23bbd7c238e56f2c6a3b44c26d377a73ceb8c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 16 Aug 2019 02:19:23 +0200 Subject: [PATCH 189/252] Update Readme Fix typo + rearrange badges + add monthly downloads badge --- README.rst | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index ef92a6222..0214f2d4b 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,26 @@ python-can ========== -|release| |docs| |build_travis| |build_appveyor| |coverage| |downloads| |formatter| +|release| |downloads| |downloads_monthly| |formatter| + +|docs| |build_travis| |build_appveyor| |coverage| .. |release| image:: https://img.shields.io/pypi/v/python-can.svg :target: https://pypi.python.org/pypi/python-can/ :alt: Latest Version on PyPi +.. |downloads| image:: https://pepy.tech/badge/python-can + :target: https://pepy.tech/project/python-can + :alt: Downloads on PePy + +.. |downloads_monthly| image:: https://pepy.tech/badge/python-can/month + :target: https://pepy.tech/project/python-can/month + :alt: Monthly downloads on PePy + +.. |formatter| image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/python/black + :alt: This project uses the black formatter. + .. |docs| image:: https://readthedocs.org/projects/python-can/badge/?version=stable :target: https://python-can.readthedocs.io/en/stable/ :alt: Documentation @@ -23,14 +37,6 @@ python-can :target: https://codecov.io/gh/hardbyte/python-can/branch/develop :alt: Test coverage reports on Codecov.io -.. |downloads| image:: https://pepy.tech/badge/python-can - :target: https://pepy.tech/project/python-can - :alt: Downloads on PePy - -.. |formatter| image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/python/black - :alt: This project uses the black formatter. - 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 and reliable deterministic @@ -44,13 +50,13 @@ messages on a can bus. The library currently supports Python 3.6+ as well as PyPy 3 and runs on Mac, Linux and Windows. -============================= =========== -Library Version Python ------------------------------ ----------- - 2.x 2.6+, 3.4+ - 3.x 2.7+, 3.5+ - 4.x *(currently on devlop)* 3.6+ -============================= =========== +============================== =========== +Library Version Python +------------------------------ ----------- + 2.x 2.6+, 3.4+ + 3.x 2.7+, 3.5+ + 4.x *(currently on develop)* 3.6+ +============================== =========== Features From fca982768ec708179fe933256dd5c3a4341af58a Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Mon, 19 Aug 2019 20:17:22 +0200 Subject: [PATCH 190/252] Fix bitrate setting in slcan --- can/interfaces/slcan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index be5d672d3..710c23185 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -97,9 +97,9 @@ def __init__( if bitrate is not None and btr is not None: raise ValueError("Bitrate and btr mutually exclusive.") if bitrate is not None: - self.set_bitrate(self, bitrate) + self.set_bitrate(bitrate) if btr is not None: - self.set_bitrate_reg(self, btr) + self.set_bitrate_reg(btr) self.open() super().__init__( From 224c803f3bfc2dcb2862e7e0d5994ecd065e21dd Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 08:18:57 +0200 Subject: [PATCH 191/252] add pylint checking for examples --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8ecb4abd5..11a31c10d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,6 +84,8 @@ jobs: # warnings to the .pylintrc-wip file to prevent them from being # re-introduced - pylint --rcfile=.pylintrc-wip can/ + # check examples + - pylint --rcfile=.pylintrc-wip examples/ # mypy checking - mypy --python-version=3.7 From 303ee65efe36914f0835b8db695a23839d10e8a1 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 08:19:15 +0200 Subject: [PATCH 192/252] cleanup asyncio_demo.py --- examples/asyncio_demo.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/examples/asyncio_demo.py b/examples/asyncio_demo.py index 534f47184..3663507f9 100644 --- a/examples/asyncio_demo.py +++ b/examples/asyncio_demo.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python + +""" +This example demonstrates how to use async IO with python-can. +""" + import asyncio import can @@ -8,7 +14,9 @@ def print_message(msg): async def main(): - can0 = can.Bus("vcan0", bustype="virtual", receive_own_messages=True) + """The main function that runs in the loop.""" + + bus = can.Bus("vcan0", bustype="virtual", receive_own_messages=True) reader = can.AsyncBufferedReader() logger = can.Logger("logfile.asc") @@ -19,9 +27,9 @@ async def main(): ] # Create Notifier with an explicit loop to use for scheduling of callbacks loop = asyncio.get_event_loop() - notifier = can.Notifier(can0, listeners, loop=loop) + notifier = can.Notifier(bus, listeners, loop=loop) # Start sending first message - can0.send(can.Message(arbitration_id=0)) + bus.send(can.Message(arbitration_id=0)) print("Bouncing 10 messages...") for _ in range(10): @@ -30,18 +38,24 @@ async def main(): # Delay response await asyncio.sleep(0.5) msg.arbitration_id += 1 - can0.send(msg) + bus.send(msg) # Wait for last message to arrive await reader.get_message() print("Done!") # Clean-up notifier.stop() - can0.shutdown() + bus.shutdown() + +if __name__ == "__main": + try: + # Get the default event loop + LOOP = asyncio.get_event_loop() + # Run until main coroutine finishes + LOOP.run_until_complete(main()) + finally: + LOOP.close() -# Get the default event loop -loop = asyncio.get_event_loop() -# Run until main coroutine finishes -loop.run_until_complete(main()) -loop.close() + # or on Python 3.7+ simply + #asyncio.run(main()) From 69d482ee6a46918fa003d1a35cb3f17bb483aa2c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 08:20:28 +0200 Subject: [PATCH 193/252] remove coding: utf-8 and __future__ statements from examples --- examples/cyclic.py | 3 --- examples/cyclic_multiple.py | 3 --- examples/receive_all.py | 2 -- examples/send_one.py | 3 --- examples/serial_com.py | 3 --- examples/simple_log_converter.py | 1 - examples/vcan_filtered.py | 1 - examples/virtual_can_demo.py | 1 - 8 files changed, 17 deletions(-) diff --git a/examples/cyclic.py b/examples/cyclic.py index 25c215dda..d0291c016 100755 --- a/examples/cyclic.py +++ b/examples/cyclic.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 """ This example exercises the periodic sending capabilities. @@ -10,8 +9,6 @@ """ -from __future__ import print_function - import logging import time diff --git a/examples/cyclic_multiple.py b/examples/cyclic_multiple.py index ae32e7416..afe69bd29 100644 --- a/examples/cyclic_multiple.py +++ b/examples/cyclic_multiple.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 """ This example exercises the periodic task's multiple message sending capabilities @@ -10,8 +9,6 @@ """ -from __future__ import print_function - import logging import time diff --git a/examples/receive_all.py b/examples/receive_all.py index ced8841bc..80e324957 100755 --- a/examples/receive_all.py +++ b/examples/receive_all.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -from __future__ import print_function - import can from can.bus import BusState diff --git a/examples/send_one.py b/examples/send_one.py index 9a3181e35..59ffe142c 100755 --- a/examples/send_one.py +++ b/examples/send_one.py @@ -1,12 +1,9 @@ #!/usr/bin/env python -# coding: utf-8 """ This example shows how sending a single message works. """ -from __future__ import print_function - import can diff --git a/examples/serial_com.py b/examples/serial_com.py index b0dba4fec..c5aaa2f4b 100755 --- a/examples/serial_com.py +++ b/examples/serial_com.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 """ This example sends every second a messages over the serial interface and also @@ -19,8 +18,6 @@ com0com: http://com0com.sourceforge.net/ """ -from __future__ import print_function - import time import threading diff --git a/examples/simple_log_converter.py b/examples/simple_log_converter.py index 782ac9b7c..0ede69c04 100755 --- a/examples/simple_log_converter.py +++ b/examples/simple_log_converter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 """ Use this to convert .can/.asc files to .log files. diff --git a/examples/vcan_filtered.py b/examples/vcan_filtered.py index b351db09b..48a43c201 100755 --- a/examples/vcan_filtered.py +++ b/examples/vcan_filtered.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 """ This shows how message filtering works. diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index a4869d51d..048972309 100755 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 """ This demo creates multiple processes of producers to spam a socketcan bus. From d321bb41718dd8f7feccd8d0b0bca4321e45cb4c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 08:49:21 +0200 Subject: [PATCH 194/252] adapt .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 11a31c10d..4c1858288 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,9 +83,9 @@ jobs: # Slowly enable all pylint warnings by adding addressed classes of # warnings to the .pylintrc-wip file to prevent them from being # re-introduced - - pylint --rcfile=.pylintrc-wip can/ + - pylint --rcfile=.pylintrc-wip can/**.py # check examples - - pylint --rcfile=.pylintrc-wip examples/ + - pylint --rcfile=.pylintrc-wip examples/**.py # mypy checking - mypy --python-version=3.7 From 725bbd05850a2e0c4b7856e167d6a52c25b236e5 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 08:50:03 +0200 Subject: [PATCH 195/252] cleanup the examples; they now pass the linter and formatter and always close the bus by using a context manager --- examples/asyncio_demo.py | 2 +- examples/cyclic.py | 24 ++++++------ examples/cyclic_multiple.py | 12 ++---- examples/receive_all.py | 31 ++++++++++------ examples/send_one.py | 37 +++++++++--------- examples/serial_com.py | 64 ++++++++++++++++++-------------- examples/simple_log_converter.py | 22 +++++++---- examples/vcan_filtered.py | 33 ++++++++++------ examples/virtual_can_demo.py | 9 ++--- 9 files changed, 131 insertions(+), 103 deletions(-) diff --git a/examples/asyncio_demo.py b/examples/asyncio_demo.py index 3663507f9..b2c84b9d1 100644 --- a/examples/asyncio_demo.py +++ b/examples/asyncio_demo.py @@ -58,4 +58,4 @@ async def main(): LOOP.close() # or on Python 3.7+ simply - #asyncio.run(main()) + # asyncio.run(main()) diff --git a/examples/cyclic.py b/examples/cyclic.py index d0291c016..573465d78 100755 --- a/examples/cyclic.py +++ b/examples/cyclic.py @@ -34,13 +34,14 @@ def simple_periodic_send(bus): def limited_periodic_send(bus): + """Send using LimitedDurationCyclicSendTaskABC.""" print("Starting to send a message every 200ms for 1s") msg = can.Message( arbitration_id=0x12345678, data=[0, 0, 0, 0, 0, 0], is_extended_id=True ) task = bus.send_periodic(msg, 0.20, 1, store_task=False) if not isinstance(task, can.LimitedDurationCyclicSendTaskABC): - print("This interface doesn't seem to support a ") + print("This interface doesn't seem to support LimitedDurationCyclicSendTaskABC") task.stop() return @@ -53,7 +54,8 @@ def limited_periodic_send(bus): def test_periodic_send_with_modifying_data(bus): - print("Starting to send a message every 200ms. Initial data is ones") + """Send using ModifiableCyclicTaskABC.""" + print("Starting to send a message every 200ms. Initial data is four consecutive 1s") msg = can.Message(arbitration_id=0x0CF02200, data=[1, 1, 1, 1]) task = bus.send_periodic(msg, 0.20) if not isinstance(task, can.ModifiableCyclicTaskABC): @@ -106,19 +108,13 @@ def test_periodic_send_with_modifying_data(bus): # print("done") -if __name__ == "__main__": - +def main(): + """Test different cyclic sending tasks.""" reset_msg = can.Message( arbitration_id=0x00, data=[0, 0, 0, 0, 0, 0], is_extended_id=False ) - for interface, channel in [ - ("socketcan", "vcan0"), - # ('ixxat', 0) - ]: - print("Carrying out cyclic tests with {} interface".format(interface)) - - bus = can.Bus(interface=interface, channel=channel, bitrate=500000) + with can.Bus(interface="virtual") as bus: bus.send(reset_msg) simple_periodic_send(bus) @@ -133,6 +129,8 @@ def test_periodic_send_with_modifying_data(bus): # can.rc['interface'] = interface # test_dual_rate_periodic_send() - bus.shutdown() - time.sleep(2) + + +if __name__ == "__main__": + main() diff --git a/examples/cyclic_multiple.py b/examples/cyclic_multiple.py index afe69bd29..43dc0cd17 100644 --- a/examples/cyclic_multiple.py +++ b/examples/cyclic_multiple.py @@ -131,14 +131,10 @@ def cyclic_multiple_send_modify(bus): if __name__ == "__main__": for interface, channel in [("socketcan", "vcan0")]: - print("Carrying out cyclic multiple tests with {} interface".format(interface)) + print(f"Carrying out cyclic multiple tests with {interface} interface") - bus = can.Bus(interface=interface, channel=channel, bitrate=500000) - - cyclic_multiple_send(bus) - - cyclic_multiple_send_modify(bus) - - bus.shutdown() + with can.Bus(interface=interface, channel=channel, bitrate=500000) as BUS: + cyclic_multiple_send(BUS) + cyclic_multiple_send_modify(BUS) time.sleep(2) diff --git a/examples/receive_all.py b/examples/receive_all.py index 80e324957..7ff532079 100755 --- a/examples/receive_all.py +++ b/examples/receive_all.py @@ -1,24 +1,33 @@ #!/usr/bin/env python +""" +Shows how the receive messages via polling. +""" + import can from can.bus import BusState def receive_all(): + """Receives all messages and prints them to the console until Ctrl+C is pressed.""" + + with can.interface.Bus( + bustype="pcan", channel="PCAN_USBBUS1", bitrate=250000 + ) as bus: + # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) - bus = can.interface.Bus(bustype="pcan", channel="PCAN_USBBUS1", bitrate=250000) - # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) - # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + # set to read-only, only supported on some interfaces + bus.state = BusState.PASSIVE - bus.state = BusState.ACTIVE # or BusState.PASSIVE + try: + while True: + msg = bus.recv(1) + if msg is not None: + print(msg) - try: - while True: - msg = bus.recv(1) - if msg is not None: - print(msg) - except KeyboardInterrupt: - pass + except KeyboardInterrupt: + pass # exit normally if __name__ == "__main__": diff --git a/examples/send_one.py b/examples/send_one.py index 59ffe142c..49a4f1ee1 100755 --- a/examples/send_one.py +++ b/examples/send_one.py @@ -8,27 +8,28 @@ def send_one(): + """Sends a single message.""" # this uses the default configuration (for example from the config file) # see https://python-can.readthedocs.io/en/stable/configuration.html - bus = can.interface.Bus() - - # Using specific buses works similar: - # bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000) - # bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) - # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) - # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) - # ... - - msg = can.Message( - arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=True - ) - - try: - bus.send(msg) - print("Message sent on {}".format(bus.channel_info)) - except can.CanError: - print("Message NOT sent") + with can.interface.Bus() as bus: + + # Using specific buses works similar: + # bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000) + # bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) + # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + # ... + + msg = can.Message( + arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=True + ) + + try: + bus.send(msg) + print(f"Message sent on {bus.channel_info}") + except can.CanError: + print("Message NOT sent") if __name__ == "__main__": diff --git a/examples/serial_com.py b/examples/serial_com.py index c5aaa2f4b..60aeec4ce 100755 --- a/examples/serial_com.py +++ b/examples/serial_com.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -This example sends every second a messages over the serial interface and also +This example sends every second a messages over the serial interface and also receives incoming messages. python3 -m examples.serial_com @@ -25,17 +25,19 @@ def send_cyclic(bus, msg, stop_event): + """The loop for sending.""" print("Start to send a message every 1s") start_time = time.time() while not stop_event.is_set(): msg.timestamp = time.time() - start_time bus.send(msg) - print("tx: {}".format(tx_msg)) + print(f"tx: {msg}") time.sleep(1) print("Stopped sending messages") def receive(bus, stop_event): + """The loop for receiving.""" print("Start receiving messages") while not stop_event.is_set(): rx_msg = bus.recv(1) @@ -44,30 +46,36 @@ def receive(bus, stop_event): print("Stopped receiving messages") -if __name__ == "__main__": - server = can.interface.Bus(bustype="serial", channel="/dev/ttyS10") - client = can.interface.Bus(bustype="serial", channel="/dev/ttyS11") - - tx_msg = can.Message( - arbitration_id=0x01, data=[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88] - ) - - # Thread for sending and receiving messages - stop_event = threading.Event() - t_send_cyclic = threading.Thread( - target=send_cyclic, args=(server, tx_msg, stop_event) - ) - t_receive = threading.Thread(target=receive, args=(client, stop_event)) - t_receive.start() - t_send_cyclic.start() - - try: - while True: - pass - except KeyboardInterrupt: - pass - - stop_event.set() - server.shutdown() - client.shutdown() +def main(): + """Controles the sender and receiver.""" + with can.interface.Bus(bustype="serial", channel="/dev/ttyS10") as server: + with can.interface.Bus(bustype="serial", channel="/dev/ttyS11") as client: + + tx_msg = can.Message( + arbitration_id=0x01, + data=[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], + ) + + # Thread for sending and receiving messages + stop_event = threading.Event() + t_send_cyclic = threading.Thread( + target=send_cyclic, args=(server, tx_msg, stop_event) + ) + t_receive = threading.Thread(target=receive, args=(client, stop_event)) + t_receive.start() + t_send_cyclic.start() + + try: + while True: + time.sleep(0) # yield + except KeyboardInterrupt: + pass # exit normally + + stop_event.set() + time.sleep(0.5) + print("Stopped script") + + +if __name__ == "__main__": + main() diff --git a/examples/simple_log_converter.py b/examples/simple_log_converter.py index 0ede69c04..f01546375 100755 --- a/examples/simple_log_converter.py +++ b/examples/simple_log_converter.py @@ -2,17 +2,25 @@ """ Use this to convert .can/.asc files to .log files. +Can be easily adapted for all sorts of files. -Usage: simpleLogConvert.py sourceLog.asc targetLog.log +Usage: python3 simple_log_convert.py sourceLog.asc targetLog.log """ import sys -import can.io.logger -import can.io.player +import can -reader = can.io.player.LogReader(sys.argv[1]) -writer = can.io.logger.Logger(sys.argv[2]) -for msg in reader: - writer.on_message_received(msg) +def main(): + """The transcoder""" + + with can.LogReader(sys.argv[1]) as reader: + with can.Logger(sys.argv[2]) as writer: + + for msg in reader: + writer.on_message_received(msg) + + +if __name__ == "__main__": + main() diff --git a/examples/vcan_filtered.py b/examples/vcan_filtered.py index 48a43c201..fa6c71547 100755 --- a/examples/vcan_filtered.py +++ b/examples/vcan_filtered.py @@ -8,16 +8,25 @@ import can + +def main(): + """Send some messages to itself and apply filtering.""" + with can.Bus(bustype="virtual", receive_own_messages=True) as bus: + + can_filters = [{"can_id": 1, "can_mask": 0xF, "extended": True}] + bus.set_filters(can_filters) + + # print all incoming messages, wich includes the ones sent, + # since we set receive_own_messages to True + # assign to some variable so it does not garbage collected + notifier = can.Notifier(bus, [can.Printer()]) # pylint: disable=unused-variable + + bus.send(can.Message(arbitration_id=1, is_extended_id=True)) + bus.send(can.Message(arbitration_id=2, is_extended_id=True)) + bus.send(can.Message(arbitration_id=1, is_extended_id=False)) + + time.sleep(1.0) + + if __name__ == "__main__": - bus = can.interface.Bus( - bustype="socketcan", channel="vcan0", receive_own_messages=True - ) - - can_filters = [{"can_id": 1, "can_mask": 0xF, "extended": True}] - bus.set_filters(can_filters) - notifier = can.Notifier(bus, [can.Printer()]) - bus.send(can.Message(arbitration_id=1, is_extended_id=True)) - bus.send(can.Message(arbitration_id=2, is_extended_id=True)) - bus.send(can.Message(arbitration_id=1, is_extended_id=False)) - - time.sleep(10) + main() diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index 048972309..6f61af7b9 100755 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -10,23 +10,22 @@ import can -def producer(id, message_count=16): +def producer(thread_id, message_count=16): """Spam the bus with messages including the data id. :param int id: the id of the thread/process """ - with can.Bus(bustype="socketcan", channel="vcan0") as bus: for i in range(message_count): msg = can.Message( - arbitration_id=0x0CF02200 + id, data=[id, i, 0, 1, 3, 1, 4, 1] + arbitration_id=0x0CF02200 + id, data=[thread_id, i, 0, 1, 3, 1, 4, 1] ) bus.send(msg) sleep(1.0) - print("Producer #{} finished sending {} messages".format(id, message_count)) + print(f"Producer #{thread_id} finished sending {message_count} messages") if __name__ == "__main__": - with ProcessPoolExecutor(max_workers=4) as executor: + with ProcessPoolExecutor() as executor: executor.map(producer, range(5)) From 65f46cd703b596ee67d58fdc0999c607cf4bc75d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 09:10:13 +0200 Subject: [PATCH 196/252] also enable mypy on the examples; this required two tiny changes somehwere else --- .travis.yml | 2 ++ can/viewer.py | 8 ++++---- examples/cyclic_multiple.py | 2 +- examples/virtual_can_demo.py | 9 +++++---- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c1858288..5920f5491 100644 --- a/.travis.yml +++ b/.travis.yml @@ -104,6 +104,8 @@ jobs: can/typechecking.py can/util.py can/io/**.py + scripts/**.py + examples/**.py - stage: linter name: "Formatting Checks" python: "3.7" diff --git a/can/viewer.py b/can/viewer.py index 707192526..95ad953f5 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -43,7 +43,7 @@ logger.warning( "You won't be able to use the viewer program without " "curses installed!" ) - curses = None + curses = None # type: ignore class CanViewer: @@ -153,7 +153,7 @@ def unpack_data( value = cmd_to_struct[key] if isinstance(value, tuple): # The struct is given as the fist argument - struct_t = value[0] # type: struct.Struct + struct_t: struct.Struct = value[0] # The conversion from raw values to SI-units are given in the rest of the tuple values = [ @@ -162,8 +162,8 @@ def unpack_data( ] else: # No conversion from SI-units is needed - struct_t = value # type: struct.Struct - values = list(struct_t.unpack(data)) + as_struct_t: struct.Struct = value + values = list(as_struct_t.unpack(data)) return values else: diff --git a/examples/cyclic_multiple.py b/examples/cyclic_multiple.py index 43dc0cd17..fe1e3d060 100644 --- a/examples/cyclic_multiple.py +++ b/examples/cyclic_multiple.py @@ -133,7 +133,7 @@ def cyclic_multiple_send_modify(bus): for interface, channel in [("socketcan", "vcan0")]: print(f"Carrying out cyclic multiple tests with {interface} interface") - with can.Bus(interface=interface, channel=channel, bitrate=500000) as BUS: + with can.Bus(interface=interface, channel=channel, bitrate=500000) as BUS: # type: ignore cyclic_multiple_send(BUS) cyclic_multiple_send_modify(BUS) diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index 6f61af7b9..711cb2cb7 100755 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -10,15 +10,16 @@ import can -def producer(thread_id, message_count=16): +def producer(thread_id: int, message_count: int = 16): """Spam the bus with messages including the data id. - :param int id: the id of the thread/process + :param thread_id: the id of the thread/process + :param message_count: the number of messages that shall be send """ - with can.Bus(bustype="socketcan", channel="vcan0") as bus: + with can.Bus(bustype="socketcan", channel="vcan0") as bus: # type: ignore for i in range(message_count): msg = can.Message( - arbitration_id=0x0CF02200 + id, data=[thread_id, i, 0, 1, 3, 1, 4, 1] + arbitration_id=0x0CF02200 + thread_id, data=[thread_id, i, 0, 1, 3, 1, 4, 1] ) bus.send(msg) sleep(1.0) From 80747e3fee92fa61771167c2f865d8f88b01342f Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 09:32:41 +0200 Subject: [PATCH 197/252] reformat files --- examples/cyclic_multiple.py | 4 +++- examples/virtual_can_demo.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/cyclic_multiple.py b/examples/cyclic_multiple.py index fe1e3d060..c34b75d62 100644 --- a/examples/cyclic_multiple.py +++ b/examples/cyclic_multiple.py @@ -133,7 +133,9 @@ def cyclic_multiple_send_modify(bus): for interface, channel in [("socketcan", "vcan0")]: print(f"Carrying out cyclic multiple tests with {interface} interface") - with can.Bus(interface=interface, channel=channel, bitrate=500000) as BUS: # type: ignore + with can.Bus( + interface=interface, channel=channel, bitrate=500000 + ) as BUS: # type: ignore cyclic_multiple_send(BUS) cyclic_multiple_send_modify(BUS) diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index 711cb2cb7..b3fdefc09 100755 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -19,7 +19,8 @@ def producer(thread_id: int, message_count: int = 16): with can.Bus(bustype="socketcan", channel="vcan0") as bus: # type: ignore for i in range(message_count): msg = can.Message( - arbitration_id=0x0CF02200 + thread_id, data=[thread_id, i, 0, 1, 3, 1, 4, 1] + arbitration_id=0x0CF02200 + thread_id, + data=[thread_id, i, 0, 1, 3, 1, 4, 1], ) bus.send(msg) sleep(1.0) From 1d05ef7c76a929df9e563e1cb55cc1dee5d362c8 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 09:47:09 +0200 Subject: [PATCH 198/252] fix a tiny typechecker regression introduced by the formatter --- examples/cyclic_multiple.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cyclic_multiple.py b/examples/cyclic_multiple.py index c34b75d62..64f0862d7 100644 --- a/examples/cyclic_multiple.py +++ b/examples/cyclic_multiple.py @@ -133,9 +133,9 @@ def cyclic_multiple_send_modify(bus): for interface, channel in [("socketcan", "vcan0")]: print(f"Carrying out cyclic multiple tests with {interface} interface") - with can.Bus( + with can.Bus( # type: ignore interface=interface, channel=channel, bitrate=500000 - ) as BUS: # type: ignore + ) as BUS: cyclic_multiple_send(BUS) cyclic_multiple_send_modify(BUS) From 1ac5510f7e18b7c5c9d87576f4aeda906ef7036a Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Sat, 17 Aug 2019 12:40:52 +0200 Subject: [PATCH 199/252] Ignore error frames in can.player by default Fixes #683 --- can/player.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/can/player.py b/can/player.py index 34be48670..b1e1c5612 100644 --- a/can/player.py +++ b/can/player.py @@ -73,6 +73,12 @@ def main(): action="store_false", ) + parser.add_argument( + "--error-frames", + help="Also send error frames to the interface.", + action="store_true", + ) + parser.add_argument( "-g", "--gap", @@ -111,6 +117,8 @@ def main(): ] can.set_logging_level(logging_level_name) + error_frames = results.error_frames + config = {"single_handle": True} if results.interface: config["interface"] = results.interface @@ -132,6 +140,8 @@ def main(): try: for m in in_sync: + if m.is_error_frame and not error_frames: + continue if verbosity >= 3: print(m) bus.send(m) From 63572da17f699e83410cc0632d87eba99d93894e Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 07:44:35 +0200 Subject: [PATCH 200/252] add setup.py to linter check --- .travis.yml | 1 + setup.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8ecb4abd5..0ba635cc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,6 +84,7 @@ jobs: # warnings to the .pylintrc-wip file to prevent them from being # re-introduced - pylint --rcfile=.pylintrc-wip can/ + - pylint --rcfile=.pylintrc setup.py # mypy checking - mypy --python-version=3.7 diff --git a/setup.py b/setup.py index 2b1bf2296..8170f67fa 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,8 @@ python-can requires the setuptools package to be installed. """ +# pylint: disable=invalid-name + from __future__ import absolute_import from os import listdir From 1947e562c6c00bc589c58fa3617c2049f730618c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 07:50:56 +0200 Subject: [PATCH 201/252] removing coding: utf-8 directive since that is now the defualt in Python 3 --- can/__init__.py | 1 - can/broadcastmanager.py | 1 - can/bus.py | 1 - can/ctypesutil.py | 1 - can/interface.py | 1 - can/interfaces/__init__.py | 1 - can/interfaces/canalystii.py | 1 - can/interfaces/ics_neovi/__init__.py | 1 - can/interfaces/ics_neovi/neovi_bus.py | 1 - can/interfaces/iscan.py | 1 - can/interfaces/ixxat/__init__.py | 1 - can/interfaces/ixxat/canlib.py | 1 - can/interfaces/ixxat/constants.py | 1 - can/interfaces/ixxat/exceptions.py | 1 - can/interfaces/ixxat/structures.py | 1 - can/interfaces/kvaser/__init__.py | 1 - can/interfaces/kvaser/canlib.py | 1 - can/interfaces/kvaser/constants.py | 1 - can/interfaces/kvaser/structures.py | 1 - can/interfaces/nican.py | 1 - can/interfaces/pcan/__init__.py | 1 - can/interfaces/pcan/basic.py | 1 - can/interfaces/pcan/pcan.py | 1 - can/interfaces/seeedstudio/__init__.py | 1 - can/interfaces/seeedstudio/seeedstudio.py | 1 - can/interfaces/serial/__init__.py | 1 - can/interfaces/serial/serial_can.py | 1 - can/interfaces/slcan.py | 1 - can/interfaces/socketcan/__init__.py | 1 - can/interfaces/socketcan/constants.py | 1 - can/interfaces/socketcan/socketcan.py | 1 - can/interfaces/socketcan/utils.py | 1 - can/interfaces/systec/__init__.py | 1 - can/interfaces/systec/constants.py | 1 - can/interfaces/systec/exceptions.py | 1 - can/interfaces/systec/structures.py | 1 - can/interfaces/systec/ucan.py | 1 - can/interfaces/systec/ucanbus.py | 1 - can/interfaces/usb2can/__init__.py | 1 - can/interfaces/usb2can/serial_selector.py | 1 - can/interfaces/usb2can/usb2canInterface.py | 1 - can/interfaces/usb2can/usb2canabstractionlayer.py | 1 - can/interfaces/vector/__init__.py | 1 - can/interfaces/vector/canlib.py | 1 - can/interfaces/vector/exceptions.py | 1 - can/interfaces/vector/xlclass.py | 1 - can/interfaces/vector/xldefine.py | 1 - can/interfaces/vector/xldriver.py | 1 - can/interfaces/virtual.py | 1 - can/io/__init__.py | 1 - can/io/asc.py | 1 - can/io/blf.py | 1 - can/io/canutils.py | 1 - can/io/csv.py | 1 - can/io/generic.py | 1 - can/io/logger.py | 1 - can/io/player.py | 1 - can/io/printer.py | 1 - can/io/sqlite.py | 1 - can/listener.py | 1 - can/logger.py | 1 - can/message.py | 1 - can/notifier.py | 1 - can/player.py | 1 - can/thread_safe_bus.py | 1 - can/util.py | 1 - can/viewer.py | 1 - 67 files changed, 67 deletions(-) diff --git a/can/__init__.py b/can/__init__.py index 3c1ac8d75..ec7a8caf4 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ ``can`` is an object-orient Controller Area Network (CAN) interface module. diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 733693802..118880a66 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Exposes several methods for transmitting cyclic messages. diff --git a/can/bus.py b/can/bus.py index e22bf6157..f1d9133ad 100644 --- a/can/bus.py +++ b/can/bus.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Contains the ABC bus implementation and its documentation. diff --git a/can/ctypesutil.py b/can/ctypesutil.py index 8dca8dc14..dcadff1aa 100644 --- a/can/ctypesutil.py +++ b/can/ctypesutil.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module contains common `ctypes` utils. diff --git a/can/interface.py b/can/interface.py index 9e32d9ca3..bd8a5c386 100644 --- a/can/interface.py +++ b/can/interface.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module contains the base implementation of :class:`can.BusABC` as well diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 174b7f7ab..792517500 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Interfaces contain low level implementations that interact with CAN hardware. diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 1134ffe51..8cfa97ca1 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -1,4 +1,3 @@ -# coding: utf-8 from ctypes import * import logging diff --git a/can/interfaces/ics_neovi/__init__.py b/can/interfaces/ics_neovi/__init__.py index 4426b1585..96af50d7e 100644 --- a/can/interfaces/ics_neovi/__init__.py +++ b/can/interfaces/ics_neovi/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 176747ad2..9eec48f45 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ ICS NeoVi interface module. diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index e0774dded..360507976 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH. diff --git a/can/interfaces/ixxat/__init__.py b/can/interfaces/ixxat/__init__.py index aef26b729..8fb17844a 100644 --- a/can/interfaces/ixxat/__init__.py +++ b/can/interfaces/ixxat/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 13c8ef779..68f1b0aba 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/constants.py b/can/interfaces/ixxat/constants.py index 46daccf8d..9608bb2bf 100644 --- a/can/interfaces/ixxat/constants.py +++ b/can/interfaces/ixxat/constants.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/exceptions.py b/can/interfaces/ixxat/exceptions.py index d89a98334..5b4347666 100644 --- a/can/interfaces/ixxat/exceptions.py +++ b/can/interfaces/ixxat/exceptions.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/structures.py b/can/interfaces/ixxat/structures.py index 881d47a13..df1ebd8a6 100644 --- a/can/interfaces/ixxat/structures.py +++ b/can/interfaces/ixxat/structures.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/kvaser/__init__.py b/can/interfaces/kvaser/__init__.py index 5cbe63386..191179ab3 100644 --- a/can/interfaces/kvaser/__init__.py +++ b/can/interfaces/kvaser/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index 89a05af1e..06c27e903 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Contains Python equivalents of the function and constant diff --git a/can/interfaces/kvaser/constants.py b/can/interfaces/kvaser/constants.py index bc31381c6..d1a7ba610 100644 --- a/can/interfaces/kvaser/constants.py +++ b/can/interfaces/kvaser/constants.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Contains Python equivalents of the function and constant diff --git a/can/interfaces/kvaser/structures.py b/can/interfaces/kvaser/structures.py index 5b6d416d8..e2dd6a6a9 100644 --- a/can/interfaces/kvaser/structures.py +++ b/can/interfaces/kvaser/structures.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Contains Python equivalents of the structures in CANLIB's canlib.h, diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index 2d5abf0d0..90d396a5c 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ NI-CAN interface module. diff --git a/can/interfaces/pcan/__init__.py b/can/interfaces/pcan/__init__.py index ceba250b5..8ae860a85 100644 --- a/can/interfaces/pcan/__init__.py +++ b/can/interfaces/pcan/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index eb29f0e16..3fe5468ea 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ PCAN-Basic API diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 26da9ded9..9a1dae2b2 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Enable basic CAN over a PCAN USB device. diff --git a/can/interfaces/seeedstudio/__init__.py b/can/interfaces/seeedstudio/__init__.py index 507ac873e..a7a69c097 100644 --- a/can/interfaces/seeedstudio/__init__.py +++ b/can/interfaces/seeedstudio/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index eebd07753..d9dfe448f 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ To Support the Seeed USB-Can analyzer interface. The device will appear diff --git a/can/interfaces/serial/__init__.py b/can/interfaces/serial/__init__.py index dced63b0f..21553a0da 100644 --- a/can/interfaces/serial/__init__.py +++ b/can/interfaces/serial/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index ba397f7bf..063b09257 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ A text based interface. For example use over serial ports like diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 710c23185..0a1931c16 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Interface for slcan compatible interfaces (win32/linux). diff --git a/can/interfaces/socketcan/__init__.py b/can/interfaces/socketcan/__init__.py index 06a9d9959..a39cc1ef7 100644 --- a/can/interfaces/socketcan/__init__.py +++ b/can/interfaces/socketcan/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ See: https://www.kernel.org/doc/Documentation/networking/can.txt diff --git a/can/interfaces/socketcan/constants.py b/can/interfaces/socketcan/constants.py index 98bbc39d1..ae6c01519 100644 --- a/can/interfaces/socketcan/constants.py +++ b/can/interfaces/socketcan/constants.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Defines shared CAN constants. diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index a0ee5c595..0e9a733cb 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ The main module of the socketcan interface containing most user-facing classes and methods diff --git a/can/interfaces/socketcan/utils.py b/can/interfaces/socketcan/utils.py index 5733443a1..f93ec1420 100644 --- a/can/interfaces/socketcan/utils.py +++ b/can/interfaces/socketcan/utils.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Defines common socketcan functions. diff --git a/can/interfaces/systec/__init__.py b/can/interfaces/systec/__init__.py index ed8eb8eb7..f211bc4ef 100644 --- a/can/interfaces/systec/__init__.py +++ b/can/interfaces/systec/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/systec/constants.py b/can/interfaces/systec/constants.py index 648ae2edc..a35e2ccb5 100644 --- a/can/interfaces/systec/constants.py +++ b/can/interfaces/systec/constants.py @@ -1,4 +1,3 @@ -# coding: utf-8 from ctypes import c_ubyte as BYTE, c_ushort as WORD, c_ulong as DWORD diff --git a/can/interfaces/systec/exceptions.py b/can/interfaces/systec/exceptions.py index 49a974cac..fd591d702 100644 --- a/can/interfaces/systec/exceptions.py +++ b/can/interfaces/systec/exceptions.py @@ -1,4 +1,3 @@ -# coding: utf-8 from .constants import ReturnCode from can import CanError diff --git a/can/interfaces/systec/structures.py b/can/interfaces/systec/structures.py index 9fd542e04..b13ee31d9 100644 --- a/can/interfaces/systec/structures.py +++ b/can/interfaces/systec/structures.py @@ -1,4 +1,3 @@ -# coding: utf-8 from ctypes import Structure, POINTER, sizeof from ctypes import ( diff --git a/can/interfaces/systec/ucan.py b/can/interfaces/systec/ucan.py index 1b08c51a0..16ff6245b 100644 --- a/can/interfaces/systec/ucan.py +++ b/can/interfaces/systec/ucan.py @@ -1,4 +1,3 @@ -# coding: utf-8 import logging import sys diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index 29a76f468..3c65143b3 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -1,4 +1,3 @@ -# coding: utf-8 import logging from threading import Event diff --git a/can/interfaces/usb2can/__init__.py b/can/interfaces/usb2can/__init__.py index 623af5bc3..4665c9fb1 100644 --- a/can/interfaces/usb2can/__init__.py +++ b/can/interfaces/usb2can/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/usb2can/serial_selector.py b/can/interfaces/usb2can/serial_selector.py index f124df196..3b0f225b5 100644 --- a/can/interfaces/usb2can/serial_selector.py +++ b/can/interfaces/usb2can/serial_selector.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 81ba027ce..29316b771 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This interface is for Windows only, otherwise use socketCAN. diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index 0aaf1b4f2..2811ba235 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This wrapper is for windows or direct access via CANAL API. diff --git a/can/interfaces/vector/__init__.py b/can/interfaces/vector/__init__.py index dac47be4a..19bb42afd 100644 --- a/can/interfaces/vector/__init__.py +++ b/can/interfaces/vector/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 7c8d9c7c3..cc674288b 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Ctypes wrapper module for Vector CAN Interface on win32/win64 systems. diff --git a/can/interfaces/vector/exceptions.py b/can/interfaces/vector/exceptions.py index 8e4de1aff..fb34517c6 100644 --- a/can/interfaces/vector/exceptions.py +++ b/can/interfaces/vector/exceptions.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ """ diff --git a/can/interfaces/vector/xlclass.py b/can/interfaces/vector/xlclass.py index 90fc611bd..49eb410d0 100644 --- a/can/interfaces/vector/xlclass.py +++ b/can/interfaces/vector/xlclass.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Definition of data types and structures for vxlapi. diff --git a/can/interfaces/vector/xldefine.py b/can/interfaces/vector/xldefine.py index fcf041683..7cc929052 100644 --- a/can/interfaces/vector/xldefine.py +++ b/can/interfaces/vector/xldefine.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Definition of constants for vxlapi. diff --git a/can/interfaces/vector/xldriver.py b/can/interfaces/vector/xldriver.py index b413e47fb..fde7cca56 100644 --- a/can/interfaces/vector/xldriver.py +++ b/can/interfaces/vector/xldriver.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Ctypes wrapper module for Vector CAN Interface on win32/win64 systems. diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index b153150ee..436988dab 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module implements an OS and hardware independent diff --git a/can/io/__init__.py b/can/io/__init__.py index 3797d4b5d..f4c1b0359 100644 --- a/can/io/__init__.py +++ b/can/io/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Read and write CAN bus messages using a range of Readers diff --git a/can/io/asc.py b/can/io/asc.py index 656ac4fbe..41f947c57 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Contains handling of ASC logging files. diff --git a/can/io/blf.py b/can/io/blf.py index 2ac553717..21252f38a 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Implements support for BLF (Binary Logging Format) which is a proprietary diff --git a/can/io/canutils.py b/can/io/canutils.py index a151b56e1..cd56ff32e 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module works with CAN data in ASCII log files (*.log). diff --git a/can/io/csv.py b/can/io/csv.py index af8fee6d8..a3793bdb6 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module contains handling for CSV (comma separated values) files. diff --git a/can/io/generic.py b/can/io/generic.py index c64051b75..10f7fc6a9 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Contains a generic class for file IO. diff --git a/can/io/logger.py b/can/io/logger.py index bc9d2c5f1..6b63f12f5 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ See the :class:`Logger` class. diff --git a/can/io/player.py b/can/io/player.py index a4089fc32..79603455e 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module contains the generic :class:`LogReader` as diff --git a/can/io/printer.py b/can/io/printer.py index d363e6917..57e34db3f 100644 --- a/can/io/printer.py +++ b/can/io/printer.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This Listener simply prints to stdout / the terminal or a file. diff --git a/can/io/sqlite.py b/can/io/sqlite.py index b9e5c2673..abeab6090 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Implements an SQL database writer and reader for storing CAN messages. diff --git a/can/listener.py b/can/listener.py index c33fec70a..44723bb99 100644 --- a/can/listener.py +++ b/can/listener.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module contains the implementation of `can.Listener` and some readers. diff --git a/can/logger.py b/can/logger.py index 00f667979..0a9055259 100644 --- a/can/logger.py +++ b/can/logger.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ logger.py logs CAN traffic to the terminal and to a file on disk. diff --git a/can/message.py b/can/message.py index d1c7c9366..385d2244b 100644 --- a/can/message.py +++ b/can/message.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module contains the implementation of :class:`can.Message`. diff --git a/can/notifier.py b/can/notifier.py index 679af384d..108a645b4 100644 --- a/can/notifier.py +++ b/can/notifier.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ This module contains the implementation of :class:`~can.Notifier`. diff --git a/can/player.py b/can/player.py index b1e1c5612..86bd0afe6 100644 --- a/can/player.py +++ b/can/player.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Replays CAN traffic saved with can.logger back diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 9f6346fb8..b31e48407 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -1,4 +1,3 @@ -# coding: utf-8 from threading import RLock diff --git a/can/util.py b/can/util.py index 51a3051bd..473057328 100644 --- a/can/util.py +++ b/can/util.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Utilities and configuration file parsing. diff --git a/can/viewer.py b/can/viewer.py index 707192526..9f3ac361a 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -1,4 +1,3 @@ -# coding: utf-8 # Copyright (C) 2018 Kristian Sloth Lauszus. # From 9e0520bd98bac6e62f0fba3ee88de800c83103aa Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 07:52:15 +0200 Subject: [PATCH 202/252] removing coding: utf-8 directive also from setup.py --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 8170f67fa..d7d2feca6 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 """ python-can requires the setuptools package to be installed. From 8f1ba2322eb401eab94cab7ae6e8f1128aa7069a Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 08:00:44 +0200 Subject: [PATCH 203/252] add doc/conf.py to pylint checking, reformat files --- .travis.yml | 5 +++- can/__init__.py | 1 - can/broadcastmanager.py | 1 - can/bus.py | 1 - can/ctypesutil.py | 1 - can/interface.py | 1 - can/interfaces/__init__.py | 1 - can/interfaces/canalystii.py | 1 - can/interfaces/ics_neovi/__init__.py | 1 - can/interfaces/ics_neovi/neovi_bus.py | 1 - can/interfaces/iscan.py | 1 - can/interfaces/ixxat/__init__.py | 1 - can/interfaces/ixxat/canlib.py | 1 - can/interfaces/ixxat/constants.py | 1 - can/interfaces/ixxat/exceptions.py | 1 - can/interfaces/ixxat/structures.py | 1 - can/interfaces/kvaser/__init__.py | 1 - can/interfaces/kvaser/canlib.py | 1 - can/interfaces/kvaser/constants.py | 1 - can/interfaces/kvaser/structures.py | 1 - can/interfaces/nican.py | 1 - can/interfaces/pcan/__init__.py | 1 - can/interfaces/pcan/basic.py | 1 - can/interfaces/pcan/pcan.py | 1 - can/interfaces/seeedstudio/__init__.py | 1 - can/interfaces/seeedstudio/seeedstudio.py | 1 - can/interfaces/serial/__init__.py | 1 - can/interfaces/serial/serial_can.py | 1 - can/interfaces/slcan.py | 1 - can/interfaces/socketcan/__init__.py | 1 - can/interfaces/socketcan/constants.py | 1 - can/interfaces/socketcan/socketcan.py | 1 - can/interfaces/socketcan/utils.py | 1 - can/interfaces/systec/__init__.py | 1 - can/interfaces/systec/constants.py | 1 - can/interfaces/systec/exceptions.py | 1 - can/interfaces/systec/structures.py | 1 - can/interfaces/systec/ucan.py | 1 - can/interfaces/systec/ucanbus.py | 1 - can/interfaces/usb2can/__init__.py | 1 - can/interfaces/usb2can/serial_selector.py | 1 - can/interfaces/usb2can/usb2canInterface.py | 1 - .../usb2can/usb2canabstractionlayer.py | 1 - can/interfaces/vector/__init__.py | 1 - can/interfaces/vector/canlib.py | 1 - can/interfaces/vector/exceptions.py | 1 - can/interfaces/vector/xlclass.py | 1 - can/interfaces/vector/xldefine.py | 1 - can/interfaces/vector/xldriver.py | 1 - can/interfaces/virtual.py | 1 - can/io/__init__.py | 1 - can/io/asc.py | 1 - can/io/blf.py | 1 - can/io/canutils.py | 1 - can/io/csv.py | 1 - can/io/generic.py | 1 - can/io/logger.py | 1 - can/io/player.py | 1 - can/io/printer.py | 1 - can/io/sqlite.py | 1 - can/listener.py | 1 - can/logger.py | 1 - can/message.py | 1 - can/notifier.py | 1 - can/player.py | 1 - can/thread_safe_bus.py | 1 - can/util.py | 1 - can/viewer.py | 1 - doc/conf.py | 24 ++++++++++--------- 69 files changed, 17 insertions(+), 79 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0ba635cc2..b904ebb42 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,7 +84,10 @@ jobs: # warnings to the .pylintrc-wip file to prevent them from being # re-introduced - pylint --rcfile=.pylintrc-wip can/ - - pylint --rcfile=.pylintrc setup.py + # check setup.py + - pylint --rcfile=.pylintrc *.py + # check doc/conf.py + - pylint --rcfile=.pylintrc doc/**.py # mypy checking - mypy --python-version=3.7 diff --git a/can/__init__.py b/can/__init__.py index ec7a8caf4..457546307 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -1,4 +1,3 @@ - """ ``can`` is an object-orient Controller Area Network (CAN) interface module. """ diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 118880a66..52f0550f2 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -1,4 +1,3 @@ - """ Exposes several methods for transmitting cyclic messages. diff --git a/can/bus.py b/can/bus.py index f1d9133ad..fb6410cf5 100644 --- a/can/bus.py +++ b/can/bus.py @@ -1,4 +1,3 @@ - """ Contains the ABC bus implementation and its documentation. """ diff --git a/can/ctypesutil.py b/can/ctypesutil.py index dcadff1aa..cba8e797b 100644 --- a/can/ctypesutil.py +++ b/can/ctypesutil.py @@ -1,4 +1,3 @@ - """ This module contains common `ctypes` utils. """ diff --git a/can/interface.py b/can/interface.py index bd8a5c386..8d96f651d 100644 --- a/can/interface.py +++ b/can/interface.py @@ -1,4 +1,3 @@ - """ This module contains the base implementation of :class:`can.BusABC` as well as a list of all available backends and some implemented diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 792517500..a163ad101 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -1,4 +1,3 @@ - """ Interfaces contain low level implementations that interact with CAN hardware. """ diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 8cfa97ca1..a57260490 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -1,4 +1,3 @@ - from ctypes import * import logging import platform diff --git a/can/interfaces/ics_neovi/__init__.py b/can/interfaces/ics_neovi/__init__.py index 96af50d7e..1ac666b6c 100644 --- a/can/interfaces/ics_neovi/__init__.py +++ b/can/interfaces/ics_neovi/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 9eec48f45..df4f5481f 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -1,4 +1,3 @@ - """ ICS NeoVi interface module. diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index 360507976..a0bb413f2 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -1,4 +1,3 @@ - """ Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH. """ diff --git a/can/interfaces/ixxat/__init__.py b/can/interfaces/ixxat/__init__.py index 8fb17844a..a4613880b 100644 --- a/can/interfaces/ixxat/__init__.py +++ b/can/interfaces/ixxat/__init__.py @@ -1,4 +1,3 @@ - """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 68f1b0aba..aa90ffafe 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -1,4 +1,3 @@ - """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/constants.py b/can/interfaces/ixxat/constants.py index 9608bb2bf..a24a3f291 100644 --- a/can/interfaces/ixxat/constants.py +++ b/can/interfaces/ixxat/constants.py @@ -1,4 +1,3 @@ - """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/exceptions.py b/can/interfaces/ixxat/exceptions.py index 5b4347666..d548510b1 100644 --- a/can/interfaces/ixxat/exceptions.py +++ b/can/interfaces/ixxat/exceptions.py @@ -1,4 +1,3 @@ - """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/ixxat/structures.py b/can/interfaces/ixxat/structures.py index df1ebd8a6..73c01823d 100644 --- a/can/interfaces/ixxat/structures.py +++ b/can/interfaces/ixxat/structures.py @@ -1,4 +1,3 @@ - """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems diff --git a/can/interfaces/kvaser/__init__.py b/can/interfaces/kvaser/__init__.py index 191179ab3..36a21db9f 100644 --- a/can/interfaces/kvaser/__init__.py +++ b/can/interfaces/kvaser/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index 06c27e903..d0610019b 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -1,4 +1,3 @@ - """ Contains Python equivalents of the function and constant definitions in CANLIB's canlib.h, with some supporting functionality diff --git a/can/interfaces/kvaser/constants.py b/can/interfaces/kvaser/constants.py index d1a7ba610..9dd3a9163 100644 --- a/can/interfaces/kvaser/constants.py +++ b/can/interfaces/kvaser/constants.py @@ -1,4 +1,3 @@ - """ Contains Python equivalents of the function and constant definitions in CANLIB's canstat.h, with some supporting functionality diff --git a/can/interfaces/kvaser/structures.py b/can/interfaces/kvaser/structures.py index e2dd6a6a9..c7d363dd4 100644 --- a/can/interfaces/kvaser/structures.py +++ b/can/interfaces/kvaser/structures.py @@ -1,4 +1,3 @@ - """ Contains Python equivalents of the structures in CANLIB's canlib.h, with some supporting functionality specific to Python. diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index 90d396a5c..cc22c4f12 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -1,4 +1,3 @@ - """ NI-CAN interface module. diff --git a/can/interfaces/pcan/__init__.py b/can/interfaces/pcan/__init__.py index 8ae860a85..3627f0a36 100644 --- a/can/interfaces/pcan/__init__.py +++ b/can/interfaces/pcan/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index 3fe5468ea..b10e5404e 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -1,4 +1,3 @@ - """ PCAN-Basic API diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 9a1dae2b2..b4d551a64 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -1,4 +1,3 @@ - """ Enable basic CAN over a PCAN USB device. """ diff --git a/can/interfaces/seeedstudio/__init__.py b/can/interfaces/seeedstudio/__init__.py index a7a69c097..cb1c17f1d 100644 --- a/can/interfaces/seeedstudio/__init__.py +++ b/can/interfaces/seeedstudio/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index d9dfe448f..aecc3c15f 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -1,4 +1,3 @@ - """ To Support the Seeed USB-Can analyzer interface. The device will appear as a serial port, for example "/dev/ttyUSB0" on Linux machines diff --git a/can/interfaces/serial/__init__.py b/can/interfaces/serial/__init__.py index 21553a0da..bd6a45b9c 100644 --- a/can/interfaces/serial/__init__.py +++ b/can/interfaces/serial/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index 063b09257..d7a81c98a 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -1,4 +1,3 @@ - """ A text based interface. For example use over serial ports like "/dev/ttyS1" or "/dev/ttyUSB0" on Linux machines or "COM1" on Windows. diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 0a1931c16..5d7450987 100644 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -1,4 +1,3 @@ - """ Interface for slcan compatible interfaces (win32/linux). diff --git a/can/interfaces/socketcan/__init__.py b/can/interfaces/socketcan/__init__.py index a39cc1ef7..e08c18f50 100644 --- a/can/interfaces/socketcan/__init__.py +++ b/can/interfaces/socketcan/__init__.py @@ -1,4 +1,3 @@ - """ See: https://www.kernel.org/doc/Documentation/networking/can.txt """ diff --git a/can/interfaces/socketcan/constants.py b/can/interfaces/socketcan/constants.py index ae6c01519..0db298371 100644 --- a/can/interfaces/socketcan/constants.py +++ b/can/interfaces/socketcan/constants.py @@ -1,4 +1,3 @@ - """ Defines shared CAN constants. """ diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index 0e9a733cb..de304e218 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -1,4 +1,3 @@ - """ The main module of the socketcan interface containing most user-facing classes and methods along some internal methods. diff --git a/can/interfaces/socketcan/utils.py b/can/interfaces/socketcan/utils.py index f93ec1420..338e41dc8 100644 --- a/can/interfaces/socketcan/utils.py +++ b/can/interfaces/socketcan/utils.py @@ -1,4 +1,3 @@ - """ Defines common socketcan functions. """ diff --git a/can/interfaces/systec/__init__.py b/can/interfaces/systec/__init__.py index f211bc4ef..4ecd39b4c 100644 --- a/can/interfaces/systec/__init__.py +++ b/can/interfaces/systec/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/systec/constants.py b/can/interfaces/systec/constants.py index a35e2ccb5..96952c17e 100644 --- a/can/interfaces/systec/constants.py +++ b/can/interfaces/systec/constants.py @@ -1,4 +1,3 @@ - from ctypes import c_ubyte as BYTE, c_ushort as WORD, c_ulong as DWORD #: Maximum number of modules that are supported. diff --git a/can/interfaces/systec/exceptions.py b/can/interfaces/systec/exceptions.py index fd591d702..72ec92cfa 100644 --- a/can/interfaces/systec/exceptions.py +++ b/can/interfaces/systec/exceptions.py @@ -1,4 +1,3 @@ - from .constants import ReturnCode from can import CanError diff --git a/can/interfaces/systec/structures.py b/can/interfaces/systec/structures.py index b13ee31d9..fbebdcdbd 100644 --- a/can/interfaces/systec/structures.py +++ b/can/interfaces/systec/structures.py @@ -1,4 +1,3 @@ - from ctypes import Structure, POINTER, sizeof from ctypes import ( c_ubyte as BYTE, diff --git a/can/interfaces/systec/ucan.py b/can/interfaces/systec/ucan.py index 16ff6245b..3f90e6cc2 100644 --- a/can/interfaces/systec/ucan.py +++ b/can/interfaces/systec/ucan.py @@ -1,4 +1,3 @@ - import logging import sys diff --git a/can/interfaces/systec/ucanbus.py b/can/interfaces/systec/ucanbus.py index 3c65143b3..2d3a23777 100644 --- a/can/interfaces/systec/ucanbus.py +++ b/can/interfaces/systec/ucanbus.py @@ -1,4 +1,3 @@ - import logging from threading import Event diff --git a/can/interfaces/usb2can/__init__.py b/can/interfaces/usb2can/__init__.py index 4665c9fb1..4ccff1cb0 100644 --- a/can/interfaces/usb2can/__init__.py +++ b/can/interfaces/usb2can/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/usb2can/serial_selector.py b/can/interfaces/usb2can/serial_selector.py index 3b0f225b5..d9beb5df4 100644 --- a/can/interfaces/usb2can/serial_selector.py +++ b/can/interfaces/usb2can/serial_selector.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 29316b771..8e0f54687 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -1,4 +1,3 @@ - """ This interface is for Windows only, otherwise use socketCAN. """ diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index 2811ba235..1f336b241 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -1,4 +1,3 @@ - """ This wrapper is for windows or direct access via CANAL API. Socket CAN is recommended under Unix/Linux systems. diff --git a/can/interfaces/vector/__init__.py b/can/interfaces/vector/__init__.py index 19bb42afd..813608a86 100644 --- a/can/interfaces/vector/__init__.py +++ b/can/interfaces/vector/__init__.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index cc674288b..40a4ea991 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -1,4 +1,3 @@ - """ Ctypes wrapper module for Vector CAN Interface on win32/win64 systems. diff --git a/can/interfaces/vector/exceptions.py b/can/interfaces/vector/exceptions.py index fb34517c6..042c9d73a 100644 --- a/can/interfaces/vector/exceptions.py +++ b/can/interfaces/vector/exceptions.py @@ -1,4 +1,3 @@ - """ """ diff --git a/can/interfaces/vector/xlclass.py b/can/interfaces/vector/xlclass.py index 49eb410d0..90513f11e 100644 --- a/can/interfaces/vector/xlclass.py +++ b/can/interfaces/vector/xlclass.py @@ -1,4 +1,3 @@ - """ Definition of data types and structures for vxlapi. diff --git a/can/interfaces/vector/xldefine.py b/can/interfaces/vector/xldefine.py index 7cc929052..1d130624b 100644 --- a/can/interfaces/vector/xldefine.py +++ b/can/interfaces/vector/xldefine.py @@ -1,4 +1,3 @@ - """ Definition of constants for vxlapi. """ diff --git a/can/interfaces/vector/xldriver.py b/can/interfaces/vector/xldriver.py index fde7cca56..9bb1a1083 100644 --- a/can/interfaces/vector/xldriver.py +++ b/can/interfaces/vector/xldriver.py @@ -1,4 +1,3 @@ - """ Ctypes wrapper module for Vector CAN Interface on win32/win64 systems. diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index 436988dab..5e24c6e1f 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -1,4 +1,3 @@ - """ This module implements an OS and hardware independent virtual CAN interface for testing purposes. diff --git a/can/io/__init__.py b/can/io/__init__.py index f4c1b0359..53389e91b 100644 --- a/can/io/__init__.py +++ b/can/io/__init__.py @@ -1,4 +1,3 @@ - """ Read and write CAN bus messages using a range of Readers and Writers based off the file extension. diff --git a/can/io/asc.py b/can/io/asc.py index 41f947c57..0a7017559 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -1,4 +1,3 @@ - """ Contains handling of ASC logging files. diff --git a/can/io/blf.py b/can/io/blf.py index 21252f38a..6b9ccebce 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -1,4 +1,3 @@ - """ Implements support for BLF (Binary Logging Format) which is a proprietary CAN log format from Vector Informatik GmbH (Germany). diff --git a/can/io/canutils.py b/can/io/canutils.py index cd56ff32e..6333503c3 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -1,4 +1,3 @@ - """ This module works with CAN data in ASCII log files (*.log). It is is compatible with "candump -L" from the canutils program diff --git a/can/io/csv.py b/can/io/csv.py index a3793bdb6..35cfcc697 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -1,4 +1,3 @@ - """ This module contains handling for CSV (comma separated values) files. diff --git a/can/io/generic.py b/can/io/generic.py index 10f7fc6a9..eefa3d028 100644 --- a/can/io/generic.py +++ b/can/io/generic.py @@ -1,4 +1,3 @@ - """ Contains a generic class for file IO. """ diff --git a/can/io/logger.py b/can/io/logger.py index 6b63f12f5..3c9cf5e46 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -1,4 +1,3 @@ - """ See the :class:`Logger` class. """ diff --git a/can/io/player.py b/can/io/player.py index 79603455e..bd206061c 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -1,4 +1,3 @@ - """ This module contains the generic :class:`LogReader` as well as :class:`MessageSync` which plays back messages diff --git a/can/io/printer.py b/can/io/printer.py index 57e34db3f..ed3006de2 100644 --- a/can/io/printer.py +++ b/can/io/printer.py @@ -1,4 +1,3 @@ - """ This Listener simply prints to stdout / the terminal or a file. """ diff --git a/can/io/sqlite.py b/can/io/sqlite.py index abeab6090..104ceb77d 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -1,4 +1,3 @@ - """ Implements an SQL database writer and reader for storing CAN messages. diff --git a/can/listener.py b/can/listener.py index 44723bb99..d03a7e0c8 100644 --- a/can/listener.py +++ b/can/listener.py @@ -1,4 +1,3 @@ - """ This module contains the implementation of `can.Listener` and some readers. """ diff --git a/can/logger.py b/can/logger.py index 0a9055259..8a3a214e6 100644 --- a/can/logger.py +++ b/can/logger.py @@ -1,4 +1,3 @@ - """ logger.py logs CAN traffic to the terminal and to a file on disk. diff --git a/can/message.py b/can/message.py index 385d2244b..57e0109af 100644 --- a/can/message.py +++ b/can/message.py @@ -1,4 +1,3 @@ - """ This module contains the implementation of :class:`can.Message`. diff --git a/can/notifier.py b/can/notifier.py index 108a645b4..2b909cae7 100644 --- a/can/notifier.py +++ b/can/notifier.py @@ -1,4 +1,3 @@ - """ This module contains the implementation of :class:`~can.Notifier`. """ diff --git a/can/player.py b/can/player.py index 86bd0afe6..d7ef866fb 100644 --- a/can/player.py +++ b/can/player.py @@ -1,4 +1,3 @@ - """ Replays CAN traffic saved with can.logger back to a CAN bus. diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index b31e48407..f36119751 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -1,4 +1,3 @@ - from threading import RLock try: diff --git a/can/util.py b/can/util.py index 473057328..2cfebddb1 100644 --- a/can/util.py +++ b/can/util.py @@ -1,4 +1,3 @@ - """ Utilities and configuration file parsing. """ diff --git a/can/viewer.py b/can/viewer.py index 9f3ac361a..feeda7835 100644 --- a/can/viewer.py +++ b/can/viewer.py @@ -1,4 +1,3 @@ - # Copyright (C) 2018 Kristian Sloth Lauszus. # # This program is free software; you can redistribute it and/or diff --git a/doc/conf.py b/doc/conf.py index 62fd649b6..568a5641d 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,23 +1,24 @@ -# -*- coding: utf-8 -*- -# -# python-can documentation build configuration file -# -# This file is execfile()d with the current directory set to its containing dir. +""" +python-can documentation build configuration file + +This file is execfile()d with the current directory set to its containing dir. +""" -from __future__ import unicode_literals, absolute_import +# -- Imports ------------------------------------------------------------------- import sys import os -# General information about the project. -project = "python-can" - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("..")) -import can +import can # pylint: disable=wrong-import-position + +# -- General configuration ----------------------------------------------------- + +# pylint: disable=invalid-name # The version info for the project, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -27,7 +28,8 @@ version = can.__version__.split("-")[0] release = can.__version__ -# -- General configuration ----------------------------------------------------- +# General information about the project. +project = "python-can" primary_domain = "py" From 81c5e157f92133afa8ae2d15fb0c725397510b9e Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 4 Sep 2019 08:04:11 +0200 Subject: [PATCH 204/252] add scripts/*.py to pylint --- .travis.yml | 2 ++ scripts/can_logger.py | 3 --- scripts/can_player.py | 3 --- scripts/can_viewer.py | 3 --- 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index b904ebb42..e0e092d48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,6 +88,8 @@ jobs: - pylint --rcfile=.pylintrc *.py # check doc/conf.py - pylint --rcfile=.pylintrc doc/**.py + # check scripts + - pylint --rcfile=.pylintrc scripts/**.py # mypy checking - mypy --python-version=3.7 diff --git a/scripts/can_logger.py b/scripts/can_logger.py index 72a92b9d0..4202448e6 100644 --- a/scripts/can_logger.py +++ b/scripts/can_logger.py @@ -1,12 +1,9 @@ #!/usr/bin/env python -# coding: utf-8 """ See :mod:`can.logger`. """ -from __future__ import absolute_import - from can.logger import main diff --git a/scripts/can_player.py b/scripts/can_player.py index afbd3df6e..1fe44175d 100644 --- a/scripts/can_player.py +++ b/scripts/can_player.py @@ -1,12 +1,9 @@ #!/usr/bin/env python -# coding: utf-8 """ See :mod:`can.player`. """ -from __future__ import absolute_import - from can.player import main diff --git a/scripts/can_viewer.py b/scripts/can_viewer.py index 3c9ba738c..eef990b0e 100644 --- a/scripts/can_viewer.py +++ b/scripts/can_viewer.py @@ -1,12 +1,9 @@ #!/usr/bin/env python -# coding: utf-8 """ See :mod:`can.viewer`. """ -from __future__ import absolute_import - from can.viewer import main From 2ef710cca38e32f6ac1cc25586b9ae8b40efa600 Mon Sep 17 00:00:00 2001 From: Alexander Bessman Date: Wed, 11 Sep 2019 14:38:10 +0200 Subject: [PATCH 205/252] ASCReader: Skip J1939TP messages --- can/io/asc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/can/io/asc.py b/can/io/asc.py index 0a7017559..c538800d1 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -73,6 +73,7 @@ def __iter__(self): elif ( not isinstance(channel, int) or dummy.strip()[0:10].lower() == "statistic:" + or dummy.split(None, 1)[0] == "J1939TP" ): pass elif dummy[-1:].lower() == "r": From 6caa9d4b7cbf26968b40c6508d53c4c6c38ff7c8 Mon Sep 17 00:00:00 2001 From: Alexander Bessman Date: Thu, 12 Sep 2019 09:46:17 +0200 Subject: [PATCH 206/252] Add example J1939TP message to logfile.asc --- test/data/logfile.asc | 1 + 1 file changed, 1 insertion(+) diff --git a/test/data/logfile.asc b/test/data/logfile.asc index 4b7c64363..842c463fe 100644 --- a/test/data/logfile.asc +++ b/test/data/logfile.asc @@ -9,6 +9,7 @@ Begin Triggerblock Sam Sep 30 15:06:13.191 2017 1.015991 1 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% 1.015991 2 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% 2.015992 1 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% + 3.297743 1 J1939TP FEE3p 6 0 0 - Rx d 23 A0 0F A6 60 3B D1 40 1F DE 80 25 DF C0 2B E1 00 4B FF FF 3C 0F 00 4B FF FF FF FF FF FF FF FF FF FF FF FF 17.876707 CAN 1 Status:chip status error passive - TxErr: 131 RxErr: 0 17.876708 1 6F9 Rx d 8 05 0C 00 00 00 00 00 00 Length = 240015 BitCount = 124 ID = 1785 17.876976 1 6F8 Rx d 8 FF 00 0C FE 00 00 00 00 Length = 239910 BitCount = 124 ID = 1784 From b268712a558e90c5b085c04a9a822ebe11805c0d Mon Sep 17 00:00:00 2001 From: Alexander Bessman Date: Thu, 12 Sep 2019 10:34:54 +0200 Subject: [PATCH 207/252] Document log entry types ignored by ASCReader --- can/io/asc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/can/io/asc.py b/can/io/asc.py index c538800d1..9d854ab0f 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -24,7 +24,8 @@ class ASCReader(BaseIOHandler): """ - Iterator of CAN messages from a ASC logging file. + Iterator of CAN messages from a ASC logging file. Meta data (comments, + bus statistics, J1939 Transport Protocol messages) is ignored. TODO: turn relative timestamps back to absolute form """ From 68855df62e3983745d48c7b44df6979ba4bd38b6 Mon Sep 17 00:00:00 2001 From: Alexander Bessman Date: Thu, 12 Sep 2019 10:44:46 +0200 Subject: [PATCH 208/252] Add some more J1939TP examples --- test/data/logfile.asc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/data/logfile.asc b/test/data/logfile.asc index 842c463fe..8582cbf05 100644 --- a/test/data/logfile.asc +++ b/test/data/logfile.asc @@ -9,11 +9,20 @@ Begin Triggerblock Sam Sep 30 15:06:13.191 2017 1.015991 1 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% 1.015991 2 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% 2.015992 1 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% + 3.098426 1 18EBFF00x Rx d 8 01 A0 0F A6 60 3B D1 40 Length = 273910 BitCount = 141 ID = 418119424x + 3.148421 1 18EBFF00x Rx d 8 02 1F DE 80 25 DF C0 2B Length = 271910 BitCount = 140 ID = 418119424x + 3.197693 1 18EBFF00x Rx d 8 03 E1 00 4B FF FF 3C 0F Length = 283910 BitCount = 146 ID = 418119424x + 3.248765 1 18EBFF00x Rx d 8 04 00 4B FF FF FF FF FF Length = 283910 BitCount = 146 ID = 418119424x 3.297743 1 J1939TP FEE3p 6 0 0 - Rx d 23 A0 0F A6 60 3B D1 40 1F DE 80 25 DF C0 2B E1 00 4B FF FF 3C 0F 00 4B FF FF FF FF FF FF FF FF FF FF FF FF 17.876707 CAN 1 Status:chip status error passive - TxErr: 131 RxErr: 0 17.876708 1 6F9 Rx d 8 05 0C 00 00 00 00 00 00 Length = 240015 BitCount = 124 ID = 1785 17.876976 1 6F8 Rx d 8 FF 00 0C FE 00 00 00 00 Length = 239910 BitCount = 124 ID = 1784 18.015997 1 Statistic: D 2 R 0 XD 0 XR 0 E 0 O 0 B 0.04% + 20.105214 2 18EBFF00x Rx d 8 01 A0 0F A6 60 3B D1 40 Length = 273925 BitCount = 141 ID = 418119424x + 20.155119 2 18EBFF00x Rx d 8 02 1F DE 80 25 DF C0 2B Length = 272152 BitCount = 140 ID = 418119424x + 20.204671 2 18EBFF00x Rx d 8 03 E1 00 4B FF FF 3C 0F Length = 283910 BitCount = 146 ID = 418119424x + 20.248887 2 18EBFF00x Rx d 8 04 00 4B FF FF FF FF FF Length = 283925 BitCount = 146 ID = 418119424x + 20.305233 2 J1939TP FEE3p 6 0 0 - Rx d 23 A0 0F A6 60 3B D1 40 1F DE 80 25 DF C0 2B E1 00 4B FF FF 3C 0F 00 4B FF FF FF FF FF FF FF FF FF FF FF FF 113.016026 1 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% 113.016026 2 Statistic: D 0 R 0 XD 0 XR 0 E 0 O 0 B 0.00% End TriggerBlock From 7bf39285967d785843919f12a4f69ba736181586 Mon Sep 17 00:00:00 2001 From: Karl Date: Tue, 6 Aug 2019 17:37:32 -0700 Subject: [PATCH 209/252] Add typing annotations for can.util This adds typing annotations for functions in can.util. In addition, this remove the redundant typing information that was previously in the docstring, since we now have sphinx-autodoc-typehints to generate the types for the docs from the annotations in the function signature. This works towards PEP 561 compatibility. --- can/typechecking.py | 2 ++ can/util.py | 41 +++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/can/typechecking.py b/can/typechecking.py index b5b79d500..7d7b1d893 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -27,3 +27,5 @@ FileLike = typing.IO[typing.Any] StringPathLike = typing.Union[str, "os.PathLike[str]"] AcceptedIOType = typing.Optional[typing.Union[FileLike, StringPathLike]] + +BusConfig = typing.NewType("BusConfig", dict) diff --git a/can/util.py b/can/util.py index 2cfebddb1..968f1f7fd 100644 --- a/can/util.py +++ b/can/util.py @@ -2,6 +2,10 @@ Utilities and configuration file parsing. """ +from typing import Dict, Optional, Union + +from can import typechecking + import json import os import os.path @@ -29,7 +33,9 @@ CONFIG_FILES.extend(["can.ini", os.path.join(os.getenv("APPDATA", ""), "can.ini")]) -def load_file_config(path=None, section="default"): +def load_file_config( + path: Optional[typechecking.AcceptedIOType] = None, section: str = "default" +) -> Dict[str, str]: """ Loads configuration from file with following content:: @@ -57,7 +63,7 @@ def load_file_config(path=None, section="default"): return _config -def load_environment_config(context=None): +def load_environment_config(context: Optional[str] = None) -> Dict[str, str]: """ Loads config dict from environmental variables (if set): @@ -83,20 +89,22 @@ def load_environment_config(context=None): context_suffix = "_{}".format(context) if context else "" - config = {} - can_config_key = "CAN_CONFIG" + context_suffix - if can_config_key in os.environ: - config = json.loads(os.environ.get(can_config_key)) + config: Dict[str, str] = json.loads(os.environ.get(can_config_key, "{}")) for key, val in mapper.items(): - if val in os.environ: - config[key] = os.environ.get(val + context_suffix) + config_option = os.environ.get(val + context_suffix, None) + if config_option: + config[key] = config_option return config -def load_config(path=None, config=None, context=None): +def load_config( + path: Optional[typechecking.AcceptedIOType] = None, + config=None, + context: Optional[str] = None, +) -> typechecking.BusConfig: """ Returns a dict with configuration details which is loaded from (in this order): @@ -213,26 +221,25 @@ def load_config(path=None, config=None, context=None): return config -def set_logging_level(level_name=None): +def set_logging_level(level_name: Optional[str] = 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())) + can_logger.setLevel(getattr(logging, level_name.upper())) # type: ignore except AttributeError: can_logger.setLevel(logging.DEBUG) log.debug("Logging set to {}".format(level_name)) -def len2dlc(length): +def len2dlc(length: int) -> int: """Calculate the DLC from data length. :param int length: Length in number of bytes (0-64) :returns: DLC (0-15) - :rtype: int """ if length <= 8: return length @@ -242,25 +249,23 @@ def len2dlc(length): return 15 -def dlc2len(dlc): +def dlc2len(dlc: int) -> int: """Calculate the data length from DLC. - :param int dlc: DLC (0-15) + :param dlc: DLC (0-15) :returns: Data length in number of bytes (0-64) - :rtype: int """ return CAN_FD_DLC[dlc] if dlc <= 15 else 64 -def channel2int(channel): +def channel2int(channel: Optional[Union[typechecking.Channel]]) -> Optional[int]: """Try to convert the channel to an integer. :param channel: Channel string (e.g. can0, CAN1) or integer :returns: Channel integer or `None` if unsuccessful - :rtype: int """ if channel is None: return None From c93a2e0d2a6a43c8fa2e7ccf3ead1a0f0dc3dd8a Mon Sep 17 00:00:00 2001 From: Karl Date: Wed, 23 Oct 2019 08:35:56 -0700 Subject: [PATCH 210/252] Fix typing annotation for LogReader constructor A bug was introduced sometime during refactoring of types in the helper typechecking.py file, when PathLike was renamed to StringPathLike. This results in mypy complaining that it can't find the PathLike type: error: Name 'can.typechecking.PathLike' is not defined As such, this changes the accepted type for the LogReader constructor to be a typechecking.StringPathLike. --- can/io/player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/player.py b/can/io/player.py index bd206061c..7b4aec7c0 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -45,7 +45,7 @@ class LogReader(BaseIOHandler): """ @staticmethod - def __new__(cls, filename: "can.typechecking.PathLike", *args, **kwargs): + def __new__(cls, filename: "can.typechecking.StringPathLike", *args, **kwargs): """ :param filename: the filename/path of the file to read from :raises ValueError: if the filename's suffix is of an unknown file type From 216ed73d3250b1622dde62a7a2fbbffbfe320880 Mon Sep 17 00:00:00 2001 From: Kai Oberbeckmann Date: Thu, 24 Oct 2019 12:21:06 +0200 Subject: [PATCH 211/252] Initialized dataBin in canutils with None to prevent crashes when reading remote frames (#713) --- can/io/canutils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/can/io/canutils.py b/can/io/canutils.py index 6333503c3..5c08e9050 100644 --- a/can/io/canutils.py +++ b/can/io/canutils.py @@ -56,10 +56,13 @@ def __iter__(self): if data and data[0].lower() == "r": isRemoteFrame = True + if len(data) > 1: dlc = int(data[1:]) else: dlc = 0 + + dataBin = None else: isRemoteFrame = False From 17093cc8b92a7d6ce3a8903851382ba87cc9baae Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 24 Oct 2019 08:59:51 -0700 Subject: [PATCH 212/252] Bump mypy version in lint step to 0.740 Update the version of mypy used in the CI Linter step to the latest release. --- requirements-lint.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-lint.txt b/requirements-lint.txt index ce953e68b..d0b374c20 100644 --- a/requirements-lint.txt +++ b/requirements-lint.txt @@ -1,4 +1,4 @@ pylint==2.3.1 black==19.3b0 -mypy==0.720 +mypy==0.740 mypy-extensions==0.4.1 From 69451507cedf48912211cbc39575e0d88094edfa Mon Sep 17 00:00:00 2001 From: tamenol <37591107+tamenol@users.noreply.github.com> Date: Thu, 7 Nov 2019 08:26:19 +0100 Subject: [PATCH 213/252] Add support for error frames on PCAN (#711) Enable reception of error frames on the PCAN interface. Fixes #707 --- can/interfaces/pcan/pcan.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index b4d551a64..5c7b2b9d7 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -209,6 +209,13 @@ def __init__( self.m_PcanHandle, pcan_bitrate, hwtype, ioport, interrupt ) + if result != PCAN_ERROR_OK: + raise PcanError(self._get_formatted_error(result)) + + result = self.m_objPCANBasic.SetValue( + self.m_PcanHandle, PCAN_ALLOW_ERROR_FRAMES, PCAN_PARAMETER_ON + ) + if result != PCAN_ERROR_OK: raise PcanError(self._get_formatted_error(result)) From c001ef274316b015f7b068414a541a573ce4ef5c Mon Sep 17 00:00:00 2001 From: karl ding Date: Wed, 6 Nov 2019 23:56:33 -0800 Subject: [PATCH 214/252] Add typing annotations for SocketCAN interface (#710) Add typing annotations for the SocketCAN interface. This works towards PEP 561 compatibility. --- .travis.yml | 1 + can/bus.py | 10 +- can/interfaces/socketcan/socketcan.py | 178 ++++++++++++++++---------- can/interfaces/socketcan/utils.py | 10 +- can/typechecking.py | 11 +- 5 files changed, 131 insertions(+), 79 deletions(-) diff --git a/.travis.yml b/.travis.yml index e0a4b0b3f..2bea7900f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -105,6 +105,7 @@ jobs: can/broadcastmanager.py can/bus.py can/interface.py + can/interfaces/socketcan/**.py can/listener.py can/logger.py can/message.py diff --git a/can/bus.py b/can/bus.py index fb6410cf5..4ef5b5fd6 100644 --- a/can/bus.py +++ b/can/bus.py @@ -2,7 +2,7 @@ Contains the ABC bus implementation and its documentation. """ -from typing import Iterator, List, Optional, Sequence, Tuple, Union +from typing import cast, Iterator, List, Optional, Sequence, Tuple, Union import can.typechecking @@ -369,8 +369,10 @@ def _matches_filters(self, msg: Message) -> bool: for _filter in self._filters: # check if this filter even applies to the message - if "extended" in _filter and _filter["extended"] != msg.is_extended_id: - continue + if "extended" in _filter: + _filter = cast(can.typechecking.CanFilterExtended, _filter) + if _filter["extended"] != msg.is_extended_id: + continue # then check for the mask and id can_id = _filter["can_id"] @@ -416,7 +418,7 @@ def state(self, new_state: BusState): raise NotImplementedError("Property is not implemented.") @staticmethod - def _detect_available_configs() -> Iterator[dict]: + def _detect_available_configs() -> List[can.typechecking.AutoDetectedConfig]: """Detect all configurations/channels that this interface could currently connect with. diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index de304e218..307763d9f 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -5,6 +5,8 @@ At the end of the file the usage of the internal methods is shown. """ +from typing import Dict, List, Optional, Sequence, Tuple, Type, Union + import logging import ctypes import ctypes.util @@ -34,11 +36,17 @@ from can.interfaces.socketcan.constants import * # CAN_RAW, CAN_*_FLAG from can.interfaces.socketcan.utils import pack_filters, find_available_interfaces - # Setup BCM struct -def bcm_header_factory(fields, alignment=8): +def bcm_header_factory( + fields: List[Tuple[str, Union[Type[ctypes.c_uint32], Type[ctypes.c_long]]]], + alignment: int = 8, +): curr_stride = 0 - results = [] + results: List[ + Tuple[ + str, Union[Type[ctypes.c_uint8], Type[ctypes.c_uint32], Type[ctypes.c_long]] + ] + ] = [] pad_index = 0 for field in fields: field_alignment = ctypes.alignment(field[1]) @@ -122,7 +130,7 @@ def bcm_header_factory(fields, alignment=8): CAN_FRAME_HEADER_STRUCT = struct.Struct("=IBB2x") -def build_can_frame(msg): +def build_can_frame(msg: Message) -> bytes: """ CAN frame packing/unpacking (see 'struct can_frame' in ) /** * struct can_frame - basic CAN frame structure @@ -166,16 +174,16 @@ def build_can_frame(msg): def build_bcm_header( - opcode, - flags, - count, - ival1_seconds, - ival1_usec, - ival2_seconds, - ival2_usec, - can_id, - nframes, -): + opcode: int, + flags: int, + count: int, + ival1_seconds: int, + ival1_usec: int, + ival2_seconds: int, + ival2_usec: int, + can_id: int, + nframes: int, +) -> bytes: result = BcmMsgHead( opcode=opcode, flags=flags, @@ -190,14 +198,19 @@ def build_bcm_header( return ctypes.string_at(ctypes.addressof(result), ctypes.sizeof(result)) -def build_bcm_tx_delete_header(can_id, flags): +def build_bcm_tx_delete_header(can_id: int, flags: int) -> bytes: opcode = CAN_BCM_TX_DELETE return build_bcm_header(opcode, flags, 0, 0, 0, 0, 0, can_id, 1) def build_bcm_transmit_header( - can_id, count, initial_period, subsequent_period, msg_flags, nframes=1 -): + can_id: int, + count: int, + initial_period: float, + subsequent_period: float, + msg_flags: int, + nframes: int = 1, +) -> bytes: opcode = CAN_BCM_TX_SETUP flags = msg_flags | SETTIMER | STARTTIMER @@ -206,7 +219,7 @@ def build_bcm_transmit_header( # Note `TX_COUNTEVT` creates the message TX_EXPIRED when count expires flags |= TX_COUNTEVT - def split_time(value): + def split_time(value: float) -> Tuple[int, int]: """Given seconds as a float, return whole seconds and microseconds""" seconds = int(value) microseconds = int(1e6 * (value - seconds)) @@ -228,11 +241,11 @@ def split_time(value): ) -def build_bcm_update_header(can_id, msg_flags, nframes=1): +def build_bcm_update_header(can_id: int, msg_flags: int, nframes: int = 1) -> bytes: return build_bcm_header(CAN_BCM_TX_SETUP, msg_flags, 0, 0, 0, 0, 0, can_id, nframes) -def dissect_can_frame(frame): +def dissect_can_frame(frame: bytes) -> Tuple[int, int, int, bytes]: can_id, can_dlc, flags = CAN_FRAME_HEADER_STRUCT.unpack_from(frame) if len(frame) != CANFD_MTU: # Flags not valid in non-FD frames @@ -240,14 +253,14 @@ def dissect_can_frame(frame): return can_id, can_dlc, flags, frame[8 : 8 + can_dlc] -def create_bcm_socket(channel): +def create_bcm_socket(channel: str) -> socket.socket: """create a broadcast manager socket and connect to the given interface""" s = socket.socket(PF_CAN, socket.SOCK_DGRAM, CAN_BCM) s.connect((channel,)) return s -def send_bcm(bcm_socket, data): +def send_bcm(bcm_socket: socket.socket, data: bytes) -> int: """ Send raw frame to a BCM socket and handle errors. """ @@ -273,7 +286,7 @@ def send_bcm(bcm_socket, data): raise e -def _add_flags_to_can_id(message): +def _add_flags_to_can_id(message: Message) -> int: can_id = message.arbitration_id if message.is_extended_id: log.debug("sending an extended id type message") @@ -300,14 +313,20 @@ class CyclicSendTask( """ - def __init__(self, bcm_socket, messages, period, duration=None): + def __init__( + self, + bcm_socket: socket.socket, + messages: Union[Sequence[Message], Message], + period: float, + duration: Optional[float] = None, + ): """ :param bcm_socket: An open BCM socket on the desired CAN channel. - :param Union[Sequence[can.Message], can.Message] messages: + :param messages: The messages to be sent periodically. - :param float period: + :param period: The rate in seconds at which to send the messages. - :param float duration: + :param duration: Approximate duration in seconds to send the messages for. """ # The following are assigned by LimitedDurationCyclicSendTaskABC: @@ -319,9 +338,8 @@ def __init__(self, bcm_socket, messages, period, duration=None): self.bcm_socket = bcm_socket self._tx_setup(self.messages) - def _tx_setup(self, messages): + def _tx_setup(self, messages: Sequence[Message]) -> None: # Create a low level packed frame to pass to the kernel - header = bytearray() body = bytearray() self.can_id_with_flags = _add_flags_to_can_id(messages[0]) self.flags = CAN_FD_FRAME if messages[0].is_fd else 0 @@ -329,10 +347,10 @@ def _tx_setup(self, messages): if self.duration: count = int(self.duration / self.period) ival1 = self.period - ival2 = 0 + ival2 = 0.0 else: count = 0 - ival1 = 0 + ival1 = 0.0 ival2 = self.period # First do a TX_READ before creating a new task, and check if we get @@ -374,7 +392,7 @@ def _tx_setup(self, messages): log.debug("Sending BCM command") send_bcm(self.bcm_socket, header + body) - def stop(self): + def stop(self) -> None: """Send a TX_DELETE message to cancel this task. This will delete the entry for the transmission of the CAN-message @@ -386,7 +404,7 @@ def stop(self): stopframe = build_bcm_tx_delete_header(self.can_id_with_flags, self.flags) send_bcm(self.bcm_socket, stopframe) - def modify_data(self, messages): + def modify_data(self, messages: Union[Sequence[Message], Message]) -> None: """Update the contents of the periodically sent messages. Note: The messages must all have the same @@ -395,7 +413,7 @@ def modify_data(self, messages): Note: The number of new cyclic messages to be sent must be equal to the original number of messages originally specified for this task. - :param Union[Sequence[can.Message], can.Message] messages: + :param messages: The messages with the new :attr:`can.Message.data`. """ messages = self._check_and_convert_messages(messages) @@ -403,7 +421,6 @@ def modify_data(self, messages): self.messages = messages - header = bytearray() body = bytearray() header = build_bcm_update_header( can_id=self.can_id_with_flags, msg_flags=self.flags, nframes=len(messages) @@ -413,7 +430,7 @@ def modify_data(self, messages): log.debug("Sending BCM command") send_bcm(self.bcm_socket, header + body) - def start(self): + def start(self) -> None: self._tx_setup(self.messages) @@ -423,7 +440,14 @@ class MultiRateCyclicSendTask(CyclicSendTask): """ - def __init__(self, channel, messages, count, initial_period, subsequent_period): + def __init__( + self, + channel: socket.socket, + messages: Sequence[Message], + count: int, + initial_period: float, + subsequent_period: float, + ): super().__init__(channel, messages, subsequent_period) # Create a low level packed frame to pass to the kernel @@ -444,7 +468,7 @@ def __init__(self, channel, messages, count, initial_period, subsequent_period): send_bcm(self.bcm_socket, header + body) -def create_socket(): +def create_socket() -> socket.socket: """Creates a raw CAN socket. The socket will be returned unbound to any interface. """ @@ -455,11 +479,11 @@ def create_socket(): return sock -def bind_socket(sock, channel="can0"): +def bind_socket(sock: socket.socket, channel: str = "can0") -> None: """ Binds the given socket to the given interface. - :param socket.socket sock: + :param sock: The socket to be bound :raises OSError: If the specified interface isn't found. @@ -469,13 +493,15 @@ def bind_socket(sock, channel="can0"): log.debug("Bound socket.") -def capture_message(sock, get_channel=False): +def capture_message( + sock: socket.socket, get_channel: bool = False +) -> Optional[Message]: """ Captures a message from given socket. - :param socket.socket sock: + :param sock: The socket to read a message from. - :param bool get_channel: + :param get_channel: Find out which channel the message comes from. :return: The received message, or None on failure. @@ -496,7 +522,7 @@ def capture_message(sock, get_channel=False): # Fetching the timestamp binary_structure = "@LL" - res = fcntl.ioctl(sock, SIOCGSTAMP, struct.pack(binary_structure, 0, 0)) + res = fcntl.ioctl(sock.fileno(), SIOCGSTAMP, struct.pack(binary_structure, 0, 0)) seconds, microseconds = struct.unpack(binary_structure, res) timestamp = seconds + microseconds * 1e-6 @@ -545,17 +571,23 @@ class SocketcanBus(BusABC): Implements :meth:`can.BusABC._detect_available_configs`. """ - def __init__(self, channel="", receive_own_messages=False, fd=False, **kwargs): + def __init__( + self, + channel: str = "", + receive_own_messages: bool = False, + fd: bool = False, + **kwargs, + ) -> None: """ - :param str channel: + :param channel: The can interface name with which to create this bus. An example channel would be 'vcan0' or 'can0'. An empty string '' will receive messages from all channels. In that case any sent messages must be explicitly addressed to a channel using :attr:`can.Message.channel`. - :param bool receive_own_messages: + :param receive_own_messages: If transmitted messages should also be received by this bus. - :param bool fd: + :param fd: If CAN-FD frames should be supported. :param list can_filters: See :meth:`can.BusABC.set_filters`. @@ -563,7 +595,7 @@ def __init__(self, channel="", receive_own_messages=False, fd=False, **kwargs): self.socket = create_socket() self.channel = channel self.channel_info = "socketcan channel '%s'" % channel - self._bcm_sockets = {} + self._bcm_sockets: Dict[str, socket.socket] = {} self._is_filtered = False # set the receive_own_messages parameter @@ -585,7 +617,7 @@ def __init__(self, channel="", receive_own_messages=False, fd=False, **kwargs): kwargs.update({"receive_own_messages": receive_own_messages, "fd": fd}) super().__init__(channel=channel, **kwargs) - def shutdown(self): + def shutdown(self) -> None: """Stops all active periodic tasks and closes the socket.""" self.stop_all_periodic_tasks() for channel in self._bcm_sockets: @@ -595,7 +627,9 @@ def shutdown(self): log.debug("Closing raw can socket") self.socket.close() - def _recv_internal(self, timeout): + def _recv_internal( + self, timeout: Optional[float] + ) -> Tuple[Optional[Message], bool]: # get all sockets that are ready (can be a list with a single value # being self.socket or an empty list if self.socket is not ready) try: @@ -609,7 +643,7 @@ def _recv_internal(self, timeout): if ready_receive_sockets: # not empty or True get_channel = self.channel == "" msg = capture_message(self.socket, get_channel) - if not msg.channel and self.channel: + if msg and not msg.channel and self.channel: # Default to our own channel msg.channel = self.channel return msg, self._is_filtered @@ -617,11 +651,11 @@ def _recv_internal(self, timeout): # socket wasn't readable or timeout occurred return None, self._is_filtered - def send(self, msg, timeout=None): + def send(self, msg: Message, timeout: Optional[float] = None) -> None: """Transmit a message to the CAN bus. - :param can.Message msg: A message object. - :param float timeout: + :param msg: A message object. + :param timeout: Wait up to this many seconds for the transmit queue to be ready. If not given, the call may fail immediately. @@ -645,7 +679,8 @@ def send(self, msg, timeout=None): if not ready: # Timeout break - sent = self._send_once(data, msg.channel) + channel = str(msg.channel) if msg.channel else None + sent = self._send_once(data, channel) if sent == len(data): return # Not all data were sent, try again with remaining data @@ -654,7 +689,7 @@ def send(self, msg, timeout=None): raise can.CanError("Transmit buffer full") - def _send_once(self, data, channel=None): + def _send_once(self, data: bytes, channel: Optional[str] = None) -> int: try: if self.channel == "" and channel: # Message must be addressed to a specific channel @@ -665,23 +700,27 @@ def _send_once(self, data, channel=None): raise can.CanError("Failed to transmit: %s" % exc) return sent - def _send_periodic_internal(self, msgs, period, duration=None): + def _send_periodic_internal( + self, + msgs: Union[Sequence[Message], Message], + period: float, + duration: Optional[float] = None, + ) -> CyclicSendTask: """Start sending messages at a given period on this bus. The kernel's Broadcast Manager SocketCAN API will be used. - :param Union[Sequence[can.Message], can.Message] messages: + :param messages: The messages to be sent periodically - :param float period: + :param period: The rate in seconds at which to send the messages. - :param float duration: + :param duration: Approximate duration in seconds to continue sending messages. If no duration is provided, the task will continue indefinitely. :return: A started task instance. This can be used to modify the data, pause/resume the transmission and to stop the transmission. - :rtype: can.interfaces.socketcan.CyclicSendTask .. note:: @@ -693,19 +732,20 @@ def _send_periodic_internal(self, msgs, period, duration=None): """ msgs = LimitedDurationCyclicSendTaskABC._check_and_convert_messages(msgs) - bcm_socket = self._get_bcm_socket(msgs[0].channel or self.channel) + msgs_channel = str(msgs[0].channel) if msgs[0].channel else None + bcm_socket = self._get_bcm_socket(msgs_channel or self.channel) # TODO: The SocketCAN BCM interface treats all cyclic tasks sharing an # Arbitration ID as the same Cyclic group. We should probably warn the # user instead of overwriting the old group? task = CyclicSendTask(bcm_socket, msgs, period, duration) return task - def _get_bcm_socket(self, channel): + def _get_bcm_socket(self, channel: str) -> socket.socket: if channel not in self._bcm_sockets: self._bcm_sockets[channel] = create_bcm_socket(self.channel) return self._bcm_sockets[channel] - def _apply_filters(self, filters): + def _apply_filters(self, filters: Optional[can.typechecking.CanFilters]) -> None: try: self.socket.setsockopt(SOL_CAN_RAW, CAN_RAW_FILTER, pack_filters(filters)) except socket.error as err: @@ -719,11 +759,11 @@ def _apply_filters(self, filters): else: self._is_filtered = True - def fileno(self): + def fileno(self) -> int: return self.socket.fileno() @staticmethod - def _detect_available_configs(): + def _detect_available_configs() -> List[can.typechecking.AutoDetectedConfig]: return [ {"interface": "socketcan", "channel": channel} for channel in find_available_interfaces() @@ -742,14 +782,14 @@ def _detect_available_configs(): # log.setLevel(logging.DEBUG) - def receiver(event): + def receiver(event: threading.Event) -> None: receiver_socket = create_socket() bind_socket(receiver_socket, "vcan0") print("Receiver is waiting for a message...") event.set() print(f"Receiver got: {capture_message(receiver_socket)}") - def sender(event): + def sender(event: threading.Event) -> None: event.wait() sender_socket = create_socket() bind_socket(sender_socket, "vcan0") diff --git a/can/interfaces/socketcan/utils.py b/can/interfaces/socketcan/utils.py index 338e41dc8..ee89b142e 100644 --- a/can/interfaces/socketcan/utils.py +++ b/can/interfaces/socketcan/utils.py @@ -2,6 +2,9 @@ Defines common socketcan functions. """ +from typing import cast, Iterable, Optional +import can.typechecking as typechecking + import logging import os import errno @@ -14,7 +17,7 @@ log = logging.getLogger(__name__) -def pack_filters(can_filters=None): +def pack_filters(can_filters: Optional[typechecking.CanFilters] = None) -> bytes: if can_filters is None: # Pass all messages can_filters = [{"can_id": 0, "can_mask": 0}] @@ -25,6 +28,7 @@ def pack_filters(can_filters=None): can_id = can_filter["can_id"] can_mask = can_filter["can_mask"] if "extended" in can_filter: + can_filter = cast(typechecking.CanFilterExtended, can_filter) # Match on either 11-bit OR 29-bit messages instead of both can_mask |= CAN_EFF_FLAG if can_filter["extended"]: @@ -38,7 +42,7 @@ def pack_filters(can_filters=None): _PATTERN_CAN_INTERFACE = re.compile(r"v?can\d+") -def find_available_interfaces(): +def find_available_interfaces() -> Iterable[str]: """Returns the names of all open can/vcan interfaces using the ``ip link list`` command. If the lookup fails, an error is logged to the console and an empty list is returned. @@ -64,7 +68,7 @@ def find_available_interfaces(): return filter(_PATTERN_CAN_INTERFACE.match, interface_names) -def error_code_to_str(code): +def error_code_to_str(code: int) -> str: """ Converts a given error code (errno) to a useful and human readable string. diff --git a/can/typechecking.py b/can/typechecking.py index 7d7b1d893..0327874f5 100644 --- a/can/typechecking.py +++ b/can/typechecking.py @@ -8,10 +8,11 @@ import mypy_extensions -CanFilter = mypy_extensions.TypedDict( - "CanFilter", {"can_id": int, "can_mask": int, "extended": bool} +CanFilter = mypy_extensions.TypedDict("CanFilter", {"can_id": int, "can_mask": int}) +CanFilterExtended = mypy_extensions.TypedDict( + "CanFilterExtended", {"can_id": int, "can_mask": int, "extended": bool} ) -CanFilters = typing.Iterable[CanFilter] +CanFilters = typing.Sequence[typing.Union[CanFilter, CanFilterExtended]] # TODO: Once buffer protocol support lands in typing, we should switch to that, # since can.message.Message attempts to call bytearray() on the given data, so @@ -29,3 +30,7 @@ AcceptedIOType = typing.Optional[typing.Union[FileLike, StringPathLike]] BusConfig = typing.NewType("BusConfig", dict) + +AutoDetectedConfig = mypy_extensions.TypedDict( + "AutoDetectedConfig", {"interface": str, "channel": Channel} +) From ac844547b45c19d25002286e4734cfe565e83bf1 Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Tue, 12 Nov 2019 08:06:53 +0100 Subject: [PATCH 215/252] vector xlclass - implement XLbusParams (#718) --- can/interfaces/vector/xlclass.py | 52 ++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/can/interfaces/vector/xlclass.py b/can/interfaces/vector/xlclass.py index 90513f11e..de799f12b 100644 --- a/can/interfaces/vector/xlclass.py +++ b/can/interfaces/vector/xlclass.py @@ -169,10 +169,58 @@ class XLcanFdConf(ctypes.Structure): ("sjwDbr", ctypes.c_uint), ("tseg1Dbr", ctypes.c_uint), ("tseg2Dbr", ctypes.c_uint), - ("reserved", ctypes.c_uint * 2), + ("reserved", ctypes.c_ubyte), + ("options", ctypes.c_ubyte), + ("reserved1", ctypes.c_ubyte * 2), + ("reserved2", ctypes.c_ubyte), + ] + + +# channel configuration structures +class s_xl_bus_params_data_can(ctypes.Structure): + _fields_ = [ + ("bitRate", ctypes.c_uint), + ("sjw", ctypes.c_ubyte), + ("tseg1", ctypes.c_ubyte), + ("tseg2", ctypes.c_ubyte), + ("sam", ctypes.c_ubyte), + ("outputMode", ctypes.c_ubyte), + ("reserved", ctypes.c_ubyte * 7), + ("canOpMode", ctypes.c_ubyte), + ] + + +class s_xl_bus_params_data_canfd(ctypes.Structure): + _fields_ = [ + ("arbitrationBitRate", ctypes.c_uint), + ("sjwAbr", ctypes.c_ubyte), + ("tseg1Abr", ctypes.c_ubyte), + ("tseg2Abr", ctypes.c_ubyte), + ("samAbr", ctypes.c_ubyte), + ("outputMode", ctypes.c_ubyte), + ("sjwDbr", ctypes.c_ubyte), + ("tseg1Dbr", ctypes.c_ubyte), + ("tseg2Dbr", ctypes.c_ubyte), + ("dataBitRate", ctypes.c_uint), + ("canOpMode", ctypes.c_ubyte), ] +class s_xl_bus_params_data(ctypes.Union): + _fields_ = [ + ("can", s_xl_bus_params_data_can), + ("canFD", s_xl_bus_params_data_canfd), + ("most", ctypes.c_ubyte * 12), + ("flexray", ctypes.c_ubyte * 12), + ("ethernet", ctypes.c_ubyte * 12), + ("a429", ctypes.c_ubyte * 28), + ] + + +class XLbusParams(ctypes.Structure): + _fields_ = [("busType", ctypes.c_uint), ("data", s_xl_bus_params_data)] + + class XLchannelConfig(ctypes.Structure): _pack_ = 1 _fields_ = [ @@ -189,7 +237,7 @@ class XLchannelConfig(ctypes.Structure): ("channelBusCapabilities", ctypes.c_uint), ("isOnBus", ctypes.c_ubyte), ("connectedBusType", ctypes.c_uint), - ("busParams", ctypes.c_ubyte * 32), + ("busParams", XLbusParams), ("_doNotUse", ctypes.c_uint), ("driverVersion", ctypes.c_uint), ("interfaceVersion", ctypes.c_uint), From 3cf42bb20412ed7a054ebd2a89397211429ed052 Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Tue, 12 Nov 2019 15:27:46 +0100 Subject: [PATCH 216/252] VectorBus - add methods to handle non message events (#708) --- can/interfaces/vector/canlib.py | 29 ++++++++++++++++ can/interfaces/vector/xlclass.py | 10 +++++- test/test_vector.py | 58 ++++++++++++++++++++++++++++---- 3 files changed, 90 insertions(+), 7 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 40a4ea991..ed366b4c8 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -396,6 +396,9 @@ def _recv_internal(self, timeout): channel=channel, ) return msg, self._is_filtered + else: + self.handle_canfd_event(event) + else: event_count.value = 1 try: @@ -431,6 +434,8 @@ def _recv_internal(self, timeout): channel=channel, ) return msg, self._is_filtered + else: + self.handle_can_event(event) if end_time is not None and time.time() > end_time: return None, self._is_filtered @@ -447,6 +452,30 @@ def _recv_internal(self, timeout): # Wait a short time until we try again time.sleep(self.poll_interval) + def handle_can_event(self, event: xlclass.XLevent) -> None: + """Handle non-message CAN events. + + Method is called by :meth:`~can.interfaces.vector.VectorBus._recv_internal` + when `event.tag` is not `XL_CAN_EV_TAG_RX_OK` or `XL_CAN_EV_TAG_TX_OK`. + Subclasses can implement this method. + + :param event: XLevent that could have a `XL_CHIP_STATE`, `XL_TIMER` or `XL_SYNC_PULSE` tag. + :return: None + """ + pass + + def handle_canfd_event(self, event: xlclass.XLcanRxEvent) -> None: + """Handle non-message CAN FD events. + + Method is called by :meth:`~can.interfaces.vector.VectorBus._recv_internal` + when `event.tag` is not `XL_RECEIVE_MSG`. Subclasses can implement this method. + + :param event: `XLcanRxEvent` that could have a `XL_CAN_EV_TAG_RX_ERROR`, `XL_CAN_EV_TAG_TX_ERROR` + or `XL_CAN_EV_TAG_CHIP_STATE` tag. + :return: None + """ + pass + def send(self, msg, timeout=None): msg_id = msg.arbitration_id diff --git a/can/interfaces/vector/xlclass.py b/can/interfaces/vector/xlclass.py index de799f12b..8c55bf058 100644 --- a/can/interfaces/vector/xlclass.py +++ b/can/interfaces/vector/xlclass.py @@ -35,6 +35,14 @@ class s_xl_can_ev_error(ctypes.Structure): _fields_ = [("errorCode", ctypes.c_ubyte), ("reserved", ctypes.c_ubyte * 95)] +class s_xl_chip_state(ctypes.Structure): + _fields_ = [ + ("busStatus", ctypes.c_ubyte), + ("txErrorCounter", ctypes.c_ubyte), + ("rxErrorCounter", ctypes.c_ubyte), + ] + + class s_xl_can_ev_chip_state(ctypes.Structure): _fields_ = [ ("busStatus", ctypes.c_ubyte), @@ -55,7 +63,7 @@ class s_xl_can_ev_sync_pulse(ctypes.Structure): # BASIC bus message structure class s_xl_tag_data(ctypes.Union): - _fields_ = [("msg", s_xl_can_msg)] + _fields_ = [("msg", s_xl_can_msg), ("chipState", s_xl_chip_state)] # CAN FD messages diff --git a/test/test_vector.py b/test/test_vector.py index 639b28de9..d99509df8 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -55,12 +55,6 @@ def setUp(self) -> None: can.interfaces.vector.canlib.xldriver.xlClosePort = Mock(return_value=0) can.interfaces.vector.canlib.xldriver.xlCloseDriver = Mock() - # receiver functions - can.interfaces.vector.canlib.xldriver.xlReceive = Mock(side_effect=xlReceive) - can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock( - side_effect=xlCanReceive - ) - # sender functions can.interfaces.vector.canlib.xldriver.xlCanTransmit = Mock(return_value=0) can.interfaces.vector.canlib.xldriver.xlCanTransmitEx = Mock(return_value=0) @@ -173,16 +167,42 @@ def test_bus_creation_fd_bitrate_timings(self) -> None: self.assertEqual(canFdConf.tseg2Dbr, 15) def test_receive(self) -> None: + can.interfaces.vector.canlib.xldriver.xlReceive = Mock(side_effect=xlReceive) self.bus = can.Bus(channel=0, bustype="vector", _testing=True) self.bus.recv(timeout=0.05) can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() def test_receive_fd(self) -> None: + can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock( + side_effect=xlCanReceive + ) + self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() + + def test_receive_non_msg_event(self) -> None: + can.interfaces.vector.canlib.xldriver.xlReceive = Mock( + side_effect=xlReceive_chipstate + ) + self.bus = can.Bus(channel=0, bustype="vector", _testing=True) + self.bus.handle_can_event = Mock() + self.bus.recv(timeout=0.05) + can.interfaces.vector.canlib.xldriver.xlReceive.assert_called() + can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_not_called() + self.bus.handle_can_event.assert_called() + + def test_receive_fd_non_msg_event(self) -> None: + can.interfaces.vector.canlib.xldriver.xlCanReceive = Mock( + side_effect=xlCanReceive_chipstate + ) self.bus = can.Bus(channel=0, bustype="vector", fd=True, _testing=True) + self.bus.handle_canfd_event = Mock() self.bus.recv(timeout=0.05) can.interfaces.vector.canlib.xldriver.xlReceive.assert_not_called() can.interfaces.vector.canlib.xldriver.xlCanReceive.assert_called() + self.bus.handle_canfd_event.assert_called() def test_send(self) -> None: self.bus = can.Bus(channel=0, bustype="vector", _testing=True) @@ -306,5 +326,31 @@ def xlCanReceive( return 0 +def xlReceive_chipstate( + port_handle: xlclass.XLportHandle, + event_count_p: ctypes.POINTER(ctypes.c_uint), + event: ctypes.POINTER(xlclass.XLevent), +) -> int: + event.tag = xldefine.XL_EventTags.XL_CHIP_STATE.value + event.tagData.chipState.busStatus = 8 + event.tagData.chipState.rxErrorCounter = 0 + event.tagData.chipState.txErrorCounter = 0 + event.timeStamp = 0 + event.chanIndex = 2 + return 0 + + +def xlCanReceive_chipstate( + port_handle: xlclass.XLportHandle, event: ctypes.POINTER(xlclass.XLcanRxEvent) +) -> int: + event.tag = xldefine.XL_CANFD_RX_EventTags.XL_CAN_EV_TAG_CHIP_STATE.value + event.tagData.canChipState.busStatus = 8 + event.tagData.canChipState.rxErrorCounter = 0 + event.tagData.canChipState.txErrorCounter = 0 + event.timeStamp = 0 + event.chanIndex = 2 + return 0 + + if __name__ == "__main__": unittest.main() From 49373824944addf3425afecb47c156c68971940c Mon Sep 17 00:00:00 2001 From: karl ding Date: Wed, 13 Nov 2019 21:34:31 -0800 Subject: [PATCH 217/252] Bump Python 3.8 from dev version in Travis CI (#723) Python 3.8 was released on October 14, 2019 and support was added to pyenv as well, which was the dependency needed by Travis. This promotes the version of Python 3.8 we were using from the development build to the official release. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2bea7900f..7b299feb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ python: # CPython; only 3.6 is supported - "3.6" - "3.7" - - 3.8-dev + - "3.8" - nightly # PyPy: - pypy3 @@ -39,7 +39,6 @@ jobs: allow_failures: # we allow all dev & nightly builds to fail, since these python versions might # still be very unstable - - python: 3.8-dev - python: nightly include: From f342c1c65e882b1254e16a4be6b559c9aeb3bcde Mon Sep 17 00:00:00 2001 From: Daniel Hrisca Date: Mon, 2 Dec 2019 22:44:43 +0200 Subject: [PATCH 218/252] fixes #732: add support for VN8900 xlGetChannelTime function (#733) --- can/interfaces/vector/canlib.py | 10 ++++++++-- can/interfaces/vector/xldriver.py | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index ed366b4c8..cb1858062 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -282,8 +282,14 @@ def __init__( # Calculate time offset for absolute timestamps offset = xlclass.XLuint64() - xldriver.xlGetSyncTime(self.port_handle, offset) - self._time_offset = time.time() - offset.value * 1e-9 + try: + try: + xldriver.xlGetSyncTime(self.port_handle, offset) + except VectorError: + xldriver.xlGetChannelTime(self.port_handle, self.mask, offset) + self._time_offset = time.time() - offset.value * 1e-9 + except VectorError: + self._time_offset = 0.0 self._is_filtered = False super().__init__(channel=channel, can_filters=can_filters, **kwargs) diff --git a/can/interfaces/vector/xldriver.py b/can/interfaces/vector/xldriver.py index 9bb1a1083..337135755 100644 --- a/can/interfaces/vector/xldriver.py +++ b/can/interfaces/vector/xldriver.py @@ -89,6 +89,15 @@ def check_status(result, function, arguments): xlGetSyncTime.restype = xlclass.XLstatus xlGetSyncTime.errcheck = check_status +xlGetChannelTime = _xlapi_dll.xlGetChannelTime +xlGetChannelTime.argtypes = [ + xlclass.XLportHandle, + xlclass.XLaccess, + ctypes.POINTER(xlclass.XLuint64), +] +xlGetChannelTime.restype = xlclass.XLstatus +xlGetChannelTime.errcheck = check_status + xlClosePort = _xlapi_dll.xlClosePort xlClosePort.argtypes = [xlclass.XLportHandle] xlClosePort.restype = xlclass.XLstatus From b0d74607de0e411ea3b19347cee20cf8ccba0dcd Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Fri, 16 Aug 2019 20:53:00 +0200 Subject: [PATCH 219/252] Refactoring of BLF module Implement append mode Write a temporary header in case logging is not stopped gracefully Move ZLIB compression level to constructor, default to ZLIB default Improve unit testing Various refactoring to improve readability and slight performance improvements --- can/io/blf.py | 459 ++++++++++++++-------------- test/data/example_data.py | 10 + test/data/logfile.blf | Bin 682 -> 0 bytes test/data/test_CanErrorFrameExt.blf | Bin 0 -> 452 bytes test/data/test_CanFdMessage.blf | Bin 0 -> 564 bytes test/data/test_CanFdMessage64.blf | Bin 0 -> 612 bytes test/data/test_CanMessage.blf | Bin 0 -> 420 bytes test/data/test_CanMessage2.blf | Bin 0 -> 436 bytes test/listener_test.py | 4 +- test/logformats_test.py | 109 +++++-- 10 files changed, 335 insertions(+), 247 deletions(-) delete mode 100644 test/data/logfile.blf create mode 100644 test/data/test_CanErrorFrameExt.blf create mode 100644 test/data/test_CanFdMessage.blf create mode 100644 test/data/test_CanFdMessage64.blf create mode 100644 test/data/test_CanMessage.blf create mode 100644 test/data/test_CanMessage2.blf diff --git a/can/io/blf.py b/can/io/blf.py index 6b9ccebce..8df41ed5a 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -17,6 +17,7 @@ import datetime import time import logging +from typing import List from can.message import Message from can.listener import Listener @@ -30,9 +31,6 @@ class BLFParseError(Exception): LOG = logging.getLogger(__name__) -# 0 = unknown, 2 = CANoe -APPLICATION_ID = 5 - # signature ("LOGG"), header size, # application ID, application major, application minor, application build, # bin log major, bin log minor, bin log build, bin log patch, @@ -49,8 +47,8 @@ class BLFParseError(Exception): # flags, client index, object version, timestamp OBJ_HEADER_V1_STRUCT = struct.Struct(" len(data): - # Object continues in next log container - break - pos += OBJ_HEADER_BASE_STRUCT.size - - # Read rest of header - header_version = header[2] - if header_version == 1: - flags, _, _, timestamp = OBJ_HEADER_V1_STRUCT.unpack_from( - data, pos - ) - pos += OBJ_HEADER_V1_STRUCT.size - elif header_version == 2: - flags, _, _, timestamp, _ = OBJ_HEADER_V2_STRUCT.unpack_from( - data, pos - ) - pos += OBJ_HEADER_V2_STRUCT.size - else: - # Unknown header version - LOG.warning( - "Unknown object header version (%d)", header_version - ) - pos = next_pos - continue - - if flags == TIME_TEN_MICS: - factor = 10 * 1e-6 - else: - factor = 1e-9 - timestamp = timestamp * factor + self.start_timestamp - - # Both CAN message types have the same starting content - if obj_type in (CAN_MESSAGE, CAN_MESSAGE2): - ( - channel, - flags, - dlc, - can_id, - can_data, - ) = CAN_MSG_STRUCT.unpack_from(data, pos) - msg = Message( - timestamp=timestamp, - arbitration_id=can_id & 0x1FFFFFFF, - is_extended_id=bool(can_id & CAN_MSG_EXT), - is_remote_frame=bool(flags & REMOTE_FLAG), - dlc=dlc, - data=can_data[:dlc], - channel=channel - 1, - ) - yield msg - elif obj_type == CAN_FD_MESSAGE: - ( - channel, - flags, - dlc, - can_id, - _, - _, - fd_flags, - _, - can_data, - ) = CAN_FD_MSG_STRUCT.unpack_from(data, pos) - length = dlc2len(dlc) - msg = Message( - timestamp=timestamp, - arbitration_id=can_id & 0x1FFFFFFF, - is_extended_id=bool(can_id & CAN_MSG_EXT), - is_remote_frame=bool(flags & REMOTE_FLAG), - is_fd=bool(fd_flags & EDL), - bitrate_switch=bool(fd_flags & BRS), - error_state_indicator=bool(fd_flags & ESI), - dlc=length, - data=can_data[:length], - channel=channel - 1, - ) - yield msg - elif obj_type == CAN_FD_MESSAGE_64: - ( - channel, - dlc, - _, - _, - can_id, - _, - fd_flags, - ) = CAN_FD_MSG_64_STRUCT.unpack_from(data, pos)[:7] - length = dlc2len(dlc) - can_data = struct.unpack_from( - "<{}s".format(length), data, pos + CAN_FD_MSG_64_STRUCT.size - )[0] - msg = Message( - timestamp=timestamp, - arbitration_id=can_id & 0x1FFFFFFF, - is_extended_id=bool(can_id & CAN_MSG_EXT), - is_remote_frame=bool(fd_flags & REMOTE_FLAG_64), - is_fd=bool(fd_flags & EDL_64), - bitrate_switch=bool(fd_flags & BRS_64), - error_state_indicator=bool(fd_flags & ESI_64), - dlc=length, - data=can_data[:length], - channel=channel - 1, - ) - yield msg - elif obj_type == CAN_ERROR_EXT: - ( - channel, - _, - _, - _, - _, - dlc, - _, - can_id, - _, - can_data, - ) = CAN_ERROR_EXT_STRUCT.unpack_from(data, pos) - msg = Message( - timestamp=timestamp, - is_error_frame=True, - is_extended_id=bool(can_id & CAN_MSG_EXT), - arbitration_id=can_id & 0x1FFFFFFF, - dlc=dlc, - data=can_data[:dlc], - channel=channel - 1, - ) - yield msg - # else: - # LOG.warning("Unknown object type (%d)", obj_type) - - pos = next_pos - - # save the remaining data that could not be processed - tail = data[pos:] + def _parse_container(self, data): + if self._tail: + data = b"".join((self._tail, data)) + self._pos = 0 + try: + yield from self._parse_data(data) + except struct.error: + # Container data exhausted + # Save the remaining data that could not be processed + self._tail = data[self._pos :] + + def _parse_data(self, data): + """Optimized inner loop by making local copies of global variables + and class members and hardcoding some values.""" + unpack_obj_header_base = OBJ_HEADER_BASE_STRUCT.unpack_from + obj_header_base_size = OBJ_HEADER_BASE_STRUCT.size + unpack_obj_header_v1 = OBJ_HEADER_V1_STRUCT.unpack_from + obj_header_v1_size = OBJ_HEADER_V1_STRUCT.size + unpack_obj_header_v2 = OBJ_HEADER_V2_STRUCT.unpack_from + obj_header_v2_size = OBJ_HEADER_V2_STRUCT.size + unpack_can_msg = CAN_MSG_STRUCT.unpack_from + unpack_can_fd_msg = CAN_FD_MSG_STRUCT.unpack_from + unpack_can_fd_64_msg = CAN_FD_MSG_64_STRUCT.unpack_from + can_fd_64_msg_size = CAN_FD_MSG_64_STRUCT.size + unpack_can_error_ext = CAN_ERROR_EXT_STRUCT.unpack_from + + start_timestamp = self.start_timestamp + pos = 0 + + # Loop until a struct unpack raises an exception + while True: + self._pos = pos + header = unpack_obj_header_base(data, pos) + signature, _, header_version, obj_size, obj_type = header + if signature != b"LOBJ": + raise BLFParseError() - self.stop() + # Calculate position of next object + next_pos = pos + obj_size + if obj_type != CAN_FD_MESSAGE_64: + # Add padding bytes + next_pos += obj_size % 4 + pos += obj_header_base_size + + # Read rest of header + if header_version == 1: + flags, _, _, timestamp = unpack_obj_header_v1(data, pos) + pos += obj_header_v1_size + elif header_version == 2: + flags, _, _, timestamp = unpack_obj_header_v2(data, pos) + pos += obj_header_v2_size + else: + LOG.warning("Unknown object header version (%d)", header_version) + pos = next_pos + continue + + # Calculate absolute timestamp in seconds + factor = 1e-5 if flags == 1 else 1e-9 + timestamp = timestamp * factor + start_timestamp + + if obj_type == CAN_MESSAGE or obj_type == CAN_MESSAGE2: + channel, flags, dlc, can_id, can_data = unpack_can_msg(data, pos) + yield Message( + timestamp=timestamp, + arbitration_id=can_id & 0x1FFFFFFF, + is_extended_id=bool(can_id & CAN_MSG_EXT), + is_remote_frame=bool(flags & REMOTE_FLAG), + dlc=dlc, + data=can_data[:dlc], + channel=channel - 1, + ) + elif obj_type == CAN_ERROR_EXT: + members = unpack_can_error_ext(data, pos) + channel = members[0] + dlc = members[5] + can_id = members[7] + can_data = members[9] + yield Message( + timestamp=timestamp, + is_error_frame=True, + is_extended_id=bool(can_id & CAN_MSG_EXT), + arbitration_id=can_id & 0x1FFFFFFF, + dlc=dlc, + data=can_data[:dlc], + channel=channel - 1, + ) + elif obj_type == CAN_FD_MESSAGE: + members = unpack_can_fd_msg(data, pos) + channel, flags, dlc, can_id, _, _, fd_flags, valid_bytes, can_data = ( + members + ) + yield Message( + timestamp=timestamp, + arbitration_id=can_id & 0x1FFFFFFF, + is_extended_id=bool(can_id & CAN_MSG_EXT), + is_remote_frame=bool(flags & REMOTE_FLAG), + is_fd=bool(fd_flags & 0x1), + bitrate_switch=bool(fd_flags & 0x2), + error_state_indicator=bool(fd_flags & 0x4), + dlc=dlc2len(dlc), + data=can_data[:valid_bytes], + channel=channel - 1, + ) + elif obj_type == CAN_FD_MESSAGE_64: + members = unpack_can_fd_64_msg(data, pos)[:7] + channel, dlc, valid_bytes, _, can_id, _, fd_flags = members + pos += can_fd_64_msg_size + yield Message( + timestamp=timestamp, + arbitration_id=can_id & 0x1FFFFFFF, + is_extended_id=bool(can_id & CAN_MSG_EXT), + is_remote_frame=bool(fd_flags & 0x0010), + is_fd=bool(fd_flags & 0x1000), + bitrate_switch=bool(fd_flags & 0x2000), + error_state_indicator=bool(fd_flags & 0x4000), + dlc=dlc2len(dlc), + data=data[pos : pos + valid_bytes], + channel=channel - 1, + ) + + pos = next_pos class BLFWriter(BaseIOHandler, Listener): @@ -352,27 +315,72 @@ class BLFWriter(BaseIOHandler, Listener): """ #: Max log container size of uncompressed data - MAX_CACHE_SIZE = 128 * 1024 + max_container_size = 128 * 1024 - #: ZLIB compression level - COMPRESSION_LEVEL = 9 + #: Application identifier for the log writer + application_id = 5 - def __init__(self, file, channel=1): + def __init__( + self, file, append: bool = False, channel: int = 1, compression_level: int = -1 + ): """ :param file: a path-like object or as file-like object to write to - If this is a file-like object, is has to opened in binary - write mode, not text write mode. + If this is a file-like object, is has to opened in mode "wb+". + :param channel: + Default channel to log as if not specified by the interface. + :param append: + Append messages to an existing log file. + :param compression_level: + An integer from 0 to 9 or -1 controlling the level of compression. + 1 (Z_BEST_SPEED) is fastest and produces the least compression. + 9 (Z_BEST_COMPRESSION) is slowest and produces the most. + 0 means that data will be stored without processing. + The default value is -1 (Z_DEFAULT_COMPRESSION). + Z_DEFAULT_COMPRESSION represents a default compromise between + speed and compression (currently equivalent to level 6). """ - super().__init__(file, mode="wb") + mode = "rb+" if append else "wb" + try: + super().__init__(file, mode=mode) + except FileNotFoundError: + # Trying to append to a non-existing file, create a new one + append = False + mode = "wb" + super().__init__(file, mode=mode) + assert self.file is not None self.channel = channel - # Header will be written after log is done - self.file.write(b"\x00" * FILE_HEADER_SIZE) - self.cache = [] - self.cache_size = 0 - self.count_of_objects = 0 - self.uncompressed_size = FILE_HEADER_SIZE - self.start_timestamp = None - self.stop_timestamp = None + self.compression_level = compression_level + self._buffer: List[bytes] = [] + self._buffer_size = 0 + if append: + # Parse file header + data = self.file.read(FILE_HEADER_STRUCT.size) + header = FILE_HEADER_STRUCT.unpack(data) + if header[0] != b"LOGG": + raise BLFParseError("Unexpected file format") + self.uncompressed_size = header[11] + self.object_count = header[12] + self.start_timestamp = systemtime_to_timestamp(header[14:22]) + self.stop_timestamp = systemtime_to_timestamp(header[22:30]) + # Jump to the end of the file + self.file.seek(0, 2) + else: + self.object_count = 0 + self.uncompressed_size = FILE_HEADER_SIZE + self.start_timestamp = None + self.stop_timestamp = None + # Write a default header which will be updated when stopped + self._write_header(FILE_HEADER_SIZE) + + def _write_header(self, filesize): + header = [b"LOGG", FILE_HEADER_SIZE, self.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.object_count, 0]) + header.extend(timestamp_to_systemtime(self.start_timestamp)) + header.extend(timestamp_to_systemtime(self.stop_timestamp)) + self.file.write(FILE_HEADER_STRUCT.pack(*header)) + # Pad to header size + self.file.write(b"\x00" * (FILE_HEADER_SIZE - FILE_HEADER_STRUCT.size)) def on_message_received(self, msg): channel = channel2int(msg.channel) @@ -386,7 +394,7 @@ def on_message_received(self, msg): if msg.is_extended_id: arb_id |= CAN_MSG_EXT flags = REMOTE_FLAG if msg.is_remote_frame else 0 - data = bytes(msg.data) + can_data = bytes(msg.data) if msg.is_error_frame: data = CAN_ERROR_EXT_STRUCT.pack( @@ -399,7 +407,7 @@ def on_message_received(self, msg): 0, # frame length arb_id, 0, # ext flags - data, + can_data, ) self._add_object(CAN_ERROR_EXT, data, msg.timestamp) elif msg.is_fd: @@ -409,11 +417,19 @@ def on_message_received(self, msg): if msg.error_state_indicator: fd_flags |= ESI data = CAN_FD_MSG_STRUCT.pack( - channel, flags, len2dlc(msg.dlc), arb_id, 0, 0, fd_flags, msg.dlc, data + channel, + flags, + len2dlc(msg.dlc), + arb_id, + 0, + 0, + fd_flags, + len(can_data), + can_data, ) self._add_object(CAN_FD_MESSAGE, data, msg.timestamp) else: - data = CAN_MSG_STRUCT.pack(channel, flags, msg.dlc, arb_id, data) + data = CAN_MSG_STRUCT.pack(channel, flags, msg.dlc, arb_id, can_data) self._add_object(CAN_MESSAGE, data, msg.timestamp) def log_event(self, text, timestamp=None): @@ -451,60 +467,59 @@ def _add_object(self, obj_type, data, timestamp=None): ) obj_header = OBJ_HEADER_V1_STRUCT.pack(TIME_ONE_NANS, 0, 0, max(timestamp, 0)) - self.cache.append(base_header) - self.cache.append(obj_header) - self.cache.append(data) + self._buffer.append(base_header) + self._buffer.append(obj_header) + self._buffer.append(data) padding_size = len(data) % 4 if padding_size: - self.cache.append(b"\x00" * padding_size) + self._buffer.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._buffer_size += obj_size + padding_size + self.object_count += 1 + if self._buffer_size >= self.max_container_size: self._flush() def _flush(self): - """Compresses and writes data in the cache to file.""" + """Compresses and writes data in the buffer to file.""" if self.file.closed: return - cache = b"".join(self.cache) - if not cache: + buffer = b"".join(self._buffer) + if not buffer: # 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_V1_STRUCT.size + LOG_CONTAINER_STRUCT.size + len(compressed_data) - ) + uncompressed_data = memoryview(buffer)[: self.max_container_size] + # Save data that comes after max size to next container + tail = buffer[self.max_container_size :] + self._buffer = [tail] + self._buffer_size = len(tail) + if not self.compression_level: + data = uncompressed_data + method = NO_COMPRESSION + else: + data = zlib.compress(uncompressed_data, self.compression_level) + method = ZLIB_DEFLATE + obj_size = OBJ_HEADER_BASE_STRUCT.size + LOG_CONTAINER_STRUCT.size + len(data) base_header = OBJ_HEADER_BASE_STRUCT.pack( b"LOBJ", OBJ_HEADER_BASE_STRUCT.size, 1, obj_size, LOG_CONTAINER ) - container_header = LOG_CONTAINER_STRUCT.pack( - ZLIB_DEFLATE, len(uncompressed_data) - ) + container_header = LOG_CONTAINER_STRUCT.pack(method, len(uncompressed_data)) self.file.write(base_header) self.file.write(container_header) - self.file.write(compressed_data) + self.file.write(data) # Write padding bytes self.file.write(b"\x00" * (obj_size % 4)) - self.uncompressed_size += OBJ_HEADER_V1_STRUCT.size + LOG_CONTAINER_STRUCT.size + self.uncompressed_size += OBJ_HEADER_BASE_STRUCT.size + self.uncompressed_size += LOG_CONTAINER_STRUCT.size self.uncompressed_size += len(uncompressed_data) def stop(self): """Stops logging and closes the file.""" self._flush() - filesize = self.file.tell() + if self.file.seekable(): + filesize = self.file.tell() + # Write header in the beginning of the file + self.file.seek(0) + self._write_header(filesize) + else: + LOG.error("Could not write BLF header since file is not seekable") super().stop() - - # Write header in the beginning of the file - header = [b"LOGG", FILE_HEADER_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.file.name, "r+b") as f: - f.write(FILE_HEADER_STRUCT.pack(*header)) diff --git a/test/data/example_data.py b/test/data/example_data.py index b91fd3bfd..773212c32 100644 --- a/test/data/example_data.py +++ b/test/data/example_data.py @@ -108,6 +108,16 @@ def sort_messages(messages): ) +TEST_MESSAGES_CAN_FD = sort_messages( + [ + Message(is_fd=True, data=range(64)), + Message(is_fd=True, data=range(8)), + Message(is_fd=True, bitrate_switch=True), + Message(is_fd=True, error_state_indicator=True), + ] +) + + TEST_MESSAGES_REMOTE_FRAMES = sort_messages( [ Message( diff --git a/test/data/logfile.blf b/test/data/logfile.blf deleted file mode 100644 index 98cafe214083ab24fab9f5d927a38728f7f29679..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 682 zcmebAcXyw_z`(!?#7t})jH{S{3?PUA2>^j05UYS$^dWrwoxB7X7#YNY?%)Di33U#` z3Kod{6>~m+EdLagkdR`KkiamBhsTE}pdf3SDRV^6L}gYNW5dlGHf=2a;c(TO;b_E! ztJx|0n^){V!erDgsl^_2nCnQ6!<^J29r0!H%qPTMuFZe?Z>r>)`JIxnb2&Ec5`Lr| zp^%sA6!x4^MA$h@{IT|VuCJ4YE+~m7x5!)U+)-Ka^9L(S<2n(8M31!`$q5c-?lL!J zwu%P|6ia-ZbvWmcLd&~6&Xi(16W`_;hg*dB78Fc(3ES@dO~^%7JEBKYs!idmRKQoS z6R*~P*_)K^z%Ze{Rl?`NE60yZn%3Ao_E2K)s&X?}%ky;kZl{ct-ysvfN*_J%di6Zd zG-p10uI5e?NceGj(~DK7p4v;B$RuR)KC_=Nf8ya(_6hl%=Ikqeh0oO1 z@2J$0dKW9kCetu+qM$R6r@ZEa`}G_f(ivuQraTB>FW$~NA@_L0lmF(XKWjWpgpZgn z@JOn#e(%YrGpD%0Y2ArmU1b~`3e}wl+Z>aWcB`a45*88@6cwI6ValXw6Q@oN4+seg z3k(gue&NccYZtFxHa9RaGBY$a-o9bWCbx6D=bb1!8}v_~VOulr8?%T9jepK^_%PVt z+vw>Q!`1eCBbUbXjy3zUZ-xZi6Am#d&XV!Pg1q>Do4W1pEBFT99fQlcV R(Vqhc7#JG9)g1yxJ^(q8;uZh^ diff --git a/test/data/test_CanErrorFrameExt.blf b/test/data/test_CanErrorFrameExt.blf new file mode 100644 index 0000000000000000000000000000000000000000..f7ea6eb35e738bdbaaaf566e8571178f513b7d77 GIT binary patch literal 452 zcmebAcXyw_z`$@t$__{}FdSh7Q*fFI!X-!=K=lz+NI-#)zmt~$10%x%pgJxjwGB`< zNL~Rb?*Qa`0x`&af`SYTN>CsusH9{J1TLXz5m5g1ep&#VF zDL{LX-B$wSg8d@8l)Gz{nr~G>;2O-v=lg zB(DIJuK@B>fEeUHK|uxvB`6RSR5Eq}f>0nxD|eW=3J4e&nV4Bv+1NQaxwv_F`S=9{ zg@i>!#l$5frKDwK<>VC*v+3@?}w`fO12F~Y+UMcWx31|Fa~ I5D)-j08ooc{r~^~ literal 0 HcmV?d00001 diff --git a/test/data/test_CanFdMessage64.blf b/test/data/test_CanFdMessage64.blf new file mode 100644 index 0000000000000000000000000000000000000000..f26eccfde811a0f386b238197c4736e1f1e8e6e0 GIT binary patch literal 612 zcmebAcXyw_z`$@t$__{}Fr+YnDL4(~LIm-U7Z~wKk|5>d@8l)Gz{ubLG>;2Op90i; zkh}s=9^~d!AO^WlP>_K^2?_+299%+yAPor0fuI8jW&*(~AlMBAXU^Px_a6ut7@3$^ zSlQS)IJvlac=`AR1cih}M8(7`T5gzU++RpGW@Br0;fB+B!0DgLGGXMYp literal 0 HcmV?d00001 diff --git a/test/data/test_CanMessage.blf b/test/data/test_CanMessage.blf new file mode 100644 index 0000000000000000000000000000000000000000..f0ad2036526703ecb5051648b98154c2b888625f GIT binary patch literal 420 zcmebAcXyw_z`$@t$__{}Ff3sNQ*fFI!X-!wK=lz+NI-#)zmt~$10zENP#qVN+5{*Y zB(DIJHvsZMR)X9oD9FH|1O}@KGvufT**^tnF0%b4Kt2e-%(DQp ri-8yxJ}~>wpa2dA4h9K^7t9EKHYoZS;qFG!c7}(62dEAN1b`R-W&bz1 literal 0 HcmV?d00001 diff --git a/test/data/test_CanMessage2.blf b/test/data/test_CanMessage2.blf new file mode 100644 index 0000000000000000000000000000000000000000..6beb4fa43cdac73a28ab7c6ec879a6e5a38e76a8 GIT binary patch literal 436 zcmebAcXyw_z`$@t$__{}Fl=E2Q*fFI!X-#5K=lz+NI-#)zmt~$10w^-O}>oVvzd;1sNEWpg>Si$=C%5Let7SX0F Date: Mon, 16 Dec 2019 22:18:52 +0200 Subject: [PATCH 220/252] Add support for "Robotell" USB-CAN interface (#731) --- can/interfaces/__init__.py | 1 + can/interfaces/robotell.py | 400 +++++++++++++++ doc/interfaces.rst | 1 + doc/interfaces/robotell.rst | 37 ++ test/test_robotell.py | 946 ++++++++++++++++++++++++++++++++++++ 5 files changed, 1385 insertions(+) create mode 100644 can/interfaces/robotell.py create mode 100644 doc/interfaces/robotell.rst create mode 100644 test/test_robotell.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index a163ad101..2f00d0309 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -20,6 +20,7 @@ "neovi": ("can.interfaces.ics_neovi", "NeoViBus"), "vector": ("can.interfaces.vector", "VectorBus"), "slcan": ("can.interfaces.slcan", "slcanBus"), + "robotell": ("can.interfaces.robotell", "robotellBus"), "canalystii": ("can.interfaces.canalystii", "CANalystIIBus"), "systec": ("can.interfaces.systec", "UcanBus"), "seeedstudio": ("can.interfaces.seeedstudio", "SeeedBus"), diff --git a/can/interfaces/robotell.py b/can/interfaces/robotell.py new file mode 100644 index 000000000..cb35aa774 --- /dev/null +++ b/can/interfaces/robotell.py @@ -0,0 +1,400 @@ +""" +Interface for Chinese Robotell compatible interfaces (win32/linux). +""" + +import time +import logging + +from can import BusABC, Message + +logger = logging.getLogger(__name__) + +try: + import serial +except ImportError: + logger.warning( + "You won't be able to use the Robotell can backend without " + "the serial module installed!" + ) + serial = None + + +class robotellBus(BusABC): + """ + robotell interface + """ + + _PACKET_HEAD = 0xAA # Frame starts with 2x FRAME_HEAD bytes + _PACKET_TAIL = 0x55 # Frame ends with 2x FRAME_END bytes + _PACKET_ESC = ( + 0xA5 # Escape char before any HEAD, TAIL or ESC chat (including in checksum) + ) + + _CAN_CONFIG_CHANNEL = 0xFF # Configuration channel of CAN + _CAN_SERIALBPS_ID = 0x01FFFE90 # USB Serial port speed + _CAN_ART_ID = 0x01FFFEA0 # Automatic retransmission + _CAN_ABOM_ID = 0x01FFFEB0 # Automatic bus management + _CAN_RESET_ID = 0x01FFFEC0 # ID for initialization + _CAN_BAUD_ID = 0x01FFFED0 # CAN baud rate + _CAN_FILTER_BASE_ID = 0x01FFFEE0 # ID for first filter (filter0) + _CAN_FILTER_MAX_ID = 0x01FFFEE0 + 13 # ID for the last filter (filter13) + _CAN_INIT_FLASH_ID = 0x01FFFEFF # Restore factory settings + _CAN_READ_SERIAL1 = 0x01FFFFF0 # Read first part of device serial number + _CAN_READ_SERIAL2 = 0x01FFFFF1 # Read first part of device serial number + _MAX_CAN_BAUD = 1000000 # Maximum supported CAN baud rate + _FILTER_ID_MASK = 0x0000000F # Filter ID mask + _CAN_FILTER_EXTENDED = 0x40000000 # Enable mask + _CAN_FILTER_ENABLE = 0x80000000 # Enable filter + + _CAN_STANDARD_FMT = 0 # Standard message ID + _CAN_EXTENDED_FMT = 1 # 29 Bit extended format ID + _CAN_DATA_FRAME = 0 # Send data frame + _CAN_REMOTE_FRAME = 1 # Request remote frame + + def __init__( + self, channel, ttyBaudrate=115200, bitrate=None, rtscts=False, **kwargs + ): + """ + :param str channel: + port of underlying serial or usb device (e.g. /dev/ttyUSB0, COM8, ...) + Must not be empty. + :param int ttyBaudrate: + baudrate of underlying serial or usb device + :param int bitrate: + CAN Bitrate in bit/s. Value is stored in the adapter and will be used as default if no bitrate is specified + :param bool rtscts: + turn hardware handshake (RTS/CTS) on and off + """ + + if not channel: # if None or empty + raise TypeError("Must specify a serial port.") + if "@" in channel: + (channel, ttyBaudrate) = channel.split("@") + self.serialPortOrig = serial.serial_for_url( + channel, baudrate=ttyBaudrate, rtscts=rtscts + ) + + ## Disable flushing queued config ACKs on lookup channel (for unit tests) + self._loopback_test = channel == "loop://" + + self._rxbuffer = bytearray() # raw bytes from the serial port + self._rxmsg = [] # extracted CAN messages waiting to be read + self._configmsg = [] # extracted config channel messages + + self._writeconfig(self._CAN_RESET_ID, 0) # Not sure if this is really necessary + + if bitrate is not None: + self.set_bitrate(bitrate) + + self.channel_info = "Robotell USB-CAN s/n %s on %s" % ( + self.get_serial_number(1), + channel, + ) + logger.info("Using device: {}".format(self.channel_info)) + + super().__init__(channel=channel, **kwargs) + + def set_bitrate(self, bitrate): + """ + :raise ValueError: if *bitrate* is greater than 1000000 + :param int bitrate: + Bitrate in bit/s + """ + if bitrate <= self._MAX_CAN_BAUD: + self._writeconfig(self._CAN_BAUD_ID, bitrate) + else: + raise ValueError( + "Invalid bitrate, must be less than " + str(self._MAX_CAN_BAUD) + ) + + def set_auto_retransmit(self, retrans_flag): + """ + :param bool retrans_flag: + Enable/disable automatic retransmission of unacknowledged CAN frames + """ + self._writeconfig(self._CAN_ART_ID, 1 if retrans_flag else 0) + + def set_auto_bus_management(self, auto_man): + """ + :param bool auto_man: + Enable/disable automatic bus management + """ + ## Not sure what "automatic bus managemenet" does. Does not seem to control + ## automatic ACK of CAN frames (listen only mode) + self._writeconfig(self._CAN_ABOM_ID, 1 if auto_man else 0) + + def set_serial_rate(self, serial_bps): + """ + :param int serial_bps: + Set the baud rate of the serial port (not CAN) interface + """ + self._writeconfig(self._CAN_SERIALBPS_ID, serial_bps) + + def set_hw_filter(self, filterid, enabled, msgid_value, msgid_mask, extended_msg): + """ + :raise ValueError: if *filterid* is not between 1 and 14 + :param int filterid: + ID of filter (1-14) + :param bool enabled: + This filter is enabled + :param int msgid_value: + CAN message ID to filter on. The test unit does not accept an extented message ID unless bit 31 of the ID was set. + :param int msgid_mask: + Mask to apply to CAN messagge ID + :param bool extended_msg: + Filter operates on extended format messages + """ + if filterid < 1 or filterid > 14: + raise ValueError("Invalid filter ID. ID must be between 0 and 13") + else: + configid = self._CAN_FILTER_BASE_ID + (filterid - 1) + msgid_value += self._CAN_FILTER_ENABLE if enabled else 0 + msgid_value += self._CAN_FILTER_EXTENDED if extended_msg else 0 + self._writeconfig(configid, msgid_value, msgid_mask) + + def _getconfigsize(self, configid): + if configid == self._CAN_ART_ID or configid == self._CAN_ABOM_ID: + return 1 + if configid == self._CAN_BAUD_ID or configid == self._CAN_INIT_FLASH_ID: + return 4 + if configid == self._CAN_SERIALBPS_ID: + return 4 + if configid == self._CAN_READ_SERIAL1 or configid <= self._CAN_READ_SERIAL2: + return 8 + if configid >= self._CAN_FILTER_BASE_ID and configid <= self._CAN_FILTER_MAX_ID: + return 8 + return 0 + + def _readconfig(self, configid, timeout): + self._writemessage( + msgid=configid, + msgdata=bytearray(8), + datalen=self._getconfigsize(configid), + msgchan=self._CAN_CONFIG_CHANNEL, + msgformat=self._CAN_EXTENDED_FMT, + msgtype=self._CAN_REMOTE_FRAME, + ) + # Read message from config channel with result. Flush any previously pending config messages + newmsg = self._readmessage(not self._loopback_test, True, timeout) + if newmsg is None: + logger.warning( + "Timeout waiting for response when reading config value {:04X}.".format( + configid + ) + ) + return None + return newmsg[4:12] + + def _writeconfig(self, configid, value, value2=0): + configsize = self._getconfigsize(configid) + configdata = bytearray(configsize) + if configsize >= 1: + configdata[0] = value & 0xFF + if configsize >= 4: + configdata[1] = (value >> 8) & 0xFF + configdata[2] = (value >> 16) & 0xFF + configdata[3] = (value >> 24) & 0xFF + if configsize >= 8: + configdata[4] = value2 & 0xFF + configdata[5] = (value2 >> 8) & 0xFF + configdata[6] = (value2 >> 16) & 0xFF + configdata[7] = (value2 >> 24) & 0xFF + self._writemessage( + msgid=configid, + msgdata=configdata, + datalen=configsize, + msgchan=self._CAN_CONFIG_CHANNEL, + msgformat=self._CAN_EXTENDED_FMT, + msgtype=self._CAN_DATA_FRAME, + ) + # Read message from config channel to verify. Flush any previously pending config messages + newmsg = self._readmessage(not self._loopback_test, True, 1) + if newmsg is None: + logger.warning( + "Timeout waiting for response when writing config value " + + str(configid) + ) + + def _readmessage(self, flushold, cfgchannel, timeout): + header = bytearray([self._PACKET_HEAD, self._PACKET_HEAD]) + terminator = bytearray([self._PACKET_TAIL, self._PACKET_TAIL]) + + msgqueue = self._configmsg if cfgchannel else self._rxmsg + if flushold: + del msgqueue[:] + + # read what is already in serial port receive buffer - unless we are doing loopback testing + if not self._loopback_test: + while self.serialPortOrig.in_waiting: + self._rxbuffer += self.serialPortOrig.read() + + # loop until we have read an appropriate message + start = time.time() + time_left = timeout + while True: + # make sure first bytes in RX buffer is a new packet header + headpos = self._rxbuffer.find(header) + if headpos > 0: + # data does not start with expected header bytes. Log error and ignore garbage + logger.warning("Ignoring extra " + str(headpos) + " garbage bytes") + del self._rxbuffer[:headpos] + headpos = self._rxbuffer.find(header) # should now be at index 0! + + # check to see if we have a complete packet in the RX buffer + termpos = self._rxbuffer.find(terminator) + if headpos == 0 and termpos > headpos: + # copy packet into message structure and un-escape bytes + newmsg = bytearray() + idx = headpos + len(header) + while idx < termpos: + if self._rxbuffer[idx] == self._PACKET_ESC: + idx += 1 + newmsg.append(self._rxbuffer[idx]) + idx += 1 + del self._rxbuffer[: termpos + len(terminator)] + + # Check one - make sure message structure is the correct length + if len(newmsg) == 17: + # Check two - verify the checksum + cs = 0 + for idx in range(16): + cs = (cs + newmsg[idx]) & 0xFF + if newmsg[16] == cs: + # OK, valid message - place it in the correct queue + if newmsg[13] == 0xFF: ## Check for config channel + self._configmsg.append(newmsg) + else: + self._rxmsg.append(newmsg) + else: + logger.warning("Incorrect message checksum, discarded message") + else: + logger.warning( + "Invalid message structure length " + + str(len(newmsg)) + + ", ignoring message" + ) + + # Check if we have a message in the desired queue - if so copy and return + if len(msgqueue) > 0: + newmsg = msgqueue[0] + del msgqueue[:1] + return newmsg + + # if we still don't have a complete message, do a blocking read + self.serialPortOrig.timeout = time_left + byte = self.serialPortOrig.read() + if byte: + self._rxbuffer += byte + # If there is time left, try next one with reduced timeout + if timeout is not None: + time_left = timeout - (time.time() - start) + if time_left <= 0: + return None + + def _writemessage(self, msgid, msgdata, datalen, msgchan, msgformat, msgtype): + msgbuf = bytearray(17) # Message structure plus checksum byte + + msgbuf[0] = msgid & 0xFF + msgbuf[1] = (msgid >> 8) & 0xFF + msgbuf[2] = (msgid >> 16) & 0xFF + msgbuf[3] = (msgid >> 24) & 0xFF + + if msgtype == self._CAN_DATA_FRAME: + for idx in range(datalen): + msgbuf[idx + 4] = msgdata[idx] + + msgbuf[12] = datalen + msgbuf[13] = msgchan + msgbuf[14] = msgformat + msgbuf[15] = msgtype + + cs = 0 + for idx in range(16): + cs = (cs + msgbuf[idx]) & 0xFF + msgbuf[16] = cs + + packet = bytearray() + packet.append(self._PACKET_HEAD) + packet.append(self._PACKET_HEAD) + for msgbyte in msgbuf: + if ( + msgbyte == self._PACKET_ESC + or msgbyte == self._PACKET_HEAD + or msgbyte == self._PACKET_TAIL + ): + packet.append(self._PACKET_ESC) + packet.append(msgbyte) + packet.append(self._PACKET_TAIL) + packet.append(self._PACKET_TAIL) + + self.serialPortOrig.write(packet) + self.serialPortOrig.flush() + + def flush(self): + del self._rxbuffer[:] + del self._rxmsg[:] + del self._configmsg[:] + while self.serialPortOrig.in_waiting: + self.serialPortOrig.read() + + def _recv_internal(self, timeout): + msgbuf = self._readmessage(False, False, timeout) + if msgbuf is not None: + msg = Message( + arbitration_id=msgbuf[0] + + (msgbuf[1] << 8) + + (msgbuf[2] << 16) + + (msgbuf[3] << 24), + is_extended_id=(msgbuf[14] == self._CAN_EXTENDED_FMT), + timestamp=time.time(), # Better than nothing... + is_remote_frame=(msgbuf[15] == self._CAN_REMOTE_FRAME), + dlc=msgbuf[12], + data=msgbuf[4 : 4 + msgbuf[12]], + ) + return msg, False + return None, False + + def send(self, msg, timeout=None): + if timeout != self.serialPortOrig.write_timeout: + self.serialPortOrig.write_timeout = timeout + self._writemessage( + msg.arbitration_id, + msg.data, + msg.dlc, + 0, + self._CAN_EXTENDED_FMT if msg.is_extended_id else self._CAN_STANDARD_FMT, + self._CAN_REMOTE_FRAME if msg.is_remote_frame else self._CAN_DATA_FRAME, + ) + + def shutdown(self): + self.serialPortOrig.close() + + def fileno(self): + if hasattr(self.serialPortOrig, "fileno"): + return self.serialPortOrig.fileno() + # Return an invalid file descriptor on Windows + return -1 + + def get_serial_number(self, timeout): + """Get serial number of the slcan interface. + :type timeout: int or None + :param timeout: + seconds to wait for serial number or None to wait indefinitely + :rtype str or None + :return: + None on timeout or a str object. + """ + + sn1 = self._readconfig(self._CAN_READ_SERIAL1, timeout) + if sn1 is None: + return None + sn2 = self._readconfig(self._CAN_READ_SERIAL2, timeout) + if sn2 is None: + return None + + serial = "" + for idx in range(0, 8, 2): + serial += "{:02X}{:02X}-".format(sn1[idx], sn1[idx + 1]) + for idx in range(0, 4, 2): + serial += "{:02X}{:02X}-".format(sn2[idx], sn2[idx + 1]) + return serial[:-1] diff --git a/doc/interfaces.rst b/doc/interfaces.rst index a19dc7e84..bd7a0d1df 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -15,6 +15,7 @@ The available interfaces are: interfaces/kvaser interfaces/serial interfaces/slcan + interfaces/robotell interfaces/ixxat interfaces/pcan interfaces/usb2can diff --git a/doc/interfaces/robotell.rst b/doc/interfaces/robotell.rst new file mode 100644 index 000000000..064e21725 --- /dev/null +++ b/doc/interfaces/robotell.rst @@ -0,0 +1,37 @@ +.. _robotell: + +Chinese CAN-USB interface (mfg. Robotell etc.) +============================================== + +An USB to CAN adapter sold on Aliexpress, etc. with the manufacturer name Robotell printed on the case. +There is also a USB stick version with a clear case. If the description or screenshots refer to ``EmbededDebug`` or ``EmbededConfig`` +the device should be compatible with this driver. +These USB devices are based on a STM32 controller with a CH340 serial interface and use a binary protocol - NOT compatible with SLCAN + +See `https://www.amobbs.com/thread-4651667-1-1.html `_ for some background on these devices. + +This driver directly uses either the local or remote (not tested) serial port. +Remote serial ports will be specified via special URL. Both raw TCP sockets as also RFC2217 ports are supported. + +Usage: use ``port or URL[@baurate]`` to open the device. +For example use ``/dev/ttyUSB0@115200`` or ``COM4@9600`` for local serial ports and +``socket://192.168.254.254:5000`` or ``rfc2217://192.168.254.254:5000`` for remote ports. + + +Supported devices +----------------- + +.. todo:: Document this. + + +Bus +--- + +.. autoclass:: can.interfaces.robotell.robotellBus + :members: + + +Internals +--------- + +.. todo:: Document the internals of robotell interface. diff --git a/test/test_robotell.py b/test/test_robotell.py new file mode 100644 index 000000000..58e2d9a7f --- /dev/null +++ b/test/test_robotell.py @@ -0,0 +1,946 @@ +#!/usr/bin/env python +# coding: utf-8 + +import unittest +import can + + +class robotellTestCase(unittest.TestCase): + def setUp(self): + # will log timeout messages since we are not feeding ack messages to the serial port at this stage + self.bus = can.Bus("loop://", bustype="robotell") + self.serial = self.bus.serialPortOrig + self.serial.read(self.serial.in_waiting) + + def tearDown(self): + self.bus.shutdown() + + def test_recv_extended(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0x56, + 0x34, + 0x12, + 0x00, + 0xA5, + 0xAA, + 0xA5, + 0xA5, + 0xA5, + 0x55, + 0xA5, + 0x55, + 0xA5, + 0xA5, + 0xA5, + 0xAA, + 0x00, + 0x00, + 0x06, + 0x00, + 0x01, + 0x00, + 0xEB, + 0x55, + 0x55, + ] + ) + ) + msg = self.bus.recv(1) + self.assertIsNotNone(msg) + self.assertEqual(msg.arbitration_id, 0x123456) + self.assertEqual(msg.is_extended_id, True) + self.assertEqual(msg.is_remote_frame, False) + self.assertEqual(msg.dlc, 6) + self.assertSequenceEqual(msg.data, [0xAA, 0xA5, 0x55, 0x55, 0xA5, 0xAA]) + data = self.serial.read(self.serial.in_waiting) + + def test_send_extended(self): + msg = can.Message( + arbitration_id=0x123456, + is_extended_id=True, + data=[0xAA, 0xA5, 0x55, 0x55, 0xA5, 0xAA], + ) + self.bus.send(msg) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0x56, + 0x34, + 0x12, + 0x00, + 0xA5, + 0xAA, + 0xA5, + 0xA5, + 0xA5, + 0x55, + 0xA5, + 0x55, + 0xA5, + 0xA5, + 0xA5, + 0xAA, + 0x00, + 0x00, + 0x06, + 0x00, + 0x01, + 0x00, + 0xEB, + 0x55, + 0x55, + ] + ), + ) + + def test_recv_standard(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0x7B, + 0x00, + 0x00, + 0x00, + 0x48, + 0x65, + 0x6C, + 0x6C, + 0x6F, + 0x31, + 0x32, + 0x33, + 0x08, + 0x00, + 0x00, + 0x00, + 0x0D, + 0x55, + 0x55, + ] + ) + ) + msg = self.bus.recv(1) + self.assertIsNotNone(msg) + self.assertEqual(msg.arbitration_id, 123) + self.assertEqual(msg.is_extended_id, False) + self.assertEqual(msg.is_remote_frame, False) + self.assertEqual(msg.dlc, 8) + self.assertSequenceEqual( + msg.data, [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x31, 0x32, 0x33] + ) + data = self.serial.read(self.serial.in_waiting) + + def test_send_standard(self): + msg = can.Message( + arbitration_id=123, + is_extended_id=False, + data=[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x31, 0x32, 0x33], + ) + self.bus.send(msg) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0x7B, + 0x00, + 0x00, + 0x00, + 0x48, + 0x65, + 0x6C, + 0x6C, + 0x6F, + 0x31, + 0x32, + 0x33, + 0x08, + 0x00, + 0x00, + 0x00, + 0x0D, + 0x55, + 0x55, + ] + ), + ) + + def test_recv_extended_remote(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0x56, + 0x34, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x07, + 0x00, + 0x01, + 0x01, + 0xA5, + 0xA5, + 0x55, + 0x55, + ] + ) + ) + msg = self.bus.recv(1) + self.assertIsNotNone(msg) + self.assertEqual(msg.arbitration_id, 0x123456) + self.assertEqual(msg.is_extended_id, True) + self.assertEqual(msg.is_remote_frame, True) + self.assertEqual(msg.dlc, 7) + data = self.serial.read(self.serial.in_waiting) + + def test_send_extended_remote(self): + msg = can.Message( + arbitration_id=0x123456, is_extended_id=True, is_remote_frame=True, dlc=7 + ) + self.bus.send(msg) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0x56, + 0x34, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x07, + 0x00, + 0x01, + 0x01, + 0xA5, + 0xA5, + 0x55, + 0x55, + ] + ), + ) + + def test_partial_recv(self): + # write some junk data and then start of message + self.serial.write( + bytearray([0x11, 0x22, 0x33, 0xAA, 0xAA, 0x7B, 0x00, 0x00, 0x00, 0x48]) + ) + msg = self.bus.recv(1) + self.assertIsNone(msg) + + # write rest of first message, and then a second message + self.serial.write( + bytearray( + [ + 0x65, + 0x6C, + 0x6C, + 0x6F, + 0x31, + 0x32, + 0x33, + 0x08, + 0x00, + 0x00, + 0x00, + 0x0D, + 0x55, + 0x55, + ] + ) + ) + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0x56, + 0x34, + 0x12, + 0x00, + 0xA5, + 0xAA, + 0xA5, + 0xA5, + 0xA5, + 0x55, + 0xA5, + 0x55, + 0xA5, + 0xA5, + 0xA5, + 0xAA, + 0x00, + 0x00, + 0x06, + 0x00, + 0x01, + 0x00, + 0xEB, + 0x55, + 0x55, + ] + ) + ) + msg = self.bus.recv(1) + self.assertIsNotNone(msg) + self.assertEqual(msg.arbitration_id, 123) + self.assertEqual(msg.is_extended_id, False) + self.assertEqual(msg.is_remote_frame, False) + self.assertEqual(msg.dlc, 8) + self.assertSequenceEqual( + msg.data, [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x31, 0x32, 0x33] + ) + + # now try to also receive 2nd message + msg = self.bus.recv(1) + self.assertIsNotNone(msg) + self.assertEqual(msg.arbitration_id, 0x123456) + self.assertEqual(msg.is_extended_id, True) + self.assertEqual(msg.is_remote_frame, False) + self.assertEqual(msg.dlc, 6) + self.assertSequenceEqual(msg.data, [0xAA, 0xA5, 0x55, 0x55, 0xA5, 0xAA]) + + # test nothing more left + msg = self.bus.recv(1) + self.assertIsNone(msg) + data = self.serial.read(self.serial.in_waiting) + + def test_serial_number(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xF0, + 0xFF, + 0xFF, + 0x01, + 0x53, + 0xFF, + 0x6A, + 0x06, + 0x49, + 0x72, + 0x48, + 0xA5, + 0x55, + 0x08, + 0xFF, + 0x01, + 0x00, + 0x11, + 0x55, + 0x55, + ] + ) + ) + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xF1, + 0xFF, + 0xFF, + 0x01, + 0x40, + 0x60, + 0x17, + 0x87, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x00, + 0x36, + 0x55, + 0x55, + ] + ) + ) + sn = self.bus.get_serial_number(1) + self.assertEqual(sn, "53FF-6A06-4972-4855-4060-1787") + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0xF0, + 0xFF, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x01, + 0xF8, + 0x55, + 0x55, + 0xAA, + 0xAA, + 0xF1, + 0xFF, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x01, + 0xF9, + 0x55, + 0x55, + ] + ), + ) + + sn = self.bus.get_serial_number(0) + self.assertIsNone(sn) + data = self.serial.read(self.serial.in_waiting) + + def test_set_bitrate(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xD0, + 0xFE, + 0xFF, + 0x01, + 0x40, + 0x42, + 0x0F, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0xFF, + 0x01, + 0x01, + 0x64, + 0x55, + 0x55, + ] + ) + ) + self.bus.set_bitrate(1000000) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0xD0, + 0xFE, + 0xFF, + 0x01, + 0x40, + 0x42, + 0x0F, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0xFF, + 0x01, + 0x00, + 0x63, + 0x55, + 0x55, + ] + ), + ) + + def test_set_auto_retransmit(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xA0, + 0xFE, + 0xFF, + 0x01, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x01, + 0xA1, + 0x55, + 0x55, + ] + ) + ) + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xA0, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x01, + 0xA0, + 0x55, + 0x55, + ] + ) + ) + self.bus.set_auto_retransmit(True) + self.bus.set_auto_retransmit(False) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0xA0, + 0xFE, + 0xFF, + 0x01, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x00, + 0xA0, + 0x55, + 0x55, + 0xAA, + 0xAA, + 0xA0, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x00, + 0x9F, + 0x55, + 0x55, + ] + ), + ) + + def test_set_auto_bus_management(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xB0, + 0xFE, + 0xFF, + 0x01, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x01, + 0xB1, + 0x55, + 0x55, + ] + ) + ) + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xB0, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x01, + 0xB0, + 0x55, + 0x55, + ] + ) + ) + self.bus.set_auto_bus_management(True) + self.bus.set_auto_bus_management(False) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0xB0, + 0xFE, + 0xFF, + 0x01, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x00, + 0xB0, + 0x55, + 0x55, + 0xAA, + 0xAA, + 0xB0, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xFF, + 0x01, + 0x00, + 0xAF, + 0x55, + 0x55, + ] + ), + ) + + def test_set_serial_rate(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0x90, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0xC2, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0xFF, + 0x01, + 0x01, + 0x56, + 0x55, + 0x55, + ] + ) + ) + self.bus.set_serial_rate(115200) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0x90, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0xC2, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0xFF, + 0x01, + 0x00, + 0xA5, + 0x55, + 0x55, + 0x55, + ] + ), + ) + + def test_set_hw_filter(self): + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xE0, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x01, + 0x67, + 0x55, + 0x55, + ] + ) + ) + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xE1, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x01, + 0xA8, + 0x55, + 0x55, + ] + ) + ) + self.serial.write( + bytearray( + [ + 0xAA, + 0xAA, + 0xE2, + 0xFE, + 0xFF, + 0x01, + 0xF0, + 0x01, + 0x00, + 0x00, + 0xF0, + 0x01, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x01, + 0xCB, + 0x55, + 0x55, + ] + ) + ) + self.bus.set_hw_filter(1, True, 0, 0, False) + self.bus.set_hw_filter(2, True, 0, 0, True) + self.bus.set_hw_filter(3, False, 0x1F0, 0x1F0, False) + data = self.serial.read(self.serial.in_waiting) + self.assertEqual( + data, + bytearray( + [ + 0xAA, + 0xAA, + 0xE0, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x00, + 0x66, + 0x55, + 0x55, + 0xAA, + 0xAA, + 0xE1, + 0xFE, + 0xFF, + 0x01, + 0x00, + 0x00, + 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x00, + 0xA7, + 0x55, + 0x55, + 0xAA, + 0xAA, + 0xE2, + 0xFE, + 0xFF, + 0x01, + 0xF0, + 0x01, + 0x00, + 0x00, + 0xF0, + 0x01, + 0x00, + 0x00, + 0x08, + 0xFF, + 0x01, + 0x00, + 0xCA, + 0x55, + 0x55, + ] + ), + ) + + +if __name__ == "__main__": + unittest.main() From 638bbeb221bf643e54b6cb13548401d098752ac1 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Mon, 23 Dec 2019 20:18:19 +1100 Subject: [PATCH 221/252] Update test deps (#745) * Update pinned versions of testing dependencies * Pin version of coverage --- setup.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index d7d2feca6..a035ff97f 100644 --- a/setup.py +++ b/setup.py @@ -33,11 +33,14 @@ } tests_require = [ - "pytest~=4.3", + "pytest~=5.3", "pytest-timeout~=1.3", - "pytest-cov~=2.6", + "pytest-cov~=2.8", + # coveragepy==5.0 fails with `Safety level may not be changed inside a transaction` + # on python 3.6 on MACOS + "coverage<5", "codecov~=2.0", - "hypothesis", + "hypothesis~=4.56", ] + extras_require["serial"] extras_require["test"] = tests_require From e11725d1acbbfd1a09251f368d30d1c58d28f962 Mon Sep 17 00:00:00 2001 From: Philipp <1062119+ptoews@users.noreply.github.com> Date: Mon, 23 Dec 2019 21:13:43 +0100 Subject: [PATCH 222/252] Fix example usage (#739) Code example does not work with python 3.6. A colon is required so that the X is recognized as formatting specifier. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 0214f2d4b..9fce86c02 100644 --- a/README.rst +++ b/README.rst @@ -95,7 +95,7 @@ Example usage # iterate over received messages for msg in bus: - print("{X}: {}".format(msg.arbitration_id, msg.data)) + print("{:X}: {}".format(msg.arbitration_id, msg.data)) # or use an asynchronous notifier notifier = can.Notifier(bus, [can.Logger("recorded.log"), can.Printer()]) From dcd694efb56d503ed9387edc5cfba9c523d559bf Mon Sep 17 00:00:00 2001 From: chrisoro <4160557+chrisoro@users.noreply.github.com> Date: Tue, 24 Dec 2019 07:16:31 +0100 Subject: [PATCH 223/252] Exclude all test packages, not just toplevel (#740) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a035ff97f..8327b1432 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ ], # Code version=version, - packages=find_packages(exclude=["test", "doc", "scripts", "examples"]), + packages=find_packages(exclude=["test*", "doc", "scripts", "examples"]), scripts=list(filter(isfile, (join("scripts/", f) for f in listdir("scripts/")))), # Author author="Brian Thorne", From 148921a4d566435808f5223c1e1bcba92de87f2a Mon Sep 17 00:00:00 2001 From: Alexander Bessman Date: Tue, 7 Jan 2020 23:33:30 +0100 Subject: [PATCH 224/252] Fix date format to show correct day of month (#754) %m is month as a zero-padded decimal number %d is day of the month as a zero-padded decimal number --- can/io/asc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index 9d854ab0f..c83458ab4 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -147,7 +147,7 @@ class ASCWriter(BaseIOHandler, Listener): "{bit_timing_conf_ext_data:>8}", ] ) - FORMAT_DATE = "%a %b %m %I:%M:%S.{} %p %Y" + FORMAT_DATE = "%a %b %d %I:%M:%S.{} %p %Y" FORMAT_EVENT = "{timestamp: 9.6f} {message}\n" def __init__(self, file, channel=1): @@ -162,7 +162,7 @@ def __init__(self, file, channel=1): self.channel = channel # write start of file header - now = datetime.now().strftime("%a %b %m %I:%M:%S.%f %p %Y") + now = datetime.now().strftime("%a %b %d %I:%M:%S.%f %p %Y") self.file.write("date %s\n" % now) self.file.write("base hex timestamps absolute\n") self.file.write("internal events logged\n") From 44b026584e7dfeac44123affbdd06a0fd8035476 Mon Sep 17 00:00:00 2001 From: Alexander Bessman Date: Fri, 4 Oct 2019 10:31:57 +0200 Subject: [PATCH 225/252] Add example with cyclic counter and checksum --- examples/crc.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 examples/crc.py diff --git a/examples/crc.py b/examples/crc.py new file mode 100644 index 000000000..fa4493ab0 --- /dev/null +++ b/examples/crc.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +This example exercises the periodic task's multiple message sending capabilities +to send a message containing a counter and a checksum. + +Expects a vcan0 interface: + + python3 -m examples.crc + +""" + +import logging +import time + +import can + +logging.basicConfig(level=logging.INFO) + + +def crc_send(bus): + """ + Sends periodic messages every 1 s with no explicit timeout + Sleeps for 10 seconds then stops the task. + """ + msg = can.Message(arbitration_id=0x12345678, data=[1, 2, 3, 4, 5, 6, 7, 0]) + messages = build_crc_msgs(msg) + + print( + "Starting to send a message with updating counter and checksum every 1 s for 8 s" + ) + task = bus.send_periodic(messages, 1) + assert isinstance(task, can.CyclicSendTaskABC) + time.sleep(8) + + msg = can.Message(arbitration_id=0x12345678, data=[8, 9, 10, 11, 12, 13, 14, 0]) + messages = build_crc_msgs(msg) + + print("Sending modified message data every 1 s for 10 s") + task.modify_data(messages) + time.sleep(10) + task.stop() + print("stopped cyclic send") + + +def build_crc_msgs(msg): + """ + Using the input message as base, create 16 messages with SAE J1939 SPN 3189 counters + and SPN 3188 checksums placed in the final byte. + """ + messages = [] + + for counter in range(16): + checksum = compute_xbr_checksum(msg, counter) + msg.data[7] = counter + (checksum << 4) + messages.append( + can.Message(arbitration_id=msg.arbitration_id, data=msg.data[:]) + ) + + return messages + + +def compute_xbr_checksum(message, counter): + """ + Computes an XBR checksum per SAE J1939 SPN 3188. + """ + checksum = sum(message.data[:7]) + checksum += sum(message.arbitration_id.to_bytes(length=4, byteorder="big")) + checksum += counter & 0x0F + xbr_checksum = ((checksum >> 4) + checksum) & 0x0F + + return xbr_checksum + + +if __name__ == "__main__": + for interface, channel in [("socketcan", "vcan0")]: + print(f"Carrying out crc test with {interface} interface") + + with can.Bus( # type: ignore + interface=interface, channel=channel, bitrate=500000 + ) as BUS: + crc_send(BUS) + + time.sleep(2) From eeb659390b76f49aacef72f8f7fd0dad3303675a Mon Sep 17 00:00:00 2001 From: Alexander Bessman Date: Fri, 4 Oct 2019 11:40:12 +0200 Subject: [PATCH 226/252] Fix crc_send docstring --- examples/crc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/crc.py b/examples/crc.py index fa4493ab0..4344fb2ea 100644 --- a/examples/crc.py +++ b/examples/crc.py @@ -20,8 +20,8 @@ def crc_send(bus): """ - Sends periodic messages every 1 s with no explicit timeout - Sleeps for 10 seconds then stops the task. + Sends periodic messages every 1 s with no explicit timeout. Modifies messages + after 8 seconds, sends for 10 more seconds, then stops. """ msg = can.Message(arbitration_id=0x12345678, data=[1, 2, 3, 4, 5, 6, 7, 0]) messages = build_crc_msgs(msg) From c92fbc24be92548bdafa8f13dab1a00e6a42b3e3 Mon Sep 17 00:00:00 2001 From: Syed Date: Thu, 30 Jan 2020 10:53:11 -0500 Subject: [PATCH 227/252] Fixing ASCII reader unable to read FD frames (#741) * Fixing ascii reader to support vector format when reading FD frames --- can/io/asc.py | 30 +++++++++++++++++++++++++++--- test/logformats_test.py | 2 +- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index c83458ab4..d2f8a4ab8 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -55,10 +55,14 @@ def __iter__(self): temp = line.strip() if not temp or not temp[0].isdigit(): continue + is_fd = False try: timestamp, channel, dummy = temp.split( None, 2 ) # , frameType, dlc, frameData + if channel == "CANFD": + timestamp, _, channel, _, dummy = temp.split(None, 4) + is_fd = True except ValueError: # we parsed an empty comment continue @@ -89,15 +93,32 @@ def __iter__(self): ) yield msg else: + brs = None + esi = None + data_length = 0 try: - # this only works if dlc > 0 and thus data is availabe - can_id_str, _, _, dlc, data = dummy.split(None, 4) + # this only works if dlc > 0 and thus data is available + if not is_fd: + can_id_str, _, _, dlc, data = dummy.split(None, 4) + else: + can_id_str, frame_name, brs, esi, dlc, data_length, data = dummy.split( + None, 6 + ) + if frame_name.isdigit(): + # Empty frame_name + can_id_str, brs, esi, dlc, data_length, data = dummy.split( + None, 5 + ) except ValueError: # but if not, we only want to get the stuff up to the dlc can_id_str, _, _, dlc = dummy.split(None, 3) # and we set data to an empty sequence manually data = "" - dlc = int(dlc) + dlc = int(dlc, 16) + if is_fd: + # For fd frames, dlc and data length might not be equal and + # data_length is the actual size of the data + dlc = int(data_length) frame = bytearray() data = data.split() for byte in data[0:dlc]: @@ -111,7 +132,10 @@ def __iter__(self): is_remote_frame=False, dlc=dlc, data=frame, + is_fd=is_fd, channel=channel, + bitrate_switch=is_fd and brs == "1", + error_state_indicator=is_fd and esi == "1", ) self.stop() diff --git a/test/logformats_test.py b/test/logformats_test.py index 3bc2695bb..4a5c408b5 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -338,7 +338,7 @@ def _setup_instance(self): super()._setup_instance_helper( can.ASCWriter, can.ASCReader, - check_fd=False, + check_fd=True, check_comments=True, preserves_channel=False, adds_default_channel=0, From 8ace40bf26c780dfccf1dc2bea4dd618c0ffe6b1 Mon Sep 17 00:00:00 2001 From: tamenol <37591107+tamenol@users.noreply.github.com> Date: Wed, 5 Feb 2020 14:44:35 +0100 Subject: [PATCH 228/252] Added keycheck for windows platform (#724) * Added keycheck for windows platform PCANBasic will log an error when the PEAK-Driver key could not be found. * Forgot the import winreg statement * added empty line between imports and rest of code * added finally statement for closing registery --- can/interfaces/pcan/basic.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index b10e5404e..6d0f80fad 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -14,6 +14,8 @@ from string import * import platform import logging +import winreg + logger = logging.getLogger("can.pcan") @@ -498,9 +500,17 @@ class PCANBasic: """ def __init__(self): - # Loads the PCANBasic.dll + # Loads the PCANBasic.dll and checks if driver is available if platform.system() == "Windows": self.__m_dllBasic = windll.LoadLibrary("PCANBasic") + aReg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) + try: + aKey = winreg.OpenKey(aReg, r"SOFTWARE\PEAK-System\PEAK-Drivers") + winreg.CloseKey(aKey) + except WindowsError: + logger.error("Exception: The PEAK-driver couldn't be found!") + finally: + winreg.CloseKey(aReg) elif platform.system() == "Darwin": self.__m_dllBasic = cdll.LoadLibrary("libPCBUSB.dylib") else: From e495478bc7eb9a8066ec4a551f0c7fb84ff85f66 Mon Sep 17 00:00:00 2001 From: tamenol <37591107+tamenol@users.noreply.github.com> Date: Wed, 5 Feb 2020 15:17:45 +0100 Subject: [PATCH 229/252] Added status_string method to return simple status strings (#725) * Added status_string method to return simple status strings status_string returns the current status of the bus in a single word * Return None when error code is not implemented Co-authored-by: Felix Divo --- can/interfaces/pcan/basic.py | 32 ++++++++++++++++++++++++++++++++ can/interfaces/pcan/pcan.py | 11 +++++++++++ 2 files changed, 43 insertions(+) diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index 6d0f80fad..90599910a 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -402,6 +402,38 @@ PCAN_TYPE_DNG_SJA = TPCANType(0x05) # PCAN-Dongle SJA1000 PCAN_TYPE_DNG_SJA_EPP = TPCANType(0x06) # PCAN-Dongle EPP SJA1000 +# string description of the error codes +PCAN_DICT_STATUS = { + PCAN_ERROR_OK: "OK", + PCAN_ERROR_XMTFULL: "XMTFULL", + PCAN_ERROR_OVERRUN: "OVERRUN", + PCAN_ERROR_BUSLIGHT: "BUSLIGHT", + PCAN_ERROR_BUSHEAVY: "BUSHEAVY", + PCAN_ERROR_BUSWARNING: "BUSWARNING", + PCAN_ERROR_BUSPASSIVE: "BUSPASSIVE", + PCAN_ERROR_BUSOFF: "BUSOFF", + PCAN_ERROR_ANYBUSERR: "ANYBUSERR", + PCAN_ERROR_QRCVEMPTY: "QRCVEMPTY", + PCAN_ERROR_QOVERRUN: "QOVERRUN", + PCAN_ERROR_QXMTFULL: "QXMTFULL", + PCAN_ERROR_REGTEST: "ERR_REGTEST", + PCAN_ERROR_NODRIVER: "NODRIVER", + PCAN_ERROR_HWINUSE: "HWINUSE", + PCAN_ERROR_NETINUSE: "NETINUSE", + PCAN_ERROR_ILLHW: "ILLHW", + PCAN_ERROR_ILLNET: "ILLNET", + PCAN_ERROR_ILLCLIENT: "ILLCLIENT", + PCAN_ERROR_ILLHANDLE: "ILLHANDLE", + PCAN_ERROR_RESOURCE: "ERR_RESOURCE", + PCAN_ERROR_ILLPARAMTYPE: "ILLPARAMTYPE", + PCAN_ERROR_ILLPARAMVAL: "ILLPARAMVAL", + PCAN_ERROR_UNKNOWN: "UNKNOWN", + PCAN_ERROR_ILLDATA: "ILLDATA", + PCAN_ERROR_CAUTION: "CAUTION", + PCAN_ERROR_INITIALIZE: "ERR_INITIALIZE", + PCAN_ERROR_ILLOPERATION: "ILLOPERATION", +} + class TPCANMsg(Structure): """ diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 5c7b2b9d7..14d8dc774 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -5,6 +5,7 @@ import logging import time +from typing import Optional from can import CanError, Message, BusABC from can.bus import BusState from can.util import len2dlc, dlc2len @@ -528,6 +529,16 @@ def _detect_available_configs(): ) return channels + def status_string(self) -> Optional[str]: + """ + Query the PCAN bus status. + :return: The status in string. + """ + if self.status() in PCAN_DICT_STATUS: + return PCAN_DICT_STATUS[self.status()] + else: + return None + class PcanError(CanError): """ From 2282bfc2b3b308a9b473b7bd6393684fa0e8e15a Mon Sep 17 00:00:00 2001 From: Auden RovelleQuartz Date: Fri, 7 Feb 2020 07:28:09 -0600 Subject: [PATCH 230/252] 'can.interface' NOT 'can.interfaces.interface' (#762) I was getting the following error (see below) #################################################################### C:\Users\arovellequartz>python auden_scratchpad_002.py Traceback (most recent call last): File "auden_scratchpad_002.py", line 18, in from can.interfaces.interface import Bus ModuleNotFoundError: No module named 'can.interfaces.interface' #################################################################### Co-authored-by: Brian Thorne --- doc/configuration.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index 0ce8f85a7..ea56c73ee 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -11,7 +11,7 @@ In Code ------- The ``can`` object exposes an ``rc`` dictionary which can be used to set -the **interface** and **channel** before importing from ``can.interfaces``. +the **interface** and **channel**. :: @@ -19,7 +19,7 @@ the **interface** and **channel** before importing from ``can.interfaces``. can.rc['interface'] = 'socketcan' can.rc['channel'] = 'vcan0' can.rc['bitrate'] = 500000 - from can.interfaces.interface import Bus + from can.interface import Bus bus = Bus() @@ -79,7 +79,7 @@ The configuration can also contain additional sections (or context): :: - from can.interfaces.interface import Bus + from can.interface import Bus hs_bus = Bus(context='HS') ms_bus = Bus(context='MS') From 651f1620af623b81bb46e17222fed95f708bdb0c Mon Sep 17 00:00:00 2001 From: yo <32378889+typecprint@users.noreply.github.com> Date: Sun, 9 Feb 2020 03:59:05 +0900 Subject: [PATCH 231/252] Supports other base number(radix) at ASCReader. (#764) --- can/io/asc.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index d2f8a4ab8..709230389 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -18,6 +18,8 @@ CAN_MSG_EXT = 0x80000000 CAN_ID_MASK = 0x1FFFFFFF +BASE_HEX = 16 +BASE_DEC = 10 logger = logging.getLogger("can.io.asc") @@ -30,27 +32,40 @@ class ASCReader(BaseIOHandler): TODO: turn relative timestamps back to absolute form """ - def __init__(self, file): + def __init__(self, file, base="hex"): """ :param file: a path-like object or as file-like object to read from If this is a file-like object, is has to opened in text read mode, not binary read mode. + :param base: Select the base(hex or dec) of id and data. + If the header of the asc file contains base information, + this value will be overwritten. Default "hex". """ super().__init__(file, mode="r") + self.base = base @staticmethod - def _extract_can_id(str_can_id): + def _extract_can_id(str_can_id, base): if str_can_id[-1:].lower() == "x": is_extended = True - can_id = int(str_can_id[0:-1], 16) + can_id = int(str_can_id[0:-1], base) else: is_extended = False - can_id = int(str_can_id, 16) + can_id = int(str_can_id, base) return can_id, is_extended + @staticmethod + def _check_base(base): + if base not in ["hex", "dec"]: + raise ValueError('base should be either "hex" or "dec"') + return BASE_DEC if base == "dec" else BASE_HEX + def __iter__(self): + base = self._check_base(self.base) for line in self.file: # logger.debug("ASCReader: parsing line: '%s'", line.splitlines()[0]) + if line.split(" ")[0] == "base": + base = self._check_base(line.split(" ")[1]) temp = line.strip() if not temp or not temp[0].isdigit(): @@ -83,7 +98,7 @@ def __iter__(self): pass elif dummy[-1:].lower() == "r": can_id_str, _ = dummy.split(None, 1) - can_id_num, is_extended_id = self._extract_can_id(can_id_str) + can_id_num, is_extended_id = self._extract_can_id(can_id_str, base) msg = Message( timestamp=timestamp, arbitration_id=can_id_num & CAN_ID_MASK, @@ -114,7 +129,7 @@ def __iter__(self): can_id_str, _, _, dlc = dummy.split(None, 3) # and we set data to an empty sequence manually data = "" - dlc = int(dlc, 16) + dlc = int(dlc, base) if is_fd: # For fd frames, dlc and data length might not be equal and # data_length is the actual size of the data @@ -122,8 +137,8 @@ def __iter__(self): frame = bytearray() data = data.split() for byte in data[0:dlc]: - frame.append(int(byte, 16)) - can_id_num, is_extended_id = self._extract_can_id(can_id_str) + frame.append(int(byte, base)) + can_id_num, is_extended_id = self._extract_can_id(can_id_str, base) yield Message( timestamp=timestamp, From b87329983d03b91b16920d47a8b4f90e650ca433 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Tue, 11 Feb 2020 20:45:45 +0100 Subject: [PATCH 232/252] Fix some BLF files can't be read (#765) Fixes #763 --- can/io/blf.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/can/io/blf.py b/can/io/blf.py index 8df41ed5a..8ac79ddb8 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -189,13 +189,13 @@ def __iter__(self): def _parse_container(self, data): if self._tail: data = b"".join((self._tail, data)) - self._pos = 0 try: yield from self._parse_data(data) except struct.error: - # Container data exhausted - # Save the remaining data that could not be processed - self._tail = data[self._pos :] + # There was not enough data in the container to unpack a struct + pass + # Save the remaining data that could not be processed + self._tail = data[self._pos :] def _parse_data(self, data): """Optimized inner loop by making local copies of global variables @@ -213,12 +213,14 @@ def _parse_data(self, data): unpack_can_error_ext = CAN_ERROR_EXT_STRUCT.unpack_from start_timestamp = self.start_timestamp + max_pos = len(data) pos = 0 # Loop until a struct unpack raises an exception while True: self._pos = pos header = unpack_obj_header_base(data, pos) + # print(header) signature, _, header_version, obj_size, obj_type = header if signature != b"LOBJ": raise BLFParseError() @@ -228,6 +230,9 @@ def _parse_data(self, data): if obj_type != CAN_FD_MESSAGE_64: # Add padding bytes next_pos += obj_size % 4 + if next_pos > max_pos: + # This object continues in the next container + return pos += obj_header_base_size # Read rest of header From cb138c09567b9502d1040b93ad2af9c16b0cafd7 Mon Sep 17 00:00:00 2001 From: Syed Date: Fri, 21 Feb 2020 14:37:44 -0500 Subject: [PATCH 233/252] Changes to add direction to CAN messages (#773) * Changes to add direction to CAN messages * Adding relevant changes to ascii reader * Reverting accidental change * Black changes * Adding more changes needed for rx_attribute & documentation * Adding more changes needed for rx_attribute & documentation2 * Adding changes to __repr__ and adding rx to equals --- can/interfaces/ics_neovi/neovi_bus.py | 2 ++ can/io/asc.py | 21 +++++++++++++++------ can/io/blf.py | 5 +++++ can/message.py | 17 ++++++++++++++++- doc/message.rst | 7 +++++++ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index df4f5481f..30c9dd8c9 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -306,6 +306,7 @@ def _ics_msg_to_message(self, ics_msg): dlc=ics_msg.NumberBytesData, is_extended_id=bool(ics_msg.StatusBitField & ics.SPY_STATUS_XTD_FRAME), is_fd=is_fd, + is_rx=not bool(ics_msg.StatusBitField & ics.SPY_STATUS_TX_MSG), is_remote_frame=bool( ics_msg.StatusBitField & ics.SPY_STATUS_REMOTE_FRAME ), @@ -325,6 +326,7 @@ def _ics_msg_to_message(self, ics_msg): dlc=ics_msg.NumberBytesData, is_extended_id=bool(ics_msg.StatusBitField & ics.SPY_STATUS_XTD_FRAME), is_fd=is_fd, + is_rx=not bool(ics_msg.StatusBitField & ics.SPY_STATUS_TX_MSG), is_remote_frame=bool( ics_msg.StatusBitField & ics.SPY_STATUS_REMOTE_FRAME ), diff --git a/can/io/asc.py b/can/io/asc.py index 709230389..c8a2aada3 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -71,13 +71,15 @@ def __iter__(self): if not temp or not temp[0].isdigit(): continue is_fd = False + is_rx = True try: timestamp, channel, dummy = temp.split( None, 2 ) # , frameType, dlc, frameData if channel == "CANFD": - timestamp, _, channel, _, dummy = temp.split(None, 4) + timestamp, _, channel, direction, dummy = temp.split(None, 4) is_fd = True + is_rx = direction == "Rx" except ValueError: # we parsed an empty comment continue @@ -97,13 +99,14 @@ def __iter__(self): ): pass elif dummy[-1:].lower() == "r": - can_id_str, _ = dummy.split(None, 1) + can_id_str, direction, _ = dummy.split(None, 2) can_id_num, is_extended_id = self._extract_can_id(can_id_str, base) msg = Message( timestamp=timestamp, arbitration_id=can_id_num & CAN_ID_MASK, is_extended_id=is_extended_id, is_remote_frame=True, + is_rx=direction == "Rx", channel=channel, ) yield msg @@ -114,7 +117,8 @@ def __iter__(self): try: # this only works if dlc > 0 and thus data is available if not is_fd: - can_id_str, _, _, dlc, data = dummy.split(None, 4) + can_id_str, direction, _, dlc, data = dummy.split(None, 4) + is_rx = direction == "Rx" else: can_id_str, frame_name, brs, esi, dlc, data_length, data = dummy.split( None, 6 @@ -148,6 +152,7 @@ def __iter__(self): dlc=dlc, data=frame, is_fd=is_fd, + is_rx=is_rx, channel=channel, bitrate_switch=is_fd and brs == "1", error_state_indicator=is_fd and esi == "1", @@ -164,7 +169,7 @@ class ASCWriter(BaseIOHandler, Listener): It the first message does not have a timestamp, it is set to zero. """ - FORMAT_MESSAGE = "{channel} {id:<15} Rx {dtype} {data}" + FORMAT_MESSAGE = "{channel} {id:<15} {dir:<4} {dtype} {data}" FORMAT_MESSAGE_FD = " ".join( [ "CANFD", @@ -276,7 +281,7 @@ def on_message_received(self, msg): serialized = self.FORMAT_MESSAGE_FD.format( channel=channel, id=arb_id, - dir="Rx", + dir="Rx" if msg.is_rx else "Tx", symbolic_name="", brs=1 if msg.bitrate_switch else 0, esi=1 if msg.error_state_indicator else 0, @@ -294,6 +299,10 @@ def on_message_received(self, msg): ) else: serialized = self.FORMAT_MESSAGE.format( - channel=channel, id=arb_id, dtype=dtype, data=" ".join(data) + channel=channel, + id=arb_id, + dir="Rx" if msg.is_rx else "Tx", + dtype=dtype, + data=" ".join(data), ) self.log_event(serialized, msg.timestamp) diff --git a/can/io/blf.py b/can/io/blf.py index 8ac79ddb8..ca0b5bd38 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -91,6 +91,7 @@ class BLFParseError(Exception): EDL = 0x1 BRS = 0x2 ESI = 0x4 +DIR = 0x1 TIME_TEN_MICS = 0x00000001 TIME_ONE_NANS = 0x00000002 @@ -258,6 +259,7 @@ def _parse_data(self, data): arbitration_id=can_id & 0x1FFFFFFF, is_extended_id=bool(can_id & CAN_MSG_EXT), is_remote_frame=bool(flags & REMOTE_FLAG), + is_rx=not bool(flags & DIR), dlc=dlc, data=can_data[:dlc], channel=channel - 1, @@ -288,6 +290,7 @@ def _parse_data(self, data): is_extended_id=bool(can_id & CAN_MSG_EXT), is_remote_frame=bool(flags & REMOTE_FLAG), is_fd=bool(fd_flags & 0x1), + is_rx=not bool(flags & DIR), bitrate_switch=bool(fd_flags & 0x2), error_state_indicator=bool(fd_flags & 0x4), dlc=dlc2len(dlc), @@ -399,6 +402,8 @@ def on_message_received(self, msg): if msg.is_extended_id: arb_id |= CAN_MSG_EXT flags = REMOTE_FLAG if msg.is_remote_frame else 0 + if not msg.is_rx: + flags |= DIR can_data = bytes(msg.data) if msg.is_error_frame: diff --git a/can/message.py b/can/message.py index 57e0109af..7ceaca489 100644 --- a/can/message.py +++ b/can/message.py @@ -42,6 +42,7 @@ class Message: "dlc", "data", "is_fd", + "is_rx", "bitrate_switch", "error_state_indicator", "__weakref__", # support weak references to messages @@ -58,6 +59,7 @@ def __init__( dlc: Optional[int] = None, data: Optional[typechecking.CanData] = None, is_fd: bool = False, + is_rx: bool = True, bitrate_switch: bool = False, error_state_indicator: bool = False, check: bool = False, @@ -81,6 +83,7 @@ def __init__( self.is_error_frame = is_error_frame self.channel = channel self.is_fd = is_fd + self.is_rx = is_rx self.bitrate_switch = bitrate_switch self.error_state_indicator = error_state_indicator @@ -114,6 +117,7 @@ def __str__(self) -> str: flag_string = " ".join( [ "X" if self.is_extended_id else "S", + "Rx" if self.is_rx else "Tx", "E" if self.is_error_frame else " ", "R" if self.is_remote_frame else " ", "F" if self.is_fd else " ", @@ -159,6 +163,9 @@ def __repr__(self) -> str: "is_extended_id={}".format(self.is_extended_id), ] + if not self.is_rx: + args.append("is_rx=False") + if self.is_remote_frame: args.append("is_remote_frame={}".format(self.is_remote_frame)) @@ -198,6 +205,7 @@ def __copy__(self) -> "Message": dlc=self.dlc, data=self.data, is_fd=self.is_fd, + is_rx=self.is_rx, bitrate_switch=self.bitrate_switch, error_state_indicator=self.error_state_indicator, ) @@ -214,6 +222,7 @@ def __deepcopy__(self, memo: dict) -> "Message": dlc=self.dlc, data=deepcopy(self.data, memo), is_fd=self.is_fd, + is_rx=self.is_rx, bitrate_switch=self.bitrate_switch, error_state_indicator=self.error_state_indicator, ) @@ -280,7 +289,10 @@ def _check(self): ) def equals( - self, other: "Message", timestamp_delta: Optional[Union[float, int]] = 1.0e-6 + self, + other: "Message", + timestamp_delta: Optional[Union[float, int]] = 1.0e-6, + check_direction: bool = True, ) -> bool: """ Compares a given message with this one. @@ -290,6 +302,8 @@ def equals( :param timestamp_delta: the maximum difference at which two timestamps are still considered equal or None to not compare timestamps + :param check_direction: do we compare the messages' directions (Tx/Rx) + :return: True iff the given message equals this one """ # see https://github.com/hardbyte/python-can/pull/413 for a discussion @@ -304,6 +318,7 @@ def equals( timestamp_delta is None or abs(self.timestamp - other.timestamp) <= timestamp_delta ) + and (self.is_rx == other.is_rx or not check_direction) and self.arbitration_id == other.arbitration_id and self.is_extended_id == other.is_extended_id and self.dlc == other.dlc diff --git a/doc/message.rst b/doc/message.rst index 921748cb9..e5745f6b5 100644 --- a/doc/message.rst +++ b/doc/message.rst @@ -145,6 +145,13 @@ Message Indicates that this message is a CAN FD message. + .. attribute:: is_rx + + :type: bool + + Indicates whether this message is a transmitted (Tx) or received (Rx) frame + + .. attribute:: bitrate_switch :type: bool From 780db5cf3ef068c0e1d5b43637cc20726980aafe Mon Sep 17 00:00:00 2001 From: Syed Date: Mon, 2 Mar 2020 16:46:54 -0500 Subject: [PATCH 234/252] Adding plugin support to can.io Reader/Writer (#783) --- can/io/logger.py | 30 +++++++++++++++++++++--------- can/io/player.py | 29 ++++++++++++++++++++--------- doc/listeners.rst | 18 ++++++++++++++++++ 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/can/io/logger.py b/can/io/logger.py index 3c9cf5e46..a4544b87d 100644 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -5,6 +5,7 @@ import pathlib import typing +from pkg_resources import iter_entry_points import can.typechecking from ..listener import Listener @@ -38,6 +39,16 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method arguments are passed on to the returned instance. """ + fetched_plugins = False + message_writers = { + ".asc": ASCWriter, + ".blf": BLFWriter, + ".csv": CSVWriter, + ".db": SqliteWriter, + ".log": CanutilsLogWriter, + ".txt": Printer, + } + @staticmethod def __new__( cls, filename: typing.Optional[can.typechecking.StringPathLike], *args, **kwargs @@ -51,17 +62,18 @@ def __new__( if filename is None: return Printer(*args, **kwargs) - lookup = { - ".asc": ASCWriter, - ".blf": BLFWriter, - ".csv": CSVWriter, - ".db": SqliteWriter, - ".log": CanutilsLogWriter, - ".txt": Printer, - } + if not Logger.fetched_plugins: + Logger.message_writers.update( + { + writer.name: writer.load() + for writer in iter_entry_points("can.io.message_writer") + } + ) + Logger.fetched_plugins = True + suffix = pathlib.PurePath(filename).suffix try: - return lookup[suffix](filename, *args, **kwargs) + return Logger.message_writers[suffix](filename, *args, **kwargs) except KeyError: raise ValueError( f'No write support for this unknown log format "{suffix}"' diff --git a/can/io/player.py b/can/io/player.py index 7b4aec7c0..88d3497fa 100644 --- a/can/io/player.py +++ b/can/io/player.py @@ -8,6 +8,8 @@ from time import time, sleep import typing +from pkg_resources import iter_entry_points + if typing.TYPE_CHECKING: import can @@ -44,24 +46,33 @@ class LogReader(BaseIOHandler): arguments are passed on to the returned instance. """ + fetched_plugins = False + message_readers = { + ".asc": ASCReader, + ".blf": BLFReader, + ".csv": CSVReader, + ".db": SqliteReader, + ".log": CanutilsLogReader, + } + @staticmethod def __new__(cls, filename: "can.typechecking.StringPathLike", *args, **kwargs): """ :param filename: the filename/path of the file to read from :raises ValueError: if the filename's suffix is of an unknown file type """ - suffix = pathlib.PurePath(filename).suffix + if not LogReader.fetched_plugins: + LogReader.message_readers.update( + { + reader.name: reader.load() + for reader in iter_entry_points("can.io.message_reader") + } + ) + LogReader.fetched_plugins = True - lookup = { - ".asc": ASCReader, - ".blf": BLFReader, - ".csv": CSVReader, - ".db": SqliteReader, - ".log": CanutilsLogReader, - } suffix = pathlib.PurePath(filename).suffix try: - return lookup[suffix](filename, *args, **kwargs) + return LogReader.message_readers[suffix](filename, *args, **kwargs) except KeyError: raise ValueError( f'No read support for this unknown log format "{suffix}"' diff --git a/doc/listeners.rst b/doc/listeners.rst index fcdc32f52..8e2a79a8b 100644 --- a/doc/listeners.rst +++ b/doc/listeners.rst @@ -30,6 +30,24 @@ readers are also documented here. completely unchanged message again, since some properties are not (yet) supported by some file formats. +.. note :: + + Additional file formats for both reading/writing log files can be added via + a plugin reader/writer. An external package can register a new reader + by using the ``can.io.message_reader`` entry point. Similarly, a writer can + be added using the ``can.io.message_writer`` entry point. + + The format of the entry point is ``reader_name=module:classname`` where ``classname`` + is a :class:`can.io.generic.BaseIOHandler` concrete implementation. + + :: + + entry_points={ + 'can.io.message_reader': [ + '.asc = my_package.io.asc:ASCReader' + ] + }, + BufferedReader -------------- From 0c698cce8524a9228384543014baa5bd37e76e19 Mon Sep 17 00:00:00 2001 From: Syed Date: Tue, 3 Mar 2020 13:35:31 -0500 Subject: [PATCH 235/252] Adding direction to virutal bus messages (#779) * Adding direction to virutal bus messages * Creaing a new msg instance for each virtual interface * Update can/interfaces/virtual.py Co-Authored-By: pierreluctg * Adding some unit tests for message direction checking as well as msg.data mutation * Changing how we were skipping test for socketcan * Splitting test case into two different test cases * Update test/back2back_test.py Co-Authored-By: pierreluctg Co-authored-by: pierreluctg Co-authored-by: Brian Thorne --- can/interfaces/virtual.py | 20 +++++++------ test/back2back_test.py | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index 5e24c6e1f..e84fa5853 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -85,18 +85,20 @@ def _recv_internal(self, timeout): def send(self, msg, timeout=None): self._check_if_open() - msg_copy = deepcopy(msg) - msg_copy.timestamp = time.time() - msg_copy.channel = self.channel_id - + timestamp = time.time() # Add message to all listening on this channel all_sent = True for bus_queue in self.channel: - if bus_queue is not self.queue or self.receive_own_messages: - try: - bus_queue.put(msg_copy, block=True, timeout=timeout) - except queue.Full: - all_sent = False + if bus_queue is self.queue and not self.receive_own_messages: + continue + msg_copy = deepcopy(msg) + msg_copy.timestamp = timestamp + msg_copy.channel = self.channel_id + msg_copy.is_rx = bus_queue is not self.queue + try: + bus_queue.put(msg_copy, block=True, timeout=timeout) + except queue.Full: + all_sent = False if not all_sent: raise CanError("Could not send message to one or more recipients") diff --git a/test/back2back_test.py b/test/back2back_test.py index b707988ec..8c1ded6c5 100644 --- a/test/back2back_test.py +++ b/test/back2back_test.py @@ -125,6 +125,66 @@ def test_dlc_less_than_eight(self): msg = can.Message(is_extended_id=False, arbitration_id=0x300, data=[4, 5, 6]) self._send_and_receive(msg) + def test_message_direction(self): + # Verify that own message received has is_rx set to False while message + # received on the other virtual interfaces have is_rx set to True + if self.INTERFACE_1 != "virtual": + raise unittest.SkipTest( + "Message direction not yet implemented for socketcan" + ) + bus3 = can.Bus( + channel=self.CHANNEL_2, + bustype=self.INTERFACE_2, + bitrate=self.BITRATE, + fd=TEST_CAN_FD, + single_handle=True, + receive_own_messages=True, + ) + try: + msg = can.Message( + is_extended_id=False, arbitration_id=0x300, data=[2, 1, 3] + ) + bus3.send(msg) + recv_msg_bus1 = self.bus1.recv(self.TIMEOUT) + recv_msg_bus2 = self.bus2.recv(self.TIMEOUT) + self_recv_msg_bus3 = bus3.recv(self.TIMEOUT) + + self.assertTrue(recv_msg_bus1.is_rx) + self.assertTrue(recv_msg_bus2.is_rx) + self.assertFalse(self_recv_msg_bus3.is_rx) + finally: + bus3.shutdown() + + def test_unique_message_instances(self): + # Verify that we have a different instances of message for each bus + if self.INTERFACE_1 != "virtual": + raise unittest.SkipTest("Not relevant for socketcan") + bus3 = can.Bus( + channel=self.CHANNEL_2, + bustype=self.INTERFACE_2, + bitrate=self.BITRATE, + fd=TEST_CAN_FD, + single_handle=True, + receive_own_messages=True, + ) + try: + msg = can.Message( + is_extended_id=False, arbitration_id=0x300, data=[2, 1, 3] + ) + bus3.send(msg) + recv_msg_bus1 = self.bus1.recv(self.TIMEOUT) + recv_msg_bus2 = self.bus2.recv(self.TIMEOUT) + self_recv_msg_bus3 = bus3.recv(self.TIMEOUT) + + self._check_received_message(recv_msg_bus1, recv_msg_bus2) + self._check_received_message(recv_msg_bus2, self_recv_msg_bus3) + + recv_msg_bus1.data[0] = 4 + self.assertNotEqual(recv_msg_bus1.data, recv_msg_bus2.data) + self.assertEqual(recv_msg_bus2.data, self_recv_msg_bus3.data) + finally: + bus3.shutdown() + @unittest.skipUnless(TEST_CAN_FD, "Don't test CAN-FD") def test_fd_message(self): msg = can.Message( From ffa1800b1933264bf4eae57bea2a5175ddd36140 Mon Sep 17 00:00:00 2001 From: pierreluctg Date: Mon, 23 Mar 2020 16:11:25 -0400 Subject: [PATCH 236/252] caching msg.data value (#798) Avoid calling msg.data multiple times by caching msg.data value --- can/interfaces/ics_neovi/neovi_bus.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 30c9dd8c9..638460a4c 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -363,11 +363,12 @@ def send(self, msg, timeout=None): flag3 |= ics.SPY_STATUS3_CANFD_ESI message.ArbIDOrHeader = msg.arbitration_id - message.NumberBytesData = len(msg.data) - message.Data = tuple(msg.data[:8]) - if msg.is_fd and len(msg.data) > 8: + msg_data = msg.data + message.NumberBytesData = len(msg_data) + message.Data = tuple(msg_data[:8]) + if msg.is_fd and len(msg_data) > 8: message.ExtraDataPtrEnabled = 1 - message.ExtraDataPtr = tuple(msg.data) + message.ExtraDataPtr = tuple(msg_data) message.StatusBitField = flag0 message.StatusBitField2 = 0 message.StatusBitField3 = flag3 From f4c2cff4bf26e825512c034adbb717625ef08b3e Mon Sep 17 00:00:00 2001 From: zariiii9003 <52598363+zariiii9003@users.noreply.github.com> Date: Sat, 28 Mar 2020 19:54:28 +0100 Subject: [PATCH 237/252] add vector hardware config popup (#774) Co-authored-by: zariiii9003 Co-authored-by: Christian Sandberg --- can/interfaces/vector/canlib.py | 9 +++++++++ can/interfaces/vector/xldriver.py | 5 +++++ test/test_vector.py | 8 ++++++++ 3 files changed, 22 insertions(+) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index cb1858062..14205327b 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -577,6 +577,15 @@ def _detect_available_configs(): ) return configs + @staticmethod + def popup_vector_hw_configuration(wait_for_finish: int = 0) -> None: + """Open vector hardware configuration window. + + :param int wait_for_finish: + Time to wait for user input in milliseconds. + """ + xldriver.xlPopupHwConfig(ctypes.c_char_p(), ctypes.c_uint(wait_for_finish)) + def get_channel_configs(): if xldriver is None: diff --git a/can/interfaces/vector/xldriver.py b/can/interfaces/vector/xldriver.py index 337135755..7a361a29d 100644 --- a/can/interfaces/vector/xldriver.py +++ b/can/interfaces/vector/xldriver.py @@ -229,3 +229,8 @@ def check_status(result, function, arguments): xlCanSetChannelOutput.argtypes = [xlclass.XLportHandle, xlclass.XLaccess, ctypes.c_char] xlCanSetChannelOutput.restype = xlclass.XLstatus xlCanSetChannelOutput.errcheck = check_status + +xlPopupHwConfig = _xlapi_dll.xlPopupHwConfig +xlPopupHwConfig.argtypes = [ctypes.c_char_p, ctypes.c_uint] +xlPopupHwConfig.restype = xlclass.XLstatus +xlPopupHwConfig.errcheck = check_status diff --git a/test/test_vector.py b/test/test_vector.py index d99509df8..a2d47c74e 100644 --- a/test/test_vector.py +++ b/test/test_vector.py @@ -240,6 +240,14 @@ def test_reset(self) -> None: can.interfaces.vector.canlib.xldriver.xlDeactivateChannel.assert_called() can.interfaces.vector.canlib.xldriver.xlActivateChannel.assert_called() + def test_popup_hw_cfg(self) -> None: + canlib.xldriver.xlPopupHwConfig = Mock() + canlib.VectorBus.popup_vector_hw_configuration(10) + assert canlib.xldriver.xlPopupHwConfig.called + args, kwargs = canlib.xldriver.xlPopupHwConfig.call_args + assert isinstance(args[0], ctypes.c_char_p) + assert isinstance(args[1], ctypes.c_uint) + def test_called_without_testing_argument(self) -> None: """This tests if an exception is thrown when we are not running on Windows.""" if os.name != "nt": From a35ab980ebd85ffd24139e986a206f4e1bb35f0f Mon Sep 17 00:00:00 2001 From: Mikhail Kulinich Date: Sat, 4 Apr 2020 03:41:05 +0300 Subject: [PATCH 238/252] Add TX/RX message direction to socketcan (#780) --- can/interfaces/socketcan/socketcan.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index 307763d9f..e6ca07f5c 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -509,10 +509,10 @@ def capture_message( # Fetching the Arb ID, DLC and Data try: if get_channel: - cf, addr = sock.recvfrom(CANFD_MTU) + cf, _, msg_flags, addr = sock.recvmsg(CANFD_MTU) channel = addr[0] if isinstance(addr, tuple) else addr else: - cf = sock.recv(CANFD_MTU) + cf, _, msg_flags, _ = sock.recvmsg(CANFD_MTU) channel = None except socket.error as exc: raise can.CanError("Error receiving: %s" % exc) @@ -539,6 +539,9 @@ def capture_message( bitrate_switch = bool(flags & CANFD_BRS) error_state_indicator = bool(flags & CANFD_ESI) + # Section 4.7.1: MSG_DONTROUTE: set when the received frame was created on the local host. + is_rx = not bool(msg_flags & socket.MSG_DONTROUTE) + if is_extended_frame_format: # log.debug("CAN: Extended") # TODO does this depend on SFF or EFF? @@ -555,6 +558,7 @@ def capture_message( is_remote_frame=is_remote_transmission_request, is_error_frame=is_error_frame, is_fd=is_fd, + is_rx=is_rx, bitrate_switch=bitrate_switch, error_state_indicator=error_state_indicator, dlc=can_dlc, From 2d672b340fc38f531a4a6470c8c2e288e5e50041 Mon Sep 17 00:00:00 2001 From: Mikhail Kulinich Date: Fri, 10 Apr 2020 00:41:21 +0300 Subject: [PATCH 239/252] Relax restriction of arbitration ID uniqueness for SocketCAN (#785) Socketcan bcm now supports sending periodic messages with the same arbitration ID. Updates documentation and tests for socketcan interface. Closes #721 --- can/interfaces/socketcan/socketcan.py | 119 ++++++++++++++++---------- doc/bus.rst | 2 +- doc/interfaces/socketcan.rst | 89 ++++++++++++------- test/test_cyclic_socketcan.py | 60 +++++++++++-- 4 files changed, 186 insertions(+), 84 deletions(-) diff --git a/can/interfaces/socketcan/socketcan.py b/can/interfaces/socketcan/socketcan.py index e6ca07f5c..e16ad0c94 100644 --- a/can/interfaces/socketcan/socketcan.py +++ b/can/interfaces/socketcan/socketcan.py @@ -14,6 +14,7 @@ import socket import struct import time +import threading import errno log = logging.getLogger(__name__) @@ -162,7 +163,7 @@ def build_can_frame(msg: Message) -> bytes: __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8))); }; """ - can_id = _add_flags_to_can_id(msg) + can_id = _compose_arbitration_id(msg) flags = 0 if msg.bitrate_switch: flags |= CANFD_BRS @@ -286,7 +287,7 @@ def send_bcm(bcm_socket: socket.socket, data: bytes) -> int: raise e -def _add_flags_to_can_id(message: Message) -> int: +def _compose_arbitration_id(message: Message) -> int: can_id = message.arbitration_id if message.is_extended_id: log.debug("sending an extended id type message") @@ -297,7 +298,6 @@ def _add_flags_to_can_id(message: Message) -> int: if message.is_error_frame: log.debug("sending error frame") can_id |= CAN_ERR_FLAG - return can_id @@ -310,18 +310,22 @@ class CyclicSendTask( - setting of a task duration - modifying the data - stopping then subsequent restarting of the task - """ def __init__( self, bcm_socket: socket.socket, + task_id: int, messages: Union[Sequence[Message], Message], period: float, duration: Optional[float] = None, ): - """ + """Construct and :meth:`~start` a task. + :param bcm_socket: An open BCM socket on the desired CAN channel. + :param task_id: + The identifier used to uniquely reference particular cyclic send task + within Linux BCM. :param messages: The messages to be sent periodically. :param period: @@ -336,12 +340,12 @@ def __init__( super().__init__(messages, period, duration) self.bcm_socket = bcm_socket + self.task_id = task_id self._tx_setup(self.messages) def _tx_setup(self, messages: Sequence[Message]) -> None: # Create a low level packed frame to pass to the kernel body = bytearray() - self.can_id_with_flags = _add_flags_to_can_id(messages[0]) self.flags = CAN_FD_FRAME if messages[0].is_fd else 0 if self.duration: @@ -353,9 +357,19 @@ def _tx_setup(self, messages: Sequence[Message]) -> None: ival1 = 0.0 ival2 = self.period - # First do a TX_READ before creating a new task, and check if we get - # EINVAL. If so, then we are referring to a CAN message with the same - # ID + self._check_bcm_task() + + header = build_bcm_transmit_header( + self.task_id, count, ival1, ival2, self.flags, nframes=len(messages) + ) + for message in messages: + body += build_can_frame(message) + log.debug("Sending BCM command") + send_bcm(self.bcm_socket, header + body) + + def _check_bcm_task(self): + # Do a TX_READ on a task ID, and check if we get EINVAL. If so, + # then we are referring to a CAN message with the existing ID check_header = build_bcm_header( opcode=CAN_BCM_TX_READ, flags=0, @@ -364,7 +378,7 @@ def _tx_setup(self, messages: Sequence[Message]) -> None: ival1_usec=0, ival2_seconds=0, ival2_usec=0, - can_id=self.can_id_with_flags, + can_id=self.task_id, nframes=0, ) try: @@ -374,45 +388,33 @@ def _tx_setup(self, messages: Sequence[Message]) -> None: raise e else: raise ValueError( - "A periodic Task for Arbitration ID {} has already been created".format( - messages[0].arbitration_id + "A periodic task for Task ID {} is already in progress by SocketCAN Linux layer".format( + self.task_id ) ) - header = build_bcm_transmit_header( - self.can_id_with_flags, - count, - ival1, - ival2, - self.flags, - nframes=len(messages), - ) - for message in messages: - body += build_can_frame(message) - log.debug("Sending BCM command") - send_bcm(self.bcm_socket, header + body) - def stop(self) -> None: - """Send a TX_DELETE message to cancel this task. + """Stop a task by sending TX_DELETE message to Linux kernel. 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). + with the specified :attr:`~task_id` 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, self.flags) + stopframe = build_bcm_tx_delete_header(self.task_id, self.flags) send_bcm(self.bcm_socket, stopframe) def modify_data(self, messages: Union[Sequence[Message], Message]) -> None: - """Update the contents of the periodically sent messages. + """Update the contents of the periodically sent CAN messages by + sending TX_SETUP message to Linux kernel. - Note: The messages must all have the same - :attr:`~can.Message.arbitration_id` like the first message. - - Note: The number of new cyclic messages to be sent must be equal to the + The number of new cyclic messages to be sent must be equal to the original number of messages originally specified for this task. + .. note:: The messages must all have the same + :attr:`~can.Message.arbitration_id` like the first message. + :param messages: The messages with the new :attr:`can.Message.data`. """ @@ -423,7 +425,7 @@ def modify_data(self, messages: Union[Sequence[Message], Message]) -> None: body = bytearray() header = build_bcm_update_header( - can_id=self.can_id_with_flags, msg_flags=self.flags, nframes=len(messages) + can_id=self.task_id, msg_flags=self.flags, nframes=len(messages) ) for message in messages: body += build_can_frame(message) @@ -431,6 +433,14 @@ def modify_data(self, messages: Union[Sequence[Message], Message]) -> None: send_bcm(self.bcm_socket, header + body) def start(self) -> None: + """Start a periodic task by sending TX_SETUP message to Linux kernel. + + It verifies presence of the particular BCM task through sending TX_READ + message to Linux kernel prior to scheduling. + + :raises ValueError: + If the task referenced by :attr:`~task_id` is already running. + """ self._tx_setup(self.messages) @@ -443,16 +453,17 @@ class MultiRateCyclicSendTask(CyclicSendTask): def __init__( self, channel: socket.socket, + task_id: int, messages: Sequence[Message], count: int, initial_period: float, subsequent_period: float, ): - super().__init__(channel, messages, subsequent_period) + super().__init__(channel, task_id, messages, subsequent_period) # Create a low level packed frame to pass to the kernel header = build_bcm_transmit_header( - self.can_id_with_flags, + self.task_id, count, initial_period, subsequent_period, @@ -571,8 +582,10 @@ def capture_message( class SocketcanBus(BusABC): - """ - Implements :meth:`can.BusABC._detect_available_configs`. + """ A SocketCAN interface to CAN. + + It implements :meth:`can.BusABC._detect_available_configs` to search for + available interfaces. """ def __init__( @@ -601,6 +614,8 @@ def __init__( self.channel_info = "socketcan channel '%s'" % channel self._bcm_sockets: Dict[str, socket.socket] = {} self._is_filtered = False + self._task_id = 0 + self._task_id_guard = threading.Lock() # set the receive_own_messages parameter try: @@ -712,18 +727,26 @@ def _send_periodic_internal( ) -> CyclicSendTask: """Start sending messages at a given period on this bus. - The kernel's Broadcast Manager SocketCAN API will be used. + The Linux kernel's Broadcast Manager SocketCAN API is used to schedule + periodic sending of CAN messages. The wrapping 32-bit counter (see + :meth:`~_get_next_task_id()`) designated to distinguish different + :class:`CyclicSendTask` within BCM provides flexibility to schedule + CAN messages sending with the same CAN ID, but different CAN data. :param messages: - The messages to be sent periodically + The message(s) to be sent periodically. :param period: The rate in seconds at which to send the messages. :param duration: Approximate duration in seconds to continue sending messages. If no duration is provided, the task will continue indefinitely. + :raises ValueError: + If task identifier passed to :class:`CyclicSendTask` can't be used + to schedule new task in Linux BCM. + :return: - A started task instance. This can be used to modify the data, + A :class:`CyclicSendTask` task instance. This can be used to modify the data, pause/resume the transmission and to stop the transmission. .. note:: @@ -732,18 +755,20 @@ def _send_periodic_internal( 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. - """ msgs = LimitedDurationCyclicSendTaskABC._check_and_convert_messages(msgs) msgs_channel = str(msgs[0].channel) if msgs[0].channel else None bcm_socket = self._get_bcm_socket(msgs_channel or self.channel) - # TODO: The SocketCAN BCM interface treats all cyclic tasks sharing an - # Arbitration ID as the same Cyclic group. We should probably warn the - # user instead of overwriting the old group? - task = CyclicSendTask(bcm_socket, msgs, period, duration) + task_id = self._get_next_task_id() + task = CyclicSendTask(bcm_socket, task_id, msgs, period, duration) return task + def _get_next_task_id(self) -> int: + with self._task_id_guard: + self._task_id = (self._task_id + 1) % (2 ** 32 - 1) + return self._task_id + def _get_bcm_socket(self, channel: str) -> socket.socket: if channel not in self._bcm_sockets: self._bcm_sockets[channel] = create_bcm_socket(self.channel) diff --git a/doc/bus.rst b/doc/bus.rst index 5c1e95606..b524d4868 100644 --- a/doc/bus.rst +++ b/doc/bus.rst @@ -67,7 +67,7 @@ This thread safe version of the :class:`~can.BusABC` class can be used by multip Sending and receiving is locked separately to avoid unnecessary delays. Conflicting calls are executed by blocking until the bus is accessible. -It can be used exactly like the normal :class:`~can.BusABC`: +It can be used exactly like the normal :class:`~can.BusABC`:: # 'socketcan' is only an example interface, it works with all the others too my_bus = can.ThreadSafeBus(interface='socketcan', channel='vcan0') diff --git a/doc/interfaces/socketcan.rst b/doc/interfaces/socketcan.rst index bdd934ca7..1783c388c 100644 --- a/doc/interfaces/socketcan.rst +++ b/doc/interfaces/socketcan.rst @@ -1,16 +1,26 @@ SocketCAN ========= -The full documentation for socketcan can be found in the kernel docs at -`networking/can.txt `_. - - -.. note:: - - Versions before 2.2 had two different implementations named +The `SocketCAN`_ documentation can be found in the Linux kernel docs at +``networking`` directory. Quoting from the SocketCAN Linux documentation:: + +> The socketcan package is an implementation of CAN protocols +> (Controller Area Network) for Linux. CAN is a networking technology +> which has widespread use in automation, embedded devices, and +> automotive fields. While there have been other CAN implementations +> for Linux based on character devices, SocketCAN uses the Berkeley +> socket API, the Linux network stack and implements the CAN device +> drivers as network interfaces. The CAN socket API has been designed +> as similar as possible to the TCP/IP protocols to allow programmers, +> familiar with network programming, to easily learn how to use CAN +> sockets. + +.. important:: + + `python-can` versions before 2.2 had two different implementations named ``socketcan_ctypes`` and ``socketcan_native``. These are now deprecated and the aliases to ``socketcan`` will be removed in - version 4.0. 3.x releases raise a DeprecationWarning. + version 4.0. 3.x releases raise a DeprecationWarning. Socketcan Quickstart @@ -53,7 +63,8 @@ existing ``can0`` interface with a bitrate of 1MB: PCAN ~~~~ -Kernels >= 3.4 supports the PCAN adapters natively via :doc:`/interfaces/socketcan`, so there is no need to install any drivers. The CAN interface can be brought like so: +Kernels >= 3.4 supports the PCAN adapters natively via :doc:`/interfaces/socketcan`, +so there is no need to install any drivers. The CAN interface can be brought like so: :: @@ -61,12 +72,22 @@ Kernels >= 3.4 supports the PCAN adapters natively via :doc:`/interfaces/socketc sudo modprobe peak_pci sudo ip link set can0 up type can bitrate 500000 +Intrepid +~~~~~~~~ + +The Intrepid Control Systems, Inc provides several devices (e.g. ValueCAN) as well +as Linux module and user-space daemon to make it possible to use them via SocketCAN. + +Refer to below repositories for installation instructions: + +- `Intrepid kernel module`_ +- `Intrepid user-space daemon`_ + 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`: +The `can-utils`_ library for Linux includes a `cansend` tool which is useful to +send known payloads. For example to send a message on `vcan0`: .. code-block:: bash @@ -138,7 +159,7 @@ To spam a bus: def producer(id): """:param id: Spam the bus with messages including the data id.""" - bus = can.interface.Bus(channel=channel, bustype=bustype) + bus = can.Bus(channel=channel, interface=bustype) for i in range(10): msg = can.Message(arbitration_id=0xc0ffee, data=[id, i, 0, 1, 3, 1, 4, 1], is_extended_id=False) bus.send(msg) @@ -170,8 +191,7 @@ function: import can - can_interface = 'vcan0' - bus = can.interface.Bus(can_interface, bustype='socketcan') + bus = can.Bus(channel='vcan0', interface='socketcan') message = bus.recv() By default, this performs a blocking read, which means ``bus.recv()`` won't @@ -204,30 +224,39 @@ socket api. This allows the cyclic transmission of CAN messages at given interva The overhead for periodic message sending is extremely low as all the heavy lifting occurs within the linux kernel. -send_periodic() -~~~~~~~~~~~~~~~ +The :class:`~can.BusABC` initialized for `socketcan` interface transparently handles +scheduling of CAN messages to Linux BCM via :meth:`~can.BusABC.send_periodic`: -An example that uses the send_periodic is included in ``python-can/examples/cyclic.py`` +.. code-block:: python -The object returned can be used to halt, alter or cancel the periodic message task. + with can.interface.Bus(interface="socketcan", channel="can0") as bus: + task = bus.send_periodic(...) -.. autoclass:: can.interfaces.socketcan.CyclicSendTask +More examples that uses :meth:`~can.BusABC.send_periodic` are included +in ``python-can/examples/cyclic.py``. + +The `task` object returned by :meth:`~can.BusABC.send_periodic` can be used to halt, +alter or cancel the periodic message task: +.. autoclass:: can.interfaces.socketcan.CyclicSendTask + :members: Bus --- -.. autoclass:: can.interfaces.socketcan.SocketcanBus +The :class:`~can.interfaces.socketcan.SocketcanBus` specializes :class:`~can.BusABC` +to ensure usage of SocketCAN Linux API. The most important differences are: - .. method:: recv(timeout=None) +- usage of SocketCAN BCM for periodic messages scheduling; +- filtering of CAN messages on Linux kernel level. - Block waiting for a message from the Bus. +.. autoclass:: can.interfaces.socketcan.SocketcanBus + :members: + :inherited-members: - :param float timeout: - seconds to wait for a message or None to wait indefinitely +.. External references - :rtype: can.Message or None - :return: - None on timeout or a :class:`can.Message` object. - :raises can.CanError: - if an error occurred while reading +.. _SocketCAN: https://www.kernel.org/doc/Documentation/networking/can.txt +.. _Intrepid kernel module: https://github.com/intrepidcs/intrepid-socketcan-kernel-module +.. _Intrepid user-space daemon: https://github.com/intrepidcs/icsscand +.. _can-utils: https://github.com/linux-can/can-utils diff --git a/test/test_cyclic_socketcan.py b/test/test_cyclic_socketcan.py index bb5411be9..40c7af582 100644 --- a/test/test_cyclic_socketcan.py +++ b/test/test_cyclic_socketcan.py @@ -244,7 +244,27 @@ def test_cyclic_initializer_different_arbitration_ids(self): with self.assertRaises(ValueError): task = self._send_bus.send_periodic(messages, self.PERIOD) - def test_create_same_id_raises_exception(self): + def test_start_already_started_task(self): + messages_a = can.Message( + arbitration_id=0x401, + data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], + is_extended_id=False, + ) + + task_a = self._send_bus.send_periodic(messages_a, self.PERIOD) + time.sleep(0.1) + + # Try to start it again, task_id is not incremented in this case + with self.assertRaises(ValueError) as ctx: + task_a.start() + self.assertEqual( + "A periodic task for Task ID 1 is already in progress by SocketCAN Linux layer", + str(ctx.exception), + ) + + task_a.stop() + + def test_create_same_id(self): messages_a = can.Message( arbitration_id=0x401, data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11], @@ -257,13 +277,41 @@ def test_create_same_id_raises_exception(self): is_extended_id=False, ) - task_a = self._send_bus.send_periodic(messages_a, 1) + task_a = self._send_bus.send_periodic(messages_a, self.PERIOD) self.assertIsInstance(task_a, can.broadcastmanager.CyclicSendTaskABC) + task_b = self._send_bus.send_periodic(messages_b, self.PERIOD) + self.assertIsInstance(task_b, can.broadcastmanager.CyclicSendTaskABC) - # The second one raises a ValueError when we attempt to create a new - # Task, since it has the same arbitration ID. - with self.assertRaises(ValueError): - task_b = self._send_bus.send_periodic(messages_b, 1) + time.sleep(self.PERIOD * 4) + + task_a.stop() + task_b.stop() + + msgs = [] + for _ in range(4): + msg = self._recv_bus.recv(self.PERIOD * 2) + self.assertIsNotNone(msg) + + msgs.append(msg) + + self.assertTrue(len(msgs) >= 4) + + # Both messages should be recevied on the bus, + # even with the same arbitration id + msg_a_data_present = msg_b_data_present = False + for rx_message in msgs: + self.assertTrue( + rx_message.arbitration_id + == messages_a.arbitration_id + == messages_b.arbitration_id + ) + if rx_message.data == messages_a.data: + msg_a_data_present = True + if rx_message.data == messages_b.data: + msg_b_data_present = True + + self.assertTrue(msg_a_data_present) + self.assertTrue(msg_b_data_present) def test_modify_data_list(self): messages_odd = [] From 56c410cda07fa62d8037da857a9b3c1446a0a638 Mon Sep 17 00:00:00 2001 From: Joey5337 <63902653+Joey5337@users.noreply.github.com> Date: Sun, 19 Apr 2020 04:44:44 +0200 Subject: [PATCH 240/252] Update api.rst (#812) Clarification for Notifier docs --- doc/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api.rst b/doc/api.rst index 193d1c707..b3191cad2 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -33,7 +33,7 @@ Utilities Notifier -------- -The Notifier object is used as a message distributor for a bus. +The Notifier object is used as a message distributor for a bus. Notifier creates a thread to read messages from the bus and distributes them to listeners. .. autoclass:: can.Notifier :members: From 0ee67040673e64d7d5d1d210af7012d41ef0a3d3 Mon Sep 17 00:00:00 2001 From: karl ding Date: Sat, 18 Apr 2020 19:56:59 -0700 Subject: [PATCH 241/252] Add typing annotations for Virtual interface (#813) This works towards PEP 561 compatibility. --- .travis.yml | 1 + can/bus.py | 4 ++-- can/interfaces/virtual.py | 31 ++++++++++++++++++++++--------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7b299feb7..76195ce51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -105,6 +105,7 @@ jobs: can/bus.py can/interface.py can/interfaces/socketcan/**.py + can/interfaces/virtual.py can/listener.py can/logger.py can/message.py diff --git a/can/bus.py b/can/bus.py index 4ef5b5fd6..e315ee9d5 100644 --- a/can/bus.py +++ b/can/bus.py @@ -2,7 +2,7 @@ Contains the ABC bus implementation and its documentation. """ -from typing import cast, Iterator, List, Optional, Sequence, Tuple, Union +from typing import cast, Any, Iterator, List, Optional, Sequence, Tuple, Union import can.typechecking @@ -43,7 +43,7 @@ class BusABC(metaclass=ABCMeta): @abstractmethod def __init__( self, - channel: can.typechecking.Channel, + channel: Any, can_filters: Optional[can.typechecking.CanFilters] = None, **kwargs: object ): diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index e84fa5853..937084095 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -5,6 +5,8 @@ Any VirtualBus instances connecting to the same channel and reside in the same process will receive the same messages. """ +from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING +from can import typechecking from copy import deepcopy import logging @@ -13,14 +15,19 @@ from threading import RLock from random import randint -from can.bus import BusABC from can import CanError +from can.bus import BusABC +from can.message import Message logger = logging.getLogger(__name__) # Channels are lists of queues, one for each connection -channels = {} +if TYPE_CHECKING: + # https://mypy.readthedocs.io/en/stable/common_issues.html#using-classes-that-are-generic-in-stubs-but-not-at-runtime + channels: Dict[Optional[Any], List[queue.Queue[Message]]] = {} +else: + channels = {} channels_lock = RLock() @@ -43,8 +50,12 @@ class VirtualBus(BusABC): """ def __init__( - self, channel=None, receive_own_messages=False, rx_queue_size=0, **kwargs - ): + self, + channel: Any = None, + receive_own_messages: bool = False, + rx_queue_size: int = 0, + **kwargs: Any + ) -> None: super().__init__( channel=channel, receive_own_messages=receive_own_messages, **kwargs ) @@ -62,10 +73,10 @@ def __init__( channels[self.channel_id] = [] self.channel = channels[self.channel_id] - self.queue = queue.Queue(rx_queue_size) + self.queue: queue.Queue[Message] = queue.Queue(rx_queue_size) self.channel.append(self.queue) - def _check_if_open(self): + def _check_if_open(self) -> None: """Raises CanError if the bus is not open. Has to be called in every method that accesses the bus. @@ -73,7 +84,9 @@ def _check_if_open(self): if not self._open: raise CanError("Operation on closed bus") - def _recv_internal(self, timeout): + def _recv_internal( + self, timeout: Optional[float] + ) -> Tuple[Optional[Message], bool]: self._check_if_open() try: msg = self.queue.get(block=True, timeout=timeout) @@ -82,7 +95,7 @@ def _recv_internal(self, timeout): else: return msg, False - def send(self, msg, timeout=None): + def send(self, msg: Message, timeout: Optional[float] = None) -> None: self._check_if_open() timestamp = time.time() @@ -102,7 +115,7 @@ def send(self, msg, timeout=None): if not all_sent: raise CanError("Could not send message to one or more recipients") - def shutdown(self): + def shutdown(self) -> None: self._check_if_open() self._open = False From 7780242183302cadb15e1f8337a94303097657f6 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Sun, 19 Apr 2020 05:32:46 +0200 Subject: [PATCH 242/252] Seek for start of object instead of calculating it (#806) Fixes #803 Fixes #786 --- can/io/blf.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/can/io/blf.py b/can/io/blf.py index ca0b5bd38..064a95f7d 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -175,7 +175,7 @@ def __iter__(self): if obj_type == LOG_CONTAINER: method, uncompressed_size = LOG_CONTAINER_STRUCT.unpack_from(obj_data) - container_data = memoryview(obj_data)[LOG_CONTAINER_STRUCT.size :] + container_data = obj_data[LOG_CONTAINER_STRUCT.size :] if method == NO_COMPRESSION: data = container_data elif method == ZLIB_DEFLATE: @@ -220,6 +220,14 @@ def _parse_data(self, data): # Loop until a struct unpack raises an exception while True: self._pos = pos + # Find next object after padding (depends on object type) + try: + pos = data.index(b"LOBJ", pos, pos + 8) + except ValueError: + if pos + 8 > max_pos: + # Not enough data in container + return + raise BLFParseError("Could not find next object") header = unpack_obj_header_base(data, pos) # print(header) signature, _, header_version, obj_size, obj_type = header @@ -228,9 +236,6 @@ def _parse_data(self, data): # Calculate position of next object next_pos = pos + obj_size - if obj_type != CAN_FD_MESSAGE_64: - # Add padding bytes - next_pos += obj_size % 4 if next_pos > max_pos: # This object continues in the next container return From b8ca2126c63b34ac1125a7f78649eb05ddd9ec34 Mon Sep 17 00:00:00 2001 From: Aajn Date: Sun, 19 Apr 2020 06:58:33 +0200 Subject: [PATCH 243/252] Notifier no longer raises handled exceptions in rx_thread (#789) --- can/notifier.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/can/notifier.py b/can/notifier.py index 2b909cae7..de2894f64 100644 --- a/can/notifier.py +++ b/can/notifier.py @@ -118,9 +118,9 @@ def _rx_thread(self, bus: BusABC): self.exception = exc if self._loop is not None: self._loop.call_soon_threadsafe(self._on_error, exc) - else: - self._on_error(exc) - raise + raise + elif not self._on_error(exc): + raise def _on_message_available(self, bus: BusABC): msg = bus.recv(0) @@ -134,10 +134,15 @@ def _on_message_received(self, msg: Message): # Schedule coroutine self._loop.create_task(res) - def _on_error(self, exc: Exception): - for listener in self.listeners: - if hasattr(listener, "on_error"): - listener.on_error(exc) + def _on_error(self, exc: Exception) -> bool: + listeners_with_on_error = [ + listener for listener in self.listeners if hasattr(listener, "on_error") + ] + + for listener in listeners_with_on_error: + listener.on_error(exc) + + return bool(listeners_with_on_error) def add_listener(self, listener: Listener): """Add new Listener to the notification list. From b418245e2826e7d7aac86153a20abe132e413954 Mon Sep 17 00:00:00 2001 From: tamenol <37591107+tamenol@users.noreply.github.com> Date: Sun, 19 Apr 2020 08:21:25 +0200 Subject: [PATCH 244/252] fix winreg bug in pcan (#802) only import winreg when on Windows --- can/interfaces/pcan/basic.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/can/interfaces/pcan/basic.py b/can/interfaces/pcan/basic.py index 90599910a..94a9ec950 100644 --- a/can/interfaces/pcan/basic.py +++ b/can/interfaces/pcan/basic.py @@ -14,7 +14,9 @@ from string import * import platform import logging -import winreg + +if platform.system() == "Windows": + import winreg logger = logging.getLogger("can.pcan") From 8c1b3f3ab481ed98b9c45ebdff645023fde0a0b3 Mon Sep 17 00:00:00 2001 From: Karl Date: Sun, 19 Apr 2020 00:30:15 -0700 Subject: [PATCH 245/252] Fix Vector CANlib treatment of empty app name In Python 2, the str type was used for text and bytes, whereas in Python 3, these are separate and incompatible types. This broke instantiation of a VectorBus when the app_name parameter in __init__ was set to None. This correctly sets it to a bytes object. Fixes #796 --- can/interfaces/vector/canlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 14205327b..ac85ef4ce 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -112,7 +112,7 @@ def __init__( else: # Assume comma separated string of channels self.channels = [int(ch.strip()) for ch in channel.split(",")] - self._app_name = app_name.encode() if app_name is not None else "" + self._app_name = app_name.encode() if app_name is not None else b"" self.channel_info = "Application %s: %s" % ( app_name, ", ".join("CAN %d" % (ch + 1) for ch in self.channels), From de0613ee32be4826f833f09f79405cf651a4ec4b Mon Sep 17 00:00:00 2001 From: Bruno Kremel Date: Wed, 29 Apr 2020 17:33:14 +0200 Subject: [PATCH 246/252] Add _detect_available_configs to serial_can (#811) Co-authored-by: Felix Divo --- can/interfaces/serial/serial_can.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index d7a81c98a..63bca5477 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -21,6 +21,11 @@ ) serial = None +try: + from serial.tools import list_ports +except ImportError: + list_ports = None + class SerialBus(BusABC): """ @@ -162,3 +167,15 @@ def fileno(self): return self.ser.fileno() # Return an invalid file descriptor on Windows return -1 + + @staticmethod + def _detect_available_configs(): + channels = [] + serial_ports = [] + + if list_ports: + serial_ports = list_ports.comports() + + for port in serial_ports: + channels.append({"interface": "serial", "channel": port.device}) + return channels From 5cada6db701724899c69db4a0c3638a66832631a Mon Sep 17 00:00:00 2001 From: karl ding Date: Sat, 2 May 2020 12:57:56 -0700 Subject: [PATCH 247/252] Add typing annotations for ASC module (#818) This works towards PEP 561 compatibility. --- can/io/asc.py | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index c8a2aada3..1ad3acef6 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -6,6 +6,9 @@ - under `test/data/logfile.asc` """ +from typing import cast, Any, Generator, IO, List, Optional, Tuple, Union +from can import typechecking + from datetime import datetime import time import logging @@ -32,7 +35,11 @@ class ASCReader(BaseIOHandler): TODO: turn relative timestamps back to absolute form """ - def __init__(self, file, base="hex"): + def __init__( + self, + file: Union[typechecking.FileLike, typechecking.StringPathLike], + base: str = "hex", + ) -> None: """ :param file: a path-like object or as file-like object to read from If this is a file-like object, is has to opened in text @@ -42,10 +49,13 @@ def __init__(self, file, base="hex"): this value will be overwritten. Default "hex". """ super().__init__(file, mode="r") + + if not self.file: + raise ValueError("The given file cannot be None") self.base = base @staticmethod - def _extract_can_id(str_can_id, base): + def _extract_can_id(str_can_id: str, base: int) -> Tuple[int, bool]: if str_can_id[-1:].lower() == "x": is_extended = True can_id = int(str_can_id[0:-1], base) @@ -55,13 +65,15 @@ def _extract_can_id(str_can_id, base): return can_id, is_extended @staticmethod - def _check_base(base): + def _check_base(base: str) -> int: if base not in ["hex", "dec"]: raise ValueError('base should be either "hex" or "dec"') return BASE_DEC if base == "dec" else BASE_HEX - def __iter__(self): + def __iter__(self) -> Generator[Message, None, None]: base = self._check_base(self.base) + # This is guaranteed to not be None since we raise ValueError in __init__ + self.file = cast(IO[Any], self.file) for line in self.file: # logger.debug("ASCReader: parsing line: '%s'", line.splitlines()[0]) if line.split(" ")[0] == "base": @@ -194,7 +206,11 @@ class ASCWriter(BaseIOHandler, Listener): FORMAT_DATE = "%a %b %d %I:%M:%S.{} %p %Y" FORMAT_EVENT = "{timestamp: 9.6f} {message}\n" - def __init__(self, file, channel=1): + def __init__( + self, + file: Union[typechecking.FileLike, typechecking.StringPathLike], + channel: int = 1, + ) -> None: """ :param file: a path-like object or as file-like object to write to If this is a file-like object, is has to opened in text @@ -203,6 +219,9 @@ def __init__(self, file, channel=1): have a channel set """ super().__init__(file, mode="w") + if not self.file: + raise ValueError("The given file cannot be None") + self.channel = channel # write start of file header @@ -213,24 +232,29 @@ def __init__(self, file, channel=1): # the last part is written with the timestamp of the first message self.header_written = False - self.last_timestamp = None - self.started = None + self.last_timestamp = 0.0 + self.started = 0.0 - def stop(self): + def stop(self) -> None: + # This is guaranteed to not be None since we raise ValueError in __init__ + self.file = cast(IO[Any], self.file) if not self.file.closed: self.file.write("End TriggerBlock\n") super().stop() - def log_event(self, message, timestamp=None): + def log_event(self, message: str, timestamp: Optional[float] = None) -> None: """Add a message to the log file. - :param str message: an arbitrary message - :param float timestamp: the absolute timestamp of the event + :param message: an arbitrary message + :param timestamp: the absolute timestamp of the event """ if not message: # if empty or None logger.debug("ASCWriter: ignoring empty message") return + # This is guaranteed to not be None since we raise ValueError in __init__ + self.file = cast(IO[Any], self.file) + # this is the case for the very first message: if not self.header_written: self.last_timestamp = timestamp or 0.0 @@ -251,14 +275,14 @@ def log_event(self, message, timestamp=None): line = self.FORMAT_EVENT.format(timestamp=timestamp, message=message) self.file.write(line) - def on_message_received(self, msg): + def on_message_received(self, msg: Message) -> None: if msg.is_error_frame: self.log_event("{} ErrorFrame".format(self.channel), msg.timestamp) return if msg.is_remote_frame: dtype = "r" - data = [] + data: List[str] = [] else: dtype = "d {}".format(msg.dlc) data = ["{:02X}".format(byte) for byte in msg.data] From fa9df7bb61f202d627b34aa28589c266994398db Mon Sep 17 00:00:00 2001 From: Mikhail Kulinich Date: Tue, 5 May 2020 12:43:11 +0300 Subject: [PATCH 248/252] Add an error callback to ThreadBasedCyclicSendTask (#781) * Add an error callback to ThreadBasedCyclicSendTask * Fix formatting error reported by black * Add return value for on_error * unit tests * black * review comments Co-authored-by: Felix Divo --- can/broadcastmanager.py | 25 ++++++++++++++++++++++--- test/simplecyclic_test.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index 52f0550f2..173727789 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -5,7 +5,7 @@ :meth:`can.BusABC.send_periodic`. """ -from typing import Optional, Sequence, Tuple, Union, TYPE_CHECKING +from typing import Optional, Sequence, Tuple, Union, Callable, TYPE_CHECKING from can import typechecking @@ -198,7 +198,7 @@ def __init__( class ThreadBasedCyclicSendTask( ModifiableCyclicTaskABC, LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC ): - """Fallback cyclic send task using thread.""" + """Fallback cyclic send task using daemon thread.""" def __init__( self, @@ -207,13 +207,28 @@ def __init__( messages: Union[Sequence[Message], Message], period: float, duration: Optional[float] = None, + on_error: Optional[Callable[[Exception], bool]] = None, ): + """Transmits `messages` with a `period` seconds for `duration` seconds on a `bus`. + + The `on_error` is called if any error happens on `bus` while sending `messages`. + If `on_error` present, and returns ``False`` when invoked, thread is + stopped immediately, otherwise, thread continuiously tries to send `messages` + ignoring errors on a `bus`. Absence of `on_error` means that thread exits immediately + on error. + + :param on_error: The callable that accepts an exception if any + error happened on a `bus` while sending `messages`, + it shall return either ``True`` or ``False`` depending + on desired behaviour of `ThreadBasedCyclicSendTask`. + """ super().__init__(messages, period, duration) self.bus = bus self.send_lock = lock self.stopped = True self.thread = None self.end_time = time.perf_counter() + duration if duration else None + self.on_error = on_error if HAS_EVENTS: self.period_ms: int = int(round(period * 1000, 0)) @@ -250,7 +265,11 @@ def _run(self): self.bus.send(self.messages[msg_index]) except Exception as exc: log.exception(exc) - break + if self.on_error: + if not self.on_error(exc): + break + else: + break if self.end_time is not None and time.perf_counter() >= self.end_time: break msg_index = (msg_index + 1) % len(self.messages) diff --git a/test/simplecyclic_test.py b/test/simplecyclic_test.py index 95bbd0a99..4e41e5e03 100644 --- a/test/simplecyclic_test.py +++ b/test/simplecyclic_test.py @@ -7,6 +7,7 @@ from time import sleep import unittest +from unittest.mock import MagicMock import gc import can @@ -151,6 +152,43 @@ def test_stopping_perodic_tasks(self): bus.shutdown() + def test_thread_based_cyclic_send_task(self): + bus = can.ThreadSafeBus(bustype="virtual") + msg = can.Message( + is_extended_id=False, arbitration_id=0x123, data=[0, 1, 2, 3, 4, 5, 6, 7] + ) + + # good case, bus is up + on_error_mock = MagicMock(return_value=False) + task = can.broadcastmanager.ThreadBasedCyclicSendTask( + bus, bus._lock_send_periodic, msg, 0.1, 3, on_error_mock + ) + task.start() + sleep(1) + on_error_mock.assert_not_called() + task.stop() + bus.shutdown() + + # bus has been shutted down + on_error_mock.reset_mock() + task = can.broadcastmanager.ThreadBasedCyclicSendTask( + bus, bus._lock_send_periodic, msg, 0.1, 3, on_error_mock + ) + task.start() + sleep(1) + self.assertTrue(on_error_mock.call_count is 1) + task.stop() + + # bus is still shutted down, but on_error returns True + on_error_mock = MagicMock(return_value=True) + task = can.broadcastmanager.ThreadBasedCyclicSendTask( + bus, bus._lock_send_periodic, msg, 0.1, 3, on_error_mock + ) + task.start() + sleep(1) + self.assertTrue(on_error_mock.call_count > 1) + task.stop() + if __name__ == "__main__": unittest.main() From 397e487875eaa0f96ce77da82c324d9cbf2406d7 Mon Sep 17 00:00:00 2001 From: Syed Raza Date: Wed, 6 May 2020 11:01:04 -0400 Subject: [PATCH 249/252] Possible fix for tests sometimes failing --- test/simplecyclic_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/simplecyclic_test.py b/test/simplecyclic_test.py index 4e41e5e03..e522468a0 100644 --- a/test/simplecyclic_test.py +++ b/test/simplecyclic_test.py @@ -168,6 +168,7 @@ def test_thread_based_cyclic_send_task(self): on_error_mock.assert_not_called() task.stop() bus.shutdown() + sleep(.5) # bus has been shutted down on_error_mock.reset_mock() From ffbdab657ede8552e576198c33db6cbce09be47f Mon Sep 17 00:00:00 2001 From: Syed Raza Date: Wed, 6 May 2020 11:13:39 -0400 Subject: [PATCH 250/252] Same idea as before but faster --- test/simplecyclic_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/simplecyclic_test.py b/test/simplecyclic_test.py index e522468a0..927855867 100644 --- a/test/simplecyclic_test.py +++ b/test/simplecyclic_test.py @@ -168,10 +168,9 @@ def test_thread_based_cyclic_send_task(self): on_error_mock.assert_not_called() task.stop() bus.shutdown() - sleep(.5) # bus has been shutted down - on_error_mock.reset_mock() + on_error_mock = MagicMock(return_value=False) task = can.broadcastmanager.ThreadBasedCyclicSendTask( bus, bus._lock_send_periodic, msg, 0.1, 3, on_error_mock ) From 52a45884e8d90536dadf3238d43aad15dc3cbec4 Mon Sep 17 00:00:00 2001 From: pierreluctg Date: Tue, 18 Feb 2020 09:19:32 -0500 Subject: [PATCH 251/252] Update windows APPDATA location --- doc/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index ea56c73ee..cff496df0 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -45,7 +45,7 @@ On Windows systems the config file is searched in the following paths: #. ``~/can.conf`` #. ``can.ini`` (current working directory) -#. ``$APPDATA/can.ini`` +#. ``%APPDATA%/can.ini`` The configuration file sets the default interface and channel: From 9291c4324f61f004e37482bd6b11dc36694f88f6 Mon Sep 17 00:00:00 2001 From: pierreluctg Date: Tue, 18 Feb 2020 09:23:23 -0500 Subject: [PATCH 252/252] Update %USERPROFILE% path --- doc/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index cff496df0..9bda3030f 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -43,7 +43,7 @@ On Linux systems the config file is searched in the following paths: On Windows systems the config file is searched in the following paths: -#. ``~/can.conf`` +#. ``%USERPROFILE%/can.conf`` #. ``can.ini`` (current working directory) #. ``%APPDATA%/can.ini``