pyxcp 0.23.0__cp311-cp311-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.

Potentially problematic release.


This version of pyxcp might be problematic. Click here for more details.

Files changed (128) hide show
  1. pyxcp/__init__.py +20 -0
  2. pyxcp/aml/EtasCANMonitoring.a2l +82 -0
  3. pyxcp/aml/EtasCANMonitoring.aml +67 -0
  4. pyxcp/aml/XCP_Common.aml +408 -0
  5. pyxcp/aml/XCPonCAN.aml +78 -0
  6. pyxcp/aml/XCPonEth.aml +33 -0
  7. pyxcp/aml/XCPonFlx.aml +113 -0
  8. pyxcp/aml/XCPonSxI.aml +66 -0
  9. pyxcp/aml/XCPonUSB.aml +106 -0
  10. pyxcp/aml/ifdata_CAN.a2l +20 -0
  11. pyxcp/aml/ifdata_Eth.a2l +11 -0
  12. pyxcp/aml/ifdata_Flx.a2l +94 -0
  13. pyxcp/aml/ifdata_SxI.a2l +13 -0
  14. pyxcp/aml/ifdata_USB.a2l +81 -0
  15. pyxcp/asam/__init__.py +0 -0
  16. pyxcp/asam/types.py +131 -0
  17. pyxcp/asamkeydll.c +116 -0
  18. pyxcp/asamkeydll.sh +2 -0
  19. pyxcp/checksum.py +732 -0
  20. pyxcp/cmdline.py +52 -0
  21. pyxcp/config/__init__.py +1113 -0
  22. pyxcp/config/legacy.py +120 -0
  23. pyxcp/constants.py +47 -0
  24. pyxcp/cpp_ext/__init__.py +0 -0
  25. pyxcp/cpp_ext/bin.hpp +104 -0
  26. pyxcp/cpp_ext/blockmem.hpp +58 -0
  27. pyxcp/cpp_ext/cpp_ext.cp310-win_arm64.pyd +0 -0
  28. pyxcp/cpp_ext/cpp_ext.cp311-win_arm64.pyd +0 -0
  29. pyxcp/cpp_ext/daqlist.hpp +206 -0
  30. pyxcp/cpp_ext/event.hpp +67 -0
  31. pyxcp/cpp_ext/extension_wrapper.cpp +100 -0
  32. pyxcp/cpp_ext/helper.hpp +280 -0
  33. pyxcp/cpp_ext/mcobject.hpp +246 -0
  34. pyxcp/cpp_ext/tsqueue.hpp +46 -0
  35. pyxcp/daq_stim/__init__.py +232 -0
  36. pyxcp/daq_stim/optimize/__init__.py +67 -0
  37. pyxcp/daq_stim/optimize/binpacking.py +41 -0
  38. pyxcp/daq_stim/scheduler.cpp +62 -0
  39. pyxcp/daq_stim/scheduler.hpp +75 -0
  40. pyxcp/daq_stim/stim.cp310-win_arm64.pyd +0 -0
  41. pyxcp/daq_stim/stim.cp311-win_arm64.pyd +0 -0
  42. pyxcp/daq_stim/stim.cpp +13 -0
  43. pyxcp/daq_stim/stim.hpp +604 -0
  44. pyxcp/daq_stim/stim_wrapper.cpp +50 -0
  45. pyxcp/dllif.py +100 -0
  46. pyxcp/errormatrix.py +878 -0
  47. pyxcp/examples/conf_can.toml +19 -0
  48. pyxcp/examples/conf_can_user.toml +16 -0
  49. pyxcp/examples/conf_can_vector.json +11 -0
  50. pyxcp/examples/conf_can_vector.toml +11 -0
  51. pyxcp/examples/conf_eth.toml +9 -0
  52. pyxcp/examples/conf_nixnet.json +20 -0
  53. pyxcp/examples/conf_socket_can.toml +12 -0
  54. pyxcp/examples/conf_sxi.json +9 -0
  55. pyxcp/examples/conf_sxi.toml +7 -0
  56. pyxcp/examples/run_daq.py +163 -0
  57. pyxcp/examples/xcp_policy.py +60 -0
  58. pyxcp/examples/xcp_read_benchmark.py +38 -0
  59. pyxcp/examples/xcp_skel.py +49 -0
  60. pyxcp/examples/xcp_unlock.py +38 -0
  61. pyxcp/examples/xcp_user_supplied_driver.py +44 -0
  62. pyxcp/examples/xcphello.py +78 -0
  63. pyxcp/examples/xcphello_recorder.py +107 -0
  64. pyxcp/master/__init__.py +9 -0
  65. pyxcp/master/errorhandler.py +442 -0
  66. pyxcp/master/master.py +2047 -0
  67. pyxcp/py.typed +0 -0
  68. pyxcp/recorder/__init__.py +101 -0
  69. pyxcp/recorder/build_clang.cmd +1 -0
  70. pyxcp/recorder/build_clang.sh +2 -0
  71. pyxcp/recorder/build_gcc.cmd +1 -0
  72. pyxcp/recorder/build_gcc.sh +2 -0
  73. pyxcp/recorder/build_gcc_arm.sh +2 -0
  74. pyxcp/recorder/converter/__init__.py +450 -0
  75. pyxcp/recorder/lz4.c +2829 -0
  76. pyxcp/recorder/lz4.h +879 -0
  77. pyxcp/recorder/lz4hc.c +2041 -0
  78. pyxcp/recorder/lz4hc.h +413 -0
  79. pyxcp/recorder/mio.hpp +1714 -0
  80. pyxcp/recorder/reader.hpp +139 -0
  81. pyxcp/recorder/reco.py +277 -0
  82. pyxcp/recorder/recorder.rst +0 -0
  83. pyxcp/recorder/rekorder.cp310-win_arm64.pyd +0 -0
  84. pyxcp/recorder/rekorder.cp311-win_arm64.pyd +0 -0
  85. pyxcp/recorder/rekorder.cpp +59 -0
  86. pyxcp/recorder/rekorder.hpp +274 -0
  87. pyxcp/recorder/setup.py +41 -0
  88. pyxcp/recorder/test_reko.py +34 -0
  89. pyxcp/recorder/unfolder.hpp +1332 -0
  90. pyxcp/recorder/wrap.cpp +189 -0
  91. pyxcp/recorder/writer.hpp +302 -0
  92. pyxcp/scripts/__init__.py +0 -0
  93. pyxcp/scripts/pyxcp_probe_can_drivers.py +20 -0
  94. pyxcp/scripts/xcp_examples.py +64 -0
  95. pyxcp/scripts/xcp_fetch_a2l.py +40 -0
  96. pyxcp/scripts/xcp_id_scanner.py +19 -0
  97. pyxcp/scripts/xcp_info.py +144 -0
  98. pyxcp/scripts/xcp_profile.py +27 -0
  99. pyxcp/scripts/xmraw_converter.py +31 -0
  100. pyxcp/stim/__init__.py +0 -0
  101. pyxcp/tests/test_asam_types.py +24 -0
  102. pyxcp/tests/test_binpacking.py +186 -0
  103. pyxcp/tests/test_can.py +1324 -0
  104. pyxcp/tests/test_checksum.py +95 -0
  105. pyxcp/tests/test_daq.py +193 -0
  106. pyxcp/tests/test_daq_opt.py +426 -0
  107. pyxcp/tests/test_frame_padding.py +156 -0
  108. pyxcp/tests/test_master.py +2006 -0
  109. pyxcp/tests/test_transport.py +81 -0
  110. pyxcp/tests/test_utils.py +30 -0
  111. pyxcp/timing.py +60 -0
  112. pyxcp/transport/__init__.py +10 -0
  113. pyxcp/transport/base.py +440 -0
  114. pyxcp/transport/base_transport.hpp +0 -0
  115. pyxcp/transport/can.py +556 -0
  116. pyxcp/transport/eth.py +219 -0
  117. pyxcp/transport/sxi.py +135 -0
  118. pyxcp/transport/transport_wrapper.cpp +0 -0
  119. pyxcp/transport/usb_transport.py +213 -0
  120. pyxcp/types.py +1000 -0
  121. pyxcp/utils.py +128 -0
  122. pyxcp/vector/__init__.py +0 -0
  123. pyxcp/vector/map.py +82 -0
  124. pyxcp-0.23.0.dist-info/LICENSE +165 -0
  125. pyxcp-0.23.0.dist-info/METADATA +107 -0
  126. pyxcp-0.23.0.dist-info/RECORD +128 -0
  127. pyxcp-0.23.0.dist-info/WHEEL +4 -0
  128. pyxcp-0.23.0.dist-info/entry_points.txt +9 -0
