Skip to content

Commit a59f7df

Browse files
author
luoja
committed
Added support for ZLG CAN
1 parent 9a766ce commit a59f7df

File tree

4 files changed

+842
-0
lines changed

4 files changed

+842
-0
lines changed

can/interfaces/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
# interface_name => (module, classname)
3838
BACKENDS: Dict[str, Tuple[str, str]] = {
39+
"zlg": ("can.interfaces.zlg", "ZlgBus"),
3940
"kvaser": ("can.interfaces.kvaser", "KvaserBus"),
4041
"socketcan": ("can.interfaces.socketcan", "SocketcanBus"),
4142
"serial": ("can.interfaces.serial.serial_can", "SerialBus"),

can/interfaces/zlg/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
__all__ = [
2+
"ZlgBus",
3+
]
4+
5+
from can.interfaces.zlg.zlg import *

can/interfaces/zlg/zlg.py

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
import can
2+
from . import zlgcan
3+
import logging
4+
from typing import Optional, Dict, List
5+
from can import BusABC
6+
import threading
7+
from queue import Queue, Empty
8+
from can.exceptions import (
9+
CanInitializationError,
10+
CanInterfaceNotImplementedError,
11+
CanOperationError,
12+
)
13+
14+
15+
class CanIso:
16+
CAN_ISO = "0"
17+
CAN_NO_ISO = "1"
18+
19+
20+
class CanMode:
21+
NORMAL = 0
22+
ONLY_LISTENING = 1
23+
24+
25+
class TransmitType:
26+
NORMAL = 0 # Retry when fail
27+
ONCE = 1 # NO Retry when fail
28+
RECEIVE_OWN_MESSAGES = 2 # Retry when fail
29+
ONCE_RECEIVE_OWN_MESSAGES = 3 # NO Retry when fail
30+
31+
32+
MAX_RCV_NUM = 1000
33+
34+
log = logging.getLogger("can.zlg")
35+
36+
37+
def raise_can_operation_error(ret: int, message):
38+
if ret != zlgcan.ZCAN_STATUS_OK:
39+
raise CanOperationError(message)
40+
41+
42+
class ZlgBus(BusABC):
43+
"""
44+
The CAN Bus implemented for the Kvaser interface.
45+
"""
46+
47+
def __init__(
48+
self,
49+
channel: int,
50+
dev_type: int,
51+
fd: bool = False,
52+
bitrate: int = 500000,
53+
data_bitrate: int = 2000000,
54+
receive_own_messages: bool = False,
55+
filters: Optional[Dict] = None,
56+
retry_when_send_fail: bool = False,
57+
**kwargs,
58+
):
59+
"""
60+
:param int channel:
61+
The Channel id to create this bus with.
62+
63+
:param int dev_type:
64+
The dev_type is type of Can device.
65+
:param bool fd:
66+
If CAN-FD frames should be supported.
67+
68+
:param int bitrate:
69+
Bitrate of channel in bit/s
70+
71+
:param int data_bitrate:
72+
Which bitrate to use for data phase in CAN FD.
73+
Defaults to arbitration bitrate.
74+
75+
:param bool driver_mode:
76+
Silent or normal.
77+
78+
:param bool receive_own_messages:
79+
If messages transmitted should also be received back.
80+
Only works if single_handle is also False.
81+
If you want to receive messages from other applications on the same
82+
computer, set this to True or set single_handle to True.
83+
84+
:param list filters:
85+
Filters to apply to the bus, it is dict, key include "is_extended/filter_start/filter_end".
86+
87+
:param bool retry_when_send_fail:
88+
If the packet fails to be sent, resend the packet automatically.
89+
90+
"""
91+
self.channel = channel
92+
self.receive_own_messages = receive_own_messages
93+
self.retry_when_send_fail = retry_when_send_fail
94+
self.fd = fd
95+
self.queue_recv = Queue()
96+
self.queue_send = Queue()
97+
self.zcanlib = zlgcan.ZCAN()
98+
self.device_handle = self.zcanlib.OpenDevice(
99+
dev_type, 0, 0
100+
) # 第二个参数是设备序号,默认0,只用第一个周立功
101+
if self.device_handle == zlgcan.INVALID_DEVICE_HANDLE:
102+
raise CanInitializationError(
103+
f"fail to get device handle, device type {dev_type}"
104+
) from None
105+
# set bitrate
106+
ret = self.zcanlib.ZCAN_SetValue(
107+
self.device_handle, str(channel) + "/canfd_abit_baud_rate", str(bitrate).encode("utf-8")
108+
)
109+
raise_can_operation_error(
110+
ret, f"fail to set data_bitrate {data_bitrate} for channel {channel}"
111+
)
112+
if fd:
113+
# set canfd
114+
ret = self.zcanlib.ZCAN_SetValue(
115+
self.device_handle, str(channel) + "/canfd_standard", CanIso.CAN_ISO.encode("utf-8")
116+
)
117+
raise_can_operation_error(ret, f"fail to set canfd for channel {channel}")
118+
# set data_bitrate for canfd
119+
ret = self.zcanlib.ZCAN_SetValue(
120+
self.device_handle,
121+
str(channel) + "/canfd_dbit_baud_rate",
122+
str(data_bitrate).encode("utf-8"),
123+
)
124+
raise_can_operation_error(
125+
ret, f"fail to set data_bitrate {data_bitrate} for channel {channel}"
126+
)
127+
# merge can canfd
128+
ret = self.zcanlib.ZCAN_SetValue(
129+
self.device_handle, str(channel) + "/set_device_recv_merge", "1".encode("utf-8")
130+
) # 0-单通道接收,1合并接收
131+
raise_can_operation_error(ret, f"set_device_recv_merge failed for channel {channel}!")
132+
chn_init_cfg = zlgcan.ZCAN_CHANNEL_INIT_CONFIG()
133+
chn_init_cfg.can_type = zlgcan.ZCAN_TYPE_CANFD if fd else zlgcan.ZCAN_TYPE_CAN
134+
chn_init_cfg.config.canfd.mode = CanMode.NORMAL
135+
# init can channel
136+
self.chn_handle = self.zcanlib.InitCAN(self.device_handle, channel, chn_init_cfg)
137+
if self.chn_handle == 0 or self.chn_handle is None:
138+
raise CanOperationError(f"init CAN-Channel {channel} failed!")
139+
# set filter
140+
if filters:
141+
for filter in filters:
142+
self.__set_filter(filter)
143+
self.__ack_filters()
144+
self._is_filtered = True
145+
else:
146+
self._is_filtered = False
147+
# start can channel
148+
ret = self.zcanlib.StartCAN(self.chn_handle)
149+
raise_can_operation_error(ret, f"start CAN-Channel {channel} failed!")
150+
# thread event
151+
self.event_recv_send_batch_zlg = threading.Event()
152+
# start thread for recv
153+
threading.Thread(
154+
None, target=self.__recv_send_batch_zlg, args=(self.event_recv_send_batch_zlg,)
155+
).start()
156+
super().__init__(
157+
channel=channel,
158+
**kwargs,
159+
)
160+
161+
def __recv_send_batch_zlg(self, event):
162+
while not event.is_set():
163+
# 发送
164+
from ctypes import sizeof, memset
165+
166+
send_size = self.queue_send.qsize()
167+
msgs: List[can.Message] = []
168+
for _ in range(send_size):
169+
msgs.append(self.queue_send.get())
170+
len_msgs = len(msgs)
171+
DataObj = (zlgcan.ZCANDataObj * len_msgs)()
172+
memset(DataObj, 0, sizeof(DataObj))
173+
for i in range(len_msgs):
174+
msg = msgs[i]
175+
data = self.__trans_data2zlg(msg.data, msg.dlc)
176+
DataObj[i].dataType = 1 # can报文
177+
DataObj[i].chnl = self.channel
178+
DataObj[i].zcanfddata.flag.frameType = 1 if msg.is_fd else 0 # 0-can,1-canfd
179+
DataObj[i].zcanfddata.flag.txDelay = 0 # 不添加延迟
180+
DataObj[i].zcanfddata.flag.txEchoRequest = 1 # 发送回显请求,0-不回显,1-回显
181+
if self.retry_when_send_fail:
182+
DataObj[i].zcanfddata.flag.transmitType = TransmitType.NORMAL
183+
else:
184+
DataObj[i].zcanfddata.flag.transmitType = TransmitType.ONCE
185+
DataObj[i].zcanfddata.frame.eff = (
186+
1 if msg.is_extended_id else 0
187+
) # 0-标准帧,1-扩展帧
188+
DataObj[i].zcanfddata.frame.rtr = (
189+
1 if msg.is_remote_frame else 0
190+
) # 0-数据帧,1-远程帧
191+
DataObj[i].zcanfddata.frame.can_id = msg.arbitration_id
192+
DataObj[i].zcanfddata.frame.len = msg.dlc
193+
if msg.is_fd:
194+
DataObj[i].zcanfddata.frame.brs = (
195+
1 if msg.bitrate_switch else 0
196+
) # BRS 加速标志位:0不加速,1加速
197+
for j in range(DataObj[i].zcanfddata.frame.len):
198+
DataObj[i].zcanfddata.frame.data[j] = data[j]
199+
ret = self.zcanlib.TransmitData(self.device_handle, DataObj, len_msgs)
200+
log.debug(f"Tranmit Num: {ret}.")
201+
# 接收
202+
rcv_num = self.zcanlib.GetReceiveNum(self.chn_handle, zlgcan.ZCAN_TYPE_MERGE)
203+
if rcv_num:
204+
read_cnt = MAX_RCV_NUM if rcv_num >= MAX_RCV_NUM else rcv_num
205+
msgs_zlg, read_cnt = self.zcanlib.ReceiveData(self.device_handle, read_cnt)
206+
for i in range(read_cnt):
207+
msg_zlg = msgs_zlg[i]
208+
if msg_zlg.dataType != 1: # 筛选出can报文
209+
continue
210+
msg = can.Message(
211+
is_fd=bool(msg_zlg.zcanfddata.flag.frameType),
212+
timestamp=float(msg_zlg.zcanfddata.timestamp) / 1000000,
213+
is_extended_id=bool(msg_zlg.zcanfddata.frame.eff),
214+
arbitration_id=msg_zlg.zcanfddata.frame.can_id,
215+
data=[
216+
msg_zlg.zcanfddata.frame.data[j]
217+
for j in range(msg_zlg.zcanfddata.frame.len)
218+
],
219+
dlc=msg_zlg.zcanfddata.frame.len,
220+
channel=self.channel,
221+
is_remote_frame=bool(msg_zlg.zcanfddata.frame.rtr),
222+
is_rx=False if msg_zlg.zcanfddata.flag.txEchoed else True,
223+
)
224+
self.queue_recv.put(msg)
225+
event.wait(timeout=0.005)
226+
227+
def add_filters(self, filters: Optional[Dict]):
228+
if filters is None:
229+
return
230+
for filter in filters:
231+
self.__set_filter(filter)
232+
self.__ack_filters()
233+
self._is_filtered = True
234+
235+
def __set_filter(self, filter: Dict):
236+
# 0 standard/1 extended
237+
is_extended = filter["is_extended"]
238+
filter_start = filter["filter_start"]
239+
filter_end = filter["filter_end"]
240+
ret = self.zcanlib.ZCAN_SetValue(
241+
self.device_handle, str(self.channel) + "/filter_mode", str(is_extended).encode("utf-8")
242+
) # 扩展帧滤波
243+
raise_can_operation_error(ret, f"Set CH{self.channel} filter_mode failed!")
244+
ret = self.zcanlib.ZCAN_SetValue(
245+
self.device_handle,
246+
str(self.channel) + "/filter_start",
247+
hex(filter_start).encode("utf-8"),
248+
)
249+
raise_can_operation_error(ret, f"Set CH{self.channel} filter_start failed!")
250+
ret = self.zcanlib.ZCAN_SetValue(
251+
self.device_handle, str(self.channel) + "/filter_end", hex(filter_end).encode("utf-8")
252+
)
253+
raise_can_operation_error(ret, f"Set CH{self.channel} filter_end failed!")
254+
255+
def __ack_filters(self):
256+
ret = self.zcanlib.ZCAN_SetValue(
257+
self.device_handle, str(self.channel) + "/filter_ack", "0".encode("utf-8")
258+
)
259+
raise_can_operation_error(ret, f"Set CH{self.channel} filter_ack failed!")
260+
261+
def flush_tx_buffer(self):
262+
raise CanInterfaceNotImplementedError("flush_tx_buffer is not implemented")
263+
264+
def _recv_internal(self, timeout=None):
265+
try:
266+
msg = self.queue_recv.get(block=True, timeout=timeout)
267+
except Empty:
268+
return None, self._is_filtered
269+
else:
270+
return msg, self._is_filtered
271+
272+
@staticmethod
273+
def __trans_data2zlg(data, dlc: int):
274+
if isinstance(data, int):
275+
data = data.to_bytes(length=dlc, byteorder="big")
276+
elif isinstance(data, bytearray) or isinstance(data, bytes):
277+
data = data
278+
else:
279+
data = list(data)
280+
return data
281+
282+
def send(self, msg: can.Message, timeout=None):
283+
self.queue_send.put(msg)
284+
285+
def shutdown(self):
286+
self.event_recv_send_batch_zlg.set()
287+
# Close CAN
288+
ret = self.zcanlib.ResetCAN(self.chn_handle)
289+
if ret == 1:
290+
log.debug("Close CAN successfully.")
291+
# Close Device
292+
ret = self.zcanlib.CloseDevice(self.device_handle)
293+
if ret == 1:
294+
log.debug("Close Device success! ")
295+
296+
@staticmethod
297+
def _detect_available_configs():
298+
zcanlib = zlgcan.ZCAN()
299+
handle = zcanlib.OpenDevice(zlgcan.ZCAN_USBCANFD_MINI, 0, 0)
300+
if handle == zlgcan.INVALID_DEVICE_HANDLE:
301+
return []
302+
info: str = str(zcanlib.GetDeviceInf(handle))
303+
zcanlib.CloseDevice(handle)
304+
param: Dict = {
305+
line_.split(":", 1)[0]: line_.split(":", 1)[1] for line_ in info.splitlines()
306+
}
307+
dev_type_name: str = param.get("Hardware Type", None)
308+
if "USBCAN" not in dev_type_name:
309+
return []
310+
dev_type_var_name: str = f'ZCAN_{dev_type_name.replace("-", "_")}'
311+
dev_type: int = eval(f"zlgcan.{dev_type_var_name}")
312+
chn_num = int(param.get("CAN Number", None))
313+
fd = "CANFD" in dev_type_name.upper()
314+
serial = param.get("Serial", None)
315+
return [
316+
dict(
317+
interface="zlg",
318+
dev_type_name=dev_type_name,
319+
dev_type=dev_type,
320+
channel=i,
321+
fd=fd,
322+
serial=serial,
323+
)
324+
for i in range(chn_num)
325+
]

0 commit comments

Comments
 (0)