pyxcp 0.25.5__cp312-cp312-macosx_11_0_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.
Files changed (153) 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 +0 -0
  18. pyxcp/asamkeydll.c +116 -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.cpython-310-darwin.so +0 -0
  30. pyxcp/cpp_ext/cpp_ext.cpython-311-darwin.so +0 -0
  31. pyxcp/cpp_ext/cpp_ext.cpython-312-darwin.so +0 -0
  32. pyxcp/cpp_ext/daqlist.hpp +374 -0
  33. pyxcp/cpp_ext/event.hpp +67 -0
  34. pyxcp/cpp_ext/extension_wrapper.cpp +208 -0
  35. pyxcp/cpp_ext/framing.hpp +360 -0
  36. pyxcp/cpp_ext/helper.hpp +280 -0
  37. pyxcp/cpp_ext/mcobject.hpp +248 -0
  38. pyxcp/cpp_ext/sxi_framing.hpp +332 -0
  39. pyxcp/cpp_ext/tsqueue.hpp +46 -0
  40. pyxcp/daq_stim/__init__.py +291 -0
  41. pyxcp/daq_stim/optimize/__init__.py +67 -0
  42. pyxcp/daq_stim/optimize/binpacking.py +41 -0
  43. pyxcp/daq_stim/scheduler.cpp +62 -0
  44. pyxcp/daq_stim/scheduler.hpp +75 -0
  45. pyxcp/daq_stim/stim.cpp +13 -0
  46. pyxcp/daq_stim/stim.cpython-310-darwin.so +0 -0
  47. pyxcp/daq_stim/stim.cpython-311-darwin.so +0 -0
  48. pyxcp/daq_stim/stim.cpython-312-darwin.so +0 -0
  49. pyxcp/daq_stim/stim.hpp +604 -0
  50. pyxcp/daq_stim/stim_wrapper.cpp +50 -0
  51. pyxcp/dllif.py +100 -0
  52. pyxcp/errormatrix.py +878 -0
  53. pyxcp/examples/conf_can.toml +19 -0
  54. pyxcp/examples/conf_can_user.toml +16 -0
  55. pyxcp/examples/conf_can_vector.json +11 -0
  56. pyxcp/examples/conf_can_vector.toml +11 -0
  57. pyxcp/examples/conf_eth.toml +9 -0
  58. pyxcp/examples/conf_nixnet.json +20 -0
  59. pyxcp/examples/conf_socket_can.toml +12 -0
  60. pyxcp/examples/run_daq.py +165 -0
  61. pyxcp/examples/xcp_policy.py +60 -0
  62. pyxcp/examples/xcp_read_benchmark.py +38 -0
  63. pyxcp/examples/xcp_skel.py +48 -0
  64. pyxcp/examples/xcp_unlock.py +36 -0
  65. pyxcp/examples/xcp_user_supplied_driver.py +43 -0
  66. pyxcp/examples/xcphello.py +65 -0
  67. pyxcp/examples/xcphello_recorder.py +107 -0
  68. pyxcp/master/__init__.py +10 -0
  69. pyxcp/master/errorhandler.py +677 -0
  70. pyxcp/master/master.py +2641 -0
  71. pyxcp/py.typed +0 -0
  72. pyxcp/recorder/.idea/.gitignore +8 -0
  73. pyxcp/recorder/.idea/misc.xml +4 -0
  74. pyxcp/recorder/.idea/modules.xml +8 -0
  75. pyxcp/recorder/.idea/recorder.iml +6 -0
  76. pyxcp/recorder/.idea/sonarlint/issuestore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +7 -0
  77. pyxcp/recorder/.idea/sonarlint/issuestore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
  78. pyxcp/recorder/.idea/sonarlint/issuestore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
  79. pyxcp/recorder/.idea/sonarlint/issuestore/index.pb +7 -0
  80. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +0 -0
  81. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
  82. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
  83. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/index.pb +7 -0
  84. pyxcp/recorder/.idea/vcs.xml +10 -0
  85. pyxcp/recorder/__init__.py +96 -0
  86. pyxcp/recorder/build_clang.cmd +1 -0
  87. pyxcp/recorder/build_clang.sh +2 -0
  88. pyxcp/recorder/build_gcc.cmd +1 -0
  89. pyxcp/recorder/build_gcc.sh +2 -0
  90. pyxcp/recorder/build_gcc_arm.sh +2 -0
  91. pyxcp/recorder/converter/__init__.py +444 -0
  92. pyxcp/recorder/lz4.c +2829 -0
  93. pyxcp/recorder/lz4.h +879 -0
  94. pyxcp/recorder/lz4hc.c +2041 -0
  95. pyxcp/recorder/lz4hc.h +413 -0
  96. pyxcp/recorder/mio.hpp +1714 -0
  97. pyxcp/recorder/reader.hpp +138 -0
  98. pyxcp/recorder/reco.py +278 -0
  99. pyxcp/recorder/recorder.rst +0 -0
  100. pyxcp/recorder/rekorder.cpp +59 -0
  101. pyxcp/recorder/rekorder.cpython-310-darwin.so +0 -0
  102. pyxcp/recorder/rekorder.cpython-311-darwin.so +0 -0
  103. pyxcp/recorder/rekorder.cpython-312-darwin.so +0 -0
  104. pyxcp/recorder/rekorder.hpp +274 -0
  105. pyxcp/recorder/setup.py +41 -0
  106. pyxcp/recorder/test_reko.py +34 -0
  107. pyxcp/recorder/unfolder.hpp +1354 -0
  108. pyxcp/recorder/wrap.cpp +184 -0
  109. pyxcp/recorder/writer.hpp +302 -0
  110. pyxcp/scripts/__init__.py +0 -0
  111. pyxcp/scripts/pyxcp_probe_can_drivers.py +20 -0
  112. pyxcp/scripts/xcp_examples.py +64 -0
  113. pyxcp/scripts/xcp_fetch_a2l.py +40 -0
  114. pyxcp/scripts/xcp_id_scanner.py +18 -0
  115. pyxcp/scripts/xcp_info.py +159 -0
  116. pyxcp/scripts/xcp_profile.py +26 -0
  117. pyxcp/scripts/xmraw_converter.py +31 -0
  118. pyxcp/stim/__init__.py +0 -0
  119. pyxcp/tests/test_asam_types.py +24 -0
  120. pyxcp/tests/test_binpacking.py +186 -0
  121. pyxcp/tests/test_can.py +1324 -0
  122. pyxcp/tests/test_checksum.py +95 -0
  123. pyxcp/tests/test_daq.py +193 -0
  124. pyxcp/tests/test_daq_opt.py +426 -0
  125. pyxcp/tests/test_frame_padding.py +156 -0
  126. pyxcp/tests/test_framing.py +262 -0
  127. pyxcp/tests/test_master.py +2116 -0
  128. pyxcp/tests/test_transport.py +177 -0
  129. pyxcp/tests/test_utils.py +30 -0
  130. pyxcp/timing.py +60 -0
  131. pyxcp/transport/__init__.py +13 -0
  132. pyxcp/transport/base.py +484 -0
  133. pyxcp/transport/base_transport.hpp +0 -0
  134. pyxcp/transport/can.py +660 -0
  135. pyxcp/transport/eth.py +254 -0
  136. pyxcp/transport/hdf5_policy.py +167 -0
  137. pyxcp/transport/sxi.py +209 -0
  138. pyxcp/transport/transport_ext.cpython-310-darwin.so +0 -0
  139. pyxcp/transport/transport_ext.cpython-311-darwin.so +0 -0
  140. pyxcp/transport/transport_ext.cpython-312-darwin.so +0 -0
  141. pyxcp/transport/transport_ext.hpp +214 -0
  142. pyxcp/transport/transport_wrapper.cpp +249 -0
  143. pyxcp/transport/usb_transport.py +229 -0
  144. pyxcp/types.py +987 -0
  145. pyxcp/utils/__init__.py +127 -0
  146. pyxcp/utils/cli.py +78 -0
  147. pyxcp/vector/__init__.py +0 -0
  148. pyxcp/vector/map.py +82 -0
  149. pyxcp-0.25.5.dist-info/METADATA +341 -0
  150. pyxcp-0.25.5.dist-info/RECORD +153 -0
  151. pyxcp-0.25.5.dist-info/WHEEL +6 -0
  152. pyxcp-0.25.5.dist-info/entry_points.txt +9 -0
  153. pyxcp-0.25.5.dist-info/licenses/LICENSE +165 -0
