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/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()