pyxcp 0.25.4__cp313-cp313-win_amd64.whl → 0.25.8__cp313-cp313-win_amd64.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 CHANGED
@@ -16,5 +16,5 @@ console = Console()
16
16
  tb_install(show_locals=True, max_frames=3) # Install custom exception handler.
17
17
 
18
18
  # if you update this manually, do not forget to update
19
- # .bumpversion.cfg and pyproject.toml.
20
- __version__ = "0.25.4"
19
+ # pyproject.toml.
20
+ __version__ = "0.25.8"
pyxcp/asamkeydll.exe CHANGED
Binary file
pyxcp/cmdline.py CHANGED
@@ -37,7 +37,7 @@ class ArgumentParser:
37
37
  self._parser = StrippingParser(parser)
38
38
  self._callout = user_parser
39
39
  self._description = description
40
- self.args = None
40
+ self.args = self._parser.parse_and_strip()
41
41
 
42
42
  def run(self, policy=None, transport_layer_interface=None):
43
43
  """Create and configure a synchronous master instance.
@@ -49,8 +49,6 @@ class ArgumentParser:
49
49
  Returns:
50
50
  A configured master instance
51
51
  """
52
- self.args = self._parser.parse_and_strip()
53
-
54
52
  # Create the application with custom arguments and callout
55
53
  application = get_application(options=[], callout=self._callout)
56
54
 
Binary file
Binary file
Binary file
Binary file
@@ -22,6 +22,21 @@ class PyTimestampInfo : public TimestampInfo {
22
22
  using TimestampInfo::TimestampInfo;
23
23
  };
24
24
 
25
+ py::dict mcobject_asdict(const McObject& self) {
26
+ py::dict d;
27
+ d["name"] = self.get_name();
28
+ d["address"] = self.get_address();
29
+ d["ext"] = self.get_ext();
30
+ d["length"] = self.get_length();
31
+ d["data_type"] = self.get_data_type();
32
+ py::list components_list;
33
+ for (const auto& component : self.get_components()) {
34
+ components_list.append(mcobject_asdict(component));
35
+ }
36
+ d["components"] = components_list;
37
+ return d;
38
+ }
39
+
25
40
 
