From eb53eeed6ae2498e0167bf9b1d7b554e7caa074d Mon Sep 17 00:00:00 2001 From: Shaoyu Date: Fri, 30 Nov 2018 17:10:57 -0600 Subject: [PATCH 1/5] Added new interface: CANalystII interface --- can/interfaces/__init__.py | 3 +- can/interfaces/canalystii.py | 110 +++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 can/interfaces/canalystii.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index e02dd2fb4..1542aa2dd 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -21,7 +21,8 @@ 'virtual': ('can.interfaces.virtual', 'VirtualBus'), 'neovi': ('can.interfaces.ics_neovi', 'NeoViBus'), 'vector': ('can.interfaces.vector', 'VectorBus'), - 'slcan': ('can.interfaces.slcan', 'slcanBus') + 'slcan': ('can.interfaces.slcan', 'slcanBus'), + 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus') } BACKENDS.update({ diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py new file mode 100644 index 000000000..18fe695c0 --- /dev/null +++ b/can/interfaces/canalystii.py @@ -0,0 +1,110 @@ +from ctypes import * +import logging +import platform +from can import BusABC, Message + +logger = logging.getLogger(__name__) + + +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)] + + +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)] + + +VCI_USBCAN2 = 4 + +STATUS_OK = 0x01 +STATUS_ERR = 0x00 + +try: + if platform.system() == "Windows": + CANalystII = WinDLL("./ControlCAN.dll") + else: + CANalystII = CDLL("./libcontrolcan.so") + logger.info("Loaded CANalystII library") +except OSError as e: + CANalystII = None + logger.info("Cannot load CANalystII library") + + +class CANalystIIBus(BusABC): + def __init__(self, channel, can_filters=None): + super(CANalystIIBus, self).__init__(channel, can_filters) + + if isinstance(channel, (list, tuple)): + self.channels = channel + elif isinstance(channel, int): + self.channels = [channel] + else: + # Assume comma separated string of channels + self.channels = [int(ch.strip()) for ch in channel.split(',')] + + self.init_config = VCI_INIT_CONFIG(0, 0xFFFFFFFF, 0, 1, 0x00, 0x1C, 0) + + if CANalystII.VCI_OpenDevice(VCI_USBCAN2, 0, 0) == STATUS_ERR: + logger.error("VCI_OpenDevice Error") + + for channel in self.channels: + if CANalystII.VCI_InitCAN(VCI_USBCAN2, 0, channel, byref(self.init_config)) == STATUS_ERR: + logger.error("VCI_InitCAN Error") + self.shutdown() + return + + if CANalystII.VCI_StartCAN(VCI_USBCAN2, 0, channel) == STATUS_ERR: + logger.error("VCI_StartCAN Error") + self.shutdown() + return + + def send(self, msg, timeout=None): + """ + + :param msg: message to send + :param timeout: timeout is not used here + :return: + """ + raw_message = VCI_CAN_OBJ(msg.arbitration_id, 0, 0, 0, 0, 0, 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.") + + CANalystII.VCI_Transmit(VCI_USBCAN2, 0, channel, byref(raw_message), 1) + + def _recv_internal(self, timeout=None): + """ + + :param timeout: float in seconds + :return: + """ + raw_message = VCI_CAN_OBJ() + + timeout = -1 if timeout is None else int(timeout * 1000) + + if CANalystII.VCI_Receive(VCI_USBCAN2, 0, self.channels[0], byref(raw_message), 1, timeout) <= STATUS_ERR: + return None, False + else: + return Message(arbitration_id=raw_message.ID, channel=0, data=raw_message.Data), False + + def shutdown(self): + CANalystII.VCI_CloseDevice(VCI_USBCAN2, 0) From f2cea5039c3da79ae52cf2448e913151c7acdbdc Mon Sep 17 00:00:00 2001 From: Shaoyu Date: Mon, 17 Dec 2018 18:06:10 -0600 Subject: [PATCH 2/5] add baudrate option --- can/interfaces/canalystii.py | 65 ++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 18fe695c0..019917add 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -45,7 +45,7 @@ class VCI_CAN_OBJ(Structure): class CANalystIIBus(BusABC): - def __init__(self, channel, can_filters=None): + def __init__(self, channel, device=0, baud=10000000, Timing0=0x00, Timing1=0x14, can_filters=None): super(CANalystIIBus, self).__init__(channel, can_filters) if isinstance(channel, (list, tuple)): @@ -56,18 +56,51 @@ def __init__(self, channel, can_filters=None): # Assume comma separated string of channels self.channels = [int(ch.strip()) for ch in channel.split(',')] - self.init_config = VCI_INIT_CONFIG(0, 0xFFFFFFFF, 0, 1, 0x00, 0x1C, 0) - - if CANalystII.VCI_OpenDevice(VCI_USBCAN2, 0, 0) == STATUS_ERR: + self.device = device + + if baud == 1000000: + Timing0, Timing1 = (0x00, 0x14) + elif baud == 800000: + Timing0, Timing1 = (0x00, 0x16) + elif baud == 666000: + Timing0, Timing1 = (0x80, 0xB6) + elif baud == 500000: + Timing0, Timing1 = (0x00, 0x1C) + elif baud == 400000: + Timing0, Timing1 = (0x80, 0xFA) + elif baud == 250000: + Timing0, Timing1 = (0x01, 0x1C) + elif baud == 200000: + Timing0, Timing1 = (0x81, 0xFA) + elif baud == 125000: + Timing0, Timing1 = (0x03, 0x1C) + elif baud == 100000: + Timing0, Timing1 = (0x04, 0x1C) + elif baud == 80000: + Timing0, Timing1 = (0x83, 0xFF) + elif baud == 50000: + Timing0, Timing1 = (0x09, 0x1C) + elif baud == 40000: + Timing0, Timing1 = (0x87, 0xFF) + elif baud == 20000: + Timing0, Timing1 = (0x18, 0x1C) + elif baud == 10000: + Timing0, Timing1 = (0x31, 0x1C) + elif baud == 5000: + Timing0, Timing1 = (0xBF, 0xFF) + + self.init_config = VCI_INIT_CONFIG(0, 0xFFFFFFFF, 0, 1, Timing0, Timing1, 0) + + if CANalystII.VCI_OpenDevice(VCI_USBCAN2, self.device, 0) == STATUS_ERR: logger.error("VCI_OpenDevice Error") for channel in self.channels: - if CANalystII.VCI_InitCAN(VCI_USBCAN2, 0, 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 - if CANalystII.VCI_StartCAN(VCI_USBCAN2, 0, channel) == STATUS_ERR: + if CANalystII.VCI_StartCAN(VCI_USBCAN2, self.device, channel) == STATUS_ERR: logger.error("VCI_StartCAN Error") self.shutdown() return @@ -79,7 +112,7 @@ def send(self, msg, timeout=None): :param timeout: timeout is not used here :return: """ - raw_message = VCI_CAN_OBJ(msg.arbitration_id, 0, 0, 0, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data), (c_byte * 3)(*[0, 0, 0])) + raw_message = VCI_CAN_OBJ(msg.arbitration_id, 0, 0, 0, msg.is_remote_frame, 0, msg.dlc, (c_ubyte * 8)(*msg.data), (c_byte * 3)(*[0, 0, 0])) if msg.channel is not None: channel = msg.channel @@ -89,7 +122,7 @@ def send(self, msg, timeout=None): raise ValueError( "msg.channel must be set when using multiple channels.") - CANalystII.VCI_Transmit(VCI_USBCAN2, 0, channel, byref(raw_message), 1) + CANalystII.VCI_Transmit(VCI_USBCAN2, self.device, channel, byref(raw_message), 1) def _recv_internal(self, timeout=None): """ @@ -101,10 +134,20 @@ def _recv_internal(self, timeout=None): timeout = -1 if timeout is None else int(timeout * 1000) - if CANalystII.VCI_Receive(VCI_USBCAN2, 0, 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 Message(arbitration_id=raw_message.ID, channel=0, data=raw_message.Data), False + return ( + Message( + timestamp=raw_message.TimeStamp if raw_message.TimeFlag else 0.0, + arbitration_id=raw_message.ID, + is_remote_frame=raw_message.RemoteFlag, + channel=0, + dlc=raw_message.DataLen, + data=raw_message.Data, + ), + False, + ) def shutdown(self): - CANalystII.VCI_CloseDevice(VCI_USBCAN2, 0) + CANalystII.VCI_CloseDevice(VCI_USBCAN2, self.device) From 753bdcee75334dae4670e472f3a3b8b0d7064c1f Mon Sep 17 00:00:00 2001 From: Shaoyu Date: Tue, 18 Dec 2018 14:34:36 -0600 Subject: [PATCH 3/5] add canalysstii documentation --- can/interfaces/canalystii.py | 15 +++++++++++++++ doc/interfaces.rst | 1 + doc/interfaces/canalystii.rst | 13 +++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 doc/interfaces/canalystii.rst diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 019917add..93c95b84f 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -46,6 +46,15 @@ class VCI_CAN_OBJ(Structure): class CANalystIIBus(BusABC): def __init__(self, channel, device=0, baud=10000000, Timing0=0x00, Timing1=0x14, can_filters=None): + """ + + :param channel: channel number + :param device: device number + :param baud: baud rate + :param Timing0: customize the timing register if baudrate is not specified + :param Timing1: + :param can_filters: filters for packet + """ super(CANalystIIBus, self).__init__(channel, can_filters) if isinstance(channel, (list, tuple)): @@ -58,6 +67,8 @@ def __init__(self, channel, device=0, baud=10000000, Timing0=0x00, Timing1=0x14, self.device = device + self.channel_info = "CANalyst-II: device {}, channels {}".format(self.device, self.channels) + if baud == 1000000: Timing0, Timing1 = (0x00, 0x14) elif baud == 800000: @@ -149,5 +160,9 @@ def _recv_internal(self, timeout=None): False, ) + def flush_tx_buffer(self): + for channel in self.channels: + CANalystII.VCI_ClearBUffer(VCI_USBCAN2, self.device, channel) + def shutdown(self): CANalystII.VCI_CloseDevice(VCI_USBCAN2, self.device) diff --git a/doc/interfaces.rst b/doc/interfaces.rst index 794959ee1..ca06dd55a 100644 --- a/doc/interfaces.rst +++ b/doc/interfaces.rst @@ -23,6 +23,7 @@ The available interfaces are: interfaces/neovi interfaces/vector interfaces/virtual + interfaces/canalystii 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. diff --git a/doc/interfaces/canalystii.rst b/doc/interfaces/canalystii.rst new file mode 100644 index 000000000..1c73f4816 --- /dev/null +++ b/doc/interfaces/canalystii.rst @@ -0,0 +1,13 @@ +CANalyst-II +=========== + +CANalyst-II(+) is a USB to CAN Analyzer. The controlcan library is originally developed by `ZLG ZHIYUAN Electronics`_. + + +Bus +--- + +.. autoclass:: can.interfaces.canalystii.CANalystIIBus + + +.. _ZLG ZHIYUAN Electronics: http://www.zlg.com/can/can/product/id/42.html From 2578185b2c8f3fcb3a650f0e0111aeccf1a7a582 Mon Sep 17 00:00:00 2001 From: Shaoyu Date: Fri, 28 Dec 2018 14:18:10 -0600 Subject: [PATCH 4/5] add timing register table --- can/interfaces/canalystii.py | 61 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/can/interfaces/canalystii.py b/can/interfaces/canalystii.py index 93c95b84f..55f349a2d 100644 --- a/can/interfaces/canalystii.py +++ b/can/interfaces/canalystii.py @@ -33,6 +33,27 @@ class VCI_CAN_OBJ(Structure): STATUS_OK = 0x01 STATUS_ERR = 0x00 +TIMING_DICT = { + 5000: (0xBF, 0xFF), + 10000: (0x31, 0x1C), + 20000: (0x18, 0x1C), + 33330: (0x09, 0x6F), + 40000: (0x87, 0xFF), + 50000: (0x09, 0x1C), + 66660: (0x04, 0x6F), + 80000: (0x83, 0xFF), + 83330: (0x03, 0x6F), + 100000: (0x04, 0x1C), + 125000: (0x03, 0x1C), + 200000: (0x81, 0xFA), + 250000: (0x01, 0x1C), + 400000: (0x80, 0xFA), + 500000: (0x00, 0x1C), + 666000: (0x80, 0xB6), + 800000: (0x00, 0x16), + 1000000: (0x00, 0x14), +} + try: if platform.system() == "Windows": CANalystII = WinDLL("./ControlCAN.dll") @@ -45,7 +66,7 @@ class VCI_CAN_OBJ(Structure): class CANalystIIBus(BusABC): - def __init__(self, channel, device=0, baud=10000000, Timing0=0x00, Timing1=0x14, can_filters=None): + def __init__(self, channel, device=0, baud=None, Timing0=None, Timing1=None, can_filters=None): """ :param channel: channel number @@ -69,36 +90,14 @@ def __init__(self, channel, device=0, baud=10000000, Timing0=0x00, Timing1=0x14, self.channel_info = "CANalyst-II: device {}, channels {}".format(self.device, self.channels) - if baud == 1000000: - Timing0, Timing1 = (0x00, 0x14) - elif baud == 800000: - Timing0, Timing1 = (0x00, 0x16) - elif baud == 666000: - Timing0, Timing1 = (0x80, 0xB6) - elif baud == 500000: - Timing0, Timing1 = (0x00, 0x1C) - elif baud == 400000: - Timing0, Timing1 = (0x80, 0xFA) - elif baud == 250000: - Timing0, Timing1 = (0x01, 0x1C) - elif baud == 200000: - Timing0, Timing1 = (0x81, 0xFA) - elif baud == 125000: - Timing0, Timing1 = (0x03, 0x1C) - elif baud == 100000: - Timing0, Timing1 = (0x04, 0x1C) - elif baud == 80000: - Timing0, Timing1 = (0x83, 0xFF) - elif baud == 50000: - Timing0, Timing1 = (0x09, 0x1C) - elif baud == 40000: - Timing0, Timing1 = (0x87, 0xFF) - elif baud == 20000: - Timing0, Timing1 = (0x18, 0x1C) - elif baud == 10000: - Timing0, Timing1 = (0x31, 0x1C) - elif baud == 5000: - Timing0, Timing1 = (0xBF, 0xFF) + if baud is not None: + try: + Timing0, Timing1 = TIMING_DICT[baud] + except KeyError: + raise ValueError("Baudrate is not supported") + + if Timing0 is None or Timing1 is None: + raise ValueError("Timing registers are not set") self.init_config = VCI_INIT_CONFIG(0, 0xFFFFFFFF, 0, 1, Timing0, Timing1, 0) From 560f7a83adfff9bd2f46dd8605a53ebaf8abf24a Mon Sep 17 00:00:00 2001 From: Shaoyu Date: Fri, 28 Dec 2018 18:53:41 -0600 Subject: [PATCH 5/5] add to contributors list and history --- CONTRIBUTORS.txt | 1 + doc/history.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 0576c2b66..68d82034b 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -22,3 +22,4 @@ Boris Wenzlaff Pierre-Luc Tessier Gagné Felix Divo Kristian Sloth Lauszus +Shaoyu Meng diff --git a/doc/history.rst b/doc/history.rst index 3ffc9bb3b..54193aff2 100644 --- a/doc/history.rst +++ b/doc/history.rst @@ -44,6 +44,8 @@ and 2018. The CAN viewer terminal script was contributed by Kristian Sloth Lauszus in 2018. +The CANalyst-II interface was contributed by Shaoyu Meng in 2018. + Support for CAN within Python -----------------------------