@@ -0,0 +1,184 @@
1
+
2
+ #include <pybind11/functional.h>
3
+ #include <pybind11/numpy.h>
4
+ #include <pybind11/pybind11.h>
5
+ #include <pybind11/stl.h>
6
+ #include <memory>
7
+
8
+ #include <cstdint>
9
+
10
+ #include "rekorder.hpp"
11
+
12
+ namespace py = pybind11;
13
+ using namespace pybind11::literals;
14
+
15
+
16
+ class PyDaqOnlinePolicy : public DaqOnlinePolicy {
17
+ public:
18
+
19
+ using DaqOnlinePolicy::DaqOnlinePolicy;
20
+
21
+ void on_daq_list(
22
+ std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1,
23
+ const std::vector<measurement_value_t>& measurement
24
+ ) override {
25
+ PYBIND11_OVERRIDE_PURE(void, DaqOnlinePolicy, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement);
26
+ }
27
+
28
+ void initialize() override {
29
+ PYBIND11_OVERRIDE(void, DaqOnlinePolicy, initialize);
30
+ }
31
+
32
+ void finalize() override {
33
+ PYBIND11_OVERRIDE(void, DaqOnlinePolicy, finalize);
34
+ }
35
+ };
36
+
37
+ class PyDaqRecorderPolicy : public DaqRecorderPolicy {
38
+ public:
39
+
40
+ using DaqRecorderPolicy::DaqRecorderPolicy;
41
+
42
+ void initialize() override {
43
+ PYBIND11_OVERRIDE(void, DaqRecorderPolicy, initialize);
44
+ }
45
+
46
+ void finalize() override {
47
+ PYBIND11_OVERRIDE(void, DaqRecorderPolicy, finalize);
48
+ }
49
+ };
50
+
51
+ class PyXcpLogFileDecoder : public XcpLogFileDecoder {
52
+ public:
53
+
54
+ using XcpLogFileDecoder::XcpLogFileDecoder;
55
+
56
+ void on_daq_list(
57
+ std::uint16_t daq_list_num, std::uint64_t timestamp0, std::uint64_t timestamp1,
58
+ const std::vector<measurement_value_t>& measurement
59
+ ) override {
60
+ PYBIND11_OVERRIDE_PURE(void, XcpLogFileDecoder, on_daq_list, daq_list_num, timestamp0, timestamp1, measurement);
61
+ }
62
+
63
+ void initialize() override {
64
+ PYBIND11_OVERRIDE(void, XcpLogFileDecoder, initialize);
65
+ }
66
+
67
+ void finalize() override {
68
+ PYBIND11_OVERRIDE(void, XcpLogFileDecoder, finalize);
69
+ }
70
+ };
71
+
72
+ PYBIND11_MODULE(rekorder, m) {
73
+ m.doc() = "XCP raw frame recorder.";
74
+ m.def("data_types", get_data_types);
75
+
76
+ py::class_<FileHeaderType>(m, "FileHeaderType")
77
+ .def(py::init<std::uint16_t, std::uint16_t, std::uint16_t, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t>())
78
+ .def("__repr__", [](const FileHeaderType& self) {
79
+ std::stringstream ss;
80
+ ss << "FileHeaderType(" << std::endl;
81
+ ss << " hdr_size=" << self.hdr_size << "," << std::endl;
82
+ ss << " version=" << self.version << "," << std::endl;
83
+ ss << " options=" << self.options << "," << std::endl;
84
+ ss << " num_containers=" << self.num_containers << "," << std::endl;
85
+ ss << " record_count=" << self.record_count << "," << std::endl;
86
+ ss << " size_compressed=" << self.size_compressed << "," << std::endl;
87
+ ss << " size_uncompressed=" << self.size_uncompressed << "," << std::endl;
88
+ ss << " compression_ratio="
89
+ << (double)((std::uint64_t)(((double)self.size_uncompressed / (double)self.size_compressed * 100.0) + 0.5)) / 100.0
90
+ << std::endl;
91
+ ss << ")" << std::endl;
92
+ return ss.str();
93
+ });
94
+
95
+ py::class_<Deserializer>(m, "Deserializer").def(py::init<const std::string&>()).def("run", &Deserializer::run);
96
+
97
+ py::class_<XcpLogFileReader>(m, "_PyXcpLogFileReader")
98
+ .def(py::init<const std::string&>())
99
+ .def("next_block", &XcpLogFileReader::next_block)
100
+ .def("reset", &XcpLogFileReader::reset)
101
+ .def("get_header_as_tuple", &XcpLogFileReader::get_header_as_tuple)
102
+ .def("get_metadata", [](const XcpLogFileReader& self) { return py::bytes(self.get_metadata()); });
103
+
104
+ py::class_<XcpLogFileWriter>(m, "_PyXcpLogFileWriter")
105
+ .def(
106
+ py::init<const std::string&, std::uint32_t, std::uint32_t, std::string_view>(), py::arg("filename"),
107
+ py::arg("prealloc"), py::arg("chunk_size"), py::arg("metadata") = ""
108
+ )
109
+ .def("finalize", &XcpLogFileWriter::finalize)
110
+ .def("add_frame", &XcpLogFileWriter::add_frame);
111
+
112
+ py::class_<MeasurementParameters>(m, "MeasurementParameters")
113
+ .def(py::init<
114
+ std::uint8_t, std::uint8_t, bool, bool, bool, bool, double, std::uint8_t, std::uint16_t, const TimestampInfo&,
115
+ const std::vector<std::shared_ptr<DaqListBase>>&, const std::vector<std::uint16_t>&>())
116
+ .def("dumps", [](const MeasurementParameters& self) { return py::bytes(self.dumps()); })
117
+ .def(
118
+ "__repr__",
119
+ [](const MeasurementParameters& self) {
120
+ std::stringstream ss;
121
+ ss << "MeasurementParameters(";
122
+ ss << "byte_order=\"" << byte_order_to_string(self.m_byte_order) << "\", ";
123
+ ss << "id_field_size=" << static_cast<std::uint16_t>(self.m_id_field_size) << ", ";
124
+ ss << "timestamps_supported=" << bool_to_string(self.m_timestamps_supported) << ", ";
125
+ ss << "ts_fixed=" << bool_to_string(self.m_ts_fixed) << ", ";
126
+ ss << "prescaler_supported=" << bool_to_string(self.m_prescaler_supported) << ", ";
127
+ ss << "selectable_timestamps=" << bool_to_string(self.m_selectable_timestamps) << ", ";
128
+ ss << "ts_scale_factor=" << self.m_ts_scale_factor << ", ";
129
+ ss << "ts_size=" << static_cast<std::uint16_t>(self.m_ts_size) << ", ";
130
+ ss << "min_daq=" << static_cast<std::uint16_t>(self.m_min_daq) << ", ";
131
+ ss << "timestamp_info=" << self.get_timestamp_info().to_string() << ", ";
132
+ ss << "daq_lists=[\n";
133
+ for (const auto& dl : self.m_daq_lists) {
134
+ ss << dl->to_string() << ",\n";
135
+ }
136
+ ss << "],\n";
137
+ ss << "first_pids=[";
138
+ for (auto fp : self.m_first_pids) {
139
+ ss << fp << ", ";
140
+ }
141
+ ss << "]";
142
+ return ss.str();
143
+ }
144
+ )
145
+ .def_property_readonly("byte_order", &MeasurementParameters::get_byte_order)
146
+ .def_property_readonly("id_field_size", &MeasurementParameters::get_id_field_size)
147
+ .def_property_readonly("timestamps_supported", &MeasurementParameters::get_timestamps_supported)
148
+ .def_property_readonly("ts_fixed", &MeasurementParameters::get_ts_fixed)
149
+ .def_property_readonly("prescaler_supported", &MeasurementParameters::get_prescaler_supported)
150
+ .def_property_readonly("selectable_timestamps", &MeasurementParameters::get_selectable_timestamps)
151
+ .def_property_readonly("ts_scale_factor", &MeasurementParameters::get_ts_scale_factor)
152
+ .def_property_readonly("ts_size", &MeasurementParameters::get_ts_size)
153
+ .def_property_readonly("min_daq", &MeasurementParameters::get_min_daq)
154
+ .def_property_readonly("timestamp_info", &MeasurementParameters::get_timestamp_info)
155
+ .def_property_readonly("daq_lists", &MeasurementParameters::get_daq_lists)
156
+ .def_property_readonly("first_pids", &MeasurementParameters::get_first_pids)
157
+ .def_property_readonly("timestamp_info", &MeasurementParameters::get_timestamp_info);
158
+
159
+ py::class_<DaqRecorderPolicy, PyDaqRecorderPolicy>(m, "DaqRecorderPolicy", py::dynamic_attr())
160
+ .def(py::init<>())
161
+ .def("create_writer", &DaqRecorderPolicy::create_writer)
162
+ .def("feed", &DaqRecorderPolicy::feed)
163
+ .def("set_parameters", &DaqRecorderPolicy::set_parameters)
164
+ .def("initialize", &DaqRecorderPolicy::initialize)
165
+ .def("finalize", &DaqRecorderPolicy::finalize);
166
+
167
+ py::class_<DaqOnlinePolicy, PyDaqOnlinePolicy>(m, "DaqOnlinePolicy", py::dynamic_attr())
168
+ .def(py::init<>())
169
+ .def("on_daq_list", &DaqOnlinePolicy::on_daq_list)
170
+ .def("feed", &DaqOnlinePolicy::feed)
171
+ .def("finalize", &DaqOnlinePolicy::finalize)
172
+ .def("set_parameters", &DaqOnlinePolicy::set_parameters)
173
+ .def("initialize", &DaqOnlinePolicy::initialize);
174
+
175
+ py::class_<XcpLogFileDecoder, PyXcpLogFileDecoder>(m, "XcpLogFileDecoder", py::dynamic_attr())
176
+ .def(py::init<const std::string&>())
177
+ .def("run", &XcpLogFileDecoder::run)
178
+ .def("on_daq_list", &XcpLogFileDecoder::on_daq_list)
179
+ .def_property_readonly("parameters", &XcpLogFileDecoder::get_parameters)
180
+ .def_property_readonly("daq_lists", &XcpLogFileDecoder::get_daq_lists)
181
+ .def("get_header", &XcpLogFileDecoder::get_header)
182
+ .def("initialize", &XcpLogFileDecoder::initialize)
183
+ .def("finalize", &XcpLogFileDecoder::finalize);
184
+ }
@@ -0,0 +1,302 @@
1
+
2
+ #ifndef RECORDER_WRITER_HPP
3
+ #define RECORDER_WRITER_HPP
4
+
5
+ constexpr std::uint64_t MASK32 = (1ULL << 32) - 1;
6
+
7
+ class XcpLogFileWriter {
8
+ public:
9
+
10
+ explicit XcpLogFileWriter(
11
+ const std::string &file_name, uint32_t prealloc = 10UL, uint32_t chunk_size = 1, std::string_view metadata = ""
12
+ ) {
13
+ if (!file_name.ends_with(detail::FILE_EXTENSION)) {
14
+ m_file_name = file_name + detail::FILE_EXTENSION;
15
+ } else {
16
+ m_file_name = file_name;
17
+ }
18
+ m_opened = false;
19
+
20
+ #if defined(_WIN32)
21
+ m_fd = CreateFileA(
22
+ m_file_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) nullptr, CREATE_ALWAYS,
23
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr
24
+ );
25
+ if (m_fd == INVALID_HANDLE_VALUE) {
26
+ throw std::runtime_error(error_string("XcpLogFileWriter::CreateFileA", get_last_error()));
27
+ } else {
28
+ m_opened = true;
29
+ }
30
+ #else
31
+ m_fd = open(m_file_name.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0666);
32
+ if (m_fd == -1) {
33
+ throw std::runtime_error(error_string("XcpLogFileWriter::open", get_last_error()));
34
+ } else {
35
+ m_opened = true;
36
+ }
37
+ #endif
38
+ m_hard_limit = megabytes(prealloc);
39
+ resize(m_hard_limit);
40
+ m_mmap = new mio::mmap_sink(m_fd);
41
+ m_chunk_size = 512 * 1024; // megabytes(chunk_size);
42
+ m_intermediate_storage = new blob_t[m_chunk_size + megabytes(1)];
43
+ m_offset = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE;
44
+ m_metadata = metadata;
45
+
46
+ if (!metadata.empty()) {
47
+ m_offset += std::size(metadata);
48
+ write_metadata();
49
+ }
50
+ start_thread();
51
+ }
52
+
53
+ ~XcpLogFileWriter() {
54
+ finalize();
55
+ #ifdef __APPLE__
56
+ if (collector_thread.joinable()) {
57
+ collector_thread.join();
58
+ }
59
+ #endif
60
+ }
61
+
62
+ void finalize() {
63
+ std::error_code ec;
64
+ if (!m_finalized) {
65
+ m_finalized = true;
66
+ stop_thread();
67
+
68
+ if (!m_opened) {
69
+ return;
70
+ }
71
+
72
+ if (m_container_record_count) {
73
+ compress_frames();
74
+ }
75
+
76
+ std::uint16_t options = m_metadata.empty() ? 0 : XMRAW_HAS_METADATA;
77
+
78
+ write_header(
79
+ detail::VERSION, options, m_num_containers, m_record_count, m_total_size_compressed, m_total_size_uncompressed
80
+ );
81
+ m_mmap->unmap();
82
+ ec = mio::detail::last_error();
83
+ if (ec.value() != 0) {
84
+ throw std::runtime_error(error_string("XcpLogFileWriter::mio::unmap", ec));
85
+ }
86
+
87
+ resize(m_offset);
88
+ #if defined(_WIN32)
89
+ if (!CloseHandle(m_fd)) {
90
+ throw std::runtime_error(error_string("XcpLogFileWriter::CloseHandle", get_last_error()));
91
+ }
92
+ #else
93
+ if (close(m_fd) == -1) {
94
+ throw std::runtime_error(error_string("XcpLogFileWriter::close", get_last_error()));
95
+ }
96
+ #endif
97
+ delete m_mmap;
98
+ delete[] m_intermediate_storage;
99
+ }
100
+ }
101
+
102
+ void add_frame(uint8_t category, uint16_t counter, std::uint64_t timestamp, uint16_t length, char const *data) {
103
+ auto payload = new char[length];
104
+
105
+ _fcopy(payload, data, length);
106
+ my_queue.put(std::make_tuple(category, counter, timestamp, length, payload));
107
+ }
108
+
109
+ protected:
110
+
111
+ void resize(std::uint64_t size, bool remap = false) {
112
+ std::error_code ec;
113
+
114
+ if (remap) {
115
+ m_mmap->unmap();
116
+ ec = mio::detail::last_error();
117
+ if (ec.value() != 0) {
118
+ throw std::runtime_error(error_string("XcpLogFileWriter::mio::unmap", ec));
119
+ }
120
+ }
121
+
122
+ #if defined(_WIN32)
123
+ LONG low_part = (MASK32 & size);
124
+ LONG high_part = size >> 32;
125
+
126
+ if (SetFilePointer(m_fd, low_part, &high_part, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
127
+ auto err = get_last_error();
128
+
129
+ if (err.value() != NO_ERROR) {
130
+ throw std::runtime_error(error_string("XcpLogFileWriter::SetFilePointer", err));
131
+ }
132
+ }
133
+ if (SetEndOfFile(m_fd) == 0) {
134
+ throw std::runtime_error(error_string("XcpLogFileWriter::SetEndOfFile", get_last_error()));
135
+ }
136
+ #else
137
+ if (ftruncate(m_fd, size) == -1) {
138
+ throw std::runtime_error(error_string("XcpLogFileWriter::ftruncate", get_last_error()));
139
+ }
140
+ #endif
141
+ if (remap) {
142
+ m_mmap->map(m_fd, 0, size, ec);
143
+ if (ec.value() != 0) {
144
+ throw std::runtime_error(error_string("XcpLogFileWriter::mio::map", ec));
145
+ }
146
+ }
147
+ }
148
+
149
+ blob_t *ptr(std::uint64_t pos = 0) const {
150
+ return (blob_t *)(m_mmap->data() + pos);
151
+ }
152
+
153
+ template<typename T>
154
+ void store_im(T const *data, std::uint32_t length) {
155
+ _fcopy(
156
+ reinterpret_cast<char *>(m_intermediate_storage + m_intermediate_storage_offset), reinterpret_cast<char const *>(data),
157
+ length
158
+ );
159
+ m_intermediate_storage_offset += length;
160
+ }
161
+
162
+ void compress_frames() {
163
+ auto container = ContainerHeaderType{};
164
+ // printf("Compressing %u frames... [%d]\n", m_container_record_count, m_intermediate_storage_offset);
165
+ const int cp_size = ::LZ4_compress_HC(
166
+ reinterpret_cast<char const *>(m_intermediate_storage),
167
+ reinterpret_cast<char *>(ptr(m_offset + detail::CONTAINER_SIZE)), m_intermediate_storage_offset,
168
+ LZ4_COMPRESSBOUND(m_intermediate_storage_offset), LZ4HC_CLEVEL_MAX
169
+ );
170
+
171
+ if (cp_size < 0) {
172
+ throw std::runtime_error("XcpLogFileWriter - LZ4 compression failed.");
173
+ }
174
+
175
+ if (m_offset > (m_hard_limit >> 1)) {
176
+ std::cout << "[INFO] " << current_timestamp() << ": Doubling measurement file size." << std::endl;
177
+ m_hard_limit <<= 1;
178
+ resize(m_hard_limit, true);
179
+ write_header(
180
+ detail::VERSION, m_metadata.empty() ? 0 : XMRAW_HAS_METADATA, m_num_containers, m_record_count,
181
+ m_total_size_compressed, m_total_size_uncompressed
182
+ );
183
+ }
184
+ container.record_count = m_container_record_count;
185
+ container.size_compressed = cp_size;
186
+ container.size_uncompressed = m_container_size_uncompressed;
187
+
188
+ _fcopy(reinterpret_cast<char *>(ptr(m_offset)), reinterpret_cast<char const *>(&container), detail::CONTAINER_SIZE);
189
+
190
+ m_offset += (detail::CONTAINER_SIZE + cp_size);
191
+ m_total_size_uncompressed += m_container_size_uncompressed;
192
+ m_total_size_compressed += cp_size;
193
+ m_record_count += m_container_record_count;
194
+ m_container_size_uncompressed = 0;
195
+ m_container_size_compressed = 0;
196
+ m_container_record_count = 0;
197
+ m_intermediate_storage_offset = 0;
198
+ m_num_containers += 1;
199
+ }
200
+
201
+ void write_bytes(std::uint64_t pos, std::uint64_t count, char const *buf) const {
202
+ auto addr = reinterpret_cast<char *>(ptr(pos));
203
+
204
+ _fcopy(addr, buf, count);
205
+ }
206
+
207
+ void write_header(
208
+ std::uint16_t version, std::uint16_t options, std::uint64_t num_containers, std::uint64_t record_count,
209
+ std::uint64_t size_compressed, std::uint64_t size_uncompressed
210
+ ) {
211
+ auto header = FileHeaderType{};
212
+ write_bytes(0x00000000UL, detail::MAGIC_SIZE, detail::MAGIC.c_str());
213
+ header.hdr_size = detail::FILE_HEADER_SIZE + detail::MAGIC_SIZE;
214
+ header.version = version;
215
+ header.options = options;
216
+ header.num_containers = num_containers;
217
+ header.record_count = record_count;
218
+ header.size_compressed = size_compressed;
219
+ header.size_uncompressed = size_uncompressed;
220
+ write_bytes(0x00000000UL + detail::MAGIC_SIZE, detail::FILE_HEADER_SIZE, reinterpret_cast<char const *>(&header));
221
+ }
222
+
223
+ void write_metadata() {
224
+ if (!m_metadata.empty()) {
225
+ write_bytes(detail::MAGIC_SIZE + detail::FILE_HEADER_SIZE, m_metadata.size(), m_metadata.c_str());
226
+ }
227
+ }
228
+
229
+ bool start_thread() {
230
+ if (collector_thread.joinable()) {
231
+ return false;
232
+ }
233
+ stop_collector_thread_flag = false;
234
+ #ifdef __APPLE__
235
+ collector_thread = std::thread([this]() {
236
+ #else
237
+ collector_thread = std::jthread([this]() {
238
+ #endif
239
+ while (!stop_collector_thread_flag) {
240
+ auto item = my_queue.get();
241
+ const auto content = item.get();
242
+ if (stop_collector_thread_flag == true) {
243
+ break;
244
+ }
245
+ const auto [category, counter, timestamp, length, payload] = *content;
246
+ const frame_header_t frame{ category, counter, timestamp, length };
247
+ store_im(&frame, sizeof(frame));
248
+ store_im(payload, length);
249
+ delete[] payload;
250
+ m_container_record_count += 1;
251
+ m_container_size_uncompressed += (sizeof(frame) + length);
252
+ if (m_container_size_uncompressed > m_chunk_size) {
253
+ compress_frames();
254
+ }
255
+ }
256
+ });
257
+
258
+ return true;
259
+ }
260
+
261
+ bool stop_thread() {
262
+ if (!collector_thread.joinable()) {
263
+ return false;
264
+ }
265
+ stop_collector_thread_flag = true;
266
+ my_queue.put(FrameTupleWriter{}); // Put something into the queue, otherwise the thread will hang forever.
267
+ collector_thread.join();
268
+ return true;
269
+ }
270
+
271
+ private:
272
+
273
+ std::string m_file_name;
274
+ std::uint64_t m_offset{ 0 };
275
+ std::uint32_t m_chunk_size{ 0 };
276
+ std::string m_metadata;
277
+ bool m_opened{ false };
278
+ std::uint64_t m_num_containers{ 0 };
279
+ std::uint64_t m_record_count{ 0UL };
280
+ std::uint32_t m_container_record_count{ 0UL };
281
+ std::uint64_t m_total_size_uncompressed{ 0UL };
282
+ std::uint64_t m_total_size_compressed{ 0UL };
283
+ std::uint32_t m_container_size_uncompressed{ 0UL };
284
+ std::uint32_t m_container_size_compressed{ 0UL };
285
+ __ALIGN blob_t *m_intermediate_storage{ nullptr };
286
+ std::uint32_t m_intermediate_storage_offset{ 0 };
287
+ std::uint64_t m_hard_limit{ 0 };
288
+ mio::file_handle_type m_fd{ INVALID_HANDLE_VALUE };
289
+ mio::mmap_sink *m_mmap{ nullptr };
290
+ bool m_finalized{ false };
291
+ #ifdef __APPLE__
292
+ std::thread collector_thread{};
293
+ #else
294
+ std::jthread collector_thread{};
295
+ #endif
296
+ std::mutex mtx;
297
+ TsQueue<FrameTupleWriter> my_queue;
298
+ BlockMemory<char, XCP_PAYLOAD_MAX, 16> mem{};
299
+ std::atomic_bool stop_collector_thread_flag{ false };
300
+ };
301
+
302
+ #endif // RECORDER_WRITER_HPP
File without changes
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env python
2
+
3
+ import can
4
+
5
+
6
+ def main():
7
+ can.log.setLevel("ERROR")
8
+ interfaces = can.detect_available_configs()
9
+ print("-" * 80)
10
+ if not interfaces:
11
+ print("No CAN-interfaces installed on your system.")
12
+ else:
13
+ print("\nInstalled CAN-interfaces on your system:\n")
14
+ interfaces = sorted(interfaces, key=lambda e: e["interface"])
15
+ for interface in interfaces:
16
+ print("\t{:20s} -- CHANNEL: {}".format(interface["interface"], interface["channel"]))
17
+
18
+
19
+ if __name__ == "__main__":
20
+ main()
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ Copy pyXCP examples to a given directory.
5
+ """
6
+
7
+ import argparse
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ from pyxcp import console
12
+
13
+
14
+ pyver = sys.version_info
15
+ if pyver.major == 3 and pyver.minor <= 9:
16
+ import pkg_resources
17
+
18
+ def copy_files_from_package(package_name: str, source_directory: str, args: argparse.Namespace) -> None:
19
+ destination_directory = args.output_directory
20
+ force = args.force
21
+ for fn in pkg_resources.resource_listdir(package_name, source_directory):
22
+ source_file = Path(pkg_resources.resource_filename(package_name, f"{source_directory}/{fn}"))
23
+ if source_file.suffix == ".py":
24
+ dest_file = Path(destination_directory) / fn
25
+ if dest_file.exists() and not force:
26
+ console.print(f"[white]Destination file [blue]{fn!r} [white]already exists. Skipping.")
27
+ continue
28
+ console.print(f"[blue]{source_file} [white]==> [green]{dest_file}")
29
+ data = source_file.read_text(encoding="utf-8")
30
+ dest_file.parent.mkdir(parents=True, exist_ok=True)
31
+ dest_file.write_text(data, encoding="utf-8")
32
+
33
+ else:
34
+ import importlib.resources
35
+
36
+ def copy_files_from_package(package_name: str, source_directory: str, args: argparse.Namespace) -> None:
37
+ destination_directory = args.output_directory
38
+ force = args.force
39
+ for fn in importlib.resources.files(f"{package_name}.{source_directory}").iterdir():
40
+ if fn.suffix == ".py":
41
+ data = fn.read_text(encoding="utf-8")
42
+ dest_file = Path(destination_directory) / fn.name
43
+ if dest_file.exists() and not force:
44
+ console.print(f"[white]Destination file [blue]{fn.name!r} [white]already exists. Skipping.")
45
+ continue
46
+ console.print(f"[blue]{fn} [white]==> [green]{dest_file}")
47
+ dest_file.parent.mkdir(parents=True, exist_ok=True)
48
+ dest_file.write_text(data, encoding="utf-8")
49
+
50
+
51
+ def main():
52
+ parser = argparse.ArgumentParser(description=__doc__)
53
+
54
+ parser.add_argument("output_directory", metavar="output_directory", type=Path, help="output directory")
55
+
56
+ parser.add_argument("-f", "--force", action="store_true", help="overwrite existing files.")
57
+
58
+ args = parser.parse_args()
59
+ print("Copying pyXCP examples...\n")
60
+ copy_files_from_package("pyxcp", "examples", args)
61
+
62
+
63
+ if __name__ == "__main__":
64
+ main()
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env python
2
+ """Fetch A2L file from XCP slave (if supported)."""
3
+
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from rich.prompt import Confirm
8
+
9
+ from pyxcp.cmdline import ArgumentParser
10
+ from pyxcp.types import XcpGetIdType
11
+
12
+
13
+ def main():
14
+ ap = ArgumentParser(description="Fetch A2L file from XCP slave.")
15
+
16
+ with ap.run() as x:
17
+ x.connect()
18
+
19
+ # TODO: error-handling.
20
+ file_name = x.identifier(XcpGetIdType.FILENAME)
21
+ content = x.identifier(XcpGetIdType.FILE_TO_UPLOAD)
22
+ x.disconnect()
23
+ if not content:
24
+ sys.exit(f"Empty response from ID '{XcpGetIdType.FILE_TO_UPLOAD!r}'.")
25
+ if not file_name:
26
+ file_name = "output.a2l"
27
+ if not file_name.lower().endswith(".a2l"):
28
+ file_name += ".a2l"
29
+ dest = Path(file_name)
30
+ if dest.exists():
31
+ if not Confirm.ask(f"Destination file [green]{dest.name!r}[/green] already exists. Do you want to overwrite it?"):
32
+ print("Aborting...")
33
+ exit(1)
34
+ with dest.open("wt", encoding="utf-8") as of:
35
+ of.write(content)
36
+ print(f"A2L data written to {file_name!r}.")
37
+
38
+
39
+ if __name__ == "__main__":
40
+ main()
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python
2
+ """Scan for available IDs."""
3
+
4
+ from pyxcp.cmdline import ArgumentParser
5
+
6
+
7
+ def main():
8
+ ap = ArgumentParser(description="Scan for available IDs.")
9
+ with ap.run() as x:
10
+ x.connect()
11
+ result = x.id_scanner()
12
+ for key, value in result.items():
13
+ print(f"{key}: {value}", end="\n\n")
14
+ x.disconnect()
15
+
16
+
17
+ if __name__ == "__main__":
18
+ main()