pyxcp/py.typed ADDED
File without changes
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env python
2
+ """XCP Frame Recording Facility.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Union
7
+
8
+ from pyxcp.types import FrameCategory
9
+
10
+
11
+ try:
12
+ import pandas as pd
13
+ except ImportError:
14
+ HAS_PANDAS = False
15
+ else:
16
+ HAS_PANDAS = True
17
+
18
+ from pyxcp.recorder.rekorder import DaqOnlinePolicy # noqa: F401
19
+ from pyxcp.recorder.rekorder import (
20
+ DaqRecorderPolicy, # noqa: F401
21
+ Deserializer, # noqa: F401
22
+ MeasurementParameters, # noqa: F401
23
+ ValueHolder, # noqa: F401
24
+ )
25
+ from pyxcp.recorder.rekorder import XcpLogFileDecoder as _XcpLogFileDecoder
26
+ from pyxcp.recorder.rekorder import _PyXcpLogFileReader, _PyXcpLogFileWriter, data_types
27
+
28
+
29
+ DATA_TYPES = data_types()
30
+
31
+
32
+ @dataclass
33
+ class XcpLogFileHeader:
34
+ """ """
35
+
36
+ version: int
37
+ options: int
38
+ num_containers: int
39
+ record_count: int
40
+ size_uncompressed: int
41
+ size_compressed: int
42
+ compression_ratio: float
43
+
44
+
45
+ COUNTER_MAX = 0xFFFF
46
+
47
+
48
+ class XcpLogFileReader:
49
+ """ """
50
+
51
+ def __init__(self, file_name):
52
+ self._reader = _PyXcpLogFileReader(file_name)
53
+
54
+ @property
55
+ def header(self):
56
+ return self._reader.get_header()
57
+
58
+ def get_header(self):
59
+ return XcpLogFileHeader(*self._reader.get_header_as_tuple())
60
+
61
+ def get_metadata(self):
62
+ return self._reader.get_metadata()
63
+
64
+ def __iter__(self):
65
+ while True:
66
+ frames = self._reader.next_block()
67
+ if frames is None:
68
+ break
69
+ for category, counter, timestamp, _, payload in frames:
70
+ yield (FrameCategory(category), counter, timestamp, payload)
71
+
72
+ def reset_iter(self):
73
+ self._reader.reset()
74
+
75
+ def as_dataframe(self):
76
+ if HAS_PANDAS:
77
+ df = pd.DataFrame((f for f in self), columns=["category", "counter", "timestamp", "payload"])
78
+ df = df.set_index("timestamp")
79
+ df.counter = df.counter.astype("uint16")
80
+ df.category = df.category.map({v: k for k, v in FrameCategory.__members__.items()}).astype("category")
81
+ return df
82
+ else:
83
+ raise NotImplementedError("method as_dataframe() requires 'pandas' package")
84
+
85
+
86
+ class XcpLogFileWriter:
87
+ """ """
88
+
89
+ def __init__(self, file_name: str, prealloc=500, chunk_size=1):
90
+ self._writer = _PyXcpLogFileWriter(file_name, prealloc, chunk_size)
91
+ self._finalized = False
92
+
93
+ def __del__(self):
94
+ if not self._finalized:
95
+ self.finalize()
96
+
97
+ def add_frame(self, category: FrameCategory, counter: int, timestamp: float, payload: Union[bytes, bytearray]):
98
+ self._writer.add_frame(category, counter % (COUNTER_MAX + 1), timestamp, len(payload), payload)
99
+
100
+ def finalize(self):
101
+ self._writer.finalize()
@@ -0,0 +1 @@
1
+ clang++ -std=c++20 -O0 -fvectorize -fexceptions -Rpass=loop-vectorize -ggdb -Wall -Wextra -Weffc++ -DLZ4_DEBUG=1 -DSTANDALONE_REKORDER=1 lz4.cpp rekorder.cpp -o rekorder.exe
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ clang++ -std=c++20 -O0 -fvectorize -Rpass=loop-vectorize -ggdb -Wall -Wextra -Weffc++ -lpthread -DLZ4_DEBUG=1 -DSTANDALONE_REKORDER=1 lz4.cpp rekorder.cpp -o rekorder
@@ -0,0 +1 @@
1
+ g++ -std=c++20 -O0 -ggdb -march=native -fexceptions -ffast-math -ftree-vectorize -msse4 -mfpmath=sse -pg -fopt-info-vec-all -Wall -Wextra -Weffc++ -fcoroutines -DLZ4_DEBUG=1 -DSTANDALONE_REKORDER=1 lz4.cpp rekorder.cpp -o rekorder.exe
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ g++ -std=c++20 -O0 -ffast-math -ftree-vectorize -ftree-vectorizer-verbose=9 -fcoroutines -ggdb -Wall -Wextra -Weffc++ -DLZ4_DEBUG=1 -DSTANDALONE_REKORDER=1 lz4.cpp rekorder.cpp -o rekorder -lpthread
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ g++ -std=c++20 -O3 -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -ffast-math -ftree-vectorize -ftree-vectorizer-verbose=9 -ggdb -Wall -Wextra -Weffc++ -DLZ4_DEBUG=1 -DSTANDALONE_REKORDER=1 lz4.cpp rekorder.cpp -o rekorder
@@ -0,0 +1,450 @@
1
+ """Convert pyXCPs .xmraw files to common data formats."""
2
+
3
+ import csv
4
+ import logging
5
+ import os
6
+ import sqlite3
7
+ from array import array
8
+ from dataclasses import dataclass, field
9
+ from mmap import PAGESIZE
10
+ from pathlib import Path
11
+ from typing import Any, List
12
+
13
+ import numpy as np
14
+ from rich.logging import RichHandler
15
+
16
+
17
+ try:
18
+ import pyarrow as pa
19
+ import pyarrow.parquet as pq
20
+
21
+ has_arrow = True
22
+ except ImportError:
23
+ has_arrow = False
24
+
25
+ try:
26
+ import h5py
27
+
28
+ has_h5py = True
29
+ except ImportError:
30
+ has_h5py = False
31
+
32
+ try:
33
+ from asammdf import MDF, Signal
34
+ from asammdf.blocks.v4_blocks import HeaderBlock
35
+ from asammdf.blocks.v4_constants import FLAG_HD_TIME_OFFSET_VALID
36
+
37
+ has_asammdf = True
38
+ except ImportError:
39
+ has_asammdf = False
40
+
41
+ try:
42
+ import xlsxwriter
43
+
44
+ has_xlsxwriter = True
45
+
46
+ except ImportError:
47
+ has_xlsxwriter = False
48
+
49
+ from pyxcp import console
50
+ from pyxcp.recorder.rekorder import XcpLogFileDecoder as _XcpLogFileDecoder
51
+
52
+
53
+ FORMAT = "%(message)s"
54
+ logging.basicConfig(level="NOTSET", format=FORMAT, datefmt="[%X]", handlers=[RichHandler()])
55
+
56
+ log = logging.getLogger("rich")
57
+
58
+ MAP_TO_ARRAY = {
59
+ "U8": "B",
60
+ "I8": "b",
61
+ "U16": "H",
62
+ "I16": "h",
63
+ "U32": "L",
64
+ "I32": "l",
65
+ "U64": "Q",
66
+ "I64": "q",
67
+ "F32": "f",
68
+ "F64": "d",
69
+ "F16": "f",
70
+ "BF16": "f",
71
+ }
72
+
73
+ MAP_TO_NP = {
74
+ "U8": np.uint8,
75
+ "I8": np.int8,
76
+ "U16": np.uint16,
77
+ "I16": np.int16,
78
+ "U32": np.uint32,
79
+ "I32": np.int32,
80
+ "U64": np.uint64,
81
+ "I64": np.int64,
82
+ "F32": np.float32,
83
+ "F64": np.float64,
84
+ "F16": np.float16,
85
+ "BF16": np.float16,
86
+ }
87
+
88
+
89
+ @dataclass
90
+ class Storage:
91
+ name: str
92
+ target_type: Any
93
+ arr: array
94
+
95
+
96
+ @dataclass
97
+ class StorageContainer:
98
+ name: str
99
+ arr: List[Storage] = field(default_factory=[])
100
+ timestamp0: List[int] = field(default_factory=lambda: array("Q"))
101
+ timestamp1: List[int] = field(default_factory=lambda: array("Q"))
102
+
103
+
104
+ class XcpLogFileDecoder(_XcpLogFileDecoder):
105
+ """"""
106
+
107
+ def __init__(
108
+ self,
109
+ recording_file_name: str,
110
+ out_file_suffix: str,
111
+ remove_file: bool = True,
112
+ target_type_map: dict = None,
113
+ target_file_name: str = "",
114
+ ):
115
+ super().__init__(recording_file_name)
116
+ self.logger = logging.getLogger("PyXCP")
117
+ self.logger.setLevel(logging.DEBUG)
118
+ self.out_file_name = Path(recording_file_name).with_suffix(out_file_suffix)
119
+ self.out_file_suffix = out_file_suffix
120
+ self.target_type_map = target_type_map or {}
121
+ if remove_file:
122
+ try:
123
+ os.unlink(self.out_file_name)
124
+ except FileNotFoundError:
125
+ pass
126
+
127
+ def initialize(self) -> None:
128
+ self.on_initialize()
129
+
130
+ def on_initialize(self) -> None:
131
+ self.setup_containers()
132
+
133
+ def finalize(self) -> None:
134
+ self.on_finalize()
135
+
136
+ def on_finalize(self) -> None:
137
+ pass
138
+
139
+ def setup_containers(self) -> None:
140
+ self.tables = []
141
+ for dl in self.daq_lists:
142
+ result = []
143
+ for name, type_str in dl.headers:
144
+ array_txpe = MAP_TO_ARRAY[type_str]
145
+ target_type = self.target_type_map.get(type_str)
146
+ sd = Storage(name, target_type, array(array_txpe))
147
+ result.append(sd)
148
+ sc = StorageContainer(dl.name, result)
149
+ self.tables.append(sc)
150
+ self.on_container(sc)
151
+
152
+ def on_container(self, sc: StorageContainer) -> None:
153
+ pass
154
+
155
+
156
+ class CollectRows:
157
+
158
+ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None:
159
+ storage_container = self.tables[daq_list_num]
160
+ storage_container.timestamp0.append(timestamp0)
161
+ storage_container.timestamp1.append(timestamp1)
162
+ for idx, elem in enumerate(measurements):
163
+ storage = storage_container.arr[idx]
164
+ storage.arr.append(elem)
165
+
166
+
167
+ class ArrowConverter(CollectRows, XcpLogFileDecoder):
168
+ """"""
169
+
170
+ MAP_TO_ARROW = {
171
+ "U8": pa.uint8(),
172
+ "I8": pa.int8(),
173
+ "U16": pa.uint16(),
174
+ "I16": pa.int16(),
175
+ "U32": pa.uint32(),
176
+ "I32": pa.int32(),
177
+ "U64": pa.uint64(),
178
+ "I64": pa.int64(),
179
+ "F32": pa.float32(),
180
+ "F64": pa.float64(),
181
+ "F16": pa.float16(),
182
+ "BF16": pa.float16(),
183
+ }
184
+
185
+ def __init__(self, recording_file_name: str, target_file_name: str = ""):
186
+ super().__init__(
187
+ recording_file_name=recording_file_name,
188
+ out_file_suffix=".parquet",
189
+ remove_file=False,
190
+ target_type_map=self.MAP_TO_ARROW,
191
+ target_file_name=target_file_name,
192
+ )
193
+
194
+ def on_initialize(self) -> None:
195
+ super().on_initialize()
196
+
197
+ def on_finalize(self) -> None:
198
+ result = []
199
+ for arr in self.tables:
200
+ timestamp0 = arr.timestamp0
201
+ timestamp1 = arr.timestamp1
202
+ names = ["timestamp0", "timestamp1"]
203
+ data = [timestamp0, timestamp1]
204
+ for sd in arr.arr:
205
+ adt = pa.array(sd.arr, type=sd.target_type)
206
+ names.append(sd.name)
207
+ data.append(adt)
208
+ table = pa.Table.from_arrays(data, names=names)
209
+ fname = f"{arr.name}{self.out_file_suffix}"
210
+ self.logger.info(f"Writing file {fname!r}")
211
+ pq.write_table(table, fname)
212
+ result.append(table)
213
+ return result
214
+
215
+
216
+ class CsvConverter(XcpLogFileDecoder):
217
+
218
+ def __init__(self, recording_file_name: str, target_file_name: str = ""):
219
+ super().__init__(
220
+ recording_file_name=recording_file_name, out_file_suffix=".csv", remove_file=False, target_file_name=target_file_name
221
+ )
222
+
223
+ def on_initialize(self) -> None:
224
+ self.csv_writers = []
225
+ super().on_initialize()
226
+
227
+ def on_container(self, sc: StorageContainer) -> None:
228
+ fname = f"{sc.name}{self.out_file_suffix}"
229
+ self.logger.info(f"Creating file {fname!r}.")
230
+ writer = csv.writer(open(fname, "w", newline=""), dialect="excel")
231
+ headers = ["timestamp0", "timestamp1"] + [e.name for e in sc.arr]
232
+ writer.writerow(headers)
233
+ self.csv_writers.append(writer)
234
+
235
+ def on_finalize(self) -> None:
236
+ self.logger.info("Done.")
237
+
238
+ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None:
239
+ writer = self.csv_writers[daq_list_num]
240
+ data = [timestamp0, timestamp1, *measurements]
241
+ writer.writerow(data)
242
+
243
+
244
+ class ExcelConverter(XcpLogFileDecoder):
245
+
246
+ def __init__(self, recording_file_name: str, target_file_name: str = ""):
247
+ super().__init__(recording_file_name=recording_file_name, out_file_suffix=".xlsx", target_file_name=target_file_name)
248
+
249
+ def on_initialize(self) -> None:
250
+ self.logger.info(f"Creating file {str(self.out_file_name)!r}.")
251
+ self.xls_workbook = xlsxwriter.Workbook(self.out_file_name)
252
+ self.xls_sheets = []
253
+ self.rows = []
254
+ super().on_initialize()
255
+
256
+ def on_container(self, sc: StorageContainer) -> None:
257
+ sheet = self.xls_workbook.add_worksheet(sc.name)
258
+ self.xls_sheets.append(sheet)
259
+ headers = ["timestamp0", "timestamp1"] + [e.name for e in sc.arr]
260
+ sheet.write_row(0, 0, headers)
261
+ self.rows.append(1)
262
+
263
+ def on_finalize(self) -> None:
264
+ self.xls_workbook.close()
265
+ self.logger.info("Done.")
266
+
267
+ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None:
268
+ sheet = self.xls_sheets[daq_list_num]
269
+ row = self.rows[daq_list_num]
270
+ data = [timestamp0, timestamp1] + measurements
271
+ sheet.write_row(row, 0, data)
272
+ self.rows[daq_list_num] += 1
273
+
274
+
275
+ class HdfConverter(CollectRows, XcpLogFileDecoder):
276
+
277
+ def __init__(self, recording_file_name: str, target_file_name: str = ""):
278
+ super().__init__(recording_file_name=recording_file_name, out_file_suffix=".h5", target_file_name=target_file_name)
279
+
280
+ def on_initialize(self) -> None:
281
+ self.logger.info(f"Creating file {str(self.out_file_name)!r}")
282
+ self.out_file = h5py.File(self.out_file_name, "w")
283
+ super().on_initialize()
284
+
285
+ def on_finalize(self) -> None:
286
+ for arr in self.tables:
287
+ timestamp0 = arr.timestamp0
288
+ timestamp1 = arr.timestamp1
289
+ self.out_file[f"/{arr.name}/timestamp0"] = timestamp0
290
+ self.out_file[f"/{arr.name}/timestamp1"] = timestamp1
291
+ for sd in arr.arr:
292
+ self.out_file[f"/{arr.name}/{sd.name}"] = sd.arr
293
+ self.logger.info(f"Writing table {arr.name!r}")
294
+ self.logger.info("Done.")
295
+ self.out_file.close()
296
+
297
+
298
+ class MdfConverter(CollectRows, XcpLogFileDecoder):
299
+
300
+ def __init__(self, recording_file_name: str, target_file_name: str = ""):
301
+ super().__init__(
302
+ recording_file_name=recording_file_name,
303
+ out_file_suffix=".mf4",
304
+ target_type_map=MAP_TO_NP,
305
+ target_file_name=target_file_name,
306
+ )
307
+
308
+ def on_initialize(self) -> None:
309
+ super().on_initialize()
310
+
311
+ def on_finalize(self) -> None:
312
+ timestamp_info = self.parameters.timestamp_info
313
+ hdr = HeaderBlock(
314
+ abs_time=timestamp_info.timestamp_ns,
315
+ tz_offset=timestamp_info.utc_offset,
316
+ daylight_save_time=timestamp_info.dst_offset,
317
+ time_flags=FLAG_HD_TIME_OFFSET_VALID,
318
+ )
319
+ hdr.comment = f"""<HDcomment><TX>Timezone: {timestamp_info.timezone}</TX></HDcomment>""" # Test-Comment.
320
+ mdf4 = MDF(version="4.10", header=hdr)
321
+ for idx, arr in enumerate(self.tables):
322
+ signals = []
323
+ timestamps = arr.timestamp0
324
+ for sd in arr.arr:
325
+ signal = Signal(samples=sd.arr, name=sd.name, timestamps=timestamps)
326
+ signals.append(signal)
327
+ self.logger.info(f"Appending data-group {arr.name!r}")
328
+ mdf4.append(signals, acq_name=arr.name, comment="Created by pyXCP recorder")
329
+ self.logger.info(f"Writing {str(self.out_file_name)!r}")
330
+ mdf4.save(self.out_file_name, compression=2, overwrite=True)
331
+ self.logger.info("Done.")
332
+
333
+
334
+ class SqliteConverter(XcpLogFileDecoder):
335
+ """ """
336
+
337
+ MAP_TO_SQL = {
338
+ "U8": "INTEGER",
339
+ "I8": "INTEGER",
340
+ "U16": "INTEGER",
341
+ "I16": "INTEGER",
342
+ "U32": "INTEGER",
343
+ "I32": "INTEGER",
344
+ "U64": "INTEGER",
345
+ "I64": "INTEGER",
346
+ "F32": "FLOAT",
347
+ "F64": "FLOAT",
348
+ "F16": "FLOAT",
349
+ "BF16": "FLOAT",
350
+ }
351
+
352
+ def __init__(self, recording_file_name: str, target_file_name: str = ""):
353
+ super().__init__(
354
+ recording_file_name=recording_file_name,
355
+ out_file_suffix=".sq3",
356
+ target_type_map=self.MAP_TO_SQL,
357
+ target_file_name=target_file_name,
358
+ )
359
+
360
+ def on_initialize(self) -> None:
361
+ self.logger.info(f"Creating database {str(self.out_file_name)!r}.")
362
+ self.create_database(self.out_file_name)
363
+ self.insert_stmt = {}
364
+ super().on_initialize()
365
+
366
+ def on_container(self, sc: StorageContainer) -> None:
367
+ self.create_table(sc)
368
+ self.logger.info(f"Creating table {sc.name!r}.")
369
+ self.insert_stmt[sc.name] = (
370
+ f"""INSERT INTO {sc.name}({', '.join(['timestamp0', 'timestamp1'] + [r.name for r in sc.arr])})"""
371
+ f""" VALUES({', '.join(["?" for _ in range(len(sc.arr) + 2)])})"""
372
+ )
373
+
374
+ def on_finalize(self) -> None:
375
+ self.conn.commit()
376
+ self.conn.close()
377
+ print("Done.")
378
+
379
+ def on_daq_list(self, daq_list_num: int, timestamp0: int, timestamp1: int, measurements: list) -> None:
380
+ sc = self.tables[daq_list_num]
381
+ insert_stmt = self.insert_stmt[sc.name]
382
+ data = [timestamp0, timestamp1, *measurements]
383
+ self.execute(insert_stmt, data)
384
+
385
+ def create_database(self, db_name: str) -> None:
386
+ self.conn = sqlite3.Connection(db_name)
387
+ self.cursor = self.conn.cursor()
388
+ self.execute("PRAGMA FOREIGN_KEYS=ON")
389
+ self.execute(f"PRAGMA PAGE_SIZE={PAGESIZE}")
390
+ self.execute("PRAGMA SYNCHRONOUS=OFF")
391
+ self.execute("PRAGMA LOCKING_MODE=EXCLUSIVE")
392
+ self.execute("PRAGMA TEMP_STORE=MEMORY")
393
+
394
+ timestamp_info = self.parameters.timestamp_info
395
+ self.execute(
396
+ "CREATE TABLE timestamp_info(timestamp_ns INTEGER, utc_offset INTEGER, dst_offset INTEGER, timezone VARCHAR(255))"
397
+ )
398
+ self.execute("CREATE TABLE table_names(name VARCHAR(255))")
399
+ self.execute(
400
+ "INSERT INTO timestamp_info VALUES(?, ?, ?, ?)",
401
+ [timestamp_info.timestamp_ns, timestamp_info.utc_offset, timestamp_info.dst_offset, timestamp_info.timezone],
402
+ )
403
+
404
+ def create_table(self, sc: StorageContainer) -> None:
405
+ columns = ["timestamp0 INTEGER", "timestamp1 INTEGER"]
406
+ for elem in sc.arr:
407
+ columns.append(f"{elem.name} {elem.target_type}")
408
+ ddl = f"CREATE TABLE {sc.name}({', '.join(columns)})"
409
+ self.execute(ddl)
410
+ self.execute("INSERT INTO table_names VALUES(?)", [sc.name])
411
+
412
+ def execute(self, *args: List[str]) -> None:
413
+ try:
414
+ self.cursor.execute(*args)
415
+ except Exception as e:
416
+ print(e)
417
+
418
+
419
+ CONVERTERS = {
420
+ "arrow": ArrowConverter,
421
+ "csv": CsvConverter,
422
+ "excel": ExcelConverter,
423
+ "hdf5": HdfConverter,
424
+ "mdf": MdfConverter,
425
+ "sqlite3": SqliteConverter,
426
+ }
427
+
428
+ CONVERTER_REQUIREMENTS = {
429
+ "arrow": (has_arrow, "pyarrow"),
430
+ "csv": (True, "csv"),
431
+ "excel": (has_xlsxwriter, "xlsxwriter"),
432
+ "hdf5": (has_h5py, "h5py"),
433
+ "mdf": (has_asammdf, "asammdf"),
434
+ "sqlite3": (True, "csv"),
435
+ }
436
+
437
+
438
+ def convert_xmraw(converter_name: str, recording_file_name: str, target_file_name: str, *args, **kwargs) -> None:
439
+ converter_class = CONVERTERS.get(converter_name.lower())
440
+ if converter_class is None:
441
+ console.print(f"Invalid converter name: {converter_name!r}")
442
+ return
443
+ available, pck_name = CONVERTER_REQUIREMENTS.get(converter_name.lower(), (True, ""))
444
+ if not available:
445
+ console.print(f"Converter {converter_name!r} requires package {pck_name!r}.")
446
+ console.print(f"Please run [green]pip install {pck_name}[/green] to install it.")
447
+ return
448
+ # Path(*p.parts[:-1], p.stem)
449
+ converter = converter_class(recording_file_name)
450
+ converter.run()