pyxcp 0.23.3__cp312-cp312-win_arm64.whl → 0.25.6__cp312-cp312-win_arm64.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.
- pyxcp/__init__.py +1 -1
- pyxcp/asamkeydll.exe +0 -0
- pyxcp/cmdline.py +15 -30
- pyxcp/config/__init__.py +73 -20
- pyxcp/cpp_ext/aligned_buffer.hpp +168 -0
- pyxcp/cpp_ext/bin.hpp +7 -6
- pyxcp/cpp_ext/cpp_ext.cp310-win_arm64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp311-win_arm64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp312-win_arm64.pyd +0 -0
- pyxcp/cpp_ext/daqlist.hpp +241 -73
- pyxcp/cpp_ext/extension_wrapper.cpp +123 -15
- pyxcp/cpp_ext/framing.hpp +360 -0
- pyxcp/cpp_ext/mcobject.hpp +5 -3
- pyxcp/cpp_ext/sxi_framing.hpp +332 -0
- pyxcp/daq_stim/__init__.py +182 -45
- pyxcp/daq_stim/optimize/binpacking.py +2 -2
- pyxcp/daq_stim/scheduler.cpp +8 -8
- pyxcp/daq_stim/stim.cp310-win_arm64.pyd +0 -0
- pyxcp/daq_stim/stim.cp311-win_arm64.pyd +0 -0
- pyxcp/daq_stim/stim.cp312-win_arm64.pyd +0 -0
- pyxcp/errormatrix.py +2 -2
- pyxcp/examples/run_daq.py +5 -3
- pyxcp/examples/xcp_policy.py +6 -6
- pyxcp/examples/xcp_read_benchmark.py +2 -2
- pyxcp/examples/xcp_skel.py +1 -2
- pyxcp/examples/xcp_unlock.py +10 -12
- pyxcp/examples/xcp_user_supplied_driver.py +1 -2
- pyxcp/examples/xcphello.py +2 -15
- pyxcp/examples/xcphello_recorder.py +2 -2
- pyxcp/master/__init__.py +1 -0
- pyxcp/master/errorhandler.py +248 -13
- pyxcp/master/master.py +838 -250
- pyxcp/recorder/.idea/.gitignore +8 -0
- pyxcp/recorder/.idea/misc.xml +4 -0
- pyxcp/recorder/.idea/modules.xml +8 -0
- pyxcp/recorder/.idea/recorder.iml +6 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +7 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/index.pb +7 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/index.pb +7 -0
- pyxcp/recorder/.idea/vcs.xml +10 -0
- pyxcp/recorder/__init__.py +5 -10
- pyxcp/recorder/converter/__init__.py +4 -10
- pyxcp/recorder/reader.hpp +0 -1
- pyxcp/recorder/reco.py +1 -0
- pyxcp/recorder/rekorder.cp310-win_arm64.pyd +0 -0
- pyxcp/recorder/rekorder.cp311-win_arm64.pyd +0 -0
- pyxcp/recorder/rekorder.cp312-win_arm64.pyd +0 -0
- pyxcp/recorder/unfolder.hpp +129 -107
- pyxcp/recorder/wrap.cpp +3 -8
- pyxcp/scripts/xcp_fetch_a2l.py +2 -2
- pyxcp/scripts/xcp_id_scanner.py +1 -2
- pyxcp/scripts/xcp_info.py +66 -51
- pyxcp/scripts/xcp_profile.py +1 -2
- pyxcp/tests/test_daq.py +1 -1
- pyxcp/tests/test_framing.py +262 -0
- pyxcp/tests/test_master.py +210 -100
- pyxcp/tests/test_transport.py +138 -42
- pyxcp/timing.py +1 -1
- pyxcp/transport/__init__.py +8 -5
- pyxcp/transport/base.py +187 -143
- pyxcp/transport/can.py +117 -13
- pyxcp/transport/eth.py +55 -20
- pyxcp/transport/hdf5_policy.py +167 -0
- pyxcp/transport/sxi.py +126 -52
- pyxcp/transport/transport_ext.cp310-win_arm64.pyd +0 -0
- pyxcp/transport/transport_ext.cp311-win_arm64.pyd +0 -0
- pyxcp/transport/transport_ext.cp312-win_arm64.pyd +0 -0
- pyxcp/transport/transport_ext.hpp +214 -0
- pyxcp/transport/transport_wrapper.cpp +249 -0
- pyxcp/transport/usb_transport.py +47 -31
- pyxcp/types.py +0 -13
- pyxcp/{utils.py → utils/__init__.py} +3 -4
- pyxcp/utils/cli.py +78 -0
- pyxcp-0.25.6.dist-info/METADATA +341 -0
- pyxcp-0.25.6.dist-info/RECORD +153 -0
- {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info}/WHEEL +1 -1
- pyxcp/examples/conf_sxi.json +0 -9
- pyxcp/examples/conf_sxi.toml +0 -7
- pyxcp-0.23.3.dist-info/METADATA +0 -219
- pyxcp-0.23.3.dist-info/RECORD +0 -131
- {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info}/entry_points.txt +0 -0
- {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info/licenses}/LICENSE +0 -0
pyxcp/daq_stim/__init__.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
import json
|
|
4
5
|
from time import time_ns
|
|
5
|
-
from typing import Dict, List, Optional, TextIO
|
|
6
|
+
from typing import Any, Dict, List, Optional, TextIO, Tuple, Union
|
|
7
|
+
|
|
8
|
+
from pyxcp.cpp_ext.cpp_ext import DaqList, PredefinedDaqList
|
|
6
9
|
|
|
7
10
|
from pyxcp import types
|
|
8
11
|
from pyxcp.config import get_application
|
|
9
|
-
from pyxcp.cpp_ext.cpp_ext import DaqList
|
|
10
12
|
from pyxcp.daq_stim.optimize import make_continuous_blocks
|
|
11
13
|
from pyxcp.daq_stim.optimize.binpacking import first_fit_decreasing
|
|
12
14
|
from pyxcp.recorder import DaqOnlinePolicy as _DaqOnlinePolicy
|
|
@@ -14,7 +16,6 @@ from pyxcp.recorder import DaqRecorderPolicy as _DaqRecorderPolicy
|
|
|
14
16
|
from pyxcp.recorder import MeasurementParameters
|
|
15
17
|
from pyxcp.utils import CurrentDatetime
|
|
16
18
|
|
|
17
|
-
|
|
18
19
|
DAQ_ID_FIELD_SIZE = {
|
|
19
20
|
"IDF_ABS_ODT_NUMBER": 1,
|
|
20
21
|
"IDF_REL_ODT_NUMBER_ABS_DAQ_LIST_NUMBER_BYTE": 2,
|
|
@@ -29,11 +30,90 @@ DAQ_TIMESTAMP_SIZE = {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
def load_daq_lists_from_json(file_path: str) -> List[DaqList]:
|
|
34
|
+
"""Load and validate DAQ-list from JSON file."""
|
|
35
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
36
|
+
config = json.load(f)
|
|
37
|
+
|
|
38
|
+
if not isinstance(config, list):
|
|
39
|
+
raise ValueError("DAQ configuration must be a JSON array (list)")
|
|
40
|
+
|
|
41
|
+
daq_lists: List[DaqList] = []
|
|
42
|
+
for idx, entry in enumerate(config):
|
|
43
|
+
if not isinstance(entry, dict):
|
|
44
|
+
raise TypeError(f"Entry {idx} must be an object/dict")
|
|
45
|
+
|
|
46
|
+
required = {"name", "event_num", "stim", "enable_timestamps", "measurements", "priority", "prescaler"}
|
|
47
|
+
missing = required - set(entry.keys())
|
|
48
|
+
if missing:
|
|
49
|
+
raise ValueError(f"Entry {idx} missing required keys: {missing}")
|
|
50
|
+
|
|
51
|
+
# Basic type conversions / checks
|
|
52
|
+
name = entry["name"]
|
|
53
|
+
if not isinstance(name, str):
|
|
54
|
+
raise TypeError(f"Entry {idx} 'name' must be a string")
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
event_num = int(entry["event_num"])
|
|
58
|
+
except Exception as e:
|
|
59
|
+
raise TypeError(f"Entry {idx} 'event_num' must be an integer") from e
|
|
60
|
+
|
|
61
|
+
stim = bool(entry["stim"])
|
|
62
|
+
enable_timestamps = bool(entry["enable_timestamps"])
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
priority = int(entry["priority"])
|
|
66
|
+
prescaler = int(entry["prescaler"])
|
|
67
|
+
except Exception as e:
|
|
68
|
+
raise TypeError(f"Entry {idx} 'priority' and 'prescaler' must be integers") from e
|
|
69
|
+
|
|
70
|
+
measurements_raw = entry["measurements"]
|
|
71
|
+
if not isinstance(measurements_raw, list):
|
|
72
|
+
raise TypeError(f"Entry {idx} 'measurements' must be a list")
|
|
73
|
+
|
|
74
|
+
measurements: List[Tuple[str, int, int, str]] = []
|
|
75
|
+
for m_idx, m in enumerate(measurements_raw):
|
|
76
|
+
if not (isinstance(m, (list, tuple)) and len(m) == 4):
|
|
77
|
+
raise ValueError(f"Entry {idx} measurement {m_idx} must be a 4-element list/tuple")
|
|
78
|
+
m_name, m_addr, m_offset, m_type = m
|
|
79
|
+
|
|
80
|
+
if not isinstance(m_name, str):
|
|
81
|
+
raise TypeError(f"Entry {idx} measurement {m_idx} name must be a string")
|
|
82
|
+
try:
|
|
83
|
+
m_addr = int(m_addr)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
raise TypeError(f"Entry {idx} measurement {m_idx} address must be an integer") from e
|
|
86
|
+
try:
|
|
87
|
+
m_offset = int(m_offset)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
raise TypeError(f"Entry {idx} measurement {m_idx} offset must be an integer") from e
|
|
90
|
+
if not isinstance(m_type, str):
|
|
91
|
+
raise TypeError(f"Entry {idx} measurement {m_idx} type must be a string")
|
|
92
|
+
|
|
93
|
+
measurements.append((m_name, m_addr, m_offset, m_type))
|
|
94
|
+
|
|
95
|
+
daq_kwargs: Dict[str, Any] = {
|
|
96
|
+
"name": name,
|
|
97
|
+
"event_num": event_num,
|
|
98
|
+
"stim": stim,
|
|
99
|
+
"enable_timestamps": enable_timestamps,
|
|
100
|
+
"measurements": measurements,
|
|
101
|
+
"priority": priority,
|
|
102
|
+
"prescaler": prescaler,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
daq_lists.append(DaqList(**daq_kwargs))
|
|
106
|
+
|
|
107
|
+
return daq_lists
|
|
108
|
+
|
|
109
|
+
|
|
32
110
|
class DaqProcessor:
|
|
33
|
-
def __init__(self, daq_lists: List[DaqList]):
|
|
34
|
-
# super().__init__()
|
|
111
|
+
def __init__(self, daq_lists: List[Union[DaqList, PredefinedDaqList]]):
|
|
35
112
|
self.daq_lists = daq_lists
|
|
113
|
+
self.is_predefined = [isinstance(d, PredefinedDaqList) for d in daq_lists]
|
|
36
114
|
self.log = get_application().log
|
|
115
|
+
# Flag indicating a fatal OS-level error occurred during DAQ (e.g., disk full, out-of-memory)
|
|
116
|
+
self._fatal_os_error: bool = False
|
|
37
117
|
|
|
38
118
|
def setup(self, start_datetime: Optional[CurrentDatetime] = None, write_multiple: bool = True):
|
|
39
119
|
if not self.xcp_master.slaveProperties.supportsDaq:
|
|
@@ -42,13 +122,14 @@ class DaqProcessor:
|
|
|
42
122
|
if start_datetime is None:
|
|
43
123
|
start_datetime = CurrentDatetime(time_ns())
|
|
44
124
|
self.start_datetime = start_datetime
|
|
45
|
-
# print(self.start_datetime)
|
|
46
125
|
try:
|
|
47
126
|
processor = self.daq_info.get("processor")
|
|
48
127
|
properties = processor.get("properties")
|
|
49
128
|
resolution = self.daq_info.get("resolution")
|
|
50
|
-
if properties["configType"] == "STATIC":
|
|
51
|
-
raise TypeError(
|
|
129
|
+
if properties["configType"] == "STATIC" and not all(self.is_predefined):
|
|
130
|
+
raise TypeError(
|
|
131
|
+
"DAQ configuration is static, but in your configuration are only dynamic DAQ lists -- cannot proceed."
|
|
132
|
+
)
|
|
52
133
|
self.supports_timestampes = properties["timestampSupported"]
|
|
53
134
|
self.supports_prescaler = properties["prescalerSupported"]
|
|
54
135
|
self.supports_pid_off = properties["pidOffSupported"]
|
|
@@ -71,22 +152,24 @@ class DaqProcessor:
|
|
|
71
152
|
max_payload_size = min(max_odt_entry_size, max_dto - header_len)
|
|
72
153
|
# First ODT may contain timestamp.
|
|
73
154
|
self.selectable_timestamps = False
|
|
155
|
+
max_payload_size_first = max_payload_size
|
|
74
156
|
if not self.supports_timestampes:
|
|
75
|
-
|
|
76
|
-
# print("NO TIMESTAMP SUPPORT")
|
|
157
|
+
self.log.info("No timestamp support")
|
|
77
158
|
else:
|
|
78
159
|
if self.ts_fixed:
|
|
79
|
-
|
|
160
|
+
self.log.debug("Fixed timestamps")
|
|
80
161
|
max_payload_size_first = max_payload_size - self.ts_size
|
|
81
162
|
else:
|
|
82
|
-
|
|
163
|
+
self.log.debug("Variable timestamps.")
|
|
83
164
|
self.selectable_timestamps = True
|
|
84
|
-
|
|
85
165
|
except Exception as e:
|
|
86
166
|
raise TypeError(f"DAQ_INFO corrupted: {e}") from e
|
|
87
167
|
|
|
88
168
|
# DAQ optimization.
|
|
89
|
-
|
|
169
|
+
# For dynamic DaqList instances, compute physical layout; skip for PredefinedDaqList.
|
|
170
|
+
for idx, daq_list in enumerate(self.daq_lists):
|
|
171
|
+
if isinstance(daq_list, PredefinedDaqList):
|
|
172
|
+
continue
|
|
90
173
|
if self.selectable_timestamps:
|
|
91
174
|
if daq_list.enable_timestamps:
|
|
92
175
|
max_payload_size_first = max_payload_size - self.ts_size
|
|
@@ -97,34 +180,47 @@ class DaqProcessor:
|
|
|
97
180
|
byte_order = 0 if self.xcp_master.slaveProperties.byteOrder == "INTEL" else 1
|
|
98
181
|
self._first_pids = []
|
|
99
182
|
daq_count = len(self.daq_lists)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
183
|
+
|
|
184
|
+
# Decide whether DAQ allocation must be performed.
|
|
185
|
+
config_static = self.daq_info.get("processor", {}).get("properties", {}).get("configType") == "STATIC"
|
|
186
|
+
|
|
187
|
+
if not config_static:
|
|
188
|
+
# For dynamic configuration, program only dynamic (non-predefined) DAQ lists.
|
|
189
|
+
self.xcp_master.freeDaq()
|
|
190
|
+
# Allocate the number of DAQ lists required.
|
|
191
|
+
self.xcp_master.allocDaq(daq_count)
|
|
192
|
+
measurement_list = []
|
|
193
|
+
for i, daq_list in enumerate(self.daq_lists, self.min_daq):
|
|
194
|
+
if isinstance(daq_list, PredefinedDaqList):
|
|
195
|
+
# Skip allocation for predefined DAQ lists.
|
|
196
|
+
continue
|
|
197
|
+
measurements = daq_list.measurements_opt
|
|
198
|
+
measurement_list.append((i, measurements))
|
|
199
|
+
odt_count = len(measurements)
|
|
200
|
+
self.xcp_master.allocOdt(i, odt_count)
|
|
201
|
+
# Iterate again over ODT entries -- we need to respect sequencing requirements.
|
|
202
|
+
for i, measurements in measurement_list:
|
|
203
|
+
for j, measurement in enumerate(measurements):
|
|
204
|
+
entry_count = len(measurement.entries)
|
|
205
|
+
self.xcp_master.allocOdtEntry(i, j, entry_count)
|
|
206
|
+
# Write DAQs (only for dynamic lists)
|
|
207
|
+
for i, daq_list in enumerate(self.daq_lists, self.min_daq):
|
|
208
|
+
if isinstance(daq_list, PredefinedDaqList):
|
|
209
|
+
continue
|
|
210
|
+
measurements = daq_list.measurements_opt
|
|
211
|
+
for j, measurement in enumerate(measurements):
|
|
212
|
+
if len(measurement.entries) == 0:
|
|
213
|
+
continue # CAN special case: No room for data in first ODT.
|
|
214
|
+
self.xcp_master.setDaqPtr(i, j, 0)
|
|
215
|
+
for entry in measurement.entries:
|
|
216
|
+
self.xcp_master.writeDaq(0xFF, entry.length, entry.ext, entry.address)
|
|
217
|
+
else:
|
|
218
|
+
# STATIC configuration on the slave: skip allocation and programming; lists/ODTs are predefined.
|
|
219
|
+
pass
|
|
124
220
|
|
|
125
221
|
# arm DAQ lists -- this is technically a function on its own.
|
|
126
|
-
|
|
127
|
-
|
|
222
|
+
first_daq_list = 0 if config_static else self.min_daq
|
|
223
|
+
for i, daq_list in enumerate(self.daq_lists, first_daq_list):
|
|
128
224
|
mode = 0x00
|
|
129
225
|
if self.supports_timestampes and (self.ts_fixed or (self.selectable_timestamps and daq_list.enable_timestamps)):
|
|
130
226
|
mode = 0x10
|
|
@@ -142,8 +238,6 @@ class DaqProcessor:
|
|
|
142
238
|
)
|
|
143
239
|
res = self.xcp_master.startStopDaqList(0x02, i)
|
|
144
240
|
self._first_pids.append(res.firstPid)
|
|
145
|
-
if start_datetime:
|
|
146
|
-
pass
|
|
147
241
|
self.measurement_params = MeasurementParameters(
|
|
148
242
|
byte_order,
|
|
149
243
|
header_len,
|
|
@@ -164,6 +258,26 @@ class DaqProcessor:
|
|
|
164
258
|
self.xcp_master.startStopSynch(0x01)
|
|
165
259
|
|
|
166
260
|
def stop(self):
|
|
261
|
+
# If a fatal OS error occurred during acquisition, skip sending stop to the slave to avoid
|
|
262
|
+
# cascading timeouts/unrecoverable errors and shut down transport gracefully instead.
|
|
263
|
+
if getattr(self, "_fatal_os_error", False):
|
|
264
|
+
with suppress(Exception):
|
|
265
|
+
self.log.error(
|
|
266
|
+
"DAQ stop skipped due to previous fatal OS error (e.g., disk full or out-of-memory). Closing transport."
|
|
267
|
+
)
|
|
268
|
+
try:
|
|
269
|
+
# Best-effort: stop listener and close transport so threads finish cleanly.
|
|
270
|
+
if hasattr(self.xcp_master, "transport") and self.xcp_master.transport is not None:
|
|
271
|
+
# Signal listeners to stop
|
|
272
|
+
with suppress(Exception):
|
|
273
|
+
if hasattr(self.xcp_master.transport, "closeEvent"):
|
|
274
|
+
self.xcp_master.transport.closeEvent.set()
|
|
275
|
+
|
|
276
|
+
# Close transport connection
|
|
277
|
+
with suppress(Exception):
|
|
278
|
+
self.xcp_master.transport.close()
|
|
279
|
+
finally:
|
|
280
|
+
return
|
|
167
281
|
self.xcp_master.startStopSynch(0x00)
|
|
168
282
|
|
|
169
283
|
def first_pids(self):
|
|
@@ -171,7 +285,6 @@ class DaqProcessor:
|
|
|
171
285
|
|
|
172
286
|
|
|
173
287
|
class DaqRecorder(DaqProcessor, _DaqRecorderPolicy):
|
|
174
|
-
|
|
175
288
|
def __init__(self, daq_lists: List[DaqList], file_name: str, prealloc: int = 200, chunk_size: int = 1):
|
|
176
289
|
DaqProcessor.__init__(self, daq_lists)
|
|
177
290
|
_DaqRecorderPolicy.__init__(self)
|
|
@@ -219,7 +332,31 @@ class DaqToCsv(DaqOnlinePolicy):
|
|
|
219
332
|
out_file.write(f"{hdr}\n")
|
|
220
333
|
|
|
221
334
|
def on_daq_list(self, daq_list: int, timestamp0: int, timestamp1: int, payload: list):
|
|
222
|
-
|
|
335
|
+
# Guard against hard OS errors (e.g., disk full) during file writes.
|
|
336
|
+
if getattr(self, "_fatal_os_error", False):
|
|
337
|
+
return
|
|
338
|
+
try:
|
|
339
|
+
self.files[daq_list].write(f"{timestamp0},{timestamp1},{', '.join([str(x) for x in payload])}\n")
|
|
340
|
+
except (OSError, MemoryError) as ex:
|
|
341
|
+
# Mark fatal condition to alter shutdown path and avoid further writes/commands.
|
|
342
|
+
self._fatal_os_error = True
|
|
343
|
+
with suppress(Exception):
|
|
344
|
+
self.log.critical(f"DAQ file write failed: {ex.__class__.__name__}: {ex}. Initiating graceful shutdown.")
|
|
345
|
+
|
|
346
|
+
# Stop listener to prevent more DAQ traffic and avoid thread crashes.
|
|
347
|
+
with suppress(Exception):
|
|
348
|
+
if hasattr(self.xcp_master, "transport") and self.xcp_master.transport is not None:
|
|
349
|
+
if hasattr(self.xcp_master.transport, "closeEvent"):
|
|
350
|
+
self.xcp_master.transport.closeEvent.set()
|
|
351
|
+
# Best-effort: close any opened files to flush buffers and release resources.
|
|
352
|
+
with suppress(Exception):
|
|
353
|
+
for f in getattr(self, "files", {}).values():
|
|
354
|
+
with suppress(Exception):
|
|
355
|
+
f.flush()
|
|
356
|
+
with suppress(Exception):
|
|
357
|
+
f.close()
|
|
358
|
+
# Do not re-raise; allow the system to continue to a controlled shutdown.
|
|
359
|
+
return
|
|
223
360
|
|
|
224
361
|
def finalize(self):
|
|
225
362
|
self.log.debug("DaqCsv::finalize()")
|
pyxcp/daq_stim/scheduler.cpp
CHANGED
|
@@ -10,10 +10,10 @@ VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) {
|
|
|
10
10
|
std::printf("TimerRoutine lpParam is NULL\n");
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
const auto* param = static_cast<const int*>(lpParam);
|
|
15
15
|
std::printf("Timer routine called. Parameter is %d.\n", *param);
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
if (TimerOrWaitFired) {
|
|
18
18
|
std::printf("The wait timed out.\n");
|
|
19
19
|
} else {
|
|
@@ -30,10 +30,10 @@ namespace {
|
|
|
30
30
|
|
|
31
31
|
#if defined(_M_X64) || defined(_M_IX86) || defined(__SSE__)
|
|
32
32
|
#include <xmmintrin.h>
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
void mul4_vectorized(float* ptr) {
|
|
35
35
|
if (ptr == nullptr) return;
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
__m128 f = _mm_loadu_ps(ptr);
|
|
38
38
|
f = _mm_mul_ps(f, f);
|
|
39
39
|
_mm_storeu_ps(ptr, f);
|
|
@@ -41,10 +41,10 @@ namespace {
|
|
|
41
41
|
|
|
42
42
|
#elif defined(_M_ARM64) || defined(__ARM_NEON)
|
|
43
43
|
#include <arm_neon.h>
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
void mul4_vectorized(float* ptr) {
|
|
46
46
|
if (ptr == nullptr) return;
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
float32x4_t f = vld1q_f32(ptr);
|
|
49
49
|
f = vmulq_f32(f, f);
|
|
50
50
|
vst1q_f32(ptr, f);
|
|
@@ -54,9 +54,9 @@ namespace {
|
|
|
54
54
|
// Scalar fallback
|
|
55
55
|
void mul4_vectorized(float* ptr) {
|
|
56
56
|
if (ptr == nullptr) return;
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
for (size_t i = 0; i < VECTOR_SIZE; ++i) {
|
|
59
59
|
ptr[i] *= ptr[i];
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
#endif
|
|
62
|
+
#endif
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
pyxcp/errormatrix.py
CHANGED
pyxcp/examples/run_daq.py
CHANGED
|
@@ -129,8 +129,9 @@ else:
|
|
|
129
129
|
),
|
|
130
130
|
]
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
# daq_parser =
|
|
132
|
+
|
|
133
|
+
# daq_parser = DaqToCsv(DAQ_LISTS) # Record to CSV file(s).
|
|
134
|
+
daq_parser = DaqRecorder(DAQ_LISTS, "run_daq_21092025_01", 8) # Record to ".xmraw" file.
|
|
134
135
|
|
|
135
136
|
with ap.run(policy=daq_parser) as x:
|
|
136
137
|
try:
|
|
@@ -149,7 +150,8 @@ with ap.run(policy=daq_parser) as x:
|
|
|
149
150
|
print("start DAQ lists.")
|
|
150
151
|
daq_parser.start() # Start DAQ lists.
|
|
151
152
|
|
|
152
|
-
time.sleep(
|
|
153
|
+
# time.sleep(2.0 * 60.0)
|
|
154
|
+
time.sleep(0.25 * 60.0 * 60.0) # Run for 15 minutes.
|
|
153
155
|
|
|
154
156
|
print("Stop DAQ....")
|
|
155
157
|
daq_parser.stop() # Stop DAQ lists.
|
pyxcp/examples/xcp_policy.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
"""Demostrates how to use frame recording policies including recorder extension.
|
|
3
|
-
|
|
2
|
+
"""Demostrates how to use frame recording policies including recorder extension."""
|
|
3
|
+
|
|
4
4
|
from pprint import pprint
|
|
5
5
|
|
|
6
6
|
from pyxcp.cmdline import ArgumentParser
|
|
@@ -11,11 +11,11 @@ ap = ArgumentParser(description="pyXCP frame recording policy example.")
|
|
|
11
11
|
|
|
12
12
|
LOG_FILE = "pyxcp"
|
|
13
13
|
|
|
14
|
-
policy = FrameRecorderPolicy(LOG_FILE)
|
|
15
|
-
use_recorder = True
|
|
14
|
+
# policy = FrameRecorderPolicy(LOG_FILE)
|
|
15
|
+
# use_recorder = True
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
policy = StdoutPolicy() # You may also try this one.
|
|
18
|
+
use_recorder = False
|
|
19
19
|
|
|
20
20
|
with ap.run(policy=policy) as x:
|
|
21
21
|
x.connect()
|
pyxcp/examples/xcp_skel.py
CHANGED
pyxcp/examples/xcp_unlock.py
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
|
-
"""Very basic hello-world example.
|
|
3
|
-
"""
|
|
4
|
-
from pyxcp.cmdline import ArgumentParser
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
"""
|
|
2
|
+
"""Very basic hello-world example."""
|
|
9
3
|
|
|
4
|
+
import argparse
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
if args.sk_dll:
|
|
13
|
-
master.seedNKeyDLL = args.sk_dll
|
|
6
|
+
from pyxcp.cmdline import ArgumentParser
|
|
14
7
|
|
|
15
8
|
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
parser = argparse.ArgumentParser(description="XCP unlock example")
|
|
10
|
+
parser.add_argument(
|
|
18
11
|
"-s",
|
|
19
12
|
"--sk-dll",
|
|
20
13
|
dest="sk_dll",
|
|
@@ -23,7 +16,12 @@ ap.parser.add_argument(
|
|
|
23
16
|
default=None,
|
|
24
17
|
)
|
|
25
18
|
|
|
19
|
+
ap = ArgumentParser(parser)
|
|
20
|
+
|
|
26
21
|
with ap.run() as x:
|
|
22
|
+
if ap.args.sk_dll:
|
|
23
|
+
x.seedNKeyDLL = ap.args.sk_dll
|
|
24
|
+
|
|
27
25
|
x.connect()
|
|
28
26
|
|
|
29
27
|
print("")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
import can
|
|
6
6
|
|
|
@@ -9,7 +9,6 @@ from pyxcp.transport.can import CanInterfaceBase
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class CustomCANInterface(CanInterfaceBase):
|
|
12
|
-
|
|
13
12
|
def init(self):
|
|
14
13
|
"""Initialize the CAN interface here."""
|
|
15
14
|
|
pyxcp/examples/xcphello.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
"""Very basic hello-world example."""
|
|
3
|
+
|
|
3
4
|
from pprint import pprint
|
|
4
5
|
|
|
5
6
|
from pyxcp.cmdline import ArgumentParser
|
|
@@ -9,21 +10,7 @@ from pyxcp.utils import decode_bytes
|
|
|
9
10
|
daq_info = False
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
global daq_info
|
|
14
|
-
if args.daq_info:
|
|
15
|
-
daq_info = True
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
ap = ArgumentParser(description="pyXCP hello world.", callout=callout)
|
|
19
|
-
ap.parser.add_argument(
|
|
20
|
-
"-d",
|
|
21
|
-
"--daq-info",
|
|
22
|
-
dest="daq_info",
|
|
23
|
-
help="Display DAQ-info",
|
|
24
|
-
default=False,
|
|
25
|
-
action="store_true",
|
|
26
|
-
)
|
|
13
|
+
ap = ArgumentParser(description="pyXCP hello world.")
|
|
27
14
|
|
|
28
15
|
with ap.run() as x:
|
|
29
16
|
x.connect()
|