Skip to content

Commit bdcc093

Browse files
committed
Add Neousys WDT_DIO CAN interface
1 parent 291af86 commit bdcc093

File tree

2 files changed

+230
-0
lines changed

2 files changed

+230
-0
lines changed

can/interfaces/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"cantact": ("can.interfaces.cantact", "CantactBus"),
2929
"gs_usb": ("can.interfaces.gs_usb", "GsUsbBus"),
3030
"nixnet": ("can.interfaces.nixnet", "NiXNETcanBus"),
31+
'neousys_wdt': ('can.interfaces.neousys_wdt', 'NeousysWdtBus'),
3132
}
3233

3334
BACKENDS.update(

can/interfaces/neousys_wdt.py

+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#
2+
# This kind of interface can be found for example on Neousys POC-551VTC
3+
# One needs to have correct drivers and DLL (Share object for Linux) from Neousys
4+
#
5+
# https://www.neousys-tech.com/en/support-service/resources/category/299-poc-551vtc-driver
6+
#
7+
# Beware this is only tested on Linux kernel higher thant 5.3. This should be drop in
8+
# with Windows but you have to replace with correct named DLL
9+
#
10+
11+
import warnings
12+
from ctypes import *
13+
import threading
14+
import logging
15+
import platform
16+
import time
17+
from can import BusABC, Message
18+
19+
logger = logging.getLogger(__name__)
20+
21+
22+
class WDT_CAN_SETUP(Structure):
23+
_fields_ = [
24+
("bitRate", c_uint),
25+
("recvConfig", c_uint),
26+
("recvId", c_uint),
27+
("recvMask", c_uint),
28+
]
29+
30+
31+
class WDT_CAN_MSG(Structure):
32+
_fields_ = [
33+
("id", c_uint),
34+
("flags", c_ushort),
35+
("extra", c_ubyte),
36+
("len", c_ubyte),
37+
("data", c_ubyte * 8),
38+
]
39+
40+
41+
# valid:2~16, sum of the Synchronization, Propagation, and Phase Buffer 1 segments, measured in time quanta.
42+
# valid:1~8, the Phase Buffer 2 segment in time quanta.
43+
# valid:1~4, Resynchronization Jump Width in time quanta
44+
# valid:1~1023, CAN_CLK divider used to determine time quanta
45+
class WDT_CAN_BITCLK(Structure):
46+
_fields_ = [
47+
("syncPropPhase1Seg", c_ushort),
48+
("phase2Seg", c_ushort),
49+
("jumpWidth", c_ushort),
50+
("quantumPrescaler", c_ushort),
51+
]
52+
53+
54+
WDT_CAN_MSG_CALLBACK = CFUNCTYPE(None, POINTER(WDT_CAN_MSG), c_uint)
55+
WDT_CAN_STATUS_CALLBACK = CFUNCTYPE(None, c_uint)
56+
57+
WDT_CAN_MSG_EXTENDED_ID = 0x0004
58+
WDT_CAN_MSG_REMOTE_FRAME = 0x0040
59+
WDT_CAN_MSG_DATA_NEW = 0x0080
60+
WDT_CAN_MSG_DATA_LOST = 0x0100
61+
62+
WDT_CAN_MSG_USE_ID_FILTER = 0x00000008
63+
WDT_CAN_MSG_USE_DIR_FILTER = (
64+
0x00000010 | WDT_CAN_MSG_USE_ID_FILTER
65+
) # only accept the direction specified in the message type
66+
WDT_CAN_MSG_USE_EXT_FILTER = (
67+
0x00000020 | WDT_CAN_MSG_USE_ID_FILTER
68+
) # filters on only extended identifiers
69+
70+
WDT_CAN_STATUS_BUS_OFF = 0x00000080
71+
WDT_CAN_STATUS_EWARN = (
72+
0x00000040 # can controller error level has reached warning level.
73+
)
74+
WDT_CAN_STATUS_EPASS = (
75+
0x00000020 # can controller error level has reached error passive level.
76+
)
77+
WDT_CAN_STATUS_LEC_STUFF = 0x00000001 # a bit stuffing error has occurred.
78+
WDT_CAN_STATUS_LEC_FORM = 0x00000002 # a formatting error has occurred.
79+
WDT_CAN_STATUS_LEC_ACK = 0x00000003 # an acknowledge error has occurred.
80+
WDT_CAN_STATUS_LEC_BIT1 = (
81+
0x00000004 # the bus remained a bit level of 1 for longer than is allowed.
82+
)
83+
WDT_CAN_STATUS_LEC_BIT0 = (
84+
0x00000005 # the bus remained a bit level of 0 for longer than is allowed.
85+
)
86+
WDT_CAN_STATUS_LEC_CRC = 0x00000006 # a crc error has occurred.
87+
WDT_CAN_STATUS_LEC_MASK = (
88+
0x00000007 # this is the mask for the can last error code (lec).
89+
)
90+
91+
92+
class NeousysWdtBus(BusABC):
93+
def __init__(self, channel, device=0, bitrate=500000, **kwargs):
94+
"""
95+
:param channel: channel number
96+
:param device: device number
97+
:param bitrate: bit rate. Renamed to bitrate in next release.
98+
"""
99+
super(NeousysWdtBus, self).__init__(channel, **kwargs)
100+
101+
self.canlib = None
102+
103+
try:
104+
if platform.system() == "Windows":
105+
self.canlib = WinDLL("./WDT_DIO.dll")
106+
else:
107+
self.canlib = CDLL("libwdt_dio.so")
108+
109+
logger.info("Loaded Neousys WDT_DIO Can driver")
110+
111+
self.channel = channel
112+
113+
self.device = device
114+
115+
self.channel_info = "Neousys WDT_DIO Can: device {}, channel {}".format(
116+
self.device, self.channel
117+
)
118+
119+
self.lock = threading.Lock()
120+
self.recv_msg_array = []
121+
122+
# Init with accept all and wanted bitrate
123+
self.init_config = WDT_CAN_SETUP(bitrate, WDT_CAN_MSG_USE_ID_FILTER, 0, 0)
124+
125+
# These can be needed in some old 2.x consepts not needed in 3.6 though
126+
# self.canlib.CAN_RegisterReceived.argtypes = [c_uint, WDT_CAN_MSG_CALLBACK]
127+
# self.canlib.CAN_RegisterReceived.restype = c_int
128+
# self.canlib.CAN_RegisterStatus.argtypes = [c_uint, WDT_CAN_STATUS_CALLBACK]
129+
# self.canlib.CAN_RegisterStatus.restype = c_int
130+
131+
self._WDTCAN_Received = WDT_CAN_MSG_CALLBACK(self._WDTCAN_Received)
132+
self._WDTCAN_Status = WDT_CAN_STATUS_CALLBACK(self._WDTCAN_Status)
133+
134+
if self.canlib.CAN_RegisterReceived(0, self._WDTCAN_Received) == 0:
135+
logger.error("Neousys WDT_DIO CANBus Setup receive callback")
136+
137+
if self.canlib.CAN_RegisterStatus(0, self._WDTCAN_Status) == 0:
138+
logger.error("Neousys WDT_DIO CANBus Setup status callback")
139+
140+
if (
141+
self.canlib.CAN_Setup(
142+
channel, byref(self.init_config), sizeof(self.init_config)
143+
)
144+
== 0
145+
):
146+
logger.error("Neousys WDT_DIO CANBus Setup Error")
147+
148+
if self.canlib.CAN_Start(channel) == 0:
149+
logger.error("Neousys WDT_DIO CANBus Start Error")
150+
151+
except OSError as e:
152+
logger.info("Cannot Neousys WDT_DIO CANBus dll or share object")
153+
154+
def send(self, msg, timeout=None):
155+
"""
156+
:param msg: message to send
157+
:param timeout: timeout is not used here
158+
:return:
159+
"""
160+
161+
if self.canlib is None:
162+
print("Can't send msg as Neousys WDT_DIO DLL/SO is not loaded")
163+
else:
164+
tx_msg = WDT_CAN_MSG(
165+
msg.arbitration_id, 0, 0, msg.dlc, (c_ubyte * 8)(*msg.data)
166+
)
167+
168+
if self.canlib.CAN_Send(self.channel, byref(tx_msg), sizeof(tx_msg)) == 0:
169+
logger.error("Neousys WDT_DIO Can can't send message")
170+
171+
def _recv_internal(self, timeout):
172+
msg = None
173+
174+
# If there is message waiting in array
175+
# pass it as new message
176+
if len(self.recv_msg_array) > 0:
177+
self.lock.acquire()
178+
msg = self.recv_msg_array.pop(0)
179+
self.lock.release()
180+
181+
return msg, False
182+
183+
def _WDTCAN_Received(self, lpMsg, cbMsg):
184+
"""
185+
:param lpMsg struct CAN_MSG
186+
:param cbMsg message number
187+
:return:
188+
"""
189+
remote_frame = False
190+
extended_frame = False
191+
192+
msg_bytes = bytearray(lpMsg.contents.data)
193+
194+
if lpMsg.contents.flags & WDT_CAN_MSG_REMOTE_FRAME:
195+
remote_frame = True
196+
197+
if lpMsg.contents.flags & WDT_CAN_MSG_EXTENDED_ID:
198+
extended_frame = True
199+
200+
if lpMsg.contents.flags & WDT_CAN_MSG_DATA_LOST:
201+
print("_WDTCAN_Received flag CAN_MSG_DATA_LOST")
202+
203+
msg = Message(
204+
timestamp=time.time(),
205+
arbitration_id=lpMsg.contents.id,
206+
is_remote_frame=remote_frame,
207+
is_extended_id=extended_frame,
208+
channel=self.channel,
209+
dlc=lpMsg.contents.len,
210+
data=msg_bytes[: lpMsg.contents.len],
211+
)
212+
213+
# Reading happens in Callback function and
214+
# with Python-CAN it happens polling
215+
# so cache stuff in array to for poll
216+
self.lock.acquire()
217+
self.recv_msg_array.append(msg)
218+
self.lock.release()
219+
220+
def _WDTCAN_Status(status):
221+
"""
222+
:param status BUS Status
223+
:return:
224+
"""
225+
226+
print("_WDTCAN_Status" + str(status))
227+
228+
def shutdown(self):
229+
WDTCan.CAN_Stop(self.channel)

0 commit comments

Comments
 (0)