From 143f7b32faf33b1c7d1784b792ad5d11d305a1a0 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Thu, 18 Feb 2021 11:12:40 +0200 Subject: [PATCH 1/8] Add Neousys WDT_DIO CAN interface --- can/interfaces/__init__.py | 1 + can/interfaces/neousys_wdt.py | 234 ++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 can/interfaces/neousys_wdt.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index ae088c4bc..96927ce5b 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -28,6 +28,7 @@ "cantact": ("can.interfaces.cantact", "CantactBus"), "gs_usb": ("can.interfaces.gs_usb", "GsUsbBus"), "nixnet": ("can.interfaces.nixnet", "NiXNETcanBus"), + "neousys_wdt": ("can.interfaces.neousys_wdt", "NeousysWdtBus"), } BACKENDS.update( diff --git a/can/interfaces/neousys_wdt.py b/can/interfaces/neousys_wdt.py new file mode 100644 index 000000000..91d40da14 --- /dev/null +++ b/can/interfaces/neousys_wdt.py @@ -0,0 +1,234 @@ +# +# This kind of interface can be found for example on Neousys POC-551VTC +# One needs to have correct drivers and DLL (Share object for Linux) from Neousys +# +# https://www.neousys-tech.com/en/support-service/resources/category/299-poc-551vtc-driver +# +# Beware this is only tested on Linux kernel higher thant 5.3. This should be drop in +# with Windows but you have to replace with correct named DLL +# + +import warnings +from ctypes import * +import threading +import logging +import platform +import time +from can import BusABC, Message + +logger = logging.getLogger(__name__) + + +class WDT_CAN_SETUP(Structure): + _fields_ = [ + ("bitRate", c_uint), + ("recvConfig", c_uint), + ("recvId", c_uint), + ("recvMask", c_uint), + ] + + +class WDT_CAN_MSG(Structure): + _fields_ = [ + ("id", c_uint), + ("flags", c_ushort), + ("extra", c_ubyte), + ("len", c_ubyte), + ("data", c_ubyte * 8), + ] + + +# valid:2~16, sum of the Synchronization, Propagation, and Phase Buffer 1 segments, measured in time quanta. +# valid:1~8, the Phase Buffer 2 segment in time quanta. +# valid:1~4, Resynchronization Jump Width in time quanta +# valid:1~1023, CAN_CLK divider used to determine time quanta +class WDT_CAN_BITCLK(Structure): + _fields_ = [ + ("syncPropPhase1Seg", c_ushort), + ("phase2Seg", c_ushort), + ("jumpWidth", c_ushort), + ("quantumPrescaler", c_ushort), + ] + + +WDT_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(WDT_CAN_MSG), c_uint) +WDT_CAN_STATUS_CALLBACK = CFUNCTYPE(None, c_uint) + +WDT_CAN_MSG_EXTENDED_ID = 0x0004 +WDT_CAN_MSG_REMOTE_FRAME = 0x0040 +WDT_CAN_MSG_DATA_NEW = 0x0080 +WDT_CAN_MSG_DATA_LOST = 0x0100 + +WDT_CAN_MSG_USE_ID_FILTER = 0x00000008 +WDT_CAN_MSG_USE_DIR_FILTER = ( + 0x00000010 | WDT_CAN_MSG_USE_ID_FILTER +) # only accept the direction specified in the message type +WDT_CAN_MSG_USE_EXT_FILTER = ( + 0x00000020 | WDT_CAN_MSG_USE_ID_FILTER +) # filters on only extended identifiers + +WDT_CAN_STATUS_BUS_OFF = 0x00000080 +WDT_CAN_STATUS_EWARN = ( + 0x00000040 # can controller error level has reached warning level. +) +WDT_CAN_STATUS_EPASS = ( + 0x00000020 # can controller error level has reached error passive level. +) +WDT_CAN_STATUS_LEC_STUFF = 0x00000001 # a bit stuffing error has occurred. +WDT_CAN_STATUS_LEC_FORM = 0x00000002 # a formatting error has occurred. +WDT_CAN_STATUS_LEC_ACK = 0x00000003 # an acknowledge error has occurred. +WDT_CAN_STATUS_LEC_BIT1 = ( + 0x00000004 # the bus remained a bit level of 1 for longer than is allowed. +) +WDT_CAN_STATUS_LEC_BIT0 = ( + 0x00000005 # the bus remained a bit level of 0 for longer than is allowed. +) +WDT_CAN_STATUS_LEC_CRC = 0x00000006 # a crc error has occurred. +WDT_CAN_STATUS_LEC_MASK = ( + 0x00000007 # this is the mask for the can last error code (lec). +) + + +class NeousysWdtBus(BusABC): + def __init__(self, channel, device=0, bitrate=500000, **kwargs): + """ + :param channel: channel number + :param device: device number + :param bitrate: bit rate. Renamed to bitrate in next release. + """ + super(NeousysWdtBus, self).__init__(channel, **kwargs) + + self.canlib = None + + try: + if platform.system() == "Windows": + self.canlib = WinDLL("./WDT_DIO.dll") + else: + self.canlib = CDLL("libwdt_dio.so") + + logger.info("Loaded Neousys WDT_DIO Can driver") + + self.channel = channel + + self.device = device + + self.channel_info = "Neousys WDT_DIO Can: device {}, channel {}".format( + self.device, self.channel + ) + + self.lock = threading.Lock() + self.recv_msg_array = [] + + # Init with accept all and wanted bitrate + self.init_config = WDT_CAN_SETUP(bitrate, WDT_CAN_MSG_USE_ID_FILTER, 0, 0) + + # These can be needed in some old 2.x consepts not needed in 3.6 though + # self.canlib.CAN_RegisterReceived.argtypes = [c_uint, WDT_CAN_MSG_CALLBACK] + # self.canlib.CAN_RegisterReceived.restype = c_int + # self.canlib.CAN_RegisterStatus.argtypes = [c_uint, WDT_CAN_STATUS_CALLBACK] + # self.canlib.CAN_RegisterStatus.restype = c_int + + self._WDTCAN_Received = WDT_CAN_MSG_CALLBACK(self._WDTCAN_Received) + self._WDTCAN_Status = WDT_CAN_STATUS_CALLBACK(self._WDTCAN_Status) + + if self.canlib.CAN_RegisterReceived(0, self._WDTCAN_Received) == 0: + logger.error("Neousys WDT_DIO CANBus Setup receive callback") + + if self.canlib.CAN_RegisterStatus(0, self._WDTCAN_Status) == 0: + logger.error("Neousys WDT_DIO CANBus Setup status callback") + + if ( + self.canlib.CAN_Setup( + channel, byref(self.init_config), sizeof(self.init_config) + ) + == 0 + ): + logger.error("Neousys WDT_DIO CANBus Setup Error") + + if self.canlib.CAN_Start(channel) == 0: + logger.error("Neousys WDT_DIO CANBus Start Error") + + except OSError as e: + logger.info("Cannot Neousys WDT_DIO CANBus dll or share object") + + def send(self, msg, timeout=None): + """ + :param msg: message to send + :param timeout: timeout is not used here + :return: + """ + + if self.canlib is None: + logger.error("Can't send msg as Neousys WDT_DIO DLL/SO is not loaded") + else: + tx_msg = WDT_CAN_MSG( + msg.arbitration_id, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data) + ) + + if self.canlib.CAN_Send(self.channel, byref(tx_msg), sizeof(tx_msg)) == 0: + logger.error("Neousys WDT_DIO Can can't send message") + + def _recv_internal(self, timeout): + msg = None + + # If there is message waiting in array + # pass it as new message + if len(self.recv_msg_array) > 0: + self.lock.acquire() + msg = self.recv_msg_array.pop(0) + self.lock.release() + + return msg, False + + def _WDTCAN_Received(self, lpMsg, cbMsg): + """ + :param lpMsg struct CAN_MSG + :param cbMsg message number + :return: + """ + remote_frame = False + extended_frame = False + + msg_bytes = bytearray(lpMsg.contents.data) + + if lpMsg.contents.flags & WDT_CAN_MSG_REMOTE_FRAME: + remote_frame = True + + if lpMsg.contents.flags & WDT_CAN_MSG_EXTENDED_ID: + extended_frame = True + + if lpMsg.contents.flags & WDT_CAN_MSG_DATA_LOST: + logger.error("_WDTCAN_Received flag CAN_MSG_DATA_LOST") + + msg = Message( + timestamp=time.time(), + arbitration_id=lpMsg.contents.id, + is_remote_frame=remote_frame, + is_extended_id=extended_frame, + channel=self.channel, + dlc=lpMsg.contents.len, + data=msg_bytes[: lpMsg.contents.len], + ) + + # Reading happens in Callback function and + # with Python-CAN it happens polling + # so cache stuff in array to for poll + self.lock.acquire() + self.recv_msg_array.append(msg) + self.lock.release() + + def _WDTCAN_Status(status): + """ + :param status BUS Status + :return: + """ + + logger.info("_WDTCAN_Status" + str(status)) + + def shutdown(self): + if self.canlib is not None: + logger.error( + "No need Can't send msg as Neousys WDT_DIO DLL/SO is not loaded" + ) + else: + self.canlib.CAN_Stop(self.channel) From 8caaa7d2b455b70661a2d3472531a376ba425716 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Thu, 18 Feb 2021 13:12:17 +0200 Subject: [PATCH 2/8] Neousys: Rename can/interfaces/neousys_wdt.py to can/interfaces/neousys/__init__.py --- .../{neousys_wdt.py => neousys/__init__.py} | 101 ++++++++++++------ 1 file changed, 68 insertions(+), 33 deletions(-) rename can/interfaces/{neousys_wdt.py => neousys/__init__.py} (72%) diff --git a/can/interfaces/neousys_wdt.py b/can/interfaces/neousys/__init__.py similarity index 72% rename from can/interfaces/neousys_wdt.py rename to can/interfaces/neousys/__init__.py index 91d40da14..a0fca82f5 100644 --- a/can/interfaces/neousys_wdt.py +++ b/can/interfaces/neousys/__init__.py @@ -1,25 +1,52 @@ +""" Neousys WDT_DIO CAN driver """ + # # This kind of interface can be found for example on Neousys POC-551VTC # One needs to have correct drivers and DLL (Share object for Linux) from Neousys # # https://www.neousys-tech.com/en/support-service/resources/category/299-poc-551vtc-driver # -# Beware this is only tested on Linux kernel higher thant 5.3. This should be drop in +# Beware this is only tested on Linux kernel higher than v5.3. This should be drop in # with Windows but you have to replace with correct named DLL # +# pylint: disable=R0903 +# pylint: disable=R0902 +# pylint: disable=C0413 +# pylint: disable=E0202 +# pylint: disable=W0611 +# pylint: disable=R1725 + import warnings -from ctypes import * import threading import logging import platform import time + +from ctypes import ( + byref, + CFUNCTYPE, + c_ubyte, + c_uint, + c_ushort, + POINTER, + sizeof, + Structure, +) + +if platform.system() == "Windows": + from ctypes import WinDLL +else: + from ctypes import CDLL from can import BusABC, Message + logger = logging.getLogger(__name__) -class WDT_CAN_SETUP(Structure): +class NeousysWdtCanSetup(Structure): + """ C CAN Setup struct """ + _fields_ = [ ("bitRate", c_uint), ("recvConfig", c_uint), @@ -28,7 +55,9 @@ class WDT_CAN_SETUP(Structure): ] -class WDT_CAN_MSG(Structure): +class NeousysWdtCanMsg(Structure): + """ C CAN Message struct """ + _fields_ = [ ("id", c_uint), ("flags", c_ushort), @@ -38,11 +67,14 @@ class WDT_CAN_MSG(Structure): ] -# valid:2~16, sum of the Synchronization, Propagation, and Phase Buffer 1 segments, measured in time quanta. +# valid:2~16, sum of the Synchronization, Propagation, and +# Phase Buffer 1 segments, measured in time quanta. # valid:1~8, the Phase Buffer 2 segment in time quanta. # valid:1~4, Resynchronization Jump Width in time quanta # valid:1~1023, CAN_CLK divider used to determine time quanta -class WDT_CAN_BITCLK(Structure): +class NeousysWdtCanBitClk(Structure): + """ C CAN BIT Clock struct """ + _fields_ = [ ("syncPropPhase1Seg", c_ushort), ("phase2Seg", c_ushort), @@ -51,7 +83,7 @@ class WDT_CAN_BITCLK(Structure): ] -WDT_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(WDT_CAN_MSG), c_uint) +WDT_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(NeousysWdtCanMsg), c_uint) WDT_CAN_STATUS_CALLBACK = CFUNCTYPE(None, c_uint) WDT_CAN_MSG_EXTENDED_ID = 0x0004 @@ -90,6 +122,8 @@ class WDT_CAN_BITCLK(Structure): class NeousysWdtBus(BusABC): + """ Neousys WDT_DIO Canbus Class""" + def __init__(self, channel, device=0, bitrate=500000, **kwargs): """ :param channel: channel number @@ -120,7 +154,9 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): self.recv_msg_array = [] # Init with accept all and wanted bitrate - self.init_config = WDT_CAN_SETUP(bitrate, WDT_CAN_MSG_USE_ID_FILTER, 0, 0) + self.init_config = NeousysWdtCanSetup( + bitrate, WDT_CAN_MSG_USE_ID_FILTER, 0, 0 + ) # These can be needed in some old 2.x consepts not needed in 3.6 though # self.canlib.CAN_RegisterReceived.argtypes = [c_uint, WDT_CAN_MSG_CALLBACK] @@ -128,13 +164,15 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): # self.canlib.CAN_RegisterStatus.argtypes = [c_uint, WDT_CAN_STATUS_CALLBACK] # self.canlib.CAN_RegisterStatus.restype = c_int - self._WDTCAN_Received = WDT_CAN_MSG_CALLBACK(self._WDTCAN_Received) - self._WDTCAN_Status = WDT_CAN_STATUS_CALLBACK(self._WDTCAN_Status) + self._neousys_wdt_recv_cb = WDT_CAN_MSG_CALLBACK(self._neousys_wdt_recv_cb) + self._neousys_wdt_status_cb = WDT_CAN_STATUS_CALLBACK( + self._neousys_wdt_status_cb + ) - if self.canlib.CAN_RegisterReceived(0, self._WDTCAN_Received) == 0: + if self.canlib.CAN_RegisterReceived(0, self._neousys_wdt_recv_cb) == 0: logger.error("Neousys WDT_DIO CANBus Setup receive callback") - if self.canlib.CAN_RegisterStatus(0, self._WDTCAN_Status) == 0: + if self.canlib.CAN_RegisterStatus(0, self._neousys_wdt_status_cb) == 0: logger.error("Neousys WDT_DIO CANBus Setup status callback") if ( @@ -148,8 +186,10 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): if self.canlib.CAN_Start(channel) == 0: logger.error("Neousys WDT_DIO CANBus Start Error") - except OSError as e: - logger.info("Cannot Neousys WDT_DIO CANBus dll or share object") + except OSError as error: + logger.info( + "Cannot Neousys WDT_DIO CANBus dll or share object: %d", format(error) + ) def send(self, msg, timeout=None): """ @@ -161,7 +201,7 @@ def send(self, msg, timeout=None): if self.canlib is None: logger.error("Can't send msg as Neousys WDT_DIO DLL/SO is not loaded") else: - tx_msg = WDT_CAN_MSG( + tx_msg = NeousysWdtCanMsg( msg.arbitration_id, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data) ) @@ -180,34 +220,34 @@ def _recv_internal(self, timeout): return msg, False - def _WDTCAN_Received(self, lpMsg, cbMsg): + def _neousys_wdt_recv_cb(self, msg, sizeof_msg): """ - :param lpMsg struct CAN_MSG - :param cbMsg message number + :param msg struct CAN_MSG + :param sizeof_msg message number :return: """ remote_frame = False extended_frame = False - msg_bytes = bytearray(lpMsg.contents.data) + msg_bytes = bytearray(msg.contents.data) - if lpMsg.contents.flags & WDT_CAN_MSG_REMOTE_FRAME: + if msg.contents.flags & WDT_CAN_MSG_REMOTE_FRAME: remote_frame = True - if lpMsg.contents.flags & WDT_CAN_MSG_EXTENDED_ID: + if msg.contents.flags & WDT_CAN_MSG_EXTENDED_ID: extended_frame = True - if lpMsg.contents.flags & WDT_CAN_MSG_DATA_LOST: - logger.error("_WDTCAN_Received flag CAN_MSG_DATA_LOST") + if msg.contents.flags & WDT_CAN_MSG_DATA_LOST: + logger.error("_neousys_wdt_recv_cb flag CAN_MSG_DATA_LOST") msg = Message( timestamp=time.time(), - arbitration_id=lpMsg.contents.id, + arbitration_id=msg.contents.id, is_remote_frame=remote_frame, is_extended_id=extended_frame, channel=self.channel, - dlc=lpMsg.contents.len, - data=msg_bytes[: lpMsg.contents.len], + dlc=msg.contents.len, + data=msg_bytes[: msg.contents.len], ) # Reading happens in Callback function and @@ -217,18 +257,13 @@ def _WDTCAN_Received(self, lpMsg, cbMsg): self.recv_msg_array.append(msg) self.lock.release() - def _WDTCAN_Status(status): + def _neousys_wdt_status_cb(self, status): """ :param status BUS Status :return: """ - - logger.info("_WDTCAN_Status" + str(status)) + logger.info("%s _neousys_wdt_status_cb: %d", self.init_config, status) def shutdown(self): if self.canlib is not None: - logger.error( - "No need Can't send msg as Neousys WDT_DIO DLL/SO is not loaded" - ) - else: self.canlib.CAN_Stop(self.channel) From d62f2ee15364d97867da5544624ae8811649995a Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Wed, 24 Mar 2021 08:36:17 +0200 Subject: [PATCH 3/8] Neousys: reorganise stuff from init to own file and rename NeousysWdtBus to NeousysBus --- can/interfaces/__init__.py | 2 +- can/interfaces/neousys/__init__.py | 268 +--------------------------- can/interfaces/neousys/neousys.py | 269 +++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+), 268 deletions(-) create mode 100644 can/interfaces/neousys/neousys.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 96927ce5b..bfedea60a 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -28,7 +28,7 @@ "cantact": ("can.interfaces.cantact", "CantactBus"), "gs_usb": ("can.interfaces.gs_usb", "GsUsbBus"), "nixnet": ("can.interfaces.nixnet", "NiXNETcanBus"), - "neousys_wdt": ("can.interfaces.neousys_wdt", "NeousysWdtBus"), + "neousys": ("can.interfaces.neousys", "NeousysBus"), } BACKENDS.update( diff --git a/can/interfaces/neousys/__init__.py b/can/interfaces/neousys/__init__.py index a0fca82f5..c5ff79481 100644 --- a/can/interfaces/neousys/__init__.py +++ b/can/interfaces/neousys/__init__.py @@ -1,269 +1,3 @@ """ Neousys WDT_DIO CAN driver """ -# -# This kind of interface can be found for example on Neousys POC-551VTC -# One needs to have correct drivers and DLL (Share object for Linux) from Neousys -# -# https://www.neousys-tech.com/en/support-service/resources/category/299-poc-551vtc-driver -# -# Beware this is only tested on Linux kernel higher than v5.3. This should be drop in -# with Windows but you have to replace with correct named DLL -# - -# pylint: disable=R0903 -# pylint: disable=R0902 -# pylint: disable=C0413 -# pylint: disable=E0202 -# pylint: disable=W0611 -# pylint: disable=R1725 - -import warnings -import threading -import logging -import platform -import time - -from ctypes import ( - byref, - CFUNCTYPE, - c_ubyte, - c_uint, - c_ushort, - POINTER, - sizeof, - Structure, -) - -if platform.system() == "Windows": - from ctypes import WinDLL -else: - from ctypes import CDLL -from can import BusABC, Message - - -logger = logging.getLogger(__name__) - - -class NeousysWdtCanSetup(Structure): - """ C CAN Setup struct """ - - _fields_ = [ - ("bitRate", c_uint), - ("recvConfig", c_uint), - ("recvId", c_uint), - ("recvMask", c_uint), - ] - - -class NeousysWdtCanMsg(Structure): - """ C CAN Message struct """ - - _fields_ = [ - ("id", c_uint), - ("flags", c_ushort), - ("extra", c_ubyte), - ("len", c_ubyte), - ("data", c_ubyte * 8), - ] - - -# valid:2~16, sum of the Synchronization, Propagation, and -# Phase Buffer 1 segments, measured in time quanta. -# valid:1~8, the Phase Buffer 2 segment in time quanta. -# valid:1~4, Resynchronization Jump Width in time quanta -# valid:1~1023, CAN_CLK divider used to determine time quanta -class NeousysWdtCanBitClk(Structure): - """ C CAN BIT Clock struct """ - - _fields_ = [ - ("syncPropPhase1Seg", c_ushort), - ("phase2Seg", c_ushort), - ("jumpWidth", c_ushort), - ("quantumPrescaler", c_ushort), - ] - - -WDT_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(NeousysWdtCanMsg), c_uint) -WDT_CAN_STATUS_CALLBACK = CFUNCTYPE(None, c_uint) - -WDT_CAN_MSG_EXTENDED_ID = 0x0004 -WDT_CAN_MSG_REMOTE_FRAME = 0x0040 -WDT_CAN_MSG_DATA_NEW = 0x0080 -WDT_CAN_MSG_DATA_LOST = 0x0100 - -WDT_CAN_MSG_USE_ID_FILTER = 0x00000008 -WDT_CAN_MSG_USE_DIR_FILTER = ( - 0x00000010 | WDT_CAN_MSG_USE_ID_FILTER -) # only accept the direction specified in the message type -WDT_CAN_MSG_USE_EXT_FILTER = ( - 0x00000020 | WDT_CAN_MSG_USE_ID_FILTER -) # filters on only extended identifiers - -WDT_CAN_STATUS_BUS_OFF = 0x00000080 -WDT_CAN_STATUS_EWARN = ( - 0x00000040 # can controller error level has reached warning level. -) -WDT_CAN_STATUS_EPASS = ( - 0x00000020 # can controller error level has reached error passive level. -) -WDT_CAN_STATUS_LEC_STUFF = 0x00000001 # a bit stuffing error has occurred. -WDT_CAN_STATUS_LEC_FORM = 0x00000002 # a formatting error has occurred. -WDT_CAN_STATUS_LEC_ACK = 0x00000003 # an acknowledge error has occurred. -WDT_CAN_STATUS_LEC_BIT1 = ( - 0x00000004 # the bus remained a bit level of 1 for longer than is allowed. -) -WDT_CAN_STATUS_LEC_BIT0 = ( - 0x00000005 # the bus remained a bit level of 0 for longer than is allowed. -) -WDT_CAN_STATUS_LEC_CRC = 0x00000006 # a crc error has occurred. -WDT_CAN_STATUS_LEC_MASK = ( - 0x00000007 # this is the mask for the can last error code (lec). -) - - -class NeousysWdtBus(BusABC): - """ Neousys WDT_DIO Canbus Class""" - - def __init__(self, channel, device=0, bitrate=500000, **kwargs): - """ - :param channel: channel number - :param device: device number - :param bitrate: bit rate. Renamed to bitrate in next release. - """ - super(NeousysWdtBus, self).__init__(channel, **kwargs) - - self.canlib = None - - try: - if platform.system() == "Windows": - self.canlib = WinDLL("./WDT_DIO.dll") - else: - self.canlib = CDLL("libwdt_dio.so") - - logger.info("Loaded Neousys WDT_DIO Can driver") - - self.channel = channel - - self.device = device - - self.channel_info = "Neousys WDT_DIO Can: device {}, channel {}".format( - self.device, self.channel - ) - - self.lock = threading.Lock() - self.recv_msg_array = [] - - # Init with accept all and wanted bitrate - self.init_config = NeousysWdtCanSetup( - bitrate, WDT_CAN_MSG_USE_ID_FILTER, 0, 0 - ) - - # These can be needed in some old 2.x consepts not needed in 3.6 though - # self.canlib.CAN_RegisterReceived.argtypes = [c_uint, WDT_CAN_MSG_CALLBACK] - # self.canlib.CAN_RegisterReceived.restype = c_int - # self.canlib.CAN_RegisterStatus.argtypes = [c_uint, WDT_CAN_STATUS_CALLBACK] - # self.canlib.CAN_RegisterStatus.restype = c_int - - self._neousys_wdt_recv_cb = WDT_CAN_MSG_CALLBACK(self._neousys_wdt_recv_cb) - self._neousys_wdt_status_cb = WDT_CAN_STATUS_CALLBACK( - self._neousys_wdt_status_cb - ) - - if self.canlib.CAN_RegisterReceived(0, self._neousys_wdt_recv_cb) == 0: - logger.error("Neousys WDT_DIO CANBus Setup receive callback") - - if self.canlib.CAN_RegisterStatus(0, self._neousys_wdt_status_cb) == 0: - logger.error("Neousys WDT_DIO CANBus Setup status callback") - - if ( - self.canlib.CAN_Setup( - channel, byref(self.init_config), sizeof(self.init_config) - ) - == 0 - ): - logger.error("Neousys WDT_DIO CANBus Setup Error") - - if self.canlib.CAN_Start(channel) == 0: - logger.error("Neousys WDT_DIO CANBus Start Error") - - except OSError as error: - logger.info( - "Cannot Neousys WDT_DIO CANBus dll or share object: %d", format(error) - ) - - def send(self, msg, timeout=None): - """ - :param msg: message to send - :param timeout: timeout is not used here - :return: - """ - - if self.canlib is None: - logger.error("Can't send msg as Neousys WDT_DIO DLL/SO is not loaded") - else: - tx_msg = NeousysWdtCanMsg( - msg.arbitration_id, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data) - ) - - if self.canlib.CAN_Send(self.channel, byref(tx_msg), sizeof(tx_msg)) == 0: - logger.error("Neousys WDT_DIO Can can't send message") - - def _recv_internal(self, timeout): - msg = None - - # If there is message waiting in array - # pass it as new message - if len(self.recv_msg_array) > 0: - self.lock.acquire() - msg = self.recv_msg_array.pop(0) - self.lock.release() - - return msg, False - - def _neousys_wdt_recv_cb(self, msg, sizeof_msg): - """ - :param msg struct CAN_MSG - :param sizeof_msg message number - :return: - """ - remote_frame = False - extended_frame = False - - msg_bytes = bytearray(msg.contents.data) - - if msg.contents.flags & WDT_CAN_MSG_REMOTE_FRAME: - remote_frame = True - - if msg.contents.flags & WDT_CAN_MSG_EXTENDED_ID: - extended_frame = True - - if msg.contents.flags & WDT_CAN_MSG_DATA_LOST: - logger.error("_neousys_wdt_recv_cb flag CAN_MSG_DATA_LOST") - - msg = Message( - timestamp=time.time(), - arbitration_id=msg.contents.id, - is_remote_frame=remote_frame, - is_extended_id=extended_frame, - channel=self.channel, - dlc=msg.contents.len, - data=msg_bytes[: msg.contents.len], - ) - - # Reading happens in Callback function and - # with Python-CAN it happens polling - # so cache stuff in array to for poll - self.lock.acquire() - self.recv_msg_array.append(msg) - self.lock.release() - - def _neousys_wdt_status_cb(self, status): - """ - :param status BUS Status - :return: - """ - logger.info("%s _neousys_wdt_status_cb: %d", self.init_config, status) - - def shutdown(self): - if self.canlib is not None: - self.canlib.CAN_Stop(self.channel) +from can.interfaces.neousys.neousys import NeousysBus diff --git a/can/interfaces/neousys/neousys.py b/can/interfaces/neousys/neousys.py new file mode 100644 index 000000000..3f5b43008 --- /dev/null +++ b/can/interfaces/neousys/neousys.py @@ -0,0 +1,269 @@ +""" Neousys WDT_DIO CAN driver """ + +# +# This kind of interface can be found for example on Neousys POC-551VTC +# One needs to have correct drivers and DLL (Share object for Linux) from Neousys +# +# https://www.neousys-tech.com/en/support-service/resources/category/299-poc-551vtc-driver +# +# Beware this is only tested on Linux kernel higher than v5.3. This should be drop in +# with Windows but you have to replace with correct named DLL +# + +# pylint: disable=R0903 +# pylint: disable=R0902 +# pylint: disable=C0413 +# pylint: disable=E0202 +# pylint: disable=W0611 +# pylint: disable=R1725 + +import warnings +import threading +import logging +import platform +import time + +from ctypes import ( + byref, + CFUNCTYPE, + c_ubyte, + c_uint, + c_ushort, + POINTER, + sizeof, + Structure, +) + +if platform.system() == "Windows": + from ctypes import WinDLL +else: + from ctypes import CDLL +from can import BusABC, Message + + +logger = logging.getLogger(__name__) + + +class NeousysCanSetup(Structure): + """ C CAN Setup struct """ + + _fields_ = [ + ("bitRate", c_uint), + ("recvConfig", c_uint), + ("recvId", c_uint), + ("recvMask", c_uint), + ] + + +class NeousysCanMsg(Structure): + """ C CAN Message struct """ + + _fields_ = [ + ("id", c_uint), + ("flags", c_ushort), + ("extra", c_ubyte), + ("len", c_ubyte), + ("data", c_ubyte * 8), + ] + + +# valid:2~16, sum of the Synchronization, Propagation, and +# Phase Buffer 1 segments, measured in time quanta. +# valid:1~8, the Phase Buffer 2 segment in time quanta. +# valid:1~4, Resynchronization Jump Width in time quanta +# valid:1~1023, CAN_CLK divider used to determine time quanta +class NeousysCanBitClk(Structure): + """ C CAN BIT Clock struct """ + + _fields_ = [ + ("syncPropPhase1Seg", c_ushort), + ("phase2Seg", c_ushort), + ("jumpWidth", c_ushort), + ("quantumPrescaler", c_ushort), + ] + + +WDT_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(NeousysCanMsg), c_uint) +WDT_CAN_STATUS_CALLBACK = CFUNCTYPE(None, c_uint) + +WDT_CAN_MSG_EXTENDED_ID = 0x0004 +WDT_CAN_MSG_REMOTE_FRAME = 0x0040 +WDT_CAN_MSG_DATA_NEW = 0x0080 +WDT_CAN_MSG_DATA_LOST = 0x0100 + +WDT_CAN_MSG_USE_ID_FILTER = 0x00000008 +WDT_CAN_MSG_USE_DIR_FILTER = ( + 0x00000010 | WDT_CAN_MSG_USE_ID_FILTER +) # only accept the direction specified in the message type +WDT_CAN_MSG_USE_EXT_FILTER = ( + 0x00000020 | WDT_CAN_MSG_USE_ID_FILTER +) # filters on only extended identifiers + +WDT_CAN_STATUS_BUS_OFF = 0x00000080 +WDT_CAN_STATUS_EWARN = ( + 0x00000040 # can controller error level has reached warning level. +) +WDT_CAN_STATUS_EPASS = ( + 0x00000020 # can controller error level has reached error passive level. +) +WDT_CAN_STATUS_LEC_STUFF = 0x00000001 # a bit stuffing error has occurred. +WDT_CAN_STATUS_LEC_FORM = 0x00000002 # a formatting error has occurred. +WDT_CAN_STATUS_LEC_ACK = 0x00000003 # an acknowledge error has occurred. +WDT_CAN_STATUS_LEC_BIT1 = ( + 0x00000004 # the bus remained a bit level of 1 for longer than is allowed. +) +WDT_CAN_STATUS_LEC_BIT0 = ( + 0x00000005 # the bus remained a bit level of 0 for longer than is allowed. +) +WDT_CAN_STATUS_LEC_CRC = 0x00000006 # a crc error has occurred. +WDT_CAN_STATUS_LEC_MASK = ( + 0x00000007 # this is the mask for the can last error code (lec). +) + + +class NeousysBus(BusABC): + """ Neousys WDT_DIO Canbus Class""" + + def __init__(self, channel, device=0, bitrate=500000, **kwargs): + """ + :param channel: channel number + :param device: device number + :param bitrate: bit rate. Renamed to bitrate in next release. + """ + super(NeousysBus, self).__init__(channel, **kwargs) + + self.canlib = None + + try: + if platform.system() == "Windows": + self.canlib = WinDLL("./WDT_DIO.dll") + else: + self.canlib = CDLL("libwdt_dio.so") + + logger.info("Loaded Neousys WDT_DIO Can driver") + + self.channel = channel + + self.device = device + + self.channel_info = "Neousys WDT_DIO Can: device {}, channel {}".format( + self.device, self.channel + ) + + self.lock = threading.Lock() + self.recv_msg_array = [] + + # Init with accept all and wanted bitrate + self.init_config = NeousysCanSetup( + bitrate, WDT_CAN_MSG_USE_ID_FILTER, 0, 0 + ) + + # These can be needed in some old 2.x consepts not needed in 3.6 though + # self.canlib.CAN_RegisterReceived.argtypes = [c_uint, WDT_CAN_MSG_CALLBACK] + # self.canlib.CAN_RegisterReceived.restype = c_int + # self.canlib.CAN_RegisterStatus.argtypes = [c_uint, WDT_CAN_STATUS_CALLBACK] + # self.canlib.CAN_RegisterStatus.restype = c_int + + self._neousys_wdt_recv_cb = WDT_CAN_MSG_CALLBACK(self._neousys_wdt_recv_cb) + self._neousys_wdt_status_cb = WDT_CAN_STATUS_CALLBACK( + self._neousys_wdt_status_cb + ) + + if self.canlib.CAN_RegisterReceived(0, self._neousys_wdt_recv_cb) == 0: + logger.error("Neousys WDT_DIO CANBus Setup receive callback") + + if self.canlib.CAN_RegisterStatus(0, self._neousys_wdt_status_cb) == 0: + logger.error("Neousys WDT_DIO CANBus Setup status callback") + + if ( + self.canlib.CAN_Setup( + channel, byref(self.init_config), sizeof(self.init_config) + ) + == 0 + ): + logger.error("Neousys WDT_DIO CANBus Setup Error") + + if self.canlib.CAN_Start(channel) == 0: + logger.error("Neousys WDT_DIO CANBus Start Error") + + except OSError as error: + logger.info( + "Cannot Neousys WDT_DIO CANBus dll or share object: %d", format(error) + ) + + def send(self, msg, timeout=None): + """ + :param msg: message to send + :param timeout: timeout is not used here + :return: + """ + + if self.canlib is None: + logger.error("Can't send msg as Neousys WDT_DIO DLL/SO is not loaded") + else: + tx_msg = NeousysCanMsg( + msg.arbitration_id, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data) + ) + + if self.canlib.CAN_Send(self.channel, byref(tx_msg), sizeof(tx_msg)) == 0: + logger.error("Neousys WDT_DIO Can can't send message") + + def _recv_internal(self, timeout): + msg = None + + # If there is message waiting in array + # pass it as new message + if len(self.recv_msg_array) > 0: + self.lock.acquire() + msg = self.recv_msg_array.pop(0) + self.lock.release() + + return msg, False + + def _neousys_wdt_recv_cb(self, msg, sizeof_msg): + """ + :param msg struct CAN_MSG + :param sizeof_msg message number + :return: + """ + remote_frame = False + extended_frame = False + + msg_bytes = bytearray(msg.contents.data) + + if msg.contents.flags & WDT_CAN_MSG_REMOTE_FRAME: + remote_frame = True + + if msg.contents.flags & WDT_CAN_MSG_EXTENDED_ID: + extended_frame = True + + if msg.contents.flags & WDT_CAN_MSG_DATA_LOST: + logger.error("_neousys_wdt_recv_cb flag CAN_MSG_DATA_LOST") + + msg = Message( + timestamp=time.time(), + arbitration_id=msg.contents.id, + is_remote_frame=remote_frame, + is_extended_id=extended_frame, + channel=self.channel, + dlc=msg.contents.len, + data=msg_bytes[: msg.contents.len], + ) + + # Reading happens in Callback function and + # with Python-CAN it happens polling + # so cache stuff in array to for poll + self.lock.acquire() + self.recv_msg_array.append(msg) + self.lock.release() + + def _neousys_wdt_status_cb(self, status): + """ + :param status BUS Status + :return: + """ + logger.info("%s _neousys_wdt_status_cb: %d", self.init_config, status) + + def shutdown(self): + if self.canlib is not None: + self.canlib.CAN_Stop(self.channel) From 86d13ed5594f46e0e1b4b550a490ea4b871ffece Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Wed, 24 Mar 2021 09:01:53 +0200 Subject: [PATCH 4/8] Neousys: Remove threading code and start using Queue as thread safe message bug --- can/interfaces/neousys/__init__.py | 2 +- can/interfaces/neousys/neousys.py | 128 ++++++++++++++--------------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/can/interfaces/neousys/__init__.py b/can/interfaces/neousys/__init__.py index c5ff79481..3aa87332c 100644 --- a/can/interfaces/neousys/__init__.py +++ b/can/interfaces/neousys/__init__.py @@ -1,3 +1,3 @@ -""" Neousys WDT_DIO CAN driver """ +""" Neousys CAN bus driver """ from can.interfaces.neousys.neousys import NeousysBus diff --git a/can/interfaces/neousys/neousys.py b/can/interfaces/neousys/neousys.py index 3f5b43008..a7dadf34f 100644 --- a/can/interfaces/neousys/neousys.py +++ b/can/interfaces/neousys/neousys.py @@ -1,4 +1,4 @@ -""" Neousys WDT_DIO CAN driver """ +""" Neousys CAN bus driver """ # # This kind of interface can be found for example on Neousys POC-551VTC @@ -18,7 +18,7 @@ # pylint: disable=R1725 import warnings -import threading +import queue import logging import platform import time @@ -83,46 +83,46 @@ class NeousysCanBitClk(Structure): ] -WDT_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(NeousysCanMsg), c_uint) -WDT_CAN_STATUS_CALLBACK = CFUNCTYPE(None, c_uint) +NEOUSYS_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(NeousysCanMsg), c_uint) +NEOUSYS_CAN_STATUS_CALLBACK = CFUNCTYPE(None, c_uint) -WDT_CAN_MSG_EXTENDED_ID = 0x0004 -WDT_CAN_MSG_REMOTE_FRAME = 0x0040 -WDT_CAN_MSG_DATA_NEW = 0x0080 -WDT_CAN_MSG_DATA_LOST = 0x0100 +NEOUSYS_CAN_MSG_EXTENDED_ID = 0x0004 +NEOUSYS_CAN_MSG_REMOTE_FRAME = 0x0040 +NEOUSYS_CAN_MSG_DATA_NEW = 0x0080 +NEOUSYS_CAN_MSG_DATA_LOST = 0x0100 -WDT_CAN_MSG_USE_ID_FILTER = 0x00000008 -WDT_CAN_MSG_USE_DIR_FILTER = ( - 0x00000010 | WDT_CAN_MSG_USE_ID_FILTER +NEOUSYS_CAN_MSG_USE_ID_FILTER = 0x00000008 +NEOUSYS_CAN_MSG_USE_DIR_FILTER = ( + 0x00000010 | NEOUSYS_CAN_MSG_USE_ID_FILTER ) # only accept the direction specified in the message type -WDT_CAN_MSG_USE_EXT_FILTER = ( - 0x00000020 | WDT_CAN_MSG_USE_ID_FILTER +NEOUSYS_CAN_MSG_USE_EXT_FILTER = ( + 0x00000020 | NEOUSYS_CAN_MSG_USE_ID_FILTER ) # filters on only extended identifiers -WDT_CAN_STATUS_BUS_OFF = 0x00000080 -WDT_CAN_STATUS_EWARN = ( +NEOUSYS_CAN_STATUS_BUS_OFF = 0x00000080 +NEOUSYS_CAN_STATUS_EWARN = ( 0x00000040 # can controller error level has reached warning level. ) -WDT_CAN_STATUS_EPASS = ( +NEOUSYS_CAN_STATUS_EPASS = ( 0x00000020 # can controller error level has reached error passive level. ) -WDT_CAN_STATUS_LEC_STUFF = 0x00000001 # a bit stuffing error has occurred. -WDT_CAN_STATUS_LEC_FORM = 0x00000002 # a formatting error has occurred. -WDT_CAN_STATUS_LEC_ACK = 0x00000003 # an acknowledge error has occurred. -WDT_CAN_STATUS_LEC_BIT1 = ( +NEOUSYS_CAN_STATUS_LEC_STUFF = 0x00000001 # a bit stuffing error has occurred. +NEOUSYS_CAN_STATUS_LEC_FORM = 0x00000002 # a formatting error has occurred. +NEOUSYS_CAN_STATUS_LEC_ACK = 0x00000003 # an acknowledge error has occurred. +NEOUSYS_CAN_STATUS_LEC_BIT1 = ( 0x00000004 # the bus remained a bit level of 1 for longer than is allowed. ) -WDT_CAN_STATUS_LEC_BIT0 = ( +NEOUSYS_CAN_STATUS_LEC_BIT0 = ( 0x00000005 # the bus remained a bit level of 0 for longer than is allowed. ) -WDT_CAN_STATUS_LEC_CRC = 0x00000006 # a crc error has occurred. -WDT_CAN_STATUS_LEC_MASK = ( +NEOUSYS_CAN_STATUS_LEC_CRC = 0x00000006 # a crc error has occurred. +NEOUSYS_CAN_STATUS_LEC_MASK = ( 0x00000007 # this is the mask for the can last error code (lec). ) class NeousysBus(BusABC): - """ Neousys WDT_DIO Canbus Class""" + """ Neousys CAN bus Class""" def __init__(self, channel, device=0, bitrate=500000, **kwargs): """ @@ -146,34 +146,27 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): self.device = device - self.channel_info = "Neousys WDT_DIO Can: device {}, channel {}".format( + self.channel_info = "Neousys Can: device {}, channel {}".format( self.device, self.channel ) - self.lock = threading.Lock() - self.recv_msg_array = [] + self.queue = queue.Queue() # Init with accept all and wanted bitrate self.init_config = NeousysCanSetup( - bitrate, WDT_CAN_MSG_USE_ID_FILTER, 0, 0 + bitrate, NEOUSYS_CAN_MSG_USE_ID_FILTER, 0, 0 ) - # These can be needed in some old 2.x consepts not needed in 3.6 though - # self.canlib.CAN_RegisterReceived.argtypes = [c_uint, WDT_CAN_MSG_CALLBACK] - # self.canlib.CAN_RegisterReceived.restype = c_int - # self.canlib.CAN_RegisterStatus.argtypes = [c_uint, WDT_CAN_STATUS_CALLBACK] - # self.canlib.CAN_RegisterStatus.restype = c_int - - self._neousys_wdt_recv_cb = WDT_CAN_MSG_CALLBACK(self._neousys_wdt_recv_cb) - self._neousys_wdt_status_cb = WDT_CAN_STATUS_CALLBACK( - self._neousys_wdt_status_cb + self._neousys_recv_cb = NEOUSYS_CAN_MSG_CALLBACK(self._neousys_recv_cb) + self._neousys_status_cb = NEOUSYS_CAN_STATUS_CALLBACK( + self._neousys_status_cb ) - if self.canlib.CAN_RegisterReceived(0, self._neousys_wdt_recv_cb) == 0: - logger.error("Neousys WDT_DIO CANBus Setup receive callback") + if self.canlib.CAN_RegisterReceived(0, self._neousys_recv_cb) == 0: + logger.error("Neousys CAN bus Setup receive callback") - if self.canlib.CAN_RegisterStatus(0, self._neousys_wdt_status_cb) == 0: - logger.error("Neousys WDT_DIO CANBus Setup status callback") + if self.canlib.CAN_RegisterStatus(0, self._neousys_status_cb) == 0: + logger.error("Neousys CAN bus Setup status callback") if ( self.canlib.CAN_Setup( @@ -181,15 +174,13 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): ) == 0 ): - logger.error("Neousys WDT_DIO CANBus Setup Error") + logger.error("Neousys CAN bus Setup Error") if self.canlib.CAN_Start(channel) == 0: - logger.error("Neousys WDT_DIO CANBus Start Error") + logger.error("Neousys CAN bus Start Error") except OSError as error: - logger.info( - "Cannot Neousys WDT_DIO CANBus dll or share object: %d", format(error) - ) + logger.info("Cannot Neousys CAN bus dll or share object: %d", format(error)) def send(self, msg, timeout=None): """ @@ -199,28 +190,24 @@ def send(self, msg, timeout=None): """ if self.canlib is None: - logger.error("Can't send msg as Neousys WDT_DIO DLL/SO is not loaded") + logger.error("Can't send msg as Neousys DLL/SO is not loaded") else: tx_msg = NeousysCanMsg( msg.arbitration_id, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data) ) if self.canlib.CAN_Send(self.channel, byref(tx_msg), sizeof(tx_msg)) == 0: - logger.error("Neousys WDT_DIO Can can't send message") + logger.error("Neousys Can can't send message") def _recv_internal(self, timeout): msg = None - # If there is message waiting in array - # pass it as new message - if len(self.recv_msg_array) > 0: - self.lock.acquire() - msg = self.recv_msg_array.pop(0) - self.lock.release() + if not self.queue.empty(): + msg = self.queue.get() return msg, False - def _neousys_wdt_recv_cb(self, msg, sizeof_msg): + def _neousys_recv_cb(self, msg, sizeof_msg): """ :param msg struct CAN_MSG :param sizeof_msg message number @@ -231,14 +218,14 @@ def _neousys_wdt_recv_cb(self, msg, sizeof_msg): msg_bytes = bytearray(msg.contents.data) - if msg.contents.flags & WDT_CAN_MSG_REMOTE_FRAME: + if msg.contents.flags & NEOUSYS_CAN_MSG_REMOTE_FRAME: remote_frame = True - if msg.contents.flags & WDT_CAN_MSG_EXTENDED_ID: + if msg.contents.flags & NEOUSYS_CAN_MSG_EXTENDED_ID: extended_frame = True - if msg.contents.flags & WDT_CAN_MSG_DATA_LOST: - logger.error("_neousys_wdt_recv_cb flag CAN_MSG_DATA_LOST") + if msg.contents.flags & NEOUSYS_CAN_MSG_DATA_LOST: + logger.error("_neousys_recv_cb flag CAN_MSG_DATA_LOST") msg = Message( timestamp=time.time(), @@ -253,17 +240,30 @@ def _neousys_wdt_recv_cb(self, msg, sizeof_msg): # Reading happens in Callback function and # with Python-CAN it happens polling # so cache stuff in array to for poll - self.lock.acquire() - self.recv_msg_array.append(msg) - self.lock.release() + if not self.queue.full(): + self.queue.put(msg) + else: + logger.error("Neousys message Queue is full") - def _neousys_wdt_status_cb(self, status): + def _neousys_status_cb(self, status): """ :param status BUS Status :return: """ - logger.info("%s _neousys_wdt_status_cb: %d", self.init_config, status) + logger.info("%s _neousys_status_cb: %d", self.init_config, status) def shutdown(self): if self.canlib is not None: self.canlib.CAN_Stop(self.channel) + + def fileno(self): + # Return an invalid file descriptor as not used + return -1 + + @staticmethod + def _detect_available_configs(): + channels = [] + + # There is only one channel + channels.append({"interface": "neousys", "channel": 0}) + return channels From 234a510701efdc7a7037bd19fa92a2a0778d2df6 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Tue, 6 Apr 2021 12:00:04 +0300 Subject: [PATCH 5/8] Neousys: Add minimal unittest case(s) for creating, receiving, sending and shutdown --- can/interfaces/neousys/neousys.py | 45 ++++++------ test/test_neousys.py | 117 ++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 21 deletions(-) create mode 100644 test/test_neousys.py diff --git a/can/interfaces/neousys/neousys.py b/can/interfaces/neousys/neousys.py index a7dadf34f..22bb93b7b 100644 --- a/can/interfaces/neousys/neousys.py +++ b/can/interfaces/neousys/neousys.py @@ -120,6 +120,18 @@ class NeousysCanBitClk(Structure): 0x00000007 # this is the mask for the can last error code (lec). ) +NEOUSYS_CANLIB = None + +try: + if platform.system() == "Windows": + NEOUSYS_CANLIB = WinDLL("./WDT_DIO.dll") + else: + NEOUSYS_CANLIB = CDLL("libwdt_dio.so") + logger.info("Loaded Neousys WDT_DIO Can driver") +except OSError as error: + logger.info("Cannot Neousys CAN bus dll or share object: %d", format(error)) + # NEOUSYS_CANLIB = None + class NeousysBus(BusABC): """ Neousys CAN bus Class""" @@ -132,16 +144,7 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): """ super(NeousysBus, self).__init__(channel, **kwargs) - self.canlib = None - - try: - if platform.system() == "Windows": - self.canlib = WinDLL("./WDT_DIO.dll") - else: - self.canlib = CDLL("libwdt_dio.so") - - logger.info("Loaded Neousys WDT_DIO Can driver") - + if NEOUSYS_CANLIB is not None: self.channel = channel self.device = device @@ -162,26 +165,23 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): self._neousys_status_cb ) - if self.canlib.CAN_RegisterReceived(0, self._neousys_recv_cb) == 0: + if NEOUSYS_CANLIB.CAN_RegisterReceived(0, self._neousys_recv_cb) == 0: logger.error("Neousys CAN bus Setup receive callback") - if self.canlib.CAN_RegisterStatus(0, self._neousys_status_cb) == 0: + if NEOUSYS_CANLIB.CAN_RegisterStatus(0, self._neousys_status_cb) == 0: logger.error("Neousys CAN bus Setup status callback") if ( - self.canlib.CAN_Setup( + NEOUSYS_CANLIB.CAN_Setup( channel, byref(self.init_config), sizeof(self.init_config) ) == 0 ): logger.error("Neousys CAN bus Setup Error") - if self.canlib.CAN_Start(channel) == 0: + if NEOUSYS_CANLIB.CAN_Start(channel) == 0: logger.error("Neousys CAN bus Start Error") - except OSError as error: - logger.info("Cannot Neousys CAN bus dll or share object: %d", format(error)) - def send(self, msg, timeout=None): """ :param msg: message to send @@ -189,14 +189,17 @@ def send(self, msg, timeout=None): :return: """ - if self.canlib is None: + if NEOUSYS_CANLIB is None: logger.error("Can't send msg as Neousys DLL/SO is not loaded") else: tx_msg = NeousysCanMsg( msg.arbitration_id, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data) ) - if self.canlib.CAN_Send(self.channel, byref(tx_msg), sizeof(tx_msg)) == 0: + if ( + NEOUSYS_CANLIB.CAN_Send(self.channel, byref(tx_msg), sizeof(tx_msg)) + == 0 + ): logger.error("Neousys Can can't send message") def _recv_internal(self, timeout): @@ -253,8 +256,8 @@ def _neousys_status_cb(self, status): logger.info("%s _neousys_status_cb: %d", self.init_config, status) def shutdown(self): - if self.canlib is not None: - self.canlib.CAN_Stop(self.channel) + if NEOUSYS_CANLIB is not None: + NEOUSYS_CANLIB.CAN_Stop(self.channel) def fileno(self): # Return an invalid file descriptor as not used diff --git a/test/test_neousys.py b/test/test_neousys.py new file mode 100644 index 000000000..ca1a646b8 --- /dev/null +++ b/test/test_neousys.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +Test for Neousys Interface +""" + +import ctypes +import os +import pickle +import unittest +from unittest.mock import Mock + +from ctypes import ( + byref, + cast, + POINTER, + sizeof, + c_ubyte, +) + +import pytest + +import can +from can.interfaces.neousys import neousys + + +class TestNeousysBus(unittest.TestCase): + def setUp(self) -> None: + can.interfaces.neousys.neousys.NEOUSYS_CANLIB = Mock() + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_RegisterReceived = Mock( + return_value=1 + ) + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_RegisterStatus = Mock( + return_value=1 + ) + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_Setup = Mock(return_value=1) + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_Start = Mock(return_value=1) + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_Send = Mock(return_value=1) + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_Stop = Mock(return_value=1) + self.bus = can.Bus(channel=0, bustype="neousys", _testing=True) + + def tearDown(self) -> None: + if self.bus: + self.bus.shutdown() + self.bus = None + + def test_bus_creation(self) -> None: + self.assertIsInstance(self.bus, neousys.NeousysBus) + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_Setup.called) + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_Start.called) + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_RegisterReceived.called) + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_RegisterStatus.called) + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_Send.not_called) + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_Stop.not_called) + + CAN_Start_args = ( + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_Setup.call_args[0] + ) + + # sizeof struct should be 16 + self.assertEqual(CAN_Start_args[0], 0) + self.assertEqual(CAN_Start_args[2], 16) + NeousysCanSetup_struct = cast( + CAN_Start_args[1], POINTER(neousys.NeousysCanSetup) + ) + self.assertEqual(NeousysCanSetup_struct.contents.bitRate, 500000) + self.assertEqual( + NeousysCanSetup_struct.contents.recvConfig, + neousys.NEOUSYS_CAN_MSG_USE_ID_FILTER, + ) + + def test_bus_creation_bitrate(self) -> None: + self.bus = can.Bus(channel=0, bustype="neousys", bitrate=200000, _testing=True) + self.assertIsInstance(self.bus, neousys.NeousysBus) + CAN_Start_args = ( + can.interfaces.neousys.neousys.NEOUSYS_CANLIB.CAN_Setup.call_args[0] + ) + + # sizeof struct should be 16 + self.assertEqual(CAN_Start_args[0], 0) + self.assertEqual(CAN_Start_args[2], 16) + NeousysCanSetup_struct = cast( + CAN_Start_args[1], POINTER(neousys.NeousysCanSetup) + ) + self.assertEqual(NeousysCanSetup_struct.contents.bitRate, 200000) + self.assertEqual( + NeousysCanSetup_struct.contents.recvConfig, + neousys.NEOUSYS_CAN_MSG_USE_ID_FILTER, + ) + + def test_receive(self) -> None: + recv_msg = self.bus.recv(timeout=0.05) + self.assertEqual(recv_msg, None) + msg_data = [0x01, 0x02, 0x03, 0x04, 0x05] + NeousysCanMsg_msg = neousys.NeousysCanMsg( + 0x01, 0x00, 0x00, 0x05, (c_ubyte * 8)(*msg_data) + ) + self.bus._neousys_recv_cb(byref(NeousysCanMsg_msg), sizeof(NeousysCanMsg_msg)) + recv_msg = self.bus.recv(timeout=0.05) + self.assertEqual(recv_msg.dlc, 5) + self.assertSequenceEqual(recv_msg.data, msg_data) + + def test_send(self) -> None: + msg = can.Message( + arbitration_id=0x01, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=False + ) + self.bus.send(msg) + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_Send.called) + + def test_shutdown(self) -> None: + self.bus.shutdown() + self.assertTrue(neousys.NEOUSYS_CANLIB.CAN_Stop.called) + + +if __name__ == "__main__": + unittest.main() From e5caac039055b0cb85e2fd93e6f6889e1b1d883a Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Thu, 15 Apr 2021 16:34:52 +1200 Subject: [PATCH 6/8] Update test_neousys.py --- test/test_neousys.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/test_neousys.py b/test/test_neousys.py index ca1a646b8..d5a59ed0e 100644 --- a/test/test_neousys.py +++ b/test/test_neousys.py @@ -1,10 +1,6 @@ #!/usr/bin/env python # coding: utf-8 -""" -Test for Neousys Interface -""" - import ctypes import os import pickle From 3378ee5d87171343f26740c8abafa6d44d99f3cc Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Sat, 17 Apr 2021 10:03:03 +0300 Subject: [PATCH 7/8] Neousys: Update Pylint disables that they are in more readable form Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com> --- can/interfaces/neousys/neousys.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/can/interfaces/neousys/neousys.py b/can/interfaces/neousys/neousys.py index 22bb93b7b..00419ac94 100644 --- a/can/interfaces/neousys/neousys.py +++ b/can/interfaces/neousys/neousys.py @@ -10,12 +10,11 @@ # with Windows but you have to replace with correct named DLL # -# pylint: disable=R0903 -# pylint: disable=R0902 -# pylint: disable=C0413 -# pylint: disable=E0202 -# pylint: disable=W0611 -# pylint: disable=R1725 +# pylint: disable=too-few-public-methods +# pylint: disable=too-many-instance-attributes +# pylint: disable=wrong-import-position +# pylint: disable=method-hidden +# pylint: disable=unused-import import warnings import queue From 4a41db7230ac731e8f1878be5721bae540840e24 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Sat, 17 Apr 2021 10:04:05 +0300 Subject: [PATCH 8/8] Neousys: Make super-method more Python 3.x Co-authored-by: Felix Divo <4403130+felixdivo@users.noreply.github.com> --- can/interfaces/neousys/neousys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/neousys/neousys.py b/can/interfaces/neousys/neousys.py index 00419ac94..a0037981b 100644 --- a/can/interfaces/neousys/neousys.py +++ b/can/interfaces/neousys/neousys.py @@ -141,7 +141,7 @@ def __init__(self, channel, device=0, bitrate=500000, **kwargs): :param device: device number :param bitrate: bit rate. Renamed to bitrate in next release. """ - super(NeousysBus, self).__init__(channel, **kwargs) + super().__init__(channel, **kwargs) if NEOUSYS_CANLIB is not None: self.channel = channel