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/BasicStructure.py +1661 -0
- can_hirain/TestBaseVCI.py +1084 -0
- can_hirain/__init__.py +3 -0
- can_hirain/hrcan.py +655 -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_test-0.1.1.dist-info/METADATA +83 -0
- python_can_hirain_test-0.1.1.dist-info/RECORD +14 -0
- python_can_hirain_test-0.1.1.dist-info/WHEEL +5 -0
- python_can_hirain_test-0.1.1.dist-info/entry_points.txt +2 -0
- python_can_hirain_test-0.1.1.dist-info/licenses/LICENSE +26 -0
- python_can_hirain_test-0.1.1.dist-info/top_level.txt +2 -0
can_hirain/__init__.py
ADDED
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
|