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
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import platform
|
|
3
|
+
import signal
|
|
4
|
+
import atexit
|
|
5
|
+
from typing import Tuple, Optional,Any, Dict, List
|
|
6
|
+
from . import BasicStructure
|
|
7
|
+
import threading
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def load_dll() -> ctypes.CDLL:
|
|
11
|
+
"""
|
|
12
|
+
加载 himetal3api 动态库。
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
ctypes.CDLL: 已加载的动态库对象。
|
|
16
|
+
"""
|
|
17
|
+
if platform.system() == "Windows":
|
|
18
|
+
dll = ctypes.CDLL("himetal3api.dll")
|
|
19
|
+
else:
|
|
20
|
+
dll = ctypes.CDLL("libhimetal3api.so")
|
|
21
|
+
return dll
|
|
22
|
+
|
|
23
|
+
dll = load_dll()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def set_exit_callback(func: callable) -> None:
|
|
27
|
+
"""注册程序退出时的回调函数(支持 Ctrl+C 和正常退出)"""
|
|
28
|
+
signal.signal(signal.SIGINT, func)
|
|
29
|
+
signal.signal(signal.SIGTERM, func)
|
|
30
|
+
atexit.register(func)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_driver_config() -> List[Dict[str, Any]]:
|
|
34
|
+
"""
|
|
35
|
+
获取驱动配置,并以标准 Python 数据结构返回所有可用通道的详细信息。
|
|
36
|
+
...
|
|
37
|
+
"""
|
|
38
|
+
driver_config = BasicStructure.H3DriverConfig()
|
|
39
|
+
# 第二个参数传空指针,表示忽略 IP 地址(与原版保持一致)
|
|
40
|
+
ip_config = ctypes.c_char_p()
|
|
41
|
+
ret = dll.H3GetDriverConfig(ctypes.byref(driver_config), ip_config)
|
|
42
|
+
|
|
43
|
+
if ret != 0:
|
|
44
|
+
print(f"driver 信息获取失败,错误码:{ret}")
|
|
45
|
+
return []
|
|
46
|
+
|
|
47
|
+
channels = []
|
|
48
|
+
for index in range(driver_config.nChannelCount):
|
|
49
|
+
ch = driver_config.h3Channels[index]
|
|
50
|
+
hw_name = ch.szHwName.decode('utf-8', errors='ignore').rstrip('\x00') if isinstance(ch.szHwName, bytes) else str(ch.szHwName)
|
|
51
|
+
hw_additional_info = ch.szHwAdditionalInfo.decode('utf-8', errors='ignore').rstrip('\x00') if isinstance(ch.szHwAdditionalInfo, bytes) else str(ch.szHwAdditionalInfo)
|
|
52
|
+
|
|
53
|
+
channel_dict = {
|
|
54
|
+
'channel_index': ch.ucChannelIndex,
|
|
55
|
+
'channel_mask': ch.unChannelMask,
|
|
56
|
+
'hw_name': hw_name,
|
|
57
|
+
'hw_type': ch.szHwType,
|
|
58
|
+
'hw_additional_info': hw_additional_info,
|
|
59
|
+
'hw_channel_no': ch.ucHwChannelNo,
|
|
60
|
+
'hw_trans_mode': ch.ucHwTransMode,
|
|
61
|
+
'bus_capabilities': ch.ucBusCapabilities,
|
|
62
|
+
}
|
|
63
|
+
channels.append(channel_dict)
|
|
64
|
+
|
|
65
|
+
return channels
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def channel_mask(channel: int) -> tuple[int, int]:
|
|
71
|
+
"""
|
|
72
|
+
获取指定通道的掩码和通道索引。
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
channel (int): 通道在配置列表中的序号(从 0 开始)。
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
tuple[int, int]: (通道掩码, 通道索引),若获取失败则返回 (0, 0)。
|
|
79
|
+
"""
|
|
80
|
+
driver_config = get_driver_config()
|
|
81
|
+
if not driver_config:
|
|
82
|
+
print("无法获取驱动配置,请检查硬件连接或驱动状态。")
|
|
83
|
+
return 0, 0
|
|
84
|
+
|
|
85
|
+
if channel < 0 or channel >= len(driver_config):
|
|
86
|
+
print(f"通道序号 {channel} 超出范围,当前可用通道数:{len(driver_config)}。")
|
|
87
|
+
return 0, 0
|
|
88
|
+
|
|
89
|
+
ch = driver_config[channel]
|
|
90
|
+
mask = ch['channel_mask'] # int
|
|
91
|
+
channel_index = ch['channel_index'] # int
|
|
92
|
+
return mask, channel_index
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def open_port(mask: int) -> int:
|
|
96
|
+
"""
|
|
97
|
+
打开指定掩码的端口,获得端口句柄。
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
mask (int): 通道掩码,由 channel_mask() 返回。
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
int: 端口句柄(正整数),若失败返回 0。
|
|
104
|
+
"""
|
|
105
|
+
port_handle = ctypes.c_size_t()
|
|
106
|
+
ret = dll.H3OpenPort(ctypes.byref(port_handle), ctypes.c_uint64(mask))
|
|
107
|
+
if ret != 0:
|
|
108
|
+
print(f"端口打开失败!错误码: {ret}")
|
|
109
|
+
return 0
|
|
110
|
+
return port_handle.value
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_device_version(port_handle: int, mask: int) -> tuple:
|
|
114
|
+
"""
|
|
115
|
+
获取设备版本信息。
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
port_handle (int): 端口句柄,由 open_port() 返回。
|
|
119
|
+
mask (int): 通道掩码(当前版本未使用,保留以兼容旧接口)。
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
tuple: (版本信息字典, 错误码)。
|
|
123
|
+
成功时返回 ({"hw_version": (...), "bt_version": (...), "sw_version": (...)}, 0);
|
|
124
|
+
失败时返回 (None, 错误码),并自动打印错误信息。
|
|
125
|
+
"""
|
|
126
|
+
dev_version = BasicStructure.DeviceVersion()
|
|
127
|
+
ret = dll.H3GetDeviceVersion(ctypes.c_size_t(port_handle), ctypes.byref(dev_version))
|
|
128
|
+
if ret != 0:
|
|
129
|
+
print(f"获取设备版本信息失败!错误码: {ret}")
|
|
130
|
+
return None, ret
|
|
131
|
+
|
|
132
|
+
version_info = {
|
|
133
|
+
"hw_version": (dev_version.hw_version_1, dev_version.hw_version_2),
|
|
134
|
+
"bt_version": (
|
|
135
|
+
dev_version.bt_version_1,
|
|
136
|
+
dev_version.bt_version_2,
|
|
137
|
+
dev_version.bt_version_3,
|
|
138
|
+
),
|
|
139
|
+
"sw_version": (
|
|
140
|
+
dev_version.sw_version_1,
|
|
141
|
+
dev_version.sw_version_2,
|
|
142
|
+
dev_version.sw_version_3,
|
|
143
|
+
),
|
|
144
|
+
}
|
|
145
|
+
return version_info, 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def active_can_channel(
|
|
149
|
+
port_handle: int,
|
|
150
|
+
channel_index: int,
|
|
151
|
+
listen_flag: int = 0,
|
|
152
|
+
baudrate: int = 500000,
|
|
153
|
+
sample_point: int = 0,
|
|
154
|
+
) -> bool:
|
|
155
|
+
"""
|
|
156
|
+
激活指定的 CAN 通道。
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
port_handle (int): 端口句柄,由 open_port() 返回。
|
|
160
|
+
channel_index (int): 通道索引,由 channel_mask() 返回。
|
|
161
|
+
listen_flag (int): 监听标志,0 表示正常模式。默认 0。
|
|
162
|
+
baudrate (int): CAN 波特率,单位 bps。默认 500000。
|
|
163
|
+
sample_point (int): 采样点位置,0 表示自动。默认 0。
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
bool: 激活成功返回 True,失败返回 False 并在终端打印错误码。
|
|
167
|
+
"""
|
|
168
|
+
channel_bus_params = BasicStructure.CHANNEL_BUS_PARAMS()
|
|
169
|
+
time_stamp = ctypes.create_string_buffer(10)
|
|
170
|
+
|
|
171
|
+
# 固定为 CAN 类型
|
|
172
|
+
channel_bus_params.ucBusType = 0x0
|
|
173
|
+
# 使用传入的参数配置 CAN 参数
|
|
174
|
+
channel_bus_params.busParam.can.ucListenFlag = listen_flag
|
|
175
|
+
channel_bus_params.busParam.can.ucBaudrate = baudrate
|
|
176
|
+
channel_bus_params.busParam.can.ucSamplePoint = sample_point
|
|
177
|
+
|
|
178
|
+
ret = dll.H3ActiveChannel(
|
|
179
|
+
ctypes.c_size_t(port_handle),
|
|
180
|
+
ctypes.c_ubyte(channel_index),
|
|
181
|
+
ctypes.byref(channel_bus_params),
|
|
182
|
+
time_stamp,
|
|
183
|
+
)
|
|
184
|
+
if ret != 0:
|
|
185
|
+
print(f"CAN 通道激活失败!错误码: {ret}")
|
|
186
|
+
return False
|
|
187
|
+
return True
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def active_canfd_channel(
|
|
191
|
+
port_handle: int,
|
|
192
|
+
channel_index: int,
|
|
193
|
+
listen_flag: int = 0,
|
|
194
|
+
abr_baudrate: int = 500_000,
|
|
195
|
+
dbr_baudrate: int = 2_000_000,
|
|
196
|
+
abr_sample_point: int = 0,
|
|
197
|
+
dbr_sample_point: int = 0,
|
|
198
|
+
) -> int:
|
|
199
|
+
"""
|
|
200
|
+
激活指定的 CAN FD 通道。
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
port_handle (int): 端口句柄,由 open_port() 返回。
|
|
204
|
+
channel_index (int): 通道索引,由 channel_mask() 返回。
|
|
205
|
+
listen_flag (int): 监听标志,0 表示正常模式。默认 0。
|
|
206
|
+
abr_baudrate (int): 仲裁段波特率(bps),默认 500_000。
|
|
207
|
+
dbr_baudrate (int): 数据段波特率(bps),默认 2_000_000。
|
|
208
|
+
abr_sample_point (int): 仲裁段采样点位置,0 为自动。默认 0。
|
|
209
|
+
dbr_sample_point (int): 数据段采样点位置,0 为自动。默认 0。
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
int: 错误码。0 表示成功;非 0 表示失败,并会在终端打印具体错误码。
|
|
213
|
+
"""
|
|
214
|
+
channel_bus_params = BasicStructure.CHANNEL_BUS_PARAMS()
|
|
215
|
+
time_stamp = ctypes.c_uint()
|
|
216
|
+
|
|
217
|
+
channel_bus_params.ucBusType = 3 # CAN FD 类型
|
|
218
|
+
channel_bus_params.busParam.canFd.ucListenFlag = listen_flag
|
|
219
|
+
channel_bus_params.busParam.canFd.abrBaudrate = abr_baudrate
|
|
220
|
+
channel_bus_params.busParam.canFd.dbrBaudrate = dbr_baudrate
|
|
221
|
+
channel_bus_params.busParam.canFd.abrSamplePoint = abr_sample_point
|
|
222
|
+
channel_bus_params.busParam.canFd.dbrSamplePoint = dbr_sample_point
|
|
223
|
+
|
|
224
|
+
ret = dll.H3ActiveChannel(
|
|
225
|
+
ctypes.c_size_t(port_handle),
|
|
226
|
+
ctypes.c_ubyte(channel_index),
|
|
227
|
+
ctypes.byref(channel_bus_params),
|
|
228
|
+
ctypes.byref(time_stamp),
|
|
229
|
+
)
|
|
230
|
+
if ret != 0:
|
|
231
|
+
print(f"CAN FD 通道激活失败!错误码: {ret}")
|
|
232
|
+
return ret
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def active_lin_channel(
|
|
236
|
+
port_handle: int,
|
|
237
|
+
channel_index: int,
|
|
238
|
+
baudrate: int = 9600,
|
|
239
|
+
lin_version: int = 0,
|
|
240
|
+
) -> int:
|
|
241
|
+
"""
|
|
242
|
+
激活指定的 LIN 通道。
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
port_handle (int): 端口句柄,由 open_port() 返回。
|
|
246
|
+
channel_index (int): 通道索引,由 channel_mask() 返回。
|
|
247
|
+
baudrate (int): LIN 波特率 (bps),默认 9600。
|
|
248
|
+
lin_version (int): LIN 协议版本号,0 表示自动/默认版本。默认 0。
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
int: 错误码。0 表示成功;非 0 表示失败,并会在终端打印错误信息。
|
|
252
|
+
"""
|
|
253
|
+
channel_bus_params = BasicStructure.CHANNEL_BUS_PARAMS()
|
|
254
|
+
time_stamp = ctypes.create_string_buffer(10)
|
|
255
|
+
|
|
256
|
+
channel_bus_params.ucBusType = 0x2 # LIN 类型
|
|
257
|
+
channel_bus_params.busParam.lin.unBaudrate = baudrate
|
|
258
|
+
channel_bus_params.busParam.lin.ucLinVersion = lin_version
|
|
259
|
+
|
|
260
|
+
ret = dll.H3ActiveChannel(
|
|
261
|
+
ctypes.c_size_t(port_handle),
|
|
262
|
+
ctypes.c_ubyte(channel_index),
|
|
263
|
+
ctypes.byref(channel_bus_params),
|
|
264
|
+
time_stamp,
|
|
265
|
+
)
|
|
266
|
+
if ret != 0:
|
|
267
|
+
print(f"LIN 通道激活失败!错误码: {ret}")
|
|
268
|
+
return ret
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def active_ethernet_channel(
|
|
272
|
+
port_handle: int,
|
|
273
|
+
channel_index: int
|
|
274
|
+
) -> int:
|
|
275
|
+
"""
|
|
276
|
+
激活指定的以太网通道。
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
port_handle (int): 端口句柄,由 open_port() 返回。
|
|
280
|
+
channel_index (int): 通道索引,由 channel_mask() 返回。
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
int: 错误码。0 表示成功;非 0 表示失败,并会在终端打印错误信息。
|
|
284
|
+
"""
|
|
285
|
+
channel_bus_param = BasicStructure.CHANNEL_BUS_PARAMS()
|
|
286
|
+
time_stamp = ctypes.c_uint()
|
|
287
|
+
|
|
288
|
+
channel_bus_param.ucBusType = 0x4
|
|
289
|
+
channel_bus_param.busParam.eth.szFilter = b''
|
|
290
|
+
channel_bus_param.busParam.eth.nMaxSize = 256
|
|
291
|
+
channel_bus_param.busParam.eth.nMaxPacks = 1024
|
|
292
|
+
channel_bus_param.busParam.eth.szDumpFileDir = br'C:\test_Ethernet'
|
|
293
|
+
|
|
294
|
+
ret = dll.H3ActiveChannel(
|
|
295
|
+
ctypes.c_size_t(port_handle),
|
|
296
|
+
ctypes.c_ubyte(channel_index),
|
|
297
|
+
channel_bus_param,
|
|
298
|
+
ctypes.byref(time_stamp),
|
|
299
|
+
)
|
|
300
|
+
if ret != 0:
|
|
301
|
+
print(f"以太网通道激活失败!错误码: {ret}")
|
|
302
|
+
return ret
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# ===================== CAN =====================
|
|
306
|
+
def can_set_resistor(
|
|
307
|
+
port_handle: int,
|
|
308
|
+
channel_index: int,
|
|
309
|
+
value: bool,
|
|
310
|
+
) -> int:
|
|
311
|
+
"""
|
|
312
|
+
设置 CAN 终端电阻。
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
port_handle (int): 端口句柄。
|
|
316
|
+
channel_index (int): 通道索引。
|
|
317
|
+
value (bool): True 启用电阻,False 关闭电阻。
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
321
|
+
"""
|
|
322
|
+
ret = dll.H3CANSetResistor(port_handle, channel_index, value)
|
|
323
|
+
if ret != 0:
|
|
324
|
+
print(f"CAN 终端电阻操作失败!错误码: {ret}")
|
|
325
|
+
return ret
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# ===================== LIN =====================
|
|
329
|
+
def lin_set_master_slave(
|
|
330
|
+
port_handle: int,
|
|
331
|
+
channel_index: int,
|
|
332
|
+
value: bool,
|
|
333
|
+
) -> int:
|
|
334
|
+
"""
|
|
335
|
+
设置 LIN 主从节点。
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
port_handle (int): 端口句柄。
|
|
339
|
+
channel_index (int): 通道索引。
|
|
340
|
+
value (bool): True 主节点,False 从节点。
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
344
|
+
"""
|
|
345
|
+
ret = dll.H3LINSetResistor(port_handle, channel_index, value)
|
|
346
|
+
if ret != 0:
|
|
347
|
+
print(f"LIN 终端电阻设置失败!错误码: {ret}")
|
|
348
|
+
return ret
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def set_lin_tx_msg(
|
|
352
|
+
port_handle: int,
|
|
353
|
+
channel_index: int,
|
|
354
|
+
tx_msg_count: int,
|
|
355
|
+
lin_msg,
|
|
356
|
+
) -> int:
|
|
357
|
+
"""
|
|
358
|
+
发送 LIN 响应报文。
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
port_handle (int): 端口句柄。
|
|
362
|
+
channel_index (int): 通道索引。
|
|
363
|
+
tx_msg_count (int): 发送报文数量。
|
|
364
|
+
lin_msg (ctypes.Structure): LIN 消息结构体(需与 DLL 定义匹配)。
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
368
|
+
"""
|
|
369
|
+
ret = dll.H3LINSetTxMsgs(port_handle, channel_index, tx_msg_count, ctypes.byref(lin_msg))
|
|
370
|
+
if ret != 0:
|
|
371
|
+
print(f"LIN 响应报文发送失败!错误码: {ret}")
|
|
372
|
+
return ret
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def set_lin_schedule(
|
|
376
|
+
port_handle: int,
|
|
377
|
+
channel_index: int,
|
|
378
|
+
lin_count: int,
|
|
379
|
+
node,
|
|
380
|
+
) -> int:
|
|
381
|
+
"""
|
|
382
|
+
发送 LIN 头部报文(调度表)。
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
port_handle (int): 端口句柄。
|
|
386
|
+
channel_index (int): 通道索引。
|
|
387
|
+
lin_count (int): 调度表条目数量。
|
|
388
|
+
node (ctypes.Structure): 调度表节点结构体(需与 DLL 定义匹配)。
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
392
|
+
"""
|
|
393
|
+
ret = dll.H3LINSetScheduleTable(port_handle, channel_index, lin_count, ctypes.byref(node))
|
|
394
|
+
if ret != 0:
|
|
395
|
+
print(f"LIN 首部报文发送失败!错误码: {ret}")
|
|
396
|
+
return ret
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def lin_clear(
|
|
400
|
+
port_handle: int,
|
|
401
|
+
channel_index: int,
|
|
402
|
+
value: int,
|
|
403
|
+
) -> int:
|
|
404
|
+
"""
|
|
405
|
+
清除 LIN 数据。
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
port_handle (int): 端口句柄。
|
|
409
|
+
channel_index (int): 通道索引。
|
|
410
|
+
value (int): 清除标志(具体含义参考硬件手册)。
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
414
|
+
"""
|
|
415
|
+
ret = dll.H3LINClear(port_handle, channel_index, value)
|
|
416
|
+
if ret != 0:
|
|
417
|
+
print(f"LIN 数据清除失败!错误码: {ret}")
|
|
418
|
+
return ret
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
# def start_stop_trace(
|
|
422
|
+
# port_handle: int,
|
|
423
|
+
# channel_index: int,
|
|
424
|
+
# value: bool,
|
|
425
|
+
# ) -> int:
|
|
426
|
+
# """
|
|
427
|
+
# 启用/停止总线监控。
|
|
428
|
+
#
|
|
429
|
+
# Args:
|
|
430
|
+
# port_handle (int): 端口句柄。
|
|
431
|
+
# channel_index (int): 通道索引。
|
|
432
|
+
# value (bool): True 启用监控,False 停止监控。
|
|
433
|
+
#
|
|
434
|
+
# Returns:
|
|
435
|
+
# int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
436
|
+
# """
|
|
437
|
+
# time_stamp = ctypes.c_void_p()
|
|
438
|
+
# ret = dll.H3StartStopBusTrace(port_handle, channel_index, value, ctypes.pointer(time_stamp))
|
|
439
|
+
# if ret != 0:
|
|
440
|
+
# print(f"启用/停止监控设置失败!错误码: {ret}")
|
|
441
|
+
# return ret
|
|
442
|
+
def start_stop_trace(port_handle: int, channel_index: int, value: bool) -> int:
|
|
443
|
+
time_stamp = ctypes.c_longlong() # 改为 64 位类型
|
|
444
|
+
ret = dll.H3StartStopBusTrace(port_handle, channel_index, value, ctypes.byref(time_stamp))
|
|
445
|
+
if ret != 0:
|
|
446
|
+
print(f"启用/停止监控设置失败!错误码: {ret}")
|
|
447
|
+
return ret
|
|
448
|
+
|
|
449
|
+
def close_port(port_handle: int) -> int:
|
|
450
|
+
"""
|
|
451
|
+
关闭端口。
|
|
452
|
+
|
|
453
|
+
Args:
|
|
454
|
+
port_handle (int): 端口句柄。
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
458
|
+
"""
|
|
459
|
+
ret = dll.H3ClosePort(port_handle)
|
|
460
|
+
if ret != 0:
|
|
461
|
+
print(f"关闭端口失败!错误码: {ret}")
|
|
462
|
+
return ret
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def deactive_channel(port_handle: int) -> int:
|
|
466
|
+
"""
|
|
467
|
+
取消激活通道。
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
port_handle (int): 端口句柄。
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
474
|
+
"""
|
|
475
|
+
ret = dll.H3DeactiveChannel(port_handle)
|
|
476
|
+
if ret != 0:
|
|
477
|
+
print(f"通道取消失败!错误码: {ret}")
|
|
478
|
+
return ret
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def get_device_time_stamp(port_handle: int) -> int:
|
|
482
|
+
"""
|
|
483
|
+
获取设备时间戳。
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
port_handle (int): 端口句柄。
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
ctypes.c_longlong: 设备时间戳(若失败会打印错误信息,返回的时间戳可能无效)。
|
|
490
|
+
"""
|
|
491
|
+
time_stamp = ctypes.c_longlong()
|
|
492
|
+
ret = dll.H3GetDeviceTimeStamp(port_handle, ctypes.pointer(time_stamp))
|
|
493
|
+
if ret != 0:
|
|
494
|
+
print(f"时间戳获取失败!错误码: {ret}")
|
|
495
|
+
return time_stamp.value
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def replay_send_packs(
|
|
499
|
+
port_handle: int,
|
|
500
|
+
msg_pack_count: int,
|
|
501
|
+
packs,
|
|
502
|
+
) -> int:
|
|
503
|
+
"""
|
|
504
|
+
发送回放报文。
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
port_handle (int): 端口句柄。
|
|
508
|
+
msg_pack_count (int): 报文包数量。
|
|
509
|
+
packs: 报文包结构体(需与 DLL 定义匹配)。
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
513
|
+
"""
|
|
514
|
+
ret = dll.H3ReplaySendPacks(port_handle, msg_pack_count, ctypes.byref(packs))
|
|
515
|
+
if ret != 0:
|
|
516
|
+
print(f"回放报文发送失败!错误码: {ret}")
|
|
517
|
+
return ret
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def can_set_periodic_tx_msgs(
|
|
521
|
+
port_handle: int,
|
|
522
|
+
channel_index: int,
|
|
523
|
+
can_id: int,
|
|
524
|
+
is_fd: bool = False,
|
|
525
|
+
dlc: int = 8,
|
|
526
|
+
brs: int = 0,
|
|
527
|
+
data = (1, 2, 3, 4, 5, 6, 7, 8),
|
|
528
|
+
period: int = 10,
|
|
529
|
+
) -> int:
|
|
530
|
+
"""
|
|
531
|
+
设置 CAN 周期发送报文。
|
|
532
|
+
|
|
533
|
+
Args:
|
|
534
|
+
port_handle (int): 端口句柄。
|
|
535
|
+
channel_index (int): 通道索引。
|
|
536
|
+
can_id (int): 发送方 CAN ID。
|
|
537
|
+
is_fd (bool): 是否为 CAN FD 帧,True 表示 FD 帧,False 为标准 CAN 帧。
|
|
538
|
+
dlc (int): 数据长度码 DLC(0~15),默认 8。
|
|
539
|
+
brs (int): 比特率开关(BRS),仅 CAN FD 有效,0 或 1,默认 0。
|
|
540
|
+
data: 发送数据,需与 ucData 字段兼容(长度不超过 CAN_MSG_LENGTH)。
|
|
541
|
+
period (int): 发送周期(单位与硬件相关)。
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
int: 错误码。0 表示成功;非 0 表示失败,并在终端打印错误信息。
|
|
545
|
+
"""
|
|
546
|
+
ptxinfo = BasicStructure.CanPeriodTxInfo()
|
|
547
|
+
can_msg = BasicStructure.GET_CAN_TRACE()
|
|
548
|
+
|
|
549
|
+
# 填充 CAN 消息结构体
|
|
550
|
+
can_msg.unCanId = can_id
|
|
551
|
+
can_msg.ucDLC = dlc
|
|
552
|
+
can_msg.ucEDL = 1 if is_fd else 0 # EDL=1 表示 CAN FD 帧
|
|
553
|
+
can_msg.ucBRS = brs # 比特率开关
|
|
554
|
+
can_msg.ucData = data
|
|
555
|
+
# ucTag 保持默认值 0(通常表示发送帧)
|
|
556
|
+
|
|
557
|
+
ptxinfo.canMsg = can_msg
|
|
558
|
+
ptxinfo.usPeriod = ctypes.c_ushort(period)
|
|
559
|
+
|
|
560
|
+
uc_tx_info_count = 1
|
|
561
|
+
time_stamp = ctypes.create_string_buffer(8) # 时间戳缓冲区,长度按硬件要求
|
|
562
|
+
ret = dll.H3CANSetPeriodicTxMsgs(
|
|
563
|
+
port_handle,
|
|
564
|
+
channel_index,
|
|
565
|
+
uc_tx_info_count,
|
|
566
|
+
ctypes.byref(ptxinfo),
|
|
567
|
+
time_stamp,
|
|
568
|
+
)
|
|
569
|
+
if ret != 0:
|
|
570
|
+
print(f"CAN 周期报文发送失败!错误码: {ret}")
|
|
571
|
+
return ret
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def can_set_one_tx_msgs(
|
|
575
|
+
port_handle: int,
|
|
576
|
+
channel_index: int,
|
|
577
|
+
can_id: int,
|
|
578
|
+
tx_data,
|
|
579
|
+
is_fd: bool = False,
|
|
580
|
+
dlc: int = 8,
|
|
581
|
+
brs: int = 0,
|
|
582
|
+
) -> int:
|
|
583
|
+
"""
|
|
584
|
+
发送 CAN 单次报文(一帧)。
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
port_handle (int): 端口句柄。
|
|
588
|
+
channel_index (int): 通道索引。
|
|
589
|
+
can_id (int): CAN 标识符 ID。
|
|
590
|
+
tx_data: 发送数据,需与 ucData 字段兼容(长度不超过 CAN_MSG_LENGTH)。
|
|
591
|
+
is_fd (bool): 是否为 CAN FD 帧,True 表示 FD 帧,False 为标准 CAN 帧。
|
|
592
|
+
dlc (int): 数据长度码 DLC(0~15),默认 8。
|
|
593
|
+
brs (int): 比特率开关(BRS),仅 CAN FD 有效,0 或 1,默认 0。
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
597
|
+
"""
|
|
598
|
+
time_stamp = ctypes.create_string_buffer(8)
|
|
599
|
+
can_msg_arr = (BasicStructure.GET_CAN_TRACE * 1)()
|
|
600
|
+
|
|
601
|
+
# 填充要发送的 CAN 消息
|
|
602
|
+
can_msg_arr[0].unCanId = can_id
|
|
603
|
+
can_msg_arr[0].ucDLC = dlc
|
|
604
|
+
can_msg_arr[0].ucEDL = 1 if is_fd else 0 # EDL=1 表示 CAN FD 帧
|
|
605
|
+
can_msg_arr[0].ucBRS = brs # 比特率开关
|
|
606
|
+
can_msg_arr[0].ucData = tx_data
|
|
607
|
+
# ucTag 默认为 0(通常表示发送帧)
|
|
608
|
+
|
|
609
|
+
tx_msg_count = 1
|
|
610
|
+
ret = dll.H3CANSetOneTimeTxMsgs(
|
|
611
|
+
port_handle,
|
|
612
|
+
channel_index,
|
|
613
|
+
tx_msg_count,
|
|
614
|
+
ctypes.byref(can_msg_arr),
|
|
615
|
+
time_stamp,
|
|
616
|
+
)
|
|
617
|
+
if ret != 0:
|
|
618
|
+
print(f"CAN 单次报文发送失败!错误码: {ret}")
|
|
619
|
+
return ret
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def ethernet_set_one_time_tx_msg(
|
|
623
|
+
port_handle: int,
|
|
624
|
+
channel_index: int,
|
|
625
|
+
data,
|
|
626
|
+
data_len: int = 82,
|
|
627
|
+
) -> int:
|
|
628
|
+
"""
|
|
629
|
+
发送以太网单次报文(无应答)。
|
|
630
|
+
|
|
631
|
+
Args:
|
|
632
|
+
port_handle (int): 端口句柄。
|
|
633
|
+
channel_index (int): 通道索引。
|
|
634
|
+
data: 发送数据(需符合 ucData 格式)。
|
|
635
|
+
data_len (int): 数据长度,默认 82。
|
|
636
|
+
|
|
637
|
+
Returns:
|
|
638
|
+
int: 错误码。0 表示成功;非 0 表示失败,并打印错误信息。
|
|
639
|
+
"""
|
|
640
|
+
ethernet_msg = BasicStructure.ETH_MSG()
|
|
641
|
+
ethernet_msg.unDataLength = data_len
|
|
642
|
+
ethernet_msg.ucData = data
|
|
643
|
+
ret = dll.H3EthernetSetOneTimeTxMsgNoResp(port_handle, channel_index, ethernet_msg)
|
|
644
|
+
if ret != 0:
|
|
645
|
+
print(f"以太网报文发送失败!错误码: {ret}")
|
|
646
|
+
return ret
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def can_set_param_15765_2(
|
|
650
|
+
port_handle: int,
|
|
651
|
+
channel_index: int,
|
|
652
|
+
Value: int,
|
|
653
|
+
Parameter: int = BasicStructure.N_Parameter.N_Bs,
|
|
654
|
+
MType: int = BasicStructure.CANMType.MTYPE_LOCAL,
|
|
655
|
+
) -> int:
|
|
656
|
+
"""
|
|
657
|
+
设置 ISO-15765 诊断参数。
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
port_handle (int): 端口句柄。
|
|
661
|
+
channel_index (int): 通道索引。
|
|
662
|
+
Value (int): 参数值。
|
|
663
|
+
Parameter (int): 要设置的参数类型,取值见 N_Parameter 枚举,默认 N_Bs。
|
|
664
|
+
MType (int): 诊断类型,MTYPE_LOCAL(1,本地)或 MTYPE_REMOTE(2,远程)。
|
|
665
|
+
|
|
666
|
+
Returns:
|
|
667
|
+
int: 错误码,0 表示成功,非 0 表示失败。
|
|
668
|
+
"""
|
|
669
|
+
udsParameter = BasicStructure.CAN_PARAMS()
|
|
670
|
+
udsParameter.ucMType = MType
|
|
671
|
+
udsParameter.ucParameter = Parameter
|
|
672
|
+
udsParameter.unValue = Value
|
|
673
|
+
n_rst = dll.H3CANSetParam15765_2(port_handle, channel_index, udsParameter)
|
|
674
|
+
return n_rst
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def can_set_diagnostic_config(
|
|
678
|
+
port_handle: int,
|
|
679
|
+
channel_index: int,
|
|
680
|
+
ReqId: int,
|
|
681
|
+
FuncReqID: int,
|
|
682
|
+
RespId: int,
|
|
683
|
+
DataPadding: int = 0xCC,
|
|
684
|
+
) -> int:
|
|
685
|
+
"""
|
|
686
|
+
设置诊断配置(ECU 信息)。
|
|
687
|
+
|
|
688
|
+
Args:
|
|
689
|
+
port_handle (int): 端口句柄。
|
|
690
|
+
channel_index (int): 通道索引。
|
|
691
|
+
ReqId (int): 请求 ID(物理寻址)。
|
|
692
|
+
FuncReqID (int): 功能寻址请求 ID。
|
|
693
|
+
RespId (int): 响应 ID。
|
|
694
|
+
DataPadding (int): 数据填充字节,默认 0xCC。
|
|
695
|
+
|
|
696
|
+
Returns:
|
|
697
|
+
int: 错误码,0 表示成功,非 0 表示失败。
|
|
698
|
+
"""
|
|
699
|
+
ecu_info = (BasicStructure.EcuInfo * 256)()
|
|
700
|
+
ecu_info[0].unReqID = ReqId
|
|
701
|
+
ecu_info[0].unUSDTRespID = RespId
|
|
702
|
+
|
|
703
|
+
diagnostic_config = BasicStructure.CAN_DIAGNOSTIC_CONFIG()
|
|
704
|
+
diagnostic_config.ucDataPadding = DataPadding
|
|
705
|
+
diagnostic_config.unFuncReqID = FuncReqID
|
|
706
|
+
diagnostic_config.ucEcuCount = 1
|
|
707
|
+
diagnostic_config.ecuInfo = ecu_info
|
|
708
|
+
|
|
709
|
+
n_rst = dll.H3CANSetDiagnosticConfig(
|
|
710
|
+
port_handle, channel_index, diagnostic_config
|
|
711
|
+
)
|
|
712
|
+
return n_rst
|
|
713
|
+
|
|
714
|
+
response_event = threading.Event()
|
|
715
|
+
current_response_data = None
|
|
716
|
+
response_lock = threading.Lock()
|
|
717
|
+
|
|
718
|
+
def can_forward_15765_2(
|
|
719
|
+
port_handle: int,
|
|
720
|
+
channel_index: int,
|
|
721
|
+
req_id: int,
|
|
722
|
+
is_fd: bool = False,
|
|
723
|
+
is_brs: bool = False,
|
|
724
|
+
dlc: int = 8,
|
|
725
|
+
m_type: int = BasicStructure.CANMType.MTYPE_LOCAL,
|
|
726
|
+
data_len: int = 2,
|
|
727
|
+
data: tuple = (0x10, 0x01),
|
|
728
|
+
wait_resp: bool = False,
|
|
729
|
+
) -> Tuple[int, Optional[Any]]:
|
|
730
|
+
"""
|
|
731
|
+
发送 ISO-15765 诊断请求(UDS)。
|
|
732
|
+
|
|
733
|
+
Args:
|
|
734
|
+
port_handle: 端口句柄。
|
|
735
|
+
channel_index: 通道索引。
|
|
736
|
+
req_id: 请求 ID。
|
|
737
|
+
is_fd: 是否使用 CAN FD 帧,默认 False。
|
|
738
|
+
is_brs: 是否使能比特率开关(BRS),仅 CAN FD 有效,默认 False。
|
|
739
|
+
dlc: 数据长度码 DLC,默认 8。
|
|
740
|
+
m_type: 诊断类型,MTYPE_LOCAL(1) 或 MTYPE_REMOTE(2)。
|
|
741
|
+
data_len: 实际数据长度(字节),默认 2。
|
|
742
|
+
data: 要发送的数据,格式为字节元组,默认 (0x10, 0x01)。
|
|
743
|
+
wait_resp: 是否等待并返回响应数据,默认 False。
|
|
744
|
+
|
|
745
|
+
Returns:
|
|
746
|
+
Tuple[int, Optional[Any]]:
|
|
747
|
+
- 错误码,0 表示成功,非 0 表示失败或超时。
|
|
748
|
+
- 响应数据,当 wait_resp=True 且成功时返回数据,否则为 None。
|
|
749
|
+
"""
|
|
750
|
+
|
|
751
|
+
# 构建 CAN 请求结构体
|
|
752
|
+
can_request = BasicStructure.CanRequest()
|
|
753
|
+
can_request.unReqID = req_id
|
|
754
|
+
|
|
755
|
+
# 计算 MValue: [bit7=保留] [bit6=FD] [bit5=BRS] [bit4..1=DLC] [bit0=MType]
|
|
756
|
+
m_value = (is_fd << 6) | (is_brs << 5) | (dlc << 1) | m_type
|
|
757
|
+
can_request.ucMType = m_value
|
|
758
|
+
|
|
759
|
+
# 设置数据长度和数据内容
|
|
760
|
+
can_request.usDataLen = data_len
|
|
761
|
+
can_request.ucData = data
|
|
762
|
+
|
|
763
|
+
# 设置超时时间(单位:ms)
|
|
764
|
+
time_out = ctypes.c_ushort(6000)
|
|
765
|
+
|
|
766
|
+
# 发送请求
|
|
767
|
+
ret_code = dll.H3CANForward15765_2(
|
|
768
|
+
port_handle, channel_index, can_request, time_out
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
# 发送失败,直接返回错误码
|
|
772
|
+
if ret_code != 0:
|
|
773
|
+
return ret_code, None
|
|
774
|
+
|
|
775
|
+
# 发送成功,根据 wait_resp 决定是否等待响应
|
|
776
|
+
if not wait_resp:
|
|
777
|
+
return ret_code, None
|
|
778
|
+
|
|
779
|
+
# 等待响应(阻塞等待,超时时间 30 秒)
|
|
780
|
+
response_received = response_event.wait(timeout=30.0)
|
|
781
|
+
|
|
782
|
+
if not response_received:
|
|
783
|
+
# 超时返回特定错误码
|
|
784
|
+
return 0x2002, None
|
|
785
|
+
|
|
786
|
+
# 成功收到响应,返回响应数据
|
|
787
|
+
with response_lock:
|
|
788
|
+
return ret_code, current_response_data
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def dlc_to_len(dlc: int) -> int:
|
|
792
|
+
"""
|
|
793
|
+
将 CAN FD 的 DLC 值转换为实际数据长度。
|
|
794
|
+
|
|
795
|
+
Args:
|
|
796
|
+
dlc: DLC 编码值(0~15)。
|
|
797
|
+
|
|
798
|
+
Returns:
|
|
799
|
+
int: 对应的实际数据长度(0~64 字节)。
|
|
800
|
+
|
|
801
|
+
映射关系:
|
|
802
|
+
0~8 -> 0~8 字节
|
|
803
|
+
9 -> 12 字节
|
|
804
|
+
10 -> 16 字节
|
|
805
|
+
11 -> 20 字节
|
|
806
|
+
12 -> 24 字节
|
|
807
|
+
13 -> 32 字节
|
|
808
|
+
14 -> 48 字节
|
|
809
|
+
15 -> 64 字节
|
|
810
|
+
"""
|
|
811
|
+
# CAN FD DLC 到实际长度的映射表
|
|
812
|
+
dlc_length_map = {
|
|
813
|
+
9: 12,
|
|
814
|
+
10: 16,
|
|
815
|
+
11: 20,
|
|
816
|
+
12: 24,
|
|
817
|
+
13: 32,
|
|
818
|
+
14: 48,
|
|
819
|
+
15: 64,
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if dlc <= 8:
|
|
823
|
+
return dlc
|
|
824
|
+
|
|
825
|
+
return dlc_length_map.get(dlc, 64) # 默认返回最大值以容错
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
# def rx_msg(
|
|
829
|
+
# port_handle: int,
|
|
830
|
+
# rx_services: BasicStructure.RX_SERVICE,
|
|
831
|
+
# count: int,
|
|
832
|
+
# response_lock: threading.Lock,
|
|
833
|
+
# response_event: threading.Event,
|
|
834
|
+
# current_response_data: dict,
|
|
835
|
+
# ) -> int:
|
|
836
|
+
# """
|
|
837
|
+
# 获取接收队列报文并解析诊断响应内容。
|
|
838
|
+
#
|
|
839
|
+
# Args:
|
|
840
|
+
# port_handle: 端口句柄。
|
|
841
|
+
# rx_services: 写入报文信息的预分配结构体。
|
|
842
|
+
# count: 期望获取的报文数量。
|
|
843
|
+
# response_lock: 响应数据的线程锁。
|
|
844
|
+
# response_event: 响应事件,用于通知等待线程。
|
|
845
|
+
# current_response_data: 存储当前响应数据的字典(用于线程间共享)。
|
|
846
|
+
#
|
|
847
|
+
# Returns:
|
|
848
|
+
# int:
|
|
849
|
+
# 0 表示成功,
|
|
850
|
+
# 非 0 表示错误码。
|
|
851
|
+
# """
|
|
852
|
+
# # 获取接收到的报文数量
|
|
853
|
+
# rx_count = ctypes.c_uint(count)
|
|
854
|
+
# ret = dll.H3GetRxService(
|
|
855
|
+
# port_handle,
|
|
856
|
+
# ctypes.pointer(rx_services),
|
|
857
|
+
# ctypes.pointer(rx_count)
|
|
858
|
+
# )
|
|
859
|
+
#
|
|
860
|
+
# if ret != 0:
|
|
861
|
+
# print(f"接收报文获取失败!错误码: {ret}")
|
|
862
|
+
# return ret
|
|
863
|
+
#
|
|
864
|
+
# # 遍历所有接收到的报文
|
|
865
|
+
# rx_count_value = rx_count.value
|
|
866
|
+
# for i in range(rx_count_value):
|
|
867
|
+
# rx_msg = rx_services[i] # 假设可以通过索引访问
|
|
868
|
+
#
|
|
869
|
+
# # 检查是否为诊断响应报文(nServiceType == 4)
|
|
870
|
+
# if rx_msg.nServiceType != 4:
|
|
871
|
+
# continue
|
|
872
|
+
#
|
|
873
|
+
# # 提取 ISO-15765 诊断响应数据
|
|
874
|
+
# diag_msg = rx_msg.service.get15765_2_RespInd
|
|
875
|
+
#
|
|
876
|
+
# # 构建响应数据字典
|
|
877
|
+
# response_data = {
|
|
878
|
+
# 'data': diag_msg.ucData[:diag_msg.usLength],
|
|
879
|
+
# 'data_len': diag_msg.usLength,
|
|
880
|
+
# 'req_id': diag_msg.unEcuRespId,
|
|
881
|
+
# }
|
|
882
|
+
#
|
|
883
|
+
# # 使用锁保护共享数据的写入
|
|
884
|
+
# with response_lock:
|
|
885
|
+
# current_response_data.update(response_data)
|
|
886
|
+
#
|
|
887
|
+
# # 判断是否为"正在处理"响应(0x78 表示待处理响应)
|
|
888
|
+
# data_array = current_response_data['data']
|
|
889
|
+
# is_pending_response = (
|
|
890
|
+
# len(data_array) >= 3 and
|
|
891
|
+
# data_array[2] == 0x78
|
|
892
|
+
# )
|
|
893
|
+
#
|
|
894
|
+
# if not is_pending_response:
|
|
895
|
+
# # 收到最终响应,通知等待线程
|
|
896
|
+
# response_event.set()
|
|
897
|
+
# else:
|
|
898
|
+
# print("收到 0x78 待处理响应,继续等待...")
|
|
899
|
+
#
|
|
900
|
+
# return 0
|
|
901
|
+
def rx_msg(port_handle: int, count: int = 1, timeout_ms: int = 0) -> List[Dict]:
|
|
902
|
+
"""
|
|
903
|
+
获取接收队列报文,返回原生 Python 字典列表。
|
|
904
|
+
|
|
905
|
+
Returns:
|
|
906
|
+
List[Dict]: 每个字典包含 nServiceType 及对应的解析后数据。
|
|
907
|
+
CAN 帧示例:{'type': 8, 'channel':..., 'id':..., 'data':..., ...}
|
|
908
|
+
Diag 帧示例:{'type': 4, 'data':..., 'data_len':..., 'req_id':...}
|
|
909
|
+
"""
|
|
910
|
+
# 分配结构体(此操作必须保留,但对外不可见)
|
|
911
|
+
rx_array = (BasicStructure.RX_SERVICE * count)()
|
|
912
|
+
rx_count = ctypes.c_uint(count)
|
|
913
|
+
ret = dll.H3GetRxService(
|
|
914
|
+
ctypes.c_size_t(port_handle),
|
|
915
|
+
ctypes.pointer(rx_array),
|
|
916
|
+
ctypes.pointer(rx_count),
|
|
917
|
+
)
|
|
918
|
+
if ret != 0:
|
|
919
|
+
return []
|
|
920
|
+
|
|
921
|
+
results = []
|
|
922
|
+
for i in range(rx_count.value):
|
|
923
|
+
svc = rx_array[i]
|
|
924
|
+
st = svc.nServiceType
|
|
925
|
+
item = {'nServiceType': st, 'ucChannelIndex': svc.ucChannelIndex}
|
|
926
|
+
if st == 8: # CAN/CANFD
|
|
927
|
+
t = svc.service.getCanTrace
|
|
928
|
+
item.update({
|
|
929
|
+
'can_id': t.unCanId,
|
|
930
|
+
'is_extended': bool((t.unCanId >> 31) & 1),
|
|
931
|
+
'is_fd': bool(t.ucEDL),
|
|
932
|
+
'brs': bool(t.ucBRS),
|
|
933
|
+
'dlc': t.ucDLC,
|
|
934
|
+
'data': bytes(t.ucData[:64]), # 全部 64 字节,上层按 DLC 截断
|
|
935
|
+
'tag': t.ucTag,
|
|
936
|
+
'timestamp': t.unTimestamp,
|
|
937
|
+
})
|
|
938
|
+
elif st == 4: # Diag response
|
|
939
|
+
d = svc.service.get15765_2_RespInd
|
|
940
|
+
item.update({
|
|
941
|
+
'data': d.ucData[:d.usLength],
|
|
942
|
+
'data_len': d.usLength,
|
|
943
|
+
'req_id': d.unEcuRespId,
|
|
944
|
+
})
|
|
945
|
+
elif st == 0: # Error
|
|
946
|
+
e = svc.service.getBusError
|
|
947
|
+
item.update({
|
|
948
|
+
'error_type': e.ucErrType,
|
|
949
|
+
'timestamp': e.unTimestamp,
|
|
950
|
+
})
|
|
951
|
+
# 可继续扩展 LIN 等其他类型...
|
|
952
|
+
results.append(item)
|
|
953
|
+
return results
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
def rx_msg_print_all(port_handle: int, count: int = 1000) -> int:
|
|
957
|
+
"""
|
|
958
|
+
从端口获取接收报文并打印所有细节(CAN/CAN FD/LIN/Error/Ethernet等)。
|
|
959
|
+
本函数主要供调试和监控使用,会直接在终端输出报文信息。
|
|
960
|
+
|
|
961
|
+
Args:
|
|
962
|
+
port_handle (int): 端口句柄。
|
|
963
|
+
count (int): 每次请求最大报文数,默认 1000。
|
|
964
|
+
|
|
965
|
+
Returns:
|
|
966
|
+
int: 0 表示成功,非 0 表示错误码。
|
|
967
|
+
"""
|
|
968
|
+
rx_services = (BasicStructure.RX_SERVICE * count)()
|
|
969
|
+
rx_count = ctypes.c_uint(count)
|
|
970
|
+
|
|
971
|
+
ret = dll.H3GetRxService(
|
|
972
|
+
ctypes.c_size_t(port_handle),
|
|
973
|
+
ctypes.pointer(rx_services),
|
|
974
|
+
ctypes.pointer(rx_count),
|
|
975
|
+
)
|
|
976
|
+
if ret != 0:
|
|
977
|
+
print(f"获取报文失败!错误码: {ret}")
|
|
978
|
+
return ret
|
|
979
|
+
|
|
980
|
+
rx_count_val = rx_count.value
|
|
981
|
+
for i in range(rx_count_val):
|
|
982
|
+
svc = rx_services[i]
|
|
983
|
+
st = svc.nServiceType
|
|
984
|
+
|
|
985
|
+
if st == 8: # CAN / CAN FD 报文
|
|
986
|
+
trace = svc.service.getCanTrace
|
|
987
|
+
msg_id = trace.unCanId
|
|
988
|
+
is_ext = (msg_id >> 31) & 1
|
|
989
|
+
can_id = msg_id & 0x7FFFFFFF
|
|
990
|
+
is_fd = trace.ucEDL & 1
|
|
991
|
+
brs = trace.ucBRS & 1
|
|
992
|
+
dlc = trace.ucDLC
|
|
993
|
+
data_len = dlc_to_len(dlc)
|
|
994
|
+
data_str = ','.join(f"{b:02X}" for b in trace.ucData[:data_len])
|
|
995
|
+
print(
|
|
996
|
+
f"[CAN{' FD' if is_fd else ''}] "
|
|
997
|
+
f"Ch={svc.ucChannelIndex} "
|
|
998
|
+
f"ID=0x{can_id:X}{' (Ext)' if is_ext else ''} "
|
|
999
|
+
f"Ts={trace.unTimestamp} "
|
|
1000
|
+
f"DLC={dlc} "
|
|
1001
|
+
f"BRS={brs} "
|
|
1002
|
+
f"Dir={'RX' if trace.ucTag else 'TX'} "
|
|
1003
|
+
f"Data=[{data_str}]"
|
|
1004
|
+
)
|
|
1005
|
+
elif st == 9: # LIN 报文
|
|
1006
|
+
lin = svc.service.getLINMsg
|
|
1007
|
+
print(
|
|
1008
|
+
f"[LIN] Ch={svc.ucChannelIndex} "
|
|
1009
|
+
f"PID=0x{lin.ucPID:02X} "
|
|
1010
|
+
f"Dir={'Master' if lin.ucDir else 'Slave'} "
|
|
1011
|
+
f"Ts={lin.unTimestamp} "
|
|
1012
|
+
f"Data=[{','.join(f'{b:02X}' for b in lin.ucData[:lin.ucDLC])}] "
|
|
1013
|
+
f"Checksum=0x{lin.ucChecksum:02X}"
|
|
1014
|
+
)
|
|
1015
|
+
elif st == 6 or st == 7: # 错误帧/事件
|
|
1016
|
+
err = svc.service.getErrorInfo
|
|
1017
|
+
print(f"[ERROR] Ch={svc.ucChannelIndex} ErrorType={err.ucErrType} "
|
|
1018
|
+
f"Ts={err.unTimestamp}")
|
|
1019
|
+
elif st == 10: # 以太网(示例)
|
|
1020
|
+
eth = svc.service.getEthMsg
|
|
1021
|
+
print(f"[ETH] Ch={svc.ucChannelIndex} Len={eth.unDataLength} "
|
|
1022
|
+
f"Ts={eth.unTimestamp}")
|
|
1023
|
+
else:
|
|
1024
|
+
# 其它未知类型可按需扩展
|
|
1025
|
+
print(f"[TYPE={st}] Ch={svc.ucChannelIndex} (raw data omitted)")
|
|
1026
|
+
|
|
1027
|
+
return 0
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
def fetch_rx_services(port_handle: int, count: int = 1):
|
|
1031
|
+
"""
|
|
1032
|
+
从接收队列获取报文,返回 Python 原生字典列表。
|
|
1033
|
+
该函数封装了 H3GetRxService 的 DLL 调用和 ctypes 结构体操作。
|
|
1034
|
+
"""
|
|
1035
|
+
rx_array = (BasicStructure.RX_SERVICE * count)()
|
|
1036
|
+
rx_count = ctypes.c_uint(count)
|
|
1037
|
+
|
|
1038
|
+
ret = dll.H3GetRxService(
|
|
1039
|
+
ctypes.c_size_t(port_handle),
|
|
1040
|
+
ctypes.pointer(rx_array),
|
|
1041
|
+
ctypes.pointer(rx_count),
|
|
1042
|
+
)
|
|
1043
|
+
if ret != 0:
|
|
1044
|
+
return []
|
|
1045
|
+
|
|
1046
|
+
results = []
|
|
1047
|
+
for i in range(rx_count.value):
|
|
1048
|
+
svc = rx_array[i]
|
|
1049
|
+
st = svc.nServiceType
|
|
1050
|
+
item = {
|
|
1051
|
+
'nServiceType': st,
|
|
1052
|
+
'ucChannelIndex': svc.ucChannelIndex,
|
|
1053
|
+
}
|
|
1054
|
+
if st == 8: # CAN / CANFD
|
|
1055
|
+
t = svc.service.getCanTrace
|
|
1056
|
+
item.update({
|
|
1057
|
+
'can_id': t.unCanId,
|
|
1058
|
+
'is_extended': bool((t.unCanId >> 31) & 1),
|
|
1059
|
+
'is_fd': bool(t.ucEDL),
|
|
1060
|
+
'brs': bool(t.ucBRS),
|
|
1061
|
+
'dlc': t.ucDLC,
|
|
1062
|
+
'data': bytes(t.ucData[:64]),
|
|
1063
|
+
'tag': t.ucTag,
|
|
1064
|
+
'timestamp': t.unTimestamp,
|
|
1065
|
+
})
|
|
1066
|
+
elif st == 4: # 15765 诊断响应
|
|
1067
|
+
d = svc.service.get15765_2_RespInd
|
|
1068
|
+
item.update({
|
|
1069
|
+
'data': d.ucData[:d.usLength],
|
|
1070
|
+
'data_len': d.usLength,
|
|
1071
|
+
'req_id': d.unEcuRespId,
|
|
1072
|
+
})
|
|
1073
|
+
elif st == 0: # 总线错误
|
|
1074
|
+
e = svc.service.getBusError
|
|
1075
|
+
item.update({
|
|
1076
|
+
'error_type': e.ucErrType,
|
|
1077
|
+
'timestamp': e.unTimestamp,
|
|
1078
|
+
})
|
|
1079
|
+
results.append(item)
|
|
1080
|
+
return results
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
if __name__ == '__main__':
|
|
1084
|
+
driver = get_driver_config()
|