26
41
  PYBIND11_MODULE(cpp_ext, m) {
27
42
  m.doc() = "C++ extensions for pyXCP.";
@@ -43,6 +58,7 @@ PYBIND11_MODULE(cpp_ext, m) {
43
58
  .def_property_readonly("components", &McObject::get_components)
44
59
 
45
60
  .def("add_component", &McObject::add_component, "component"_a)
61
+ .def("asdict", &mcobject_asdict)
46
62
  .def("__eq__", [](const McObject& self, const McObject& other) { return self == other; })
47
63
  .def("__repr__", [](const McObject& self) { return to_string(self); })
48
64
  .def("__hash__", [](const McObject& self) { return self.get_hash(); })
@@ -54,6 +70,17 @@ PYBIND11_MODULE(cpp_ext, m) {
54
70
  .def_property("residual_capacity", &Bin::get_residual_capacity, &Bin::set_residual_capacity)
55
71
  .def_property("entries", &Bin::get_entries, nullptr)
56
72
  .def("append", &Bin::append)
73
+ .def("asdict", [](const Bin& self) {
74
+ py::dict d;
75
+ d["size"] = self.get_size();
76
+ d["residual_capacity"] = self.get_residual_capacity();
77
+ py::list entries_list;
78
+ for (const auto& entry : self.get_entries()) {
79
+ entries_list.append(mcobject_asdict(entry));
80
+ }
81
+ d["entries"] = entries_list;
82
+ return d;
83
+ })
57
84
  .def("__repr__", [](const Bin& self) { return to_string(self); })
58
85
  .def("__eq__", [](const Bin& self, const Bin& other) { return self == other; })
59
86
  .def("__len__", [](const Bin& self) { return std::size(self.get_entries()); });
@@ -69,7 +96,22 @@ PYBIND11_MODULE(cpp_ext, m) {
69
96
  .def_property("headers", &DaqListBase::get_headers, nullptr)
70
97
  .def_property("odt_count", &DaqListBase::get_odt_count, nullptr)
71
98
  .def_property("total_entries", &DaqListBase::get_total_entries, nullptr)
72
- .def_property("total_length", &DaqListBase::get_total_length, nullptr);
99
+ .def_property("total_length", &DaqListBase::get_total_length, nullptr)
100
+ .def("asdict", [](const DaqListBase& self) {
101
+ py::dict d;
102
+ d["name"] = self.get_name();
103
+ d["event_num"] = self.get_event_num();
104
+ d["priority"] = self.get_priority();
105
+ d["prescaler"] = self.get_prescaler();
106
+ d["stim"] = self.get_stim();
107
+ d["enable_timestamps"] = self.get_enable_timestamps();
108
+ d["measurements_opt"] = self.get_measurements_opt();
109
+ d["headers"] = self.get_headers();
110
+ d["odt_count"] = self.get_odt_count();
111
+ d["total_entries"] = self.get_total_entries();
112
+ d["total_length"] = self.get_total_length();
113
+ return d;
114
+ });
73
115
 
74
116
  py::class_<DaqList, DaqListBase, std::shared_ptr<DaqList>>(m, "DaqList")
75
117
  .def(
@@ -78,7 +120,27 @@ PYBIND11_MODULE(cpp_ext, m) {
78
120
  "priority"_a=0, "prescaler"_a=1
79
121
  )
80
122
  .def("__repr__", [](const DaqList& self) { return self.to_string(); })
81
- .def_property("measurements", &DaqList::get_measurements, nullptr);
123
+ .def_property("measurements", &DaqList::get_measurements, nullptr)
124
+ .def("asdict", [](const DaqList& self) {
125
+ py::dict d;
126
+ d["name"] = self.get_name();
127
+ d["event_num"] = self.get_event_num();
128
+ d["priority"] = self.get_priority();
129
+ d["prescaler"] = self.get_prescaler();
130
+ d["stim"] = self.get_stim();
131
+ d["enable_timestamps"] = self.get_enable_timestamps();
132
+ d["measurements_opt"] = self.get_measurements_opt();
133
+ d["headers"] = self.get_headers();
134
+ d["odt_count"] = self.get_odt_count();
135
+ d["total_entries"] = self.get_total_entries();
136
+ d["total_length"] = self.get_total_length();
137
+ py::list measurements_list;
138
+ for (const auto& measurement : self.get_measurements()) {
139
+ measurements_list.append(mcobject_asdict(measurement));
140
+ }
141
+ d["measurements"] = measurements_list;
142
+ return d;
143
+ });
82
144
 
83
145
  py::class_<PredefinedDaqList, DaqListBase, std::shared_ptr<PredefinedDaqList>>(m, "PredefinedDaqList")
84
146
  .def(
@@ -94,6 +156,21 @@ PYBIND11_MODULE(cpp_ext, m) {
94
156
  } catch (...) {
95
157
  return std::string("PredefinedDaqList(<repr error: unknown>)");
96
158
  }
159
+ })
160
+ .def("asdict", [](const PredefinedDaqList& self) {
161
+ py::dict d;
162
+ d["name"] = self.get_name();
163
+ d["event_num"] = self.get_event_num();
164
+ d["priority"] = self.get_priority();
165
+ d["prescaler"] = self.get_prescaler();
166
+ d["stim"] = self.get_stim();
167
+ d["enable_timestamps"] = self.get_enable_timestamps();
168
+ d["measurements_opt"] = self.get_measurements_opt();
169
+ d["headers"] = self.get_headers();
170
+ d["odt_count"] = self.get_odt_count();
171
+ d["total_entries"] = self.get_total_entries();
172
+ d["total_length"] = self.get_total_length();
173
+ return d;
97
174
  })
98
175
  ;
99
176
 
@@ -1,8 +1,9 @@
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, Union
6
+ from typing import Any, Dict, List, Optional, TextIO, Tuple, Union
6
7
 
7
8
  from pyxcp.cpp_ext.cpp_ext import DaqList, PredefinedDaqList
8
9
 
@@ -29,6 +30,83 @@ 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
111
  def __init__(self, daq_lists: List[Union[DaqList, PredefinedDaqList]]):
34
112
  self.daq_lists = daq_lists
Binary file
Binary file
Binary file
Binary file
pyxcp/master/master.py CHANGED
@@ -13,7 +13,7 @@ import struct
13
13
  import traceback
14
14
  import warnings
15
15
  from contextlib import suppress
16
- from typing import Any, Callable, Collection, Dict, Optional, TypeVar
16
+ from typing import Any, Callable, Collection, TypeVar
17
17
 
18
18
  from pyxcp.daq_stim.stim import DaqEventInfo, Stim
19
19
 
@@ -133,8 +133,7 @@ class Master:
133
133
  Custom transport layer interface, by default None
134
134
  """
135
135
 
136
- def __init__(self, transport_name: str | None, config: Any, policy: Any = None,
137
- transport_layer_interface: Any = None) -> None:
136
+ def __init__(self, transport_name: str | None, config: Any, policy: Any = None, transport_layer_interface: Any = None) -> None:
138
137
  """Initialize a new Master instance.
139
138
 
140
139
  Parameters
@@ -168,8 +167,7 @@ class Master:
168
167
  # Set up transport layer
169
168
  self.transport_name: str = transport_name.lower()
170
169
  transport_config: Any = config.transport
171
- self.transport: BaseTransport = create_transport(transport_name, transport_config, policy,
172
- transport_layer_interface)
170
+ self.transport: BaseTransport = create_transport(transport_name, transport_config, policy, transport_layer_interface)
173
171
 
174
172
  # Set up STIM (stimulation) support
175
173
  self.stim: Stim = Stim(self.config.stim_support)
@@ -227,10 +225,7 @@ class Master:
227
225
  self.transport.connect()
228
226
  return self
229
227
 
230
- def __exit__(
231
- self, exc_type: type[BaseException] | None, exc_val: BaseException | None,
232
- exc_tb
233
- ) -> None:
228
+ def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb) -> None:
234
229
  """Context manager exit part.
235
230
 
236
231
  This method is called when exiting a context manager block.
@@ -750,11 +745,11 @@ class Master:
750
745
  # Larger sizes will send in multiple CAN messages
751
746
  # Each valid message will start with 0xFF followed by the upload bytes
752
747
  # The last message might be padded to the required DLC
753
- remaining_bytes = byte_count - len(response)
754
- while remaining_bytes:
748
+ remaining_bytes = byte_count - len(response) # NOTE: Due to padding the result may negative!
749
+ while remaining_bytes > 0:
755
750
  if len(self.transport.resQueue):
756
751
  data = self.transport.resQueue.popleft()
757
- response += data[1: remaining_bytes + 1]
752
+ response += data[1 : remaining_bytes + 1]
758
753
  remaining_bytes = byte_count - len(response)
759
754
  else:
760
755
  short_sleep()
@@ -1064,17 +1059,17 @@ class Master:
1064
1059
  )
1065
1060
 
1066
1061
  def _generalized_downloader(
1067
- self,
1068
- address: int,
1069
- address_ext: int,
1070
- data: bytes,
1071
- maxCto: int,
1072
- maxBs: int,
1073
- minSt: int,
1074
- master_block_mode: bool,
1075
- dl_func: Callable[[bytes, int, bool], Any],
1076
- dl_next_func: Callable[[bytes, int, bool], Any],
1077
- callback: Callable[[int], None] | None = None,
1062
+ self,
1063
+ address: int,
1064
+ address_ext: int,
1065
+ data: bytes,
1066
+ maxCto: int,
1067
+ maxBs: int,
1068
+ minSt: int,
1069
+ master_block_mode: bool,
1070
+ dl_func: Callable[[bytes, int, bool], Any],
1071
+ dl_next_func: Callable[[bytes, int, bool], Any],
1072
+ callback: Callable[[int], None] | None = None,
1078
1073
  ) -> None:
1079
1074
  """Generic implementation for downloading data to the slave.
1080
1075
 
@@ -1138,13 +1133,13 @@ class Master:
1138
1133
  self._download_normal_mode(data, total_length, max_payload, offset, dl_func, callback)
1139
1134
 
1140
1135
  def _download_master_block_mode(
1141
- self,
1142
- data: bytes,
1143
- total_length: int,
1144
- max_payload: int,
1145
- offset: int,
1146
- block_downloader: Callable[[bytes], Any],
1147
- callback: Callable[[int], None] | None = None,
1136
+ self,
1137
+ data: bytes,
1138
+ total_length: int,
1139
+ max_payload: int,
1140
+ offset: int,
1141
+ block_downloader: Callable[[bytes], Any],
1142
+ callback: Callable[[int], None] | None = None,
1148
1143
  ) -> None:
1149
1144
  """Download data using master block mode.
1150
1145
 
@@ -1171,7 +1166,7 @@ class Master:
1171
1166
 
1172
1167
  # Process full blocks
1173
1168
  for _ in blocks:
1174
- block = data[offset: offset + max_payload]
1169
+ block = data[offset : offset + max_payload]
1175
1170
  block_downloader(block)
1176
1171
  offset += max_payload
1177
1172
  remaining -= max_payload
@@ -1183,19 +1178,19 @@ class Master:
1183
1178
 
1184
1179
  # Process remaining partial block
1185
1180
  if remaining_block_size:
1186
- block = data[offset: offset + remaining_block_size]
1181
+ block = data[offset : offset + remaining_block_size]
1187
1182
  block_downloader(block)
1188
1183
  if callback:
1189
1184
  callback(percent_complete)
1190
1185
 
1191
1186
  def _download_normal_mode(
1192
- self,
1193
- data: bytes,
1194
- total_length: int,
1195
- max_payload: int,
1196
- offset: int,
1197
- dl_func: Callable[[bytes, int, bool], Any],
1198
- callback: Callable[[int], None] | None = None,
1187
+ self,
1188
+ data: bytes,
1189
+ total_length: int,
1190
+ max_payload: int,
1191
+ offset: int,
1192
+ dl_func: Callable[[bytes, int, bool], Any],
1193
+ callback: Callable[[int], None] | None = None,
1199
1194
  ) -> None:
1200
1195
  """Download data using normal mode.
1201
1196
 
@@ -1223,7 +1218,7 @@ class Master:
1223
1218
 
1224
1219
  # Process full chunks
1225
1220
  for _ in chunks:
1226
- block = data[offset: offset + max_payload]
1221
+ block = data[offset : offset + max_payload]
1227
1222
  dl_func(block, max_payload, last=False)
1228
1223
  offset += max_payload
1229
1224
  callback_remaining -= chunk_size
@@ -1235,17 +1230,17 @@ class Master:
1235
1230
 
1236
1231
  # Process remaining partial chunk
1237
1232
  if remaining:
1238
- block = data[offset: offset + remaining]
1233
+ block = data[offset : offset + remaining]
1239
1234
  dl_func(block, remaining, last=True)
1240
1235
  if callback:
1241
1236
  callback(percent_complete)
1242
1237
 
1243
1238
  def _block_downloader(
1244
- self,
1245
- data: bytes,
1246
- dl_func: Callable[[bytes, int, bool], Any] | None = None,
1247
- dl_next_func: Callable[[bytes, int, bool], Any] | None = None,
1248
- minSt: float = 0.0,
1239
+ self,
1240
+ data: bytes,
1241
+ dl_func: Callable[[bytes, int, bool], Any] | None = None,
1242
+ dl_next_func: Callable[[bytes, int, bool], Any] | None = None,
1243
+ minSt: float = 0.0,
1249
1244
  ) -> None:
1250
1245
  """Re-usable block downloader for transferring data in blocks.
1251
1246
 
@@ -1279,7 +1274,7 @@ class Master:
1279
1274
  index = 0
1280
1275
  for index in packets:
1281
1276
  # Extract packet data
1282
- packet_data = data[offset: offset + max_packet_size]
1277
+ packet_data = data[offset : offset + max_packet_size]
1283
1278
 
1284
1279
  # Determine if this is the last packet
1285
1280
  last = (remaining_block_size - max_packet_size) == 0
@@ -1302,7 +1297,7 @@ class Master:
1302
1297
  # Process remaining partial packet
1303
1298
  if remaining:
1304
1299
  # Extract remaining data
1305
- packet_data = data[offset: offset + remaining]
1300
+ packet_data = data[offset : offset + remaining]
1306
1301
 
1307
1302
  # Send packet using appropriate function
1308
1303
  if index == 0:
@@ -1745,8 +1740,7 @@ class Master:
1745
1740
 
1746
1741
  @wrapped
1747
1742
  def setDaqPackedMode(
1748
- self, daq_list_number: int, daq_packed_mode: int, dpm_timestamp_mode: int = None,
1749
- dpm_sample_count: int = None
1743
+ self, daq_list_number: int, daq_packed_mode: int, dpm_timestamp_mode: int = None, dpm_sample_count: int = None
1750
1744
  ):
1751
1745
  """Set DAQ List Packed Mode.
1752
1746
 
@@ -1921,8 +1915,7 @@ class Master:
1921
1915
  return self.transport.request(types.Command.PROGRAM_PREPARE, 0x00, *cs)
1922
1916
 
1923
1917
  @wrapped
1924
- def programFormat(self, compression_method: int, encryption_method: int, programming_method: int,
1925
- access_method: int):
1918
+ def programFormat(self, compression_method: int, encryption_method: int, programming_method: int, access_method: int):
1926
1919
  return self.transport.request(
1927
1920
  types.Command.PROGRAM_FORMAT,
1928
1921
  compression_method,
@@ -2190,8 +2183,7 @@ class Master:
2190
2183
  @wrapped
2191
2184
  def timeCorrelationProperties(self, set_properties: int, get_properties_request: int, cluster_id: int):
2192
2185
  response = self.transport.request(
2193
- types.Command.TIME_CORRELATION_PROPERTIES, set_properties, get_properties_request, 0,
2194
- *self.WORD_pack(cluster_id)
2186
+ types.Command.TIME_CORRELATION_PROPERTIES, set_properties, get_properties_request, 0, *self.WORD_pack(cluster_id)
2195
2187
  )
2196
2188
  return types.TimeCorrelationPropertiesResponse.parse(response, byteOrder=self.slaveProperties.byteOrder)
2197
2189
 
@@ -2327,7 +2319,7 @@ class Master:
2327
2319
  if self.currentProtectionStatus is None:
2328
2320
  try:
2329
2321
  status = self.getStatus()
2330
- except Exception as e: # may temporary ERR_OUT_OF_RANGE
2322
+ except Exception: # may temporary ERR_OUT_OF_RANGE
2331
2323
  return {"dbg": None, "pgm": None, "stim": None, "daq": None, "calpag": None}
2332
2324
  self._setProtectionStatus(status.resourceProtectionStatus)
2333
2325
  return self.currentProtectionStatus
@@ -2372,8 +2364,7 @@ class Master:
2372
2364
 
2373
2365
  protection_status = self.getCurrentProtectionStatus()
2374
2366
  if any(protection_status.values()) and (not (self.seed_n_key_dll or self.seed_n_key_function)):
2375
- raise RuntimeError(
2376
- "Neither seed-and-key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError
2367
+ raise RuntimeError("Neither seed-and-key DLL nor function specified, cannot proceed.") # TODO: ConfigurationError
2377
2368
  if resources is None:
2378
2369
  result = []
2379
2370
  if self.slaveProperties["supportsCalpag"]:
@@ -2501,7 +2492,10 @@ class Master:
2501
2492
  name = STD_IDS[id_value]
2502
2493
  else:
2503
2494
  name = f"USER_{idx}"
2504
- yield id_value, name,
2495
+ yield (
2496
+ id_value,
2497
+ name,
2498
+ )
2505
2499
 
2506
2500
  return generate()
2507
2501
 
@@ -2623,7 +2617,7 @@ def make_tick_converter(resolution):
2623
2617
  """
2624
2618
  exponent = types.DAQ_TIMESTAMP_UNIT_TO_EXP[resolution.timestampMode.unit]
2625
2619
  tick_resolution = resolution.timestampTicks
2626
- base = (10 ** exponent) * tick_resolution
2620
+ base = (10**exponent) * tick_resolution
2627
2621
 
2628
2622
  def ticks_to_seconds(ticks):
2629
2623
  """Convert DAQ timestamp/tick value to seconds.
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import argparse
5
+ import sys
6
+ import time
7
+
8
+ from pyxcp.cmdline import ArgumentParser
9
+ from pyxcp.daq_stim import DaqList, DaqRecorder, DaqToCsv, load_daq_lists_from_json # noqa: F401
10
+ from pyxcp.types import XcpTimeoutError
11
+
12
+ parser = argparse.ArgumentParser(description="XCP DAQ list recorder")
13
+ parser.add_argument(
14
+ "DAQ_configuration_file",
15
+ type=str,
16
+ default=None,
17
+ )
18
+
19
+ ap = ArgumentParser(description="XCP DAQ list recorder", user_parser=parser)
20
+
21
+ args = ap.args
22
+ DAQ_LISTS = load_daq_lists_from_json(args.DAQ_configuration_file)
23
+
24
+ # daq_parser = DaqToCsv(DAQ_LISTS) # Record to CSV file(s).
25
+ daq_parser = DaqRecorder(DAQ_LISTS, "run_daq_21092025_01", 8) # Record to ".xmraw" file.
26
+
27
+ with ap.run(policy=daq_parser) as x:
28
+ try:
29
+ x.connect()
30
+ except XcpTimeoutError:
31
+ sys.exit(2)
32
+
33
+ if x.slaveProperties.optionalCommMode:
34
+ x.getCommModeInfo()
35
+
36
+ x.cond_unlock("DAQ") # DAQ resource is locked in many cases.
37
+
38
+ print("setup DAQ lists.")
39
+ daq_parser.setup() # Execute setup procedures.
40
+ print("start DAQ lists.")
41
+ daq_parser.start() # Start DAQ lists.
42
+
43
+ time.sleep(0.25 * 60.0 * 60.0) # Run for 15 minutes.
44
+
45
+ print("Stop DAQ....")
46
+ daq_parser.stop() # Stop DAQ lists.
47
+ print("finalize DAQ lists.\n")
48
+ x.disconnect()
49
+
50
+ if hasattr(daq_parser, "files"): # `files` attribute is specific to `DaqToCsv`.
51
+ print("Data written to:")
52
+ print("================")
53
+ for fl in daq_parser.files.values():
54
+ print(fl.name)
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env python
2
2
 
3
+ import datetime
3
4
  from pathlib import Path
4
5
  from typing import List, Dict
5
6
 
6
7
  import h5py
7
8
  import numpy as np
8
9
  from pyxcp.daq_stim import DaqOnlinePolicy, DaqList
9
-
10
+ from pyxcp import __version__ as pyxcp_version
10
11
 
11
12
  BATCH_SIZE = 4096
12
13
 
@@ -25,6 +26,21 @@ MAP_TO_NP = {
25
26
  "BF16": np.float16,
26
27
  }
27
28
 
29
+ MAP_TO_ASAM_HO = {
30
+ "U8": "A_UINT8",
31
+ "I8": "A_INT8",
32
+ "U16": "A_UINT16",
33
+ "I16": "A_INT16",
34
+ "U32": "A_UINT32",
35
+ "I32": "A_INT32",
36
+ "U64": "A_UINT64",
37
+ "I64": "A_INT64",
38
+ "F32": "A_FLOAT32",
39
+ "F64": "A_FLOAT64",
40
+ "F16": "A_FLOAT16",
41
+ "BF16": "A_FLOAT16",
42
+ }
43
+
28
44
 
29
45
  class BufferedDataset:
30
46
  def __init__(self, dataset: h5py.Dataset):
@@ -72,22 +88,35 @@ class DatasetGroup:
72
88
 
73
89
 
74
90
  def create_timestamp_column(hdf_file: h5py.File, group_name: str, num: int) -> h5py.Dataset:
75
- return hdf_file.create_dataset(
91
+ result = hdf_file.create_dataset(
76
92
  f"/{group_name}/timestamp{num}",
77
93
  shape=(0,),
78
94
  maxshape=(None,),
79
95
  dtype=np.uint64,
80
96
  chunks=True,
81
97
  )
98
+ result.attrs["asam_data_type"] = "A_UINT64"
99
+ result.attrs["resolution"] = ("1 nanosecond",)
100
+ return result
82
101
 
83
102
 
84
103
  class Hdf5OnlinePolicy(DaqOnlinePolicy):
85
- def __init__(self, file_name: str | Path, daq_lists: List[DaqList]):
104
+ def __init__(self, file_name: str | Path, daq_lists: List[DaqList], **metadata):
86
105
  super().__init__(daq_lists=daq_lists)
87
106
  path = Path(file_name)
88
107
  if path.suffix != ".h5":
89
108
  path = path.with_suffix(".h5")
90
109
  self.hdf = h5py.File(path, "w", libver="latest")
110
+ self.metadata = self.set_metadata(**metadata)
111
+
112
+ def set_metadata(self, **metadata):
113
+ basic = {
114
+ "tool_name": "pyXCP",
115
+ "tool_version": f"{pyxcp_version}",
116
+ "created": f"{datetime.datetime.now().astimezone().isoformat()}",
117
+ }
118
+ for k, v in (basic | metadata).items():
119
+ self.hdf.attrs[k] = v
91
120
 
92
121
  def initialize(self):
93
122
  self.log.debug("Hdf5OnlinePolicy::Initialize()")
@@ -95,7 +124,12 @@ class Hdf5OnlinePolicy(DaqOnlinePolicy):
95
124
  for num, daq_list in enumerate(self.daq_lists):
96
125
  if daq_list.stim:
97
126
  continue
98
- self.hdf.create_group(daq_list.name)
127
+ grp = self.hdf.create_group(daq_list.name)
128
+ grp.attrs["event_num"] = daq_list.event_num
129
+ grp.attrs["enable_timestamps"] = daq_list.enable_timestamps
130
+ grp.attrs["prescaler"] = daq_list.prescaler
131
+ grp.attrs["priority"] = daq_list.priority
132
+ grp.attrs["direction"] = "STIM" if daq_list.stim else "DAQ"
99
133
  ts0 = BufferedDataset(create_timestamp_column(self.hdf, daq_list.name, 0))
100
134
  ts1 = BufferedDataset(create_timestamp_column(self.hdf, daq_list.name, 1))
101
135
  meas_map = {m.name: m for m in self.daq_lists[num].measurements}
@@ -103,12 +137,16 @@ class Hdf5OnlinePolicy(DaqOnlinePolicy):
103
137
  for name, _ in daq_list.headers:
104
138
  meas = meas_map[name]
105
139
  dataset = self.hdf.create_dataset(
106
- f"/{daq_list.name}/{meas.name}",
140
+ f"/{daq_list.name}/{meas.name}/raw",
107
141
  shape=(0,),
108
142
  maxshape=(None,),
109
143
  dtype=MAP_TO_NP[meas.data_type],
110
144
  chunks=(1024,),
111
145
  )
146
+ sub_group = dataset.parent
147
+ sub_group.attrs["asam_data_type"] = MAP_TO_ASAM_HO.get(meas.data_type, "n/a")
148
+ dataset.attrs["ecu_address"] = meas.address
149
+ dataset.attrs["ecu_address_extension"] = meas.ext
112
150
  dsets.append(BufferedDataset(dataset))
113
151
  self.datasets[num] = DatasetGroup(ts0_ds=ts0, ts1_ds=ts1, datasets=dsets)
114
152
  self.hdf.flush()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyxcp
3
- Version: 0.25.4
3
+ Version: 0.25.8
4
4
  Summary: Universal Calibration Protocol for Python
5
5
  License: LGPLv3
6
6
  License-File: LICENSE
@@ -1,4 +1,4 @@
1
- pyxcp/__init__.py,sha256=ObYrIi4LzK61FvVRRAtNbs7Xsm-b8_QDiqDszh0ACAQ,547
1
+ pyxcp/__init__.py,sha256=Bm5vH6ppREERjuTwbs9IGEjoHsSTUs15ypEoXyNb5YM,526
2
2
  pyxcp/aml/EtasCANMonitoring.a2l,sha256=EJYwe3Z3H24vyWAa6lUgcdKnQY8pwFxjyCN6ZU1ST8w,1509
3
3
  pyxcp/aml/EtasCANMonitoring.aml,sha256=xl0DdyeiIaLW0mmmJNAyJS0CQdOLSxt9dxfgrdSlU8Y,2405
4
4
  pyxcp/aml/ifdata_CAN.a2l,sha256=NCUnCUEEgRbZYSLGtUGwL2e7zJ8hrp0SbmLHGv8uY58,612
@@ -15,10 +15,10 @@ pyxcp/aml/XCPonUSB.aml,sha256=Xcu52ZckyuEX0v5rwYQxz8gJCAs4qyepXmd_bkCJVlA,4953
15
15
  pyxcp/asam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  pyxcp/asam/types.py,sha256=_gKcpBF5mon_SDWZBUW0PGBMcb37yrvhhEuk1wslg-s,2441
17
17
  pyxcp/asamkeydll.c,sha256=dVEvU0S1kgIo62S0La-T8xHSw668LM_DYc_fiQ0No6g,2952
18
- pyxcp/asamkeydll.exe,sha256=0ZFD9Y9YESKAP03iTYuwVw8LtZNnM2UHZHO9tNj2WJ8,11264
18
+ pyxcp/asamkeydll.exe,sha256=7m3EYufxBx3y-tz_ACPqHnd-vr-Ab8B7JkCHcjf_FPk,11264
19
19
  pyxcp/asamkeydll.sh,sha256=DC2NKUMwvi39OQgJ6514Chr4wc1LYbTmQHmMq9jAHHs,59
20
20
  pyxcp/checksum.py,sha256=hO2JW_eiO9I0A4eiqovMV9d_y5oidq_w8jKdAE4FeOo,11899
21
- pyxcp/cmdline.py,sha256=hWVfGIKoM5mM3LGAvYjPip3Otob4_UX6bEnZC2TGi7Q,2385
21
+ pyxcp/cmdline.py,sha256=PltxEwPp6KnXkStI3u3_K9fLNAfCDQauIxMktMzMhxA,2357
22
22
  pyxcp/config/__init__.py,sha256=kZ-6hotQYuVa4C6cEGUSqga6CTezKCC2HRa89yVQtXM,48272
23
23
  pyxcp/config/legacy.py,sha256=4QdDheX8DbBKv5JVT72_C_cjCgKvZmhN3tJ6hsvBEtI,5220
24
24
  pyxcp/constants.py,sha256=9yGfujC0ImTYQWfn41wyw8pluJTSrhMGWIVeIZTgsLg,1160
@@ -26,27 +26,27 @@ pyxcp/cpp_ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  pyxcp/cpp_ext/aligned_buffer.hpp,sha256=KoUYDjvIJRc2IDOSQXbbSfQ6pdhnBcCi_bxZFC9C6Ko,5448
27
27
  pyxcp/cpp_ext/bin.hpp,sha256=WF7ljBEbATQ3wplk7pna4qWtM0MJVHRs5pnILxfkbGU,2673
28
28
  pyxcp/cpp_ext/blockmem.hpp,sha256=ysaJwmTWGTfE54Outk3gJYOfAVFd_QaonBMtXLcXwCc,1242
29
- pyxcp/cpp_ext/cpp_ext.cp310-win_amd64.pyd,sha256=c-m4hker8FJz7HVqYHlcuF44p2vFqUZ2O_yJhpwdXE4,331264
30
- pyxcp/cpp_ext/cpp_ext.cp311-win_amd64.pyd,sha256=jvtnpYcA8yf-bBEYqDBPIdATsrjKIvJcwIJo-NTPitI,333312
31
- pyxcp/cpp_ext/cpp_ext.cp312-win_amd64.pyd,sha256=vZghqhyVtUWnc1B0It5JDVKw3l5YLNch85qccetRxMA,337920
32
- pyxcp/cpp_ext/cpp_ext.cp313-win_amd64.pyd,sha256=SRMCCsDEqgioFKFboI97JaAHxsGKdKA2ZCLZ-V-v1Ho,338432
29
+ pyxcp/cpp_ext/cpp_ext.cp310-win_amd64.pyd,sha256=oxbCxPamWQByLooFc2PAnP_1JVtV-WvsWhNv1g5-kU8,342528
30
+ pyxcp/cpp_ext/cpp_ext.cp311-win_amd64.pyd,sha256=8x-Ymd8Ek7CftpyKM9KWCAETzNz7_8Rh4_wfSOLnVKM,344576
31
+ pyxcp/cpp_ext/cpp_ext.cp312-win_amd64.pyd,sha256=6o16PiwJffaKI_GXaO1zSh4GfcmdCFFsY3vD5nMBuxw,351232
32
+ pyxcp/cpp_ext/cpp_ext.cp313-win_amd64.pyd,sha256=JoCqAjBO2804zgWVwe76RUKbNQY8lre8kgzHGP4Drls,351232
33
33
  pyxcp/cpp_ext/daqlist.hpp,sha256=Dv5v9JvaP3Xhe4uTrYfdBsySElIRO50H9Bu4Xl92yfQ,13996
34
34
  pyxcp/cpp_ext/event.hpp,sha256=Z-1yxsEKsr81NnLVEWJ2ANA8FV7YsM7EbNxaw-elheE,1200
35
- pyxcp/cpp_ext/extension_wrapper.cpp,sha256=JIuOYM9DbMQ0iDA1okZGsTKta9cfjOun1zwIyl92Atg,6540
35
+ pyxcp/cpp_ext/extension_wrapper.cpp,sha256=TRrhjinnKkaH9wcY9Tx16kMoVEwSwAFoRVZ2Uot1hao,9852
36
36
  pyxcp/cpp_ext/framing.hpp,sha256=8k2juQO0FJvMIiEXjZRMh4CtJdjRBBgH2N3CY57hbyY,11660
37
37
  pyxcp/cpp_ext/helper.hpp,sha256=ONAsVupIqqmNDp8bgGWS0TfSYeCFkk3kwwZbbqsh0HQ,7813
38
38
  pyxcp/cpp_ext/mcobject.hpp,sha256=t_sjka8_TaWF0EG7mIx8feWAF6uf_CyMtuvCmhE4DB4,6522
39
39
  pyxcp/cpp_ext/sxi_framing.hpp,sha256=FvaDOFD5zK8AJUn6yBVoTr-pclstmr478_q_W6COANo,11837
40
40
  pyxcp/cpp_ext/tsqueue.hpp,sha256=FWMemzXNgV5dQ7gYmTCzC0QYfgl0VI9JCybYelBcCHU,1026
41
- pyxcp/daq_stim/__init__.py,sha256=xA12U0QmOBOgDjnel96crCMOKiNNm6TUQcR0Dzxyr1o,13369
41
+ pyxcp/daq_stim/__init__.py,sha256=mWHeLkq9fuoylNypGz_CCKShYN_3c3Su6IEB76Z4U2M,16509
42
42
  pyxcp/daq_stim/optimize/__init__.py,sha256=FUWK0GkNpNT-sUlhibp7xa2aSYpm6Flh5yA2w2IOJqg,2520
43
43
  pyxcp/daq_stim/optimize/binpacking.py,sha256=RcRTMNmcb5oIAY2G5RHygb_g0GgNf35-cp3utIeUA9w,1257
44
44
  pyxcp/daq_stim/scheduler.cpp,sha256=uUR4RUpukE5qZpJNbT_1ARFslAbWupGv0TQJHO_OM0s,1466
45
45
  pyxcp/daq_stim/scheduler.hpp,sha256=U_6tUbebmzX5vVZS0EFSgTaPsyxMg6yRXHG_aPWA0x4,1884
46
- pyxcp/daq_stim/stim.cp310-win_amd64.pyd,sha256=DxyoArqBs2zu1vdLcFWLaL6-K02vvSLjXQL4X33r6Jk,208896
47
- pyxcp/daq_stim/stim.cp311-win_amd64.pyd,sha256=uFGWZ2Vm5ykPlw1kyVNulAKQvw1VVC75yXkwQXY5YqU,210432
48
- pyxcp/daq_stim/stim.cp312-win_amd64.pyd,sha256=ZLISu4AoRLTkvhEAMNCU99dGJNfyoI7GxAwCZxpHHVc,214528
49
- pyxcp/daq_stim/stim.cp313-win_amd64.pyd,sha256=g8JUqjsnGwsR99pAA094BU4o4TDCuPaX4Wwe-PFex3g,214528
46
+ pyxcp/daq_stim/stim.cp310-win_amd64.pyd,sha256=HURS8tDC0S4JggbfuEiTvsGN3Gsgb2tLn_D1DpnyAcA,208896
47
+ pyxcp/daq_stim/stim.cp311-win_amd64.pyd,sha256=eQtJ1HMQ07UJsZHzOZLreonxBg6PUooe3wveagBC_5k,210432
48
+ pyxcp/daq_stim/stim.cp312-win_amd64.pyd,sha256=TsjOX1kV4IGmo33aExy8XMd5irjZqcKYaa-VMP0U8sI,214528
49
+ pyxcp/daq_stim/stim.cp313-win_amd64.pyd,sha256=6z9yU9qUNAq0JboTFnCV3Y-k55lPfmwwEmJH2atFKY0,214528
50
50
  pyxcp/daq_stim/stim.cpp,sha256=F2OG67W4KKwTTiUCxm-9egIv3TLFdOkRunX6xf7YOtc,177
51
51
  pyxcp/daq_stim/stim.hpp,sha256=U-uInRrA6OCdMl1l1SWbQ_KEPpnNYrWut924IvbW6R0,18508
52
52
  pyxcp/daq_stim/stim_wrapper.cpp,sha256=iT2yxJ3LRG7HoYC1bwhM3tCAxF9X_HHierBNsLRmTJg,1995
@@ -69,7 +69,7 @@ pyxcp/examples/xcphello.py,sha256=6hLOzeqPyNERxQn94_Vup87FbGeRjmjPBe4ZLl6tkmU,23
69
69
  pyxcp/examples/xcphello_recorder.py,sha256=c7ldGXpjyHe7DCit_S8D5AkqVX81pleLEvLDugcdCeU,3409
70
70
  pyxcp/master/__init__.py,sha256=RbMGgWZWRMEpembwgV-kkdSfQxuUZeXsybcspAhMzj0,320
71
71
  pyxcp/master/errorhandler.py,sha256=Hyn6geDgrH7EJdpgPWuQYbe_fDOQ2zzYFqRnHFny1Fw,26628
72
- pyxcp/master/master.py,sha256=UrAczXI6WFP3u6_ilsFmfSjzF44-Mq8UqAGp6f8BE_E,101701
72
+ pyxcp/master/master.py,sha256=DgAUzBo2RnglbUWxdwPGnuJ8mbN1y27vRu0WlewYTrk,101515
73
73
  pyxcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  pyxcp/recorder/.idea/.gitignore,sha256=G_3s42Re2O01YDDyLMIATcP0Af8GCsPSTegRw7zYLhY,184
75
75
  pyxcp/recorder/.idea/misc.xml,sha256=XfcpM2YHByf1o3XaftWMr5UIaGSVc9LMzRnZmUp1ynM,142
@@ -99,10 +99,10 @@ pyxcp/recorder/mio.hpp,sha256=5ASJLKSEykH0deAQD5uak-_yAgd5p2n8t06315GSGrg,63346
99
99
  pyxcp/recorder/reader.hpp,sha256=ssdaAaG_fHKrJMuMo_tfcCaCjFrIC5HQGNXgR343bVY,5131
100
100
  pyxcp/recorder/reco.py,sha256=2CPKxIZNSr6BhjfPrWtxdt81zaJlgU-e73RneG9IsJA,8727
101
101
  pyxcp/recorder/recorder.rst,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
- pyxcp/recorder/rekorder.cp310-win_amd64.pyd,sha256=RVdOvX0n9jxR4OVIagqVoVBCu1fuECmdaXGYA4caTZc,396800
103
- pyxcp/recorder/rekorder.cp311-win_amd64.pyd,sha256=g8Mk-ZJrT9P1qMUrnkM11Mx-RO5F6ZuqGD3fcsDfY0w,398336
104
- pyxcp/recorder/rekorder.cp312-win_amd64.pyd,sha256=tEKIMFNX1bv3nMffEwo-CIrMptzqx2jTIMuFuae2ygE,404480
105
- pyxcp/recorder/rekorder.cp313-win_amd64.pyd,sha256=mFI9_jWq9MsevOsLJBaNe1hV9cGHwMxo8uwcDx-QSOo,404480
102
+ pyxcp/recorder/rekorder.cp310-win_amd64.pyd,sha256=VAcmnVoAKV0U22EpKVWl3j8mIBJ_IKWH-imcfwt_68E,396800
103
+ pyxcp/recorder/rekorder.cp311-win_amd64.pyd,sha256=JCqn8UGpWFbaNvjWDEowiWK2kX8RFvK8-aTntqrofJ0,398336
104
+ pyxcp/recorder/rekorder.cp312-win_amd64.pyd,sha256=LMusSqBjJ14FhuNhDnN-kT3jcxpY_1dvFGTpNSbk6eA,404480
105
+ pyxcp/recorder/rekorder.cp313-win_amd64.pyd,sha256=fmru0QrN9WOdoanDbGVTIzkqv0f6ffgLuWLVI8MTuJQ,404480
106
106
  pyxcp/recorder/rekorder.cpp,sha256=U0LMyk8pZXx9emgS_WPVthvn_9IpgE7JGrh4kg-8CX4,1900
107
107
  pyxcp/recorder/rekorder.hpp,sha256=sWvRch9bVt6mmgrFHp5mwWhap7HoFG4geeb7UqEIzio,7638
108
108
  pyxcp/recorder/setup.py,sha256=_99XFPQAd5V4LcJaSGJwdnbxgxJ7kl8DEXfHsnKO1Yg,998
@@ -112,6 +112,7 @@ pyxcp/recorder/wrap.cpp,sha256=oCsEOnRsvLlaWjyN09X7mcHLzzqCyLskREKb6kmyqNE,8707
112
112
  pyxcp/recorder/writer.hpp,sha256=rNjtRTtJes5z-BzKR2K56P_Kvc9MEVQgycu8J0wKf1g,11284
113
113
  pyxcp/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  pyxcp/scripts/pyxcp_probe_can_drivers.py,sha256=P_gscDTAofbSVA_Wd1GATrnyWGTf1-Dz_oPdlRFfjuk,567
115
+ pyxcp/scripts/xcp_daq_recorder.py,sha256=Gwg03TWvqZWPSeynHy0pTqoPL6SmTvNpmg1yd-pcus0,1585
115
116
  pyxcp/scripts/xcp_examples.py,sha256=q2wNXHVZu4GjcIlZwp2j9Sqm5QH39Olro4BQaLoIqiM,2583
116
117
  pyxcp/scripts/xcp_fetch_a2l.py,sha256=mRS42707lEFjH9q9ip2TgIOstc6QBnZ3IMMkf7GtEtk,1239
117
118
  pyxcp/scripts/xcp_id_scanner.py,sha256=zsqjEJko8OtzRtHZT6YVVanD7hbaGPv0qsC_jK1Gzig,419
@@ -136,12 +137,12 @@ pyxcp/transport/base.py,sha256=Eb5qLeAP-VXQwHcX2M58Rdl0jZ8K_RFYSoE-Pq6kW64,18784
136
137
  pyxcp/transport/base_transport.hpp,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
138
  pyxcp/transport/can.py,sha256=JeASLHW2A66Zaq6X5XG9vdfX-eXdhWbttbaMR0Zow20,24032
138
139
  pyxcp/transport/eth.py,sha256=8QRzHIQIzzM9T5fbAIMk7FLSQLkMsCJa3UPW_61rXDw,10530
139
- pyxcp/transport/hdf5_policy.py,sha256=3oLr3nCcm_QshtuDi8oxrbpxaagVo5Xtg8aOFFzMYno,4070
140
+ pyxcp/transport/hdf5_policy.py,sha256=KIewRq5AI-EPNPzw-MHb5lriF-K4Jt5cuaxYFvreonU,5544
140
141
  pyxcp/transport/sxi.py,sha256=P3ppV-MzuvLMGPldm8sB_nXKyTCRJeqraQj8yzRwmqI,7379
141
- pyxcp/transport/transport_ext.cp310-win_amd64.pyd,sha256=GFwxlEB5lQsEh4G_b70I9Y1MqIcTHTkYuQgZYceY4so,386048
142
- pyxcp/transport/transport_ext.cp311-win_amd64.pyd,sha256=3gzFKFIYa_-1rqhfz-uySR_y6eLett7geYUJy60kBdE,388096
143
- pyxcp/transport/transport_ext.cp312-win_amd64.pyd,sha256=V6QFS51roQni63rLvzdqRL31o_J5vTOwI6-JUZY2AIE,395776
144
- pyxcp/transport/transport_ext.cp313-win_amd64.pyd,sha256=UxRDEa9ZFO4hf5RdniTPBMDeF0KhEH1xwAowuZ6zjZQ,396288
142
+ pyxcp/transport/transport_ext.cp310-win_amd64.pyd,sha256=T3jOQqkf_RrbIX1KXuJl2XshwBT0ckZzJMRoMikMfC4,386048
143
+ pyxcp/transport/transport_ext.cp311-win_amd64.pyd,sha256=Lq5_QlaQb0_wjkWVOe7oxdNVlDNeDHdmrpmpi0bb8tc,388096
144
+ pyxcp/transport/transport_ext.cp312-win_amd64.pyd,sha256=pnjkJiLbmrK_rHa67MZnyyBYV5KtORpxBojHz50Q0kE,395776
145
+ pyxcp/transport/transport_ext.cp313-win_amd64.pyd,sha256=XRMdq2FEYFDAFYRO31G1zwtb_mLxdtQZyLAghNonpmM,396288
145
146
  pyxcp/transport/transport_ext.hpp,sha256=ABxcvoDPua_IUJ9NhrmxcSoTr3ofLVXlezHtN_bHY5w,6511
146
147
  pyxcp/transport/transport_wrapper.cpp,sha256=fJmsiGrH4WZvDri2rJpwTosTHTqlANpWi4kHu7vda8M,12485
147
148
  pyxcp/transport/usb_transport.py,sha256=K_tAESxgSVllJinoYanJbFB_j28yOId6m5EgdXvPItQ,8902
@@ -150,8 +151,8 @@ pyxcp/utils/__init__.py,sha256=d4QmrEZiK7xtdCThhZapV2Zbyi0YB_ELNDL49mkji5Y,3509
150
151
  pyxcp/utils/cli.py,sha256=BnwQvMk8GERns4B-2I0hDuBk9pE-krc_o36NkBsezB8,3027
151
152
  pyxcp/vector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
152
153
  pyxcp/vector/map.py,sha256=7Gnhvr79geMeqqGVIJPxODXGwABdNDinnqzhpooN5TE,2306
153
- pyxcp-0.25.4.dist-info/entry_points.txt,sha256=LkHsEwubm30s4oiyCy0cKj6k97ALvQ6KjAVdyEcqu7g,358
154
- pyxcp-0.25.4.dist-info/licenses/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
155
- pyxcp-0.25.4.dist-info/METADATA,sha256=mgeFETPID1gQkYFa5mFKg3wWgu-uZnH4Y7aB2FylsPM,12775
156
- pyxcp-0.25.4.dist-info/WHEEL,sha256=HCjMoC9qqRBH4O7rtqnFiDpt5hhaxeHHcIYjwJPshyQ,98
157
- pyxcp-0.25.4.dist-info/RECORD,,
154
+ pyxcp-0.25.8.dist-info/entry_points.txt,sha256=LkHsEwubm30s4oiyCy0cKj6k97ALvQ6KjAVdyEcqu7g,358
155
+ pyxcp-0.25.8.dist-info/licenses/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
156
+ pyxcp-0.25.8.dist-info/METADATA,sha256=RsFpqcTgWLDZcfvJlhNtnrEZprtf9LSfpJW6FbysG8E,12775
157
+ pyxcp-0.25.8.dist-info/WHEEL,sha256=mbhp6OISYNyQV0z23S-pvC1L-suhijlWHyj3c994WU4,98
158
+ pyxcp-0.25.8.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.3.0
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp313-cp313-win_amd64