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