python-can-hirain-test 0.1.1__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/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .hrcan import HRCANBus
2
+
3
+ __all__ = ["HRCANBus"]
can_hirain/hrcan.py ADDED
@@ -0,0 +1,655 @@
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
+ ProjectStartTime = time.time()
16
+ HwStartTime = dict()
17
+
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
+ channels = get_driver_config() # 现在返回 List[Dict]
33
+ device_dict = {} # {设备名: [(数组下标, pythoncan_idx, vci通道索引)]}
34
+ pc_idx = 0
35
+
36
+ for i, ch in enumerate(channels):
37
+ if ch['bus_capabilities'] == 3: # CAN/CANFD
38
+ device_name = ch['hw_name']
39
+ if device_name not in device_dict:
40
+ device_dict[device_name] = [(i, pc_idx, ch['channel_index'])]
41
+ else:
42
+ device_dict[device_name].append((i, pc_idx, ch['channel_index']))
43
+ # 原 get_tosun 无 pc_idx 自增,此处保持原逻辑
44
+
45
+ if len(device_dict) == 0:
46
+ return {}
47
+
48
+ device_info_list = {}
49
+ device_idx = 0
50
+ for device_name in device_dict:
51
+ device_info = {
52
+ "manufacturer": "HIRAIN",
53
+ "product": "TestBaseVCI",
54
+ "Serial": device_name,
55
+ "device_type": "TestBaseVCI",
56
+ "device_name": device_name,
57
+ "can_counts": len(device_dict[device_name]),
58
+ "is_canfd": True,
59
+ "lin_counts": 0,
60
+ "fr_counts": 0,
61
+ "eth_counts": 0
62
+ }
63
+ device_info_list[device_idx] = device_info
64
+ device_idx += 1
65
+ return device_info_list
66
+
67
+
68
+ def get_hr_hw_devices_info():
69
+ channels = get_driver_config() # List[Dict]
70
+ device_dict = {}
71
+ pc_idx = 0
72
+
73
+ for i, ch in enumerate(channels):
74
+ if ch['bus_capabilities'] == 3: # CAN/CANFD
75
+ device_name = ch['hw_name']
76
+ if device_name not in device_dict:
77
+ device_dict[device_name] = [(i, pc_idx, ch['channel_index'])]
78
+ else:
79
+ device_dict[device_name].append((i, pc_idx, ch['channel_index']))
80
+ pc_idx += 1 # 原 get_hr 中有自增,保留
81
+
82
+ if len(device_dict) == 0:
83
+ return {}
84
+
85
+ device_info_list = {}
86
+ device_idx = 0
87
+ for device_name in device_dict:
88
+ device_info = {
89
+ "manufacturer": "HIRAIN",
90
+ "product": "TestBaseVCI",
91
+ "device_name": device_name,
92
+ "can_counts": len(device_dict[device_name]),
93
+ "channel": device_dict[device_name] # [(下标, pythoncan通道号, vci通道号)]
94
+ }
95
+ device_info_list[device_idx] = device_info
96
+ device_idx += 1
97
+ return device_info_list
98
+
99
+
100
+ # 获取通道字典
101
+ # def get_hr_can_canfd_channels():
102
+ # driver_config = get_driver_config()
103
+ # channels = {}
104
+ # for i in range(driver_config.nChannelCount):
105
+ # channel = driver_config.h3Channels[i]
106
+ # if channel.ucBusCapabilities == 3: # CAN/FD通道
107
+ # channels[("")]
108
+
109
+ class HRCANBus(BusABC):
110
+ def __init__(self, channel, **kwargs):
111
+ super().__init__(channel=channel, **kwargs)
112
+ self._lock = threading.Lock()
113
+ self.configs = kwargs.get('configs', [{}])[0]
114
+ self.channel = channel
115
+ self.ChannelIdx = channel
116
+ self.channel_dex = channel
117
+ self.canids = []
118
+ self.len_canids = 0
119
+ self.start_time = 0
120
+ self.series_num = None
121
+ self.last_msg_time = 0
122
+ self.time_cycle = 0
123
+ self.hr_filter = None
124
+
125
+ # 15765诊断第二响应
126
+ self._response_queue = queue.Queue()
127
+ self._response_event = threading.Event()
128
+ self._current_response_data = None
129
+ self._response_lock = threading.Lock()
130
+
131
+ # 支持以同星格式下发的参数
132
+ self.ts_rate_baudrate = kwargs.get('rate_baudrate', None)
133
+ self.ts_data_baudrate = kwargs.get('data_baudrate', 2000)
134
+ self.ts_enable_120hm = kwargs.get('enable_120hm', True)
135
+ self.ts_is_fd = kwargs.get('is_fd', True)
136
+ self.is_include_tx = kwargs.get('is_include_tx', True)
137
+ self.ts_hwserial = kwargs.get('hwserial', None)
138
+ self.ts_hwserial = self.ts_hwserial.decode('gbk') if isinstance(self.ts_hwserial, bytes) else self.ts_hwserial
139
+ # 设置报文过滤器
140
+ filters = kwargs.get('filters', None)
141
+ if filters and len(filters) > 0:
142
+ dict_filter = filters[0] # 暂时只支持设置一个过滤器
143
+ if dict_filter['extend'] == False:
144
+ self.hr_filter = (dict_filter['min'], dict_filter['max'])
145
+ elif dict_filter['extend'] == True:
146
+ self.hr_filter = (dict_filter['min'] + 0x80000000, dict_filter['max'] + 0x80000000)
147
+ else:
148
+ self.hr_filter = None
149
+
150
+ if self.ts_rate_baudrate != None and self.configs == {}: # 仅在下发同星参数名且未下发VCI参数时使用同星参数
151
+ self.ts_rate_baudrate = 500 if self.ts_rate_baudrate == None else self.ts_rate_baudrate
152
+ if self.ts_is_fd == True:
153
+ self.configs = {
154
+ 'resistance': self.ts_enable_120hm, # 启用终端电阻
155
+ 'mode': TestBaseVCI_ChlMode.CANFD, # 设置CANFD通道
156
+ 'listenFlag': 0, # 0-正常状态,1-通道为仅监听状态,不给ACK响应
157
+ 'abrBaudrate': self.ts_rate_baudrate * 1000,
158
+ 'dbrBaudrate': self.ts_data_baudrate * 1000,
159
+ 'abrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_75,
160
+ 'dbrSamplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_75}
161
+ else:
162
+ self.configs = {
163
+ 'resistance': self.ts_enable_120hm, # 启用终端电阻
164
+ 'mode': TestBaseVCI_ChlMode.CAN, # 设置CAN通道
165
+ 'listenFlag': 0, # 0-正常状态,1-通道为仅监听状态,不给ACK响应
166
+ 'baudrate': self.ts_rate_baudrate * 1000,
167
+ 'samplePoint': TestBaseVCI_SAMPLEPOINT.SAMPLE_75}
168
+ # 匹配通道号
169
+ devices = get_hr_hw_devices_info()
170
+ b_find_device = False
171
+ for key, value in devices.items():
172
+ if value['device_name'] == self.ts_hwserial and self.channel < len(value['channel']):
173
+ self.channel = value['channel'][self.channel][1]
174
+ self.channel_dex = self.channel
175
+ b_find_device = True
176
+ if self.ts_hwserial != None and not b_find_device:
177
+ raise can.CanError(f"Failed to find device: {self.ts_hwserial}")
178
+
179
+ if self._get_param_with_warning('mode', 0) == 0:
180
+ self.can_init()
181
+ else:
182
+ self.canfd_init()
183
+
184
+ def can_init(self):
185
+ channels = get_driver_config() # 列表,元素为字典
186
+ resistance = self._get_param_with_warning('resistance', 1)
187
+ ListenFlag = self._get_param_with_warning('listenFlag', 0)
188
+ Baudrate = self._get_param_with_warning('baudrate', 500000)
189
+ SamplePoint = self._get_param_with_warning('samplePoint', 0)
190
+
191
+ for ch in channels:
192
+ if ch['bus_capabilities'] == 3:
193
+ if self.channel == 0:
194
+ self.channel_index = ch['channel_index']
195
+ channel_mask = ch['channel_mask']
196
+ self.port_handle = open_port(channel_mask)
197
+
198
+ self.series_num = ch['hw_name']
199
+ if self.series_num in HwStartTime:
200
+ self.start_time = HwStartTime[self.series_num]
201
+ else:
202
+ sys_time = time.time()
203
+ dev_time = get_device_time_stamp(self.port_handle) # 新函数返回 int
204
+ self.start_time = float(dev_time) - (sys_time - ProjectStartTime) * 1e6
205
+ HwStartTime[self.series_num] = self.start_time
206
+
207
+ # active_can_channel 现在返回 bool,参数名改为下划线格式
208
+ if not active_can_channel(
209
+ self.port_handle,
210
+ self.channel_index,
211
+ listen_flag=ListenFlag,
212
+ baudrate=Baudrate,
213
+ sample_point=SamplePoint
214
+ ):
215
+ raise can.CanError("Failed to activate CAN channel")
216
+
217
+ if resistance == 1:
218
+ ret = can_set_resistor(self.port_handle, self.channel_index, True)
219
+ if ret != 0:
220
+ raise can.CanError(f"Failed to set resistor, code: {ret}")
221
+
222
+ ret = start_stop_trace(self.port_handle, self.channel_index, True)
223
+ if ret != 0:
224
+ raise can.CanError(f"Failed to start trace, code: {ret}")
225
+
226
+ print(f"Device:{ch['hw_name']} Channel:{self.channel_dex} is open")
227
+ self.channel_info = ch['hw_name'] + "-Can" + str(self.channel_dex)
228
+ self._is_shutdown = False
229
+ break
230
+ else:
231
+ self.channel -= 1
232
+
233
+ def canfd_init(self):
234
+ channels = get_driver_config()
235
+ resistance = self._get_param_with_warning('resistance', 1)
236
+ ListenFlag = self._get_param_with_warning('listenFlag', 0)
237
+ abrBaudrate = self._get_param_with_warning('abrBaudrate', 500000)
238
+ dbrBaudrate = self._get_param_with_warning('dbrBaudrate', 2000000)
239
+ abrSamplePoint = self._get_param_with_warning('abrSamplePoint', 0)
240
+ dbrSamplePoint = self._get_param_with_warning('dbrSamplePoint', 0)
241
+
242
+ for ch in channels:
243
+ if ch['bus_capabilities'] == 3:
244
+ if self.channel == 0:
245
+ self.channel_index = ch['channel_index']
246
+ channel_mask = ch['channel_mask']
247
+ self.port_handle = open_port(channel_mask)
248
+
249
+ self.series_num = ch['hw_name']
250
+ if self.series_num in HwStartTime:
251
+ self.start_time = HwStartTime[self.series_num]
252
+ else:
253
+ sys_time = time.time()
254
+ dev_time = get_device_time_stamp(self.port_handle)
255
+ self.start_time = float(dev_time) - (sys_time - ProjectStartTime) * 1e6
256
+ HwStartTime[self.series_num] = self.start_time
257
+
258
+ nRet = active_canfd_channel(
259
+ self.port_handle,
260
+ self.channel_index,
261
+ listen_flag=ListenFlag,
262
+ abr_baudrate=abrBaudrate,
263
+ dbr_baudrate=dbrBaudrate,
264
+ abr_sample_point=abrSamplePoint,
265
+ dbr_sample_point=dbrSamplePoint,
266
+ )
267
+ if nRet != 0:
268
+ raise can.CanError("Failed to activate CANFD channel, code: " + str(nRet))
269
+
270
+ if resistance == 1:
271
+ nRet = can_set_resistor(self.port_handle, self.channel_index, True)
272
+ if nRet != 0:
273
+ raise can.CanError(f"Failed to set resistor, code: {nRet}")
274
+
275
+ nRet = start_stop_trace(self.port_handle, self.channel_index, True)
276
+ if nRet != 0:
277
+ raise can.CanError(f"Failed to start trace, code: {nRet}")
278
+
279
+ print(f"Device:{ch['hw_name']} Channel:{self.channel_dex} is open")
280
+ self.channel_info = ch['hw_name'] + "-Can" + str(self.channel_dex)
281
+ self._is_shutdown = False
282
+ break
283
+ else:
284
+ self.channel -= 1
285
+
286
+ def _get_param_with_warning(self, param_name, default):
287
+ """统一处理参数获取和警告"""
288
+ value = self.configs.get(param_name, default)
289
+ if param_name not in self.configs:
290
+ warnings.warn(
291
+ f"配置参数 '{param_name}' 未提供,使用默认值: {default}",
292
+ UserWarning,
293
+ stacklevel=2
294
+ )
295
+ return value
296
+
297
+ def send(self, msg, timeout=None):
298
+ with self._lock:
299
+ if not hasattr(self, 'port_handle'):
300
+ raise can.CanError("Device not initialized")
301
+
302
+ # 构建消息并拆解参数调用新接口
303
+ can_id = (1 << 31) + msg.arbitration_id if msg.is_extended_id else msg.arbitration_id
304
+ is_fd = msg.is_fd
305
+ dlc = self._len_to_dlc(msg.dlc)
306
+ brs = msg.bitrate_switch if is_fd else 0
307
+ tx_data = tuple(msg.data)
308
+
309
+ ret = can_set_one_tx_msgs(
310
+ self.port_handle,
311
+ self.channel_index,
312
+ can_id=can_id,
313
+ tx_data=tx_data,
314
+ is_fd=is_fd,
315
+ dlc=dlc,
316
+ brs=brs,
317
+ )
318
+ if ret != 0:
319
+ raise can.CanError("Failed to send CAN message")
320
+
321
+ def send_periodic(self, msgs: can.Message, period: float):
322
+ with self._lock:
323
+ if not hasattr(self, 'port_handle'):
324
+ raise can.CanError("Device not initialized")
325
+
326
+ # 构建消息参数
327
+ can_id = (1 << 31) + msgs.arbitration_id if msgs.is_extended_id else msgs.arbitration_id
328
+ is_fd = msgs.is_fd
329
+ dlc = self._len_to_dlc(msgs.dlc)
330
+ brs = msgs.bitrate_switch if is_fd else 0
331
+ tx_data = tuple(msgs.data)
332
+
333
+ # 发送周期报文(删除操作由 period==0 控制,还需根据业务处理)
334
+ ret = can_set_periodic_tx_msgs(
335
+ self.port_handle,
336
+ self.channel_index,
337
+ can_id=can_id,
338
+ is_fd=is_fd,
339
+ dlc=dlc,
340
+ brs=brs,
341
+ data=tx_data,
342
+ period=int(1000 * period) # 单位根据文档调整
343
+ )
344
+ if ret != 0:
345
+ raise can.CanError("Failed to set periodic message")
346
+
347
+ # 管理发送列表(保持原有逻辑)
348
+ if can_id not in self.canids and period != 0:
349
+ self.canids.append(can_id)
350
+ self.len_canids += 1
351
+ if self.len_canids > 150:
352
+ raise can.CanError("Periodic message limit (150) reached.")
353
+ elif period == 0:
354
+ if can_id in self.canids:
355
+ self.canids.remove(can_id)
356
+ self.len_canids -= 1
357
+ else:
358
+ raise can.CanError(f"No active periodic message with ID {can_id}")
359
+
360
+ # 返回任务对象(传递必要信息以支持停止周期发送)
361
+ class CyclicSendTask(can.broadcastmanager.CyclicSendTaskABC):
362
+ def __init__(self, port, ch_idx, can_id, is_fd, dlc, brs, data):
363
+ self._port = port
364
+ self._ch_idx = ch_idx
365
+ self._can_id = can_id
366
+ self._is_fd = is_fd
367
+ self._dlc = dlc
368
+ self._brs = brs
369
+ self._data = data
370
+
371
+ def stop(self):
372
+ can_set_periodic_tx_msgs(
373
+ self._port, self._ch_idx,
374
+ can_id=self._can_id, is_fd=self._is_fd,
375
+ dlc=self._dlc, brs=self._brs,
376
+ data=self._data, period=0
377
+ )
378
+
379
+ task = CyclicSendTask(self.port_handle, self.channel_index,
380
+ can_id, is_fd, dlc, brs, tx_data)
381
+ task.arbitration_id = msgs.arbitration_id
382
+ task.messages = msgs
383
+ task.period = period
384
+ task.period_ns = int(period * 1e9)
385
+ return task
386
+
387
+ def stop_all_periodic_tasks(self):
388
+ while (self.len_canids != 0):
389
+ msg = can.Message(
390
+ arbitration_id=self.canids[0],
391
+ )
392
+ ret = self.send_periodic(msg, 0)
393
+ if ret != None:
394
+ raise can.CanError("stop_all_periodic_tasks")
395
+
396
+ # def recv(self, timeout=None):
397
+ # with self._lock:
398
+ # if not hasattr(self, 'port_handle'):
399
+ # raise can.CanError("Device not initialized")
400
+ #
401
+ # start_time = time.time()
402
+ # while not self._is_shutdown:
403
+ # rx_msg = BasicStructure.RX_SERVICE()
404
+ # rx_count = ctypes.c_uint(1)
405
+ # n_rst = dll.H3GetRxService(self.port_handle, ctypes.byref(rx_msg), ctypes.byref(rx_count))
406
+ # rx_count = rx_count.value
407
+ # if rx_count > 0:
408
+ # if rx_msg.nServiceType == 8: # CAN报文
409
+ # if not self.is_include_tx and rx_msg.service.getCanTrace.ucTag == 0:
410
+ # continue
411
+ # if self.hr_filter != None:
412
+ # if rx_msg.service.getCanTrace.unCanId < self.hr_filter[0] \
413
+ # or rx_msg.service.getCanTrace.unCanId > self.hr_filter[1]:
414
+ # continue
415
+ # parsed_msg = self._parse_raw_msg(rx_msg)
416
+ # if parsed_msg and not self._is_shutdown:
417
+ # return parsed_msg
418
+ # else:
419
+ # pass
420
+ # elif rx_msg.nServiceType == 4: # DIAGRESP报文
421
+ # diag_msg = rx_msg.service.get15765_2_RespInd
422
+ # with self._response_lock:
423
+ # # 存储响应数据
424
+ # self._current_response_data = {
425
+ # 'data': diag_msg.ucData[:diag_msg.usLength],
426
+ # 'data_len': diag_msg.usLength,
427
+ # 'req_id': diag_msg.unEcuRespId,
428
+ # }
429
+ # # 触发等待的事件
430
+ # if len(self._current_response_data['data']) != 3 and \
431
+ # self._current_response_data['data'][2] != 0x78:
432
+ # self._response_event.set()
433
+ # else:
434
+ # print("收到78响应,等待中...")
435
+ # # 超时处理
436
+ # if timeout is not None:
437
+ # if timeout == 0:
438
+ # return None
439
+ # if time.time() - start_time >= timeout:
440
+ # return None
441
+ # time.sleep(0.001)
442
+
443
+ def recv(self, timeout=None):
444
+ if not hasattr(self, 'port_handle'):
445
+ raise can.CanError("Device not initialized")
446
+
447
+ start_time = time.time()
448
+ while not self._is_shutdown:
449
+ # 使用原生接口获取一条报文,返回 List[Dict]
450
+ msgs = fetch_rx_services(self.port_handle, count=1)
451
+ if msgs:
452
+ raw = msgs[0] # 取第一条,是一个字典
453
+
454
+ # 处理 CAN 报文
455
+ if raw['nServiceType'] == 8:
456
+ if not self.is_include_tx and raw['tag'] == 0:
457
+ continue # 忽略自己发送的报文
458
+ if self.hr_filter is not None:
459
+ if raw['can_id'] < self.hr_filter[0] or raw['can_id'] > self.hr_filter[1]:
460
+ continue
461
+ parsed = self._parse_raw_msg(raw) # 传入字典
462
+ if parsed and not self._is_shutdown:
463
+ return parsed
464
+
465
+ # 处理诊断响应(保留原有逻辑)
466
+ elif raw['nServiceType'] == 4:
467
+ with self._response_lock:
468
+ self._current_response_data = {
469
+ 'data': raw['data'],
470
+ 'data_len': raw['data_len'],
471
+ 'req_id': raw['req_id'],
472
+ }
473
+ # 如果不是 0x78 多帧等待,则通知等待的线程
474
+ if not (len(self._current_response_data['data']) >= 3 and
475
+ self._current_response_data['data'][2] == 0x78):
476
+ self._response_event.set()
477
+ else:
478
+ print("收到 0x78 响应,继续等待...")
479
+
480
+ # 其他类型(如错误帧)可在此处补充
481
+
482
+ # 超时处理
483
+ if timeout is not None:
484
+ if timeout == 0:
485
+ return None
486
+ if time.time() - start_time >= timeout:
487
+ return None
488
+ time.sleep(0.001)
489
+
490
+ def _parse_raw_msg(self, raw: dict):
491
+ """将原生字典转换成 can.Message"""
492
+ if raw['nServiceType'] == 8: # CAN 或 CANFD
493
+ can_id = raw['can_id']
494
+ dlc = raw['dlc']
495
+ data_len = self._dlc_to_len(dlc)
496
+ self.last_msg_time = raw['timestamp']
497
+ return Message(
498
+ arbitration_id=can_id & 0x7FFFFFFF,
499
+ data=bytes(raw['data'][:data_len]),
500
+ dlc=data_len,
501
+ is_extended_id=raw['is_extended'],
502
+ is_rx=(raw['tag'] == 1),
503
+ is_fd=raw['is_fd'],
504
+ bitrate_switch=raw['brs'] and raw['is_fd'],
505
+ timestamp=self._normalize_timestamp(raw['timestamp']),
506
+ channel=self.channel_dex
507
+ )
508
+ elif raw['nServiceType'] == 0: # 错误帧
509
+ self.last_msg_time = raw['timestamp']
510
+ return Message(
511
+ is_error_frame=True,
512
+ arbitration_id=0,
513
+ timestamp=self._normalize_timestamp(raw['timestamp']),
514
+ channel=self.channel_dex
515
+ )
516
+ # 其他类型忽略,返回 None
517
+ return None
518
+
519
+ # def _parse_raw_msg(self, rx_msg):
520
+ # """解析消息(兼容正常帧和错误帧)"""
521
+ # if rx_msg.nServiceType == 0x8:
522
+ # Msg = Message(
523
+ # arbitration_id=rx_msg.service.getCanTrace.unCanId & 0x7FFFFFFF,
524
+ # data=rx_msg.service.getCanTrace.ucData[:self._dlc_to_len(rx_msg.service.getCanTrace.ucDLC)],
525
+ # # data = [rx_msg.service.getCanTrace.ucData[i] for i in range(self._dlc_to_len(rx_msg.service.getCanTrace.ucDLC))],
526
+ # dlc=self._dlc_to_len(rx_msg.service.getCanTrace.ucDLC),
527
+ # is_extended_id=rx_msg.service.getCanTrace.unCanId >> 31,
528
+ # is_rx=rx_msg.service.getCanTrace.ucTag,
529
+ # is_fd=rx_msg.service.getCanTrace.ucEDL,
530
+ # timestamp=self._normalize_timestamp(rx_msg.service.getCanTrace.unTimestamp),
531
+ # channel=self.channel_dex,
532
+ # bitrate_switch= rx_msg.service.getCanTrace.ucBRS == 1 and rx_msg.service.getCanTrace.ucEDL == 1,
533
+ # is_remote_frame= rx_msg.service.getCanTrace.ucBRS == 1 and rx_msg.service.getCanTrace.ucEDL == 0
534
+ # )
535
+ # self.last_msg_time = rx_msg.service.getCanTrace.unTimestamp
536
+ # return Msg
537
+ # elif rx_msg.nServiceType == 0x0:
538
+ # self.last_msg_time = rx_msg.service.getCanTrace.unTimestamp
539
+ # return Message(
540
+ # is_error_frame=True,
541
+ # arbitration_id=0x00000000,
542
+ # timestamp=self._normalize_timestamp(rx_msg.service.getBusError.unTimestamp),
543
+ # channel=self.channel_dex
544
+ # )
545
+ # else:
546
+ # return None
547
+
548
+ # def _normalize_timestamp(self, ts):
549
+ # """处理时间同步"""
550
+ # """处理设备时间戳到工程启动时间"""
551
+ # if ts > self.last_msg_time:
552
+ # _time = ts - self.start_time
553
+ # elif ts + 1 < self.last_msg_time:
554
+ # self.time_cycle += 1
555
+ # _time = (0xFFFFFFFF & 0xFFFFFFFF) * self.time_cycle + (-1) * (ts - self.start_time)
556
+ # else:
557
+ # _time = ts - self.start_time
558
+ # # 将硬件时间戳转换为秒(假设是微秒
559
+ # return _time / 1e
560
+
561
+ def _normalize_timestamp(self, ts):
562
+ """处理时间同步:将设备时间戳转换为工程启动后的相对时间(秒)
563
+
564
+ 假设设备时间戳是32位微秒计数器(范围0-0xFFFFFFFF),会周期性回绕
565
+ """
566
+ # 检测时间戳回绕:当前值小于上一次值且差值超过阈值(0x80000000)
567
+ if ts < self.last_msg_time and (self.last_msg_time - ts) > 0x80000000:
568
+ self.time_cycle += 1 # 增加回绕周期计数
569
+
570
+ # 计算完整时间戳(考虑回绕周期)
571
+ full_ts = ts + self.time_cycle * 0x100000000 # 0x100000000 = 2^32
572
+
573
+ # 计算相对于工程启动的时间差(微秒)
574
+ time_delta = full_ts - self.start_time
575
+
576
+ # 更新记录的上一个有效时间戳
577
+ self.last_msg_time = ts
578
+
579
+ # 转换为秒(假设时间戳单位是微秒)
580
+ return time_delta / 1e6
581
+
582
+ def _len_to_dlc(self, len):
583
+ if len <= 8:
584
+ dlc = len
585
+ elif len <= 12:
586
+ dlc = 9
587
+ elif len <= 16:
588
+ dlc = 10
589
+ elif len <= 20:
590
+ dlc = 11
591
+ elif len <= 24:
592
+ dlc = 12
593
+ elif len <= 32:
594
+ dlc = 13
595
+ elif len <= 48:
596
+ dlc = 14
597
+ elif len <= 64:
598
+ dlc = 15
599
+ return dlc
600
+
601
+ def _dlc_to_len(self, dlc):
602
+ if dlc <= 8:
603
+ len = dlc
604
+ elif dlc == 9:
605
+ len = 12
606
+ elif dlc == 10:
607
+ len = 16
608
+ elif dlc == 11:
609
+ len = 20
610
+ elif dlc == 12:
611
+ len = 24
612
+ elif dlc == 13:
613
+ len = 32
614
+ elif dlc == 14:
615
+ len = 48
616
+ elif dlc == 15:
617
+ len = 64
618
+ return len
619
+
620
+ def shutdown(self):
621
+ self._is_shutdown = True
622
+ with self._lock:
623
+ if not hasattr(self, 'port_handle'): # 检查是否初始化成功
624
+ raise can.CanError("Device not initialized")
625
+ ret = start_stop_trace(self.port_handle, self.channel_index, False)
626
+ ret = close_port(self.port_handle)
627
+ # if ret != 0:
628
+ # print('关闭端口失败!', '错误码', ret)
629
+
630
+ def can_set_param_15765_2(self, Value, Parameter=N_Parameter.N_Bs, MType=CANMType.MTYPE_LOCAL):
631
+ return can_set_param_15765_2(
632
+ self.port_handle, self.channel_index,
633
+ Value=Value, Parameter=Parameter, MType=MType
634
+ )
635
+
636
+ def can_set_diagnostic_config(self, ReqId, FuncReqID, RespId, DataPadding=0xCC):
637
+ return can_set_diagnostic_config(
638
+ self.port_handle, self.channel_index,
639
+ ReqId=ReqId, FuncReqID=FuncReqID, RespId=RespId, DataPadding=DataPadding
640
+ )
641
+
642
+ def can_forward_15765_2(self, ReqId, isFd=False, isBrs=False, Dlc=8,
643
+ MType=CANMType.MTYPE_LOCAL, DataLen=2, data=(0x10, 0x01), WaitResp=False):
644
+ ret_code, resp = can_forward_15765_2(
645
+ self.port_handle, self.channel_index,
646
+ req_id=ReqId,
647
+ is_fd=isFd,
648
+ is_brs=isBrs,
649
+ dlc=Dlc,
650
+ m_type=MType,
651
+ data_len=DataLen,
652
+ data=data,
653
+ wait_resp=WaitResp,
654
+ )
655
+ return ret_code, resp