pyxcp 0.25.2__cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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 +20 -0
- pyxcp/aml/EtasCANMonitoring.a2l +82 -0
- pyxcp/aml/EtasCANMonitoring.aml +67 -0
- pyxcp/aml/XCP_Common.aml +408 -0
- pyxcp/aml/XCPonCAN.aml +78 -0
- pyxcp/aml/XCPonEth.aml +33 -0
- pyxcp/aml/XCPonFlx.aml +113 -0
- pyxcp/aml/XCPonSxI.aml +66 -0
- pyxcp/aml/XCPonUSB.aml +106 -0
- pyxcp/aml/ifdata_CAN.a2l +20 -0
- pyxcp/aml/ifdata_Eth.a2l +11 -0
- pyxcp/aml/ifdata_Flx.a2l +94 -0
- pyxcp/aml/ifdata_SxI.a2l +13 -0
- pyxcp/aml/ifdata_USB.a2l +81 -0
- pyxcp/asam/__init__.py +0 -0
- pyxcp/asam/types.py +131 -0
- pyxcp/asamkeydll +0 -0
- pyxcp/asamkeydll.c +116 -0
- pyxcp/asamkeydll.sh +2 -0
- pyxcp/checksum.py +732 -0
- pyxcp/cmdline.py +83 -0
- pyxcp/config/__init__.py +1257 -0
- pyxcp/config/legacy.py +120 -0
- pyxcp/constants.py +47 -0
- pyxcp/cpp_ext/__init__.py +0 -0
- pyxcp/cpp_ext/aligned_buffer.hpp +168 -0
- pyxcp/cpp_ext/bin.hpp +105 -0
- pyxcp/cpp_ext/blockmem.hpp +58 -0
- pyxcp/cpp_ext/cpp_ext.cpython-310-x86_64-linux-gnu.so +0 -0
- pyxcp/cpp_ext/cpp_ext.cpython-311-x86_64-linux-gnu.so +0 -0
- pyxcp/cpp_ext/cpp_ext.cpython-312-x86_64-linux-gnu.so +0 -0
- pyxcp/cpp_ext/cpp_ext.cpython-313-x86_64-linux-gnu.so +0 -0
- pyxcp/cpp_ext/daqlist.hpp +374 -0
- pyxcp/cpp_ext/event.hpp +67 -0
- pyxcp/cpp_ext/extension_wrapper.cpp +131 -0
- pyxcp/cpp_ext/framing.hpp +360 -0
- pyxcp/cpp_ext/helper.hpp +280 -0
- pyxcp/cpp_ext/mcobject.hpp +248 -0
- pyxcp/cpp_ext/sxi_framing.hpp +332 -0
- pyxcp/cpp_ext/tsqueue.hpp +46 -0
- pyxcp/daq_stim/__init__.py +306 -0
- pyxcp/daq_stim/optimize/__init__.py +67 -0
- pyxcp/daq_stim/optimize/binpacking.py +41 -0
- pyxcp/daq_stim/scheduler.cpp +62 -0
- pyxcp/daq_stim/scheduler.hpp +75 -0
- pyxcp/daq_stim/stim.cpp +13 -0
- pyxcp/daq_stim/stim.cpython-310-x86_64-linux-gnu.so +0 -0
- pyxcp/daq_stim/stim.cpython-311-x86_64-linux-gnu.so +0 -0
- pyxcp/daq_stim/stim.cpython-312-x86_64-linux-gnu.so +0 -0
- pyxcp/daq_stim/stim.cpython-313-x86_64-linux-gnu.so +0 -0
- pyxcp/daq_stim/stim.hpp +604 -0
- pyxcp/daq_stim/stim_wrapper.cpp +50 -0
- pyxcp/dllif.py +100 -0
- pyxcp/errormatrix.py +878 -0
- pyxcp/examples/conf_can.toml +19 -0
- pyxcp/examples/conf_can_user.toml +16 -0
- pyxcp/examples/conf_can_vector.json +11 -0
- pyxcp/examples/conf_can_vector.toml +11 -0
- pyxcp/examples/conf_eth.toml +9 -0
- pyxcp/examples/conf_nixnet.json +20 -0
- pyxcp/examples/conf_socket_can.toml +12 -0
- pyxcp/examples/run_daq.py +165 -0
- pyxcp/examples/xcp_policy.py +60 -0
- pyxcp/examples/xcp_read_benchmark.py +38 -0
- pyxcp/examples/xcp_skel.py +48 -0
- pyxcp/examples/xcp_unlock.py +38 -0
- pyxcp/examples/xcp_user_supplied_driver.py +43 -0
- pyxcp/examples/xcphello.py +79 -0
- pyxcp/examples/xcphello_recorder.py +107 -0
- pyxcp/master/__init__.py +10 -0
- pyxcp/master/errorhandler.py +677 -0
- pyxcp/master/master.py +2645 -0
- pyxcp/py.typed +0 -0
- pyxcp/recorder/.idea/.gitignore +8 -0
- pyxcp/recorder/.idea/misc.xml +4 -0
- pyxcp/recorder/.idea/modules.xml +8 -0
- pyxcp/recorder/.idea/recorder.iml +6 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +7 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
- pyxcp/recorder/.idea/sonarlint/issuestore/index.pb +7 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
- pyxcp/recorder/.idea/sonarlint/securityhotspotstore/index.pb +7 -0
- pyxcp/recorder/.idea/vcs.xml +10 -0
- pyxcp/recorder/__init__.py +96 -0
- pyxcp/recorder/build_clang.cmd +1 -0
- pyxcp/recorder/build_clang.sh +2 -0
- pyxcp/recorder/build_gcc.cmd +1 -0
- pyxcp/recorder/build_gcc.sh +2 -0
- pyxcp/recorder/build_gcc_arm.sh +2 -0
- pyxcp/recorder/converter/__init__.py +445 -0
- pyxcp/recorder/lz4.c +2829 -0
- pyxcp/recorder/lz4.h +879 -0
- pyxcp/recorder/lz4hc.c +2041 -0
- pyxcp/recorder/lz4hc.h +413 -0
- pyxcp/recorder/mio.hpp +1714 -0
- pyxcp/recorder/reader.hpp +138 -0
- pyxcp/recorder/reco.py +278 -0
- pyxcp/recorder/recorder.rst +0 -0
- pyxcp/recorder/rekorder.cpp +59 -0
- pyxcp/recorder/rekorder.cpython-310-x86_64-linux-gnu.so +0 -0
- pyxcp/recorder/rekorder.cpython-311-x86_64-linux-gnu.so +0 -0
- pyxcp/recorder/rekorder.cpython-312-x86_64-linux-gnu.so +0 -0
- pyxcp/recorder/rekorder.cpython-313-x86_64-linux-gnu.so +0 -0
- pyxcp/recorder/rekorder.hpp +274 -0
- pyxcp/recorder/setup.py +41 -0
- pyxcp/recorder/test_reko.py +34 -0
- pyxcp/recorder/unfolder.hpp +1354 -0
- pyxcp/recorder/wrap.cpp +184 -0
- pyxcp/recorder/writer.hpp +302 -0
- pyxcp/scripts/__init__.py +0 -0
- pyxcp/scripts/pyxcp_probe_can_drivers.py +20 -0
- pyxcp/scripts/xcp_examples.py +64 -0
- pyxcp/scripts/xcp_fetch_a2l.py +40 -0
- pyxcp/scripts/xcp_id_scanner.py +18 -0
- pyxcp/scripts/xcp_info.py +144 -0
- pyxcp/scripts/xcp_profile.py +26 -0
- pyxcp/scripts/xmraw_converter.py +31 -0
- pyxcp/stim/__init__.py +0 -0
- pyxcp/tests/test_asam_types.py +24 -0
- pyxcp/tests/test_binpacking.py +186 -0
- pyxcp/tests/test_can.py +1324 -0
- pyxcp/tests/test_checksum.py +95 -0
- pyxcp/tests/test_daq.py +193 -0
- pyxcp/tests/test_daq_opt.py +426 -0
- pyxcp/tests/test_frame_padding.py +156 -0
- pyxcp/tests/test_framing.py +262 -0
- pyxcp/tests/test_master.py +2116 -0
- pyxcp/tests/test_transport.py +177 -0
- pyxcp/tests/test_utils.py +30 -0
- pyxcp/timing.py +60 -0
- pyxcp/transport/__init__.py +13 -0
- pyxcp/transport/base.py +484 -0
- pyxcp/transport/base_transport.hpp +0 -0
- pyxcp/transport/can.py +660 -0
- pyxcp/transport/eth.py +254 -0
- pyxcp/transport/sxi.py +209 -0
- pyxcp/transport/transport_ext.hpp +214 -0
- pyxcp/transport/transport_wrapper.cpp +249 -0
- pyxcp/transport/usb_transport.py +229 -0
- pyxcp/types.py +987 -0
- pyxcp/utils.py +127 -0
- pyxcp/vector/__init__.py +0 -0
- pyxcp/vector/map.py +82 -0
- pyxcp-0.25.2.dist-info/METADATA +341 -0
- pyxcp-0.25.2.dist-info/RECORD +151 -0
- pyxcp-0.25.2.dist-info/WHEEL +6 -0
- pyxcp-0.25.2.dist-info/entry_points.txt +9 -0
- pyxcp-0.25.2.dist-info/licenses/LICENSE +165 -0
pyxcp/recorder/wrap.cpp
ADDED
|
@@ -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()
|