python-can-hirain 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- can_hirain/BasicStructure.py +1607 -0
- can_hirain/TestBaseVCI.py +386 -0
- can_hirain/__init__.py +3 -0
- can_hirain/config.py +52 -0
- can_hirain/hrcan.py +581 -0
- demo/Test_Canfd.py +112 -0
- demo/Test_can.py +81 -0
- demo/Test_diag.py +87 -0
- demo/Test_env.py +2 -0
- python_can_hirain-0.1.0.dist-info/METADATA +81 -0
- python_can_hirain-0.1.0.dist-info/RECORD +15 -0
- python_can_hirain-0.1.0.dist-info/WHEEL +5 -0
- python_can_hirain-0.1.0.dist-info/entry_points.txt +2 -0
- python_can_hirain-0.1.0.dist-info/licenses/LICENSE +26 -0
- python_can_hirain-0.1.0.dist-info/top_level.txt +2 -0
can_hirain/hrcan.py
ADDED
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import can
|
|
3
|
+
from can import BusABC, Message
|
|
4
|
+
import threading
|
|
5
|
+
import queue
|
|
6
|
+
import time
|
|
7
|
+
from can.util import load_config
|
|
8
|
+
import warnings
|
|
9
|
+
from can import BusABC, interface
|
|
10
|
+
|
|
11
|
+
from . import BasicStructure
|
|
12
|
+
from .TestBaseVCI import *
|
|
13
|
+
from .config import *
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
ProjectStartTime = time.time()
|
|
17
|
+
HwStartTime = dict()
|
|
18
|
+
|
|
19
|
+
class CyclicSendTask(can.broadcastmanager.CyclicSendTaskABC):
|
|
20
|
+
def __init__(self, vci_msg, port_handle, channel_index):
|
|
21
|
+
self._vci_msg = vci_msg
|
|
22
|
+
self._port = port_handle
|
|
23
|
+
self._channel_index = channel_index
|
|
24
|
+
|
|
25
|
+
def stop(self):
|
|
26
|
+
ptxinfo = BasicStructure.CanPeriodTxInfo()
|
|
27
|
+
ptxinfo.canMsg = self._vci_msg
|
|
28
|
+
ptxinfo.usPeriod = 0
|
|
29
|
+
ret = can_set_periodic_tx_msgs(self._port, self._channel_index, ptxinfo)
|
|
30
|
+
|
|
31
|
+
def get_tosun_hw_devices_info():
|
|
32
|
+
driver_config = get_driver_config()
|
|
33
|
+
|
|
34
|
+
# 获取设备字典 {HR3230500022: [(i(数组下标), pythoncan_idx(pythoncan通道索引), channel_index(VCI通道索引))]}
|
|
35
|
+
device_dict = {}
|
|
36
|
+
pc_idx = 0
|
|
37
|
+
for i in range(driver_config.nChannelCount):
|
|
38
|
+
channel = driver_config.h3Channels[i]
|
|
39
|
+
if channel.ucBusCapabilities == 3: # CAN/CANFD
|
|
40
|
+
device_name = str(ctypes.string_at(channel.szHwName).decode('gbk'))
|
|
41
|
+
if device_name not in device_dict:
|
|
42
|
+
device_dict[device_name] = [(i, pc_idx, channel.ucChannelIndex)]
|
|
43
|
+
else:
|
|
44
|
+
device_dict[device_name].append((i, pc_idx, channel.ucChannelIndex))
|
|
45
|
+
|
|
46
|
+
if len(device_dict) == 0:
|
|
47
|
+
return {}
|
|
48
|
+
|
|
49
|
+
# 设备字典转为ToSun字典
|
|
50
|
+
device_info_list = {}
|
|
51
|
+
device_idx = 0
|
|
52
|
+
for device_name in device_dict:
|
|
53
|
+
device_info = {
|
|
54
|
+
"manufacturer": "HIRAIN",
|
|
55
|
+
"product": "TestBaseVCI",
|
|
56
|
+
"Serial": device_name,
|
|
57
|
+
"device_type": "TestBaseVCI",
|
|
58
|
+
"device_name": device_name,
|
|
59
|
+
"can_counts": len(device_dict[device_name]),
|
|
60
|
+
"is_canfd": True,
|
|
61
|
+
"lin_counts": 0, # PythonCAN库不读取无关通道
|
|
62
|
+
"fr_counts": 0,
|
|
63
|
+
"eth_counts": 0
|
|
64
|
+
}
|
|
65
|
+
device_info_list[device_idx] = device_info
|
|
66
|
+
device_idx += 1
|
|
67
|
+
return device_info_list
|
|
68
|
+
|
|
69
|
+
def get_hr_hw_devices_info():
|
|
70
|
+
driver_config = get_driver_config()
|
|
71
|
+
|
|
72
|
+
# 获取设备字典 {HR3230500022: [(i(数组下标), pythoncan_idx(pythoncan通道索引), channel_index(VCI通道索引))]}
|
|
73
|
+
device_dict = {}
|
|
74
|
+
pc_idx = 0
|
|
75
|
+
for i in range(driver_config.nChannelCount):
|
|
76
|
+
channel = driver_config.h3Channels[i]
|
|
77
|
+
if channel.ucBusCapabilities == 3: # CAN/CANFD
|
|
78
|
+
device_name = str(ctypes.string_at(channel.szHwName).decode('gbk'))
|
|
79
|
+
if device_name not in device_dict:
|
|
80
|
+
device_dict[device_name] = [(i, pc_idx, channel.ucChannelIndex)]
|
|
81
|
+
else:
|
|
82
|
+
device_dict[device_name].append((i, pc_idx, channel.ucChannelIndex))
|
|
83
|
+
pc_idx += 1
|
|
84
|
+
|
|
85
|
+
if len(device_dict) == 0:
|
|
86
|
+
return {}
|
|
87
|
+
|
|
88
|
+
device_info_list = {}
|
|
89
|
+
device_idx = 0
|
|
90
|
+
for device_name in device_dict:
|
|
91
|
+
device_info = {
|
|
92
|
+
"manufacturer": "HIRAIN",
|
|
93
|
+
"product": "TestBaseVCI",
|
|
94
|
+
"device_name": device_name,
|
|
95
|
+
"can_counts": len(device_dict[device_name]),
|
|
96
|
+
"channel": device_dict[device_name] # [(下标,pythoncan通道号,vci通道号)]
|
|
97
|
+
}
|
|
98
|
+
device_info_list[device_idx] = device_info
|
|
99
|
+
device_idx += 1
|
|
100
|
+
return device_info_list
|
|
101
|
+
|
|
102
|
+
# 获取通道字典
|
|
103
|
+
# def get_hr_can_canfd_channels():
|
|
104
|
+
# driver_config = get_driver_config()
|
|
105
|
+
# channels = {}
|
|
106
|
+
# for i in range(driver_config.nChannelCount):
|
|
107
|
+
# channel = driver_config.h3Channels[i]
|
|
108
|
+
# if channel.ucBusCapabilities == 3: # CAN/FD通道
|
|
109
|
+
# channels[("")]
|
|
110
|
+
|
|
111
|
+
class HRCANBus(BusABC):
|
|
112
|
+
def __init__(self, channel, **kwargs):
|
|
113
|
+
super().__init__(channel=channel, **kwargs)
|
|
114
|
+
self._lock = threading.Lock()
|
|
115
|
+
self.configs = kwargs.get('configs', [{}])[0]
|
|
116
|
+
self.channel = channel
|
|
117
|
+
self.ChannelIdx = channel
|
|
118
|
+
self.channel_dex = channel
|
|
119
|
+
self.canids = []
|
|
120
|
+
self.len_canids = 0
|
|
121
|
+
self.start_time = 0
|
|
122
|
+
self.series_num = None
|
|
123
|
+
self.last_msg_time = 0
|
|
124
|
+
self.time_cycle = 0
|
|
125
|
+
self.hr_filter = None
|
|
126
|
+
|
|
127
|
+
# 15765诊断第二响应
|
|
128
|
+
self._response_queue = queue.Queue()
|
|
129
|
+
self._response_event = threading.Event()
|
|
130
|
+
self._current_response_data = None
|
|
131
|
+
self._response_lock = threading.Lock()
|
|
132
|
+
|
|
133
|
+
# 支持以同星格式下发的参数
|
|
134
|
+
self.ts_rate_baudrate = kwargs.get('rate_baudrate', None)
|
|
135
|
+
self.ts_data_baudrate = kwargs.get('data_baudrate', 2000)
|
|
136
|
+
self.ts_enable_120hm = kwargs.get('enable_120hm', True)
|
|
137
|
+
self.ts_is_fd = kwargs.get('is_fd', True)
|
|
138
|
+
self.is_include_tx = kwargs.get('is_include_tx', True)
|
|
139
|
+
self.ts_hwserial = kwargs.get('hwserial', None)
|
|
140
|
+
self.ts_hwserial = self.ts_hwserial.decode('gbk') if isinstance(self.ts_hwserial, bytes) else self.ts_hwserial
|
|
141
|
+
# 设置报文过滤器
|
|
142
|
+
filters = kwargs.get('filters', None)
|
|
143
|
+
if filters and len(filters) > 0:
|
|
144
|
+
dict_filter = filters[0] # 暂时只支持设置一个过滤器
|
|
145
|
+
if dict_filter['extend'] == False:
|
|
146
|
+
self.hr_filter = (dict_filter['min'], dict_filter['max'])
|
|
147
|
+
elif dict_filter['extend'] == True:
|
|
148
|
+
self.hr_filter = (dict_filter['min'] + 0x80000000, dict_filter['max'] + 0x80000000)
|
|
149
|
+
else:
|
|
150
|
+
self.hr_filter = None
|
|
151
|
+
|
|
152
|
+
if self.ts_rate_baudrate != None and self.configs == {}: # 仅在下发同星参数名且未下发VCI参数时使用同星参数
|
|
153
|
+
self.ts_rate_baudrate = 500 if self.ts_rate_baudrate == None else self.ts_rate_baudrate
|
|
154
|
+
if self.ts_is_fd == True:
|
|
155
|
+
self.configs = {
|
|
156
|
+
'resistance': self.ts_enable_120hm, # 启用终端电阻
|
|
157
|
+
'mode': TestBaseVCI_ChlMode.CANFD, # 设置CANFD通道
|
|
158
|
+
'listenFlag' : 0, # 0-正常状态,1-通道为仅监听状态,不给ACK响应
|
|
159
|
+
'abrBaudrate': self.ts_rate_baudrate * 1000,
|
|
160
|
+
'dbrBaudrate': self.ts_data_baudrate * 1000,
|
|
161
|
+
'abrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_75,
|
|
162
|
+
'dbrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_75}
|
|
163
|
+
else:
|
|
164
|
+
self.configs = {
|
|
165
|
+
'resistance': self.ts_enable_120hm, # 启用终端电阻
|
|
166
|
+
'mode' : TestBaseVCI_ChlMode.CAN, # 设置CAN通道
|
|
167
|
+
'listenFlag' : 0, # 0-正常状态,1-通道为仅监听状态,不给ACK响应
|
|
168
|
+
'baudrate': self.ts_rate_baudrate * 1000,
|
|
169
|
+
'samplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_75}
|
|
170
|
+
# 匹配通道号
|
|
171
|
+
devices = get_hr_hw_devices_info()
|
|
172
|
+
b_find_device = False
|
|
173
|
+
for key,value in devices.items():
|
|
174
|
+
if value['device_name'] == self.ts_hwserial and self.channel < len(value['channel']):
|
|
175
|
+
self.channel = value['channel'][self.channel][1]
|
|
176
|
+
self.channel_dex = self.channel
|
|
177
|
+
b_find_device = True
|
|
178
|
+
if self.ts_hwserial != None and not b_find_device:
|
|
179
|
+
raise can.CanError(f"Failed to find device: {self.ts_hwserial}")
|
|
180
|
+
|
|
181
|
+
if self._get_param_with_warning('mode', 0) == 0:
|
|
182
|
+
self.can_init()
|
|
183
|
+
else:
|
|
184
|
+
self.canfd_init()
|
|
185
|
+
|
|
186
|
+
def can_init(self):
|
|
187
|
+
driver_config = get_driver_config()
|
|
188
|
+
resistance = self._get_param_with_warning('resistance', 1)
|
|
189
|
+
ListenFlag = self._get_param_with_warning('listenFlag', 0)
|
|
190
|
+
Baudrate = self._get_param_with_warning('baudrate', 500000)
|
|
191
|
+
SamplePoint = self._get_param_with_warning('samplePoint', 0)
|
|
192
|
+
for i in range(driver_config.nChannelCount):
|
|
193
|
+
if driver_config.h3Channels[i].ucBusCapabilities == 3:
|
|
194
|
+
if self.channel == 0:
|
|
195
|
+
self.channel_index = driver_config.h3Channels[i].ucChannelIndex
|
|
196
|
+
channel_mask = driver_config.h3Channels[i].unChannelMask
|
|
197
|
+
self.port_handle = open_port(channel_mask)
|
|
198
|
+
|
|
199
|
+
self.series_num = ctypes.string_at(driver_config.h3Channels[i].szHwName)
|
|
200
|
+
if self.series_num in HwStartTime:
|
|
201
|
+
self.start_time = HwStartTime[self.series_num]
|
|
202
|
+
else:
|
|
203
|
+
systemTime = time.time()
|
|
204
|
+
devTime = get_device_time(self.port_handle)
|
|
205
|
+
#float_var = float(c_var.value)
|
|
206
|
+
self.start_time = float(devTime.value) - (systemTime - ProjectStartTime) * 1e6
|
|
207
|
+
HwStartTime[self.series_num] = self.start_time
|
|
208
|
+
|
|
209
|
+
nRet = active_can_channel(self.port_handle, self.channel_index, ListenFlag=ListenFlag, Baudrate=Baudrate, SamplePoint=SamplePoint)
|
|
210
|
+
if nRet != 0:
|
|
211
|
+
raise can.CanError(f"Failed to actice canfd channel, code: " + str(nRet))
|
|
212
|
+
if resistance == 1:
|
|
213
|
+
nRet = can_set_resistor(self.port_handle, self.channel_index, True)
|
|
214
|
+
if nRet != 0:
|
|
215
|
+
raise can.CanError(f"Failed to set resistor, code: " + str(nRet))
|
|
216
|
+
nRet = start_stop_trace(self.port_handle, self.channel_index, True)
|
|
217
|
+
if nRet != 0:
|
|
218
|
+
raise can.CanError(f"Failed to start stop trace, code: " + str(nRet))
|
|
219
|
+
print("Device:", driver_config.h3Channels[i].szHwName.decode("utf-8"), "Channel:", self.channel_dex, "is open" )
|
|
220
|
+
self.channel_info = driver_config.h3Channels[i].szHwName.decode("utf-8") + "-Can" + str(self.channel_dex)
|
|
221
|
+
self._is_shutdown = False
|
|
222
|
+
break
|
|
223
|
+
else:
|
|
224
|
+
self.channel -= 1
|
|
225
|
+
|
|
226
|
+
def canfd_init(self):
|
|
227
|
+
driver_config = get_driver_config()
|
|
228
|
+
resistance = self._get_param_with_warning('resistance', 1)
|
|
229
|
+
ListenFlag = self._get_param_with_warning('listenFlag', 0)
|
|
230
|
+
abrBaudrate = self._get_param_with_warning('abrBaudrate', 500000)
|
|
231
|
+
dbrBaudrate = self._get_param_with_warning('dbrBaudrate', 2000000)
|
|
232
|
+
abrSamplePoint = self._get_param_with_warning('abrSamplePoint', 0)
|
|
233
|
+
dbrSamplePoint = self._get_param_with_warning('dbrSamplePoint', 0)
|
|
234
|
+
for i in range(driver_config.nChannelCount):
|
|
235
|
+
if driver_config.h3Channels[i].ucBusCapabilities == 3:
|
|
236
|
+
if self.channel == 0:
|
|
237
|
+
self.channel_index = driver_config.h3Channels[i].ucChannelIndex
|
|
238
|
+
channel_mask = driver_config.h3Channels[i].unChannelMask
|
|
239
|
+
self.port_handle = open_port(channel_mask)
|
|
240
|
+
|
|
241
|
+
self.series_num = ctypes.string_at(driver_config.h3Channels[i].szHwName)
|
|
242
|
+
if self.series_num in HwStartTime:
|
|
243
|
+
self.start_time = HwStartTime[self.series_num]
|
|
244
|
+
else:
|
|
245
|
+
systemTime = time.time()
|
|
246
|
+
devTime = get_device_time(self.port_handle)
|
|
247
|
+
# float_var = float(c_var.value)
|
|
248
|
+
self.start_time = float(devTime.value) - (systemTime - ProjectStartTime) * 1e6
|
|
249
|
+
HwStartTime[self.series_num] = self.start_time
|
|
250
|
+
|
|
251
|
+
nRet = active_canfd_channel(self.port_handle, self.channel_index, ListenFlag=ListenFlag, abrBaudrate=abrBaudrate, dbrBaudrate=dbrBaudrate, abrSamplePoint=abrSamplePoint, dbrSamplePoint=dbrSamplePoint)
|
|
252
|
+
if nRet != 0:
|
|
253
|
+
raise can.CanError(f"Failed to actice canfd channel, code: " + str(nRet))
|
|
254
|
+
if resistance == 1:
|
|
255
|
+
nRet = can_set_resistor(self.port_handle, self.channel_index, True)
|
|
256
|
+
if nRet != 0:
|
|
257
|
+
raise can.CanError(f"Failed to set resistor, code: " + str(nRet))
|
|
258
|
+
nRet = start_stop_trace(self.port_handle, self.channel_index, True)
|
|
259
|
+
if nRet != 0:
|
|
260
|
+
raise can.CanError(f"Failed to start stop trace, code: " + str(nRet))
|
|
261
|
+
print("Device:", driver_config.h3Channels[i].szHwName.decode("utf-8"), "Channel:", self.channel_dex, "is open" )
|
|
262
|
+
self.channel_info = driver_config.h3Channels[i].szHwName.decode("utf-8")+ "-Can" + str(self.channel_dex)
|
|
263
|
+
self._is_shutdown = False
|
|
264
|
+
break
|
|
265
|
+
else:
|
|
266
|
+
self.channel -= 1
|
|
267
|
+
|
|
268
|
+
def _get_param_with_warning(self, param_name, default):
|
|
269
|
+
"""统一处理参数获取和警告"""
|
|
270
|
+
value = self.configs.get(param_name, default)
|
|
271
|
+
if param_name not in self.configs:
|
|
272
|
+
warnings.warn(
|
|
273
|
+
f"配置参数 '{param_name}' 未提供,使用默认值: {default}",
|
|
274
|
+
UserWarning,
|
|
275
|
+
stacklevel=2
|
|
276
|
+
)
|
|
277
|
+
return value
|
|
278
|
+
|
|
279
|
+
def send(self, msg, timeout=None):
|
|
280
|
+
with self._lock:
|
|
281
|
+
if not hasattr(self, 'port_handle'): # 检查是否初始化成功
|
|
282
|
+
raise can.CanError("Device not initialized")
|
|
283
|
+
|
|
284
|
+
# 创建报文对象
|
|
285
|
+
can_msg = BasicStructure.GET_CAN_TRACE()
|
|
286
|
+
|
|
287
|
+
# 处理参数
|
|
288
|
+
if msg.is_extended_id:
|
|
289
|
+
can_msg.unCanId = (1 << 31) + msg.arbitration_id
|
|
290
|
+
else:
|
|
291
|
+
can_msg.unCanId = msg.arbitration_id
|
|
292
|
+
if msg.is_fd:
|
|
293
|
+
can_msg.ucEDL = 1
|
|
294
|
+
can_msg.ucBRS = msg.bitrate_switch
|
|
295
|
+
else:
|
|
296
|
+
can_msg.ucEDL = 0
|
|
297
|
+
can_msg.ucBRS = msg.is_remote_frame
|
|
298
|
+
can_msg.ucData = tuple(msg.data)
|
|
299
|
+
can_msg.ucDLC = self._len_to_dlc(msg.dlc)
|
|
300
|
+
|
|
301
|
+
# 调用公司DLL发送消息
|
|
302
|
+
ret = can_set_one_tx_msg_no_resp(
|
|
303
|
+
self.port_handle,
|
|
304
|
+
self.channel_index,
|
|
305
|
+
can_msg
|
|
306
|
+
)
|
|
307
|
+
if ret != 0:
|
|
308
|
+
raise can.CanError("Failed to call can_set_one_tx_msg_no_resp.")
|
|
309
|
+
|
|
310
|
+
def send_periodic(self, msgs:can.Message, period: float):
|
|
311
|
+
with self._lock:
|
|
312
|
+
if not hasattr(self, 'port_handle'): # 检查是否初始化成功
|
|
313
|
+
raise can.CanError("Device not initialized")
|
|
314
|
+
|
|
315
|
+
# 创建报文对象
|
|
316
|
+
can_msg = BasicStructure.GET_CAN_TRACE()
|
|
317
|
+
|
|
318
|
+
# 处理参数
|
|
319
|
+
if msgs.is_extended_id and period != 0:
|
|
320
|
+
can_msg.unCanId = (1 << 31) + msgs.arbitration_id
|
|
321
|
+
else:
|
|
322
|
+
can_msg.unCanId = msgs.arbitration_id
|
|
323
|
+
if msgs.is_fd:
|
|
324
|
+
can_msg.ucEDL = 1
|
|
325
|
+
can_msg.ucBRS = msgs.bitrate_switch
|
|
326
|
+
else:
|
|
327
|
+
can_msg.ucEDL = 0
|
|
328
|
+
can_msg.ucBRS = msgs.is_remote_frame
|
|
329
|
+
can_msg.ucData = tuple(msgs.data)
|
|
330
|
+
can_msg.ucDLC = self._len_to_dlc(msgs.dlc)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
if can_msg.unCanId not in self.canids and period != 0:
|
|
334
|
+
self.canids.append(can_msg.unCanId)
|
|
335
|
+
self.len_canids += 1
|
|
336
|
+
if self.len_canids > 150:
|
|
337
|
+
raise can.CanError("Periodic message limit (150) reached.")
|
|
338
|
+
elif period == 0:
|
|
339
|
+
if can_msg.unCanId in self.canids:
|
|
340
|
+
self.canids.remove(can_msg.unCanId)
|
|
341
|
+
self.len_canids -= 1
|
|
342
|
+
else:
|
|
343
|
+
raise can.CanError(f"No active periodic message found with CAN ID {can_msg.unCanId} to delete")
|
|
344
|
+
|
|
345
|
+
ptxinfo = BasicStructure.CanPeriodTxInfo()
|
|
346
|
+
ptxinfo.canMsg = can_msg
|
|
347
|
+
ptxinfo.usPeriod = ctypes.c_ushort(int(1000 * period))
|
|
348
|
+
ret = can_set_periodic_tx_msgs(self.port_handle, self.channel_index, ptxinfo)
|
|
349
|
+
|
|
350
|
+
if ret != 0:
|
|
351
|
+
raise can.CanError("Failed to can_set_periodic_tx_msgs")
|
|
352
|
+
|
|
353
|
+
task = CyclicSendTask(can_msg, self.port_handle, self.channel_index)
|
|
354
|
+
task.arbitration_id = msgs.arbitration_id
|
|
355
|
+
task.messages = msgs
|
|
356
|
+
task.period = period
|
|
357
|
+
task.period_ns = int(period * 1e9)
|
|
358
|
+
return task
|
|
359
|
+
|
|
360
|
+
def stop_all_periodic_tasks(self):
|
|
361
|
+
while(self.len_canids != 0):
|
|
362
|
+
msg = can.Message(
|
|
363
|
+
arbitration_id = self.canids[0],
|
|
364
|
+
)
|
|
365
|
+
ret = self.send_periodic(msg, 0)
|
|
366
|
+
if ret != None:
|
|
367
|
+
raise can.CanError("stop_all_periodic_tasks")
|
|
368
|
+
|
|
369
|
+
def recv(self, timeout=None):
|
|
370
|
+
with self._lock:
|
|
371
|
+
if not hasattr(self, 'port_handle'):
|
|
372
|
+
raise can.CanError("Device not initialized")
|
|
373
|
+
|
|
374
|
+
start_time = time.time()
|
|
375
|
+
while not self._is_shutdown:
|
|
376
|
+
rx_msg = BasicStructure.RX_SERVICE()
|
|
377
|
+
rx_count = ctypes.c_uint(1)
|
|
378
|
+
n_rst = dll.H3GetRxService(self.port_handle, ctypes.byref(rx_msg), ctypes.byref(rx_count))
|
|
379
|
+
rx_count = rx_count.value
|
|
380
|
+
if rx_count > 0:
|
|
381
|
+
if rx_msg.nServiceType == 8: # CAN报文
|
|
382
|
+
if not self.is_include_tx and rx_msg.service.getCanTrace.ucTag == 0:
|
|
383
|
+
continue
|
|
384
|
+
if self.hr_filter != None:
|
|
385
|
+
if rx_msg.service.getCanTrace.unCanId < self.hr_filter[0] \
|
|
386
|
+
or rx_msg.service.getCanTrace.unCanId > self.hr_filter[1]:
|
|
387
|
+
continue
|
|
388
|
+
parsed_msg = self._parse_raw_msg(rx_msg)
|
|
389
|
+
if parsed_msg and not self._is_shutdown:
|
|
390
|
+
return parsed_msg
|
|
391
|
+
else:
|
|
392
|
+
pass
|
|
393
|
+
elif rx_msg.nServiceType == 4: # DIAGRESP报文
|
|
394
|
+
diag_msg = rx_msg.service.get15765_2_RespInd
|
|
395
|
+
with self._response_lock:
|
|
396
|
+
# 存储响应数据
|
|
397
|
+
self._current_response_data = {
|
|
398
|
+
'data': diag_msg.ucData[:diag_msg.usLength],
|
|
399
|
+
'data_len': diag_msg.usLength,
|
|
400
|
+
'req_id': diag_msg.unEcuRespId,
|
|
401
|
+
}
|
|
402
|
+
# 触发等待的事件
|
|
403
|
+
if len(self._current_response_data['data']) != 3 and \
|
|
404
|
+
self._current_response_data['data'][2] != 0x78:
|
|
405
|
+
self._response_event.set()
|
|
406
|
+
else:
|
|
407
|
+
print("收到78响应,等待中...")
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
# 超时处理
|
|
413
|
+
if timeout is not None:
|
|
414
|
+
if timeout == 0:
|
|
415
|
+
return None
|
|
416
|
+
if time.time() - start_time >= timeout:
|
|
417
|
+
return None
|
|
418
|
+
time.sleep(0.001)
|
|
419
|
+
|
|
420
|
+
def _parse_raw_msg(self, rx_msg):
|
|
421
|
+
"""解析消息(兼容正常帧和错误帧)"""
|
|
422
|
+
if rx_msg.nServiceType == 0x8:
|
|
423
|
+
Msg = Message(
|
|
424
|
+
arbitration_id=rx_msg.service.getCanTrace.unCanId & 0x7FFFFFFF,
|
|
425
|
+
data=rx_msg.service.getCanTrace.ucData[:self._dlc_to_len(rx_msg.service.getCanTrace.ucDLC)],
|
|
426
|
+
# data = [rx_msg.service.getCanTrace.ucData[i] for i in range(self._dlc_to_len(rx_msg.service.getCanTrace.ucDLC))],
|
|
427
|
+
dlc=self._dlc_to_len(rx_msg.service.getCanTrace.ucDLC),
|
|
428
|
+
is_extended_id=rx_msg.service.getCanTrace.unCanId >> 31,
|
|
429
|
+
is_rx=rx_msg.service.getCanTrace.ucTag,
|
|
430
|
+
is_fd=rx_msg.service.getCanTrace.ucEDL,
|
|
431
|
+
timestamp=self._normalize_timestamp(rx_msg.service.getCanTrace.unTimestamp),
|
|
432
|
+
channel=self.channel_dex,
|
|
433
|
+
bitrate_switch= rx_msg.service.getCanTrace.ucBRS == 1 and rx_msg.service.getCanTrace.ucEDL == 1,
|
|
434
|
+
is_remote_frame= rx_msg.service.getCanTrace.ucBRS == 1 and rx_msg.service.getCanTrace.ucEDL == 0
|
|
435
|
+
)
|
|
436
|
+
self.last_msg_time = rx_msg.service.getCanTrace.unTimestamp
|
|
437
|
+
return Msg
|
|
438
|
+
elif rx_msg.nServiceType == 0x0:
|
|
439
|
+
self.last_msg_time = rx_msg.service.getCanTrace.unTimestamp
|
|
440
|
+
return Message(
|
|
441
|
+
is_error_frame=True,
|
|
442
|
+
arbitration_id=0x00000000,
|
|
443
|
+
timestamp=self._normalize_timestamp(rx_msg.service.getBusError.unTimestamp),
|
|
444
|
+
channel=self.channel_dex
|
|
445
|
+
)
|
|
446
|
+
else:
|
|
447
|
+
return None
|
|
448
|
+
|
|
449
|
+
# def _normalize_timestamp(self, ts):
|
|
450
|
+
# """处理时间同步"""
|
|
451
|
+
# """处理设备时间戳到工程启动时间"""
|
|
452
|
+
# if ts > self.last_msg_time:
|
|
453
|
+
# _time = ts - self.start_time
|
|
454
|
+
# elif ts + 1 < self.last_msg_time:
|
|
455
|
+
# self.time_cycle += 1
|
|
456
|
+
# _time = (0xFFFFFFFF & 0xFFFFFFFF) * self.time_cycle + (-1) * (ts - self.start_time)
|
|
457
|
+
# else:
|
|
458
|
+
# _time = ts - self.start_time
|
|
459
|
+
# # 将硬件时间戳转换为秒(假设是微秒
|
|
460
|
+
# return _time / 1e6
|
|
461
|
+
|
|
462
|
+
def _normalize_timestamp(self, ts):
|
|
463
|
+
"""处理时间同步:将设备时间戳转换为工程启动后的相对时间(秒)
|
|
464
|
+
|
|
465
|
+
假设设备时间戳是32位微秒计数器(范围0-0xFFFFFFFF),会周期性回绕
|
|
466
|
+
"""
|
|
467
|
+
# 检测时间戳回绕:当前值小于上一次值且差值超过阈值(0x80000000)
|
|
468
|
+
if ts < self.last_msg_time and (self.last_msg_time - ts) > 0x80000000:
|
|
469
|
+
self.time_cycle += 1 # 增加回绕周期计数
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
# 计算完整时间戳(考虑回绕周期)
|
|
473
|
+
full_ts = ts + self.time_cycle * 0x100000000 # 0x100000000 = 2^32
|
|
474
|
+
|
|
475
|
+
# 计算相对于工程启动的时间差(微秒)
|
|
476
|
+
time_delta = full_ts - self.start_time
|
|
477
|
+
|
|
478
|
+
# 更新记录的上一个有效时间戳
|
|
479
|
+
self.last_msg_time = ts
|
|
480
|
+
|
|
481
|
+
# 转换为秒(假设时间戳单位是微秒)
|
|
482
|
+
return time_delta / 1e6
|
|
483
|
+
|
|
484
|
+
def _len_to_dlc(self, len):
|
|
485
|
+
if len <= 8:
|
|
486
|
+
dlc = len
|
|
487
|
+
elif len <= 12:
|
|
488
|
+
dlc = 9
|
|
489
|
+
elif len <= 16:
|
|
490
|
+
dlc = 10
|
|
491
|
+
elif len <= 20:
|
|
492
|
+
dlc = 11
|
|
493
|
+
elif len <= 24:
|
|
494
|
+
dlc = 12
|
|
495
|
+
elif len <= 32:
|
|
496
|
+
dlc = 13
|
|
497
|
+
elif len <= 48:
|
|
498
|
+
dlc = 14
|
|
499
|
+
elif len <= 64:
|
|
500
|
+
dlc = 15
|
|
501
|
+
return dlc
|
|
502
|
+
|
|
503
|
+
def _dlc_to_len(self, dlc):
|
|
504
|
+
if dlc <= 8:
|
|
505
|
+
len = dlc
|
|
506
|
+
elif dlc == 9:
|
|
507
|
+
len = 12
|
|
508
|
+
elif dlc == 10:
|
|
509
|
+
len = 16
|
|
510
|
+
elif dlc == 11:
|
|
511
|
+
len = 20
|
|
512
|
+
elif dlc == 12:
|
|
513
|
+
len = 24
|
|
514
|
+
elif dlc == 13:
|
|
515
|
+
len = 32
|
|
516
|
+
elif dlc == 14:
|
|
517
|
+
len = 48
|
|
518
|
+
elif dlc == 15:
|
|
519
|
+
len = 64
|
|
520
|
+
return len
|
|
521
|
+
|
|
522
|
+
def shutdown(self):
|
|
523
|
+
self._is_shutdown = True
|
|
524
|
+
with self._lock:
|
|
525
|
+
if not hasattr(self, 'port_handle'): # 检查是否初始化成功
|
|
526
|
+
raise can.CanError("Device not initialized")
|
|
527
|
+
ret = start_stop_trace(self.port_handle, self.channel_index, False)
|
|
528
|
+
ret = close_port(self.port_handle)
|
|
529
|
+
# if ret != 0:
|
|
530
|
+
# print('关闭端口失败!', '错误码', ret)
|
|
531
|
+
|
|
532
|
+
def can_set_param_15765_2(self, Value, Parameter = N_Parameter.N_Bs, MType = CANMType.MTYPE_LOCAL):
|
|
533
|
+
udsParameter = BasicStructure.CAN_Parameter()
|
|
534
|
+
udsParameter.ucMType = MType
|
|
535
|
+
udsParameter.ucParameter = Parameter
|
|
536
|
+
udsParameter.unValue = Value
|
|
537
|
+
n_rst = dll.H3CANSetParam15765_2(self.port_handle, self.channel_index, udsParameter)
|
|
538
|
+
return n_rst
|
|
539
|
+
|
|
540
|
+
def can_set_diagnostic_config(self, ReqId, FuncReqID, RespId, DataPadding = 0xCC):
|
|
541
|
+
# 设置ECU信息
|
|
542
|
+
ecu_info = (BasicStructure.EcuInfo * 256)()
|
|
543
|
+
ecu_info[0].unReqID = ReqId
|
|
544
|
+
ecu_info[0].unUSDTRespID = RespId
|
|
545
|
+
diagnostic_config = BasicStructure.CAN_DIAGNOSTIC_CONFIG()
|
|
546
|
+
diagnostic_config.ucDataPadding = DataPadding
|
|
547
|
+
diagnostic_config.unFuncReqID = FuncReqID
|
|
548
|
+
diagnostic_config.ucEcuCount = 1
|
|
549
|
+
diagnostic_config.ecuInfo = ecu_info
|
|
550
|
+
n_rst = dll.H3CANSetDiagnosticConfig(self.port_handle, self.channel_index, diagnostic_config)
|
|
551
|
+
return n_rst
|
|
552
|
+
|
|
553
|
+
def can_forward_15765_2(self, ReqId, isFd = False, isBrs = False, Dlc = 8,
|
|
554
|
+
MType = CANMType.MTYPE_LOCAL, DataLen = 2, data = (0x10, 0x01), WaitResp = False):
|
|
555
|
+
# 发送报文
|
|
556
|
+
can_request = BasicStructure.CanRequest()
|
|
557
|
+
can_request.unReqID = ReqId
|
|
558
|
+
MValue = (isFd << 6) + (isBrs << 5) + (Dlc << 1) + MType
|
|
559
|
+
can_request.ucMType = MValue # UDS诊断类型,1-本地 2-远程
|
|
560
|
+
can_request.usDataLen = DataLen
|
|
561
|
+
can_request.ucData = data
|
|
562
|
+
time_out = ctypes.c_ushort(6000)
|
|
563
|
+
|
|
564
|
+
# 清空之前的响应状态
|
|
565
|
+
with self._response_lock:
|
|
566
|
+
self._current_response_data = None
|
|
567
|
+
self._response_event.clear()
|
|
568
|
+
# 发送请求
|
|
569
|
+
n_rst = dll.H3CANForward15765_2(self.port_handle, self.channel_index, can_request, time_out)
|
|
570
|
+
if n_rst == 0:
|
|
571
|
+
if WaitResp == True:
|
|
572
|
+
# 等待响应(阻塞等待)
|
|
573
|
+
if self._response_event.wait(timeout=30.0): # 6秒超时
|
|
574
|
+
with self._response_lock:
|
|
575
|
+
return n_rst, self._current_response_data
|
|
576
|
+
else:
|
|
577
|
+
return 0x2002, None # 超时
|
|
578
|
+
else:
|
|
579
|
+
return n_rst, None
|
|
580
|
+
else:
|
|
581
|
+
return n_rst, None # 发送失败
|
demo/Test_Canfd.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import can
|
|
2
|
+
import time
|
|
3
|
+
from can_hirain.config import TestBaseVCI_ChlMode, TestBaseVCI_SAMPLEPOINT # 导入枚举值
|
|
4
|
+
from can_hirain.hrcan import get_hr_hw_devices_info # 导入获取全部设备信息函数
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def multi_channel():
|
|
8
|
+
|
|
9
|
+
# 初始化通道0(发送端)和通道1(接收端)
|
|
10
|
+
with can.Bus(interface="hrcan", channel=0, # 激活通道0为CANFD通道
|
|
11
|
+
configs = [{
|
|
12
|
+
'resistance': 1, # 启用终端电阻
|
|
13
|
+
'mode' : TestBaseVCI_ChlMode.CANFD, # 设置CANFD通道
|
|
14
|
+
'listenFlag' : 0, # 0-正常状态,1-通道为仅监听状态,不给ACK响应
|
|
15
|
+
'abrBaudrate': 500_000,
|
|
16
|
+
'dbrBaudrate': 2000_000,
|
|
17
|
+
'abrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_80,
|
|
18
|
+
'dbrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_80,}]) as bus_tx, \
|
|
19
|
+
can.Bus(interface="hrcan", channel=4, # 激活通道1为CANFD通道
|
|
20
|
+
configs=[{
|
|
21
|
+
'resistance': 1, # 启用终端电阻
|
|
22
|
+
'mode': TestBaseVCI_ChlMode.CANFD, # 设置CANFD通道
|
|
23
|
+
'listenFlag' : 0, # 0-正常状态,1-通道为仅监听状态,不给ACK响应
|
|
24
|
+
'abrBaudrate': 500_000,
|
|
25
|
+
'dbrBaudrate': 2000_000,
|
|
26
|
+
'abrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_80,
|
|
27
|
+
'dbrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_80}]) as bus_rx:
|
|
28
|
+
# with can.Bus(
|
|
29
|
+
# interface="hrcan",
|
|
30
|
+
# channel=0,
|
|
31
|
+
# rate_baudrate=500,
|
|
32
|
+
# data_baudrate=2000,
|
|
33
|
+
# enable_120hm=True,
|
|
34
|
+
# is_fd=True,
|
|
35
|
+
# is_include_tx=True,
|
|
36
|
+
# hwserial=b"HRA000020000" # 使用设备SN作为硬件序列号
|
|
37
|
+
# ) as bus_tx,\
|
|
38
|
+
# can.Bus(
|
|
39
|
+
# interface="hrcan",
|
|
40
|
+
# channel=1,
|
|
41
|
+
# rate_baudrate=500,
|
|
42
|
+
# data_baudrate=2000,
|
|
43
|
+
# enable_120hm=True,
|
|
44
|
+
# is_fd=True,
|
|
45
|
+
# is_include_tx=True,
|
|
46
|
+
# hwserial=b"HRA000020000" # 使用设备SN作为硬件序列号
|
|
47
|
+
# ) as bus_rx:
|
|
48
|
+
|
|
49
|
+
# # 配置接收端监听器(打印+日志)
|
|
50
|
+
# listeners_rx = [can.Printer(), can.Logger("rx_channel.asc")]
|
|
51
|
+
# notifier_rx = can.Notifier(bus_rx, listeners_rx)
|
|
52
|
+
# time.sleep(0.1) # 确保监听器就绪
|
|
53
|
+
#
|
|
54
|
+
# # 配置发送端监听器(打印+日志)
|
|
55
|
+
# listeners_tx = [can.Printer(), can.Logger("tx_channel.asc")]
|
|
56
|
+
# notifier_tx = can.Notifier(bus_tx, listeners_tx)
|
|
57
|
+
# time.sleep(0.1) # 确保监听器就绪
|
|
58
|
+
|
|
59
|
+
# 配置总计监听器(日志)(不配置Printer以降低IO资源占用)
|
|
60
|
+
listeners = [can.Printer(), can.Logger("rxtx_channel_test.blf")]
|
|
61
|
+
notifier = can.Notifier([bus_tx,bus_rx], listeners)
|
|
62
|
+
time.sleep(0.1) # 确保监听器就绪
|
|
63
|
+
|
|
64
|
+
# 发送测试消息(从通道0发送到通道1)
|
|
65
|
+
test_msg_1 = can.Message(
|
|
66
|
+
arbitration_id=0x123,
|
|
67
|
+
data=[0xAA, 0xBB, 0xCC],
|
|
68
|
+
dlc=3,
|
|
69
|
+
is_extended_id=True,
|
|
70
|
+
is_fd=True,
|
|
71
|
+
bitrate_switch=True
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# 发送测试消息(从通道0发送到通道1)
|
|
75
|
+
test_msg_2 = can.Message(
|
|
76
|
+
arbitration_id=0x1FFFFFFE,
|
|
77
|
+
data=[i for i in range(8)],
|
|
78
|
+
dlc=8,
|
|
79
|
+
is_extended_id=True,
|
|
80
|
+
is_fd=True,
|
|
81
|
+
bitrate_switch=True
|
|
82
|
+
)
|
|
83
|
+
print(f"发送消息: ID=0x{test_msg_1.arbitration_id:X}, Data={','.join(str(_) for _ in test_msg_1.data)}")
|
|
84
|
+
bus_tx.send(test_msg_1)
|
|
85
|
+
print(f"发送消息: ID=0x{test_msg_2.arbitration_id:X}, Data={','.join(str(_) for _ in test_msg_2.data)}")
|
|
86
|
+
bus_tx.send(test_msg_2)
|
|
87
|
+
|
|
88
|
+
time.sleep(2)
|
|
89
|
+
# canfd_id = [0x85, 0xE0, 0x100, 0x103, 0x122, 0x123, 0x124, 0x125, 0x162, 0x165, 0x1A5, 0x218, 0x220, 0x228, 0x256, 0x26D, 0x288, 0x292, 0x2A1, 0x2A5, 0x2A7, 0x2AC, 0x2E0, 0x2F7, 0x360, 0x380, 0x3D0, 0x3F1, 0x405, 0x744, 0x783, 0x795, 0x7C6, 0x7DF, 0x7E2, 0x1F0, 0x1F1, 0x1F2, 0x283, 0x284]
|
|
90
|
+
canfd_id = [0x7E2, 0x1F0, 0x1F1, 0x1F2, 0x283, 0x284]
|
|
91
|
+
# test_msg_2.arbitration_id = 0x1FFFEEEE
|
|
92
|
+
num = 0
|
|
93
|
+
test_msg_2.is_extended_id = False
|
|
94
|
+
for i in canfd_id:
|
|
95
|
+
test_msg_2.arbitration_id = i
|
|
96
|
+
bus_tx.send_periodic(test_msg_2, 0.01)
|
|
97
|
+
print(f"发送周期消息: ID=0x{test_msg_2.arbitration_id:X}")
|
|
98
|
+
num += 1
|
|
99
|
+
print(f"共有{num}条周期报文")
|
|
100
|
+
|
|
101
|
+
print("等待100s...")
|
|
102
|
+
time.sleep(100)
|
|
103
|
+
|
|
104
|
+
bus_tx.stop_all_periodic_tasks()
|
|
105
|
+
time.sleep(10)
|
|
106
|
+
notifier.stop()
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
devices = get_hr_hw_devices_info()
|
|
110
|
+
print(devices)
|
|
111
|
+
time.sleep(2)
|
|
112
|
+
multi_channel()
|