pyxcp 0.23.3__cp312-cp312-win_arm64.whl → 0.25.6__cp312-cp312-win_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. pyxcp/__init__.py +1 -1
  2. pyxcp/asamkeydll.exe +0 -0
  3. pyxcp/cmdline.py +15 -30
  4. pyxcp/config/__init__.py +73 -20
  5. pyxcp/cpp_ext/aligned_buffer.hpp +168 -0
  6. pyxcp/cpp_ext/bin.hpp +7 -6
  7. pyxcp/cpp_ext/cpp_ext.cp310-win_arm64.pyd +0 -0
  8. pyxcp/cpp_ext/cpp_ext.cp311-win_arm64.pyd +0 -0
  9. pyxcp/cpp_ext/cpp_ext.cp312-win_arm64.pyd +0 -0
  10. pyxcp/cpp_ext/daqlist.hpp +241 -73
  11. pyxcp/cpp_ext/extension_wrapper.cpp +123 -15
  12. pyxcp/cpp_ext/framing.hpp +360 -0
  13. pyxcp/cpp_ext/mcobject.hpp +5 -3
  14. pyxcp/cpp_ext/sxi_framing.hpp +332 -0
  15. pyxcp/daq_stim/__init__.py +182 -45
  16. pyxcp/daq_stim/optimize/binpacking.py +2 -2
  17. pyxcp/daq_stim/scheduler.cpp +8 -8
  18. pyxcp/daq_stim/stim.cp310-win_arm64.pyd +0 -0
  19. pyxcp/daq_stim/stim.cp311-win_arm64.pyd +0 -0
  20. pyxcp/daq_stim/stim.cp312-win_arm64.pyd +0 -0
  21. pyxcp/errormatrix.py +2 -2
  22. pyxcp/examples/run_daq.py +5 -3
  23. pyxcp/examples/xcp_policy.py +6 -6
  24. pyxcp/examples/xcp_read_benchmark.py +2 -2
  25. pyxcp/examples/xcp_skel.py +1 -2
  26. pyxcp/examples/xcp_unlock.py +10 -12
  27. pyxcp/examples/xcp_user_supplied_driver.py +1 -2
  28. pyxcp/examples/xcphello.py +2 -15
  29. pyxcp/examples/xcphello_recorder.py +2 -2
  30. pyxcp/master/__init__.py +1 -0
  31. pyxcp/master/errorhandler.py +248 -13
  32. pyxcp/master/master.py +838 -250
  33. pyxcp/recorder/.idea/.gitignore +8 -0
  34. pyxcp/recorder/.idea/misc.xml +4 -0
  35. pyxcp/recorder/.idea/modules.xml +8 -0
  36. pyxcp/recorder/.idea/recorder.iml +6 -0
  37. pyxcp/recorder/.idea/sonarlint/issuestore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +7 -0
  38. pyxcp/recorder/.idea/sonarlint/issuestore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
  39. pyxcp/recorder/.idea/sonarlint/issuestore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
  40. pyxcp/recorder/.idea/sonarlint/issuestore/index.pb +7 -0
  41. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/3/8/3808afc69ac1edb9d760000a2f137335b1b99728 +0 -0
  42. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/9/a/9a2aa4db38d3115ed60da621e012c0efc0172aae +0 -0
  43. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/b/4/b49006702b459496a8e8c94ebe60947108361b91 +0 -0
  44. pyxcp/recorder/.idea/sonarlint/securityhotspotstore/index.pb +7 -0
  45. pyxcp/recorder/.idea/vcs.xml +10 -0
  46. pyxcp/recorder/__init__.py +5 -10
  47. pyxcp/recorder/converter/__init__.py +4 -10
  48. pyxcp/recorder/reader.hpp +0 -1
  49. pyxcp/recorder/reco.py +1 -0
  50. pyxcp/recorder/rekorder.cp310-win_arm64.pyd +0 -0
  51. pyxcp/recorder/rekorder.cp311-win_arm64.pyd +0 -0
  52. pyxcp/recorder/rekorder.cp312-win_arm64.pyd +0 -0
  53. pyxcp/recorder/unfolder.hpp +129 -107
  54. pyxcp/recorder/wrap.cpp +3 -8
  55. pyxcp/scripts/xcp_fetch_a2l.py +2 -2
  56. pyxcp/scripts/xcp_id_scanner.py +1 -2
  57. pyxcp/scripts/xcp_info.py +66 -51
  58. pyxcp/scripts/xcp_profile.py +1 -2
  59. pyxcp/tests/test_daq.py +1 -1
  60. pyxcp/tests/test_framing.py +262 -0
  61. pyxcp/tests/test_master.py +210 -100
  62. pyxcp/tests/test_transport.py +138 -42
  63. pyxcp/timing.py +1 -1
  64. pyxcp/transport/__init__.py +8 -5
  65. pyxcp/transport/base.py +187 -143
  66. pyxcp/transport/can.py +117 -13
  67. pyxcp/transport/eth.py +55 -20
  68. pyxcp/transport/hdf5_policy.py +167 -0
  69. pyxcp/transport/sxi.py +126 -52
  70. pyxcp/transport/transport_ext.cp310-win_arm64.pyd +0 -0
  71. pyxcp/transport/transport_ext.cp311-win_arm64.pyd +0 -0
  72. pyxcp/transport/transport_ext.cp312-win_arm64.pyd +0 -0
  73. pyxcp/transport/transport_ext.hpp +214 -0
  74. pyxcp/transport/transport_wrapper.cpp +249 -0
  75. pyxcp/transport/usb_transport.py +47 -31
  76. pyxcp/types.py +0 -13
  77. pyxcp/{utils.py → utils/__init__.py} +3 -4
  78. pyxcp/utils/cli.py +78 -0
  79. pyxcp-0.25.6.dist-info/METADATA +341 -0
  80. pyxcp-0.25.6.dist-info/RECORD +153 -0
  81. {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info}/WHEEL +1 -1
  82. pyxcp/examples/conf_sxi.json +0 -9
  83. pyxcp/examples/conf_sxi.toml +0 -7
  84. pyxcp-0.23.3.dist-info/METADATA +0 -219
  85. pyxcp-0.23.3.dist-info/RECORD +0 -131
  86. {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info}/entry_points.txt +0 -0
  87. {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,214 @@
1
+
2
+ #include <cstdint>
3
+
4
+ #include <bit>
5
+ #include <optional>
6
+ #include <iostream>
7
+ #include <map>
8
+ #include <set>
9
+ #include <tuple>
10
+ #include <vector>
11
+
12
+ #include "rekorder.hpp"
13
+
14
+ const std::map<FrameCategory, std::string> FrameCategoryName {
15
+ {FrameCategory::META, "METADATA"},
16
+ {FrameCategory::CMD, "CMD"},
17
+ {FrameCategory::RES, "RESPONSE"},
18
+ {FrameCategory::ERR, "ERROR"},
19
+ {FrameCategory::EV, "EVENT"},
20
+ {FrameCategory::SERV, "SERV"},
21
+ {FrameCategory::DAQ, "DAQ"},
22
+ {FrameCategory::STIM, "STIM"},
23
+ };
24
+
25
+ /*
26
+ Base class for all frame acquisition policies.
27
+
28
+ Parameters
29
+ ---------
30
+ filter_out: set or None
31
+ A set of frame types to filter out.
32
+ If None, all frame types are accepted for further processing.
33
+
34
+ Example: (FrameType.REQUEST, FrameType.RESPONSE, FrameType.EVENT, FrameType.SERV)
35
+ ==> care only about DAQ frames.
36
+ */
37
+ class FrameAcquisitionPolicy {
38
+ public:
39
+
40
+ using payload_t = std::string;
41
+ using filter_t = std::set<FrameCategory>;
42
+ using frame_t = std::tuple<std::uint32_t, std::uint64_t, const payload_t>;
43
+
44
+
45
+ FrameAcquisitionPolicy(const std::optional<filter_t>& filter_out) {
46
+ if (!filter_out) {
47
+ m_filter_out = filter_t{};
48
+ } else {
49
+ m_filter_out = filter_out;
50
+ }
51
+ }
52
+
53
+ std::optional<filter_t> get_filtered_out() const {
54
+ return m_filter_out;
55
+ }
56
+
57
+ FrameAcquisitionPolicy(const FrameAcquisitionPolicy&) = delete;
58
+ FrameAcquisitionPolicy(FrameAcquisitionPolicy&&) = delete;
59
+ FrameAcquisitionPolicy() = delete;
60
+
61
+ virtual ~FrameAcquisitionPolicy() {}
62
+
63
+ virtual void feed(FrameCategory frame_category, std::uint32_t counter, std::uint64_t timestamp, const payload_t& payload) = 0;
64
+
65
+ virtual void finalize() = 0;
66
+
67
+ protected:
68
+
69
+ std::optional<filter_t> m_filter_out;
70
+
71
+ };
72
+
73
+
74
+ /*
75
+ No operation / do nothing policy.
76
+ */
77
+ class NoOpPolicy : public FrameAcquisitionPolicy {
78
+ public:
79
+
80
+ NoOpPolicy(const std::optional<filter_t>& filter_out) : FrameAcquisitionPolicy(filter_out) {}
81
+
82
+ void feed(FrameCategory frame_category, std::uint32_t counter, std::uint64_t timestamp, const payload_t& payload) override {}
83
+
84
+ void finalize() override {}
85
+ };
86
+
87
+
88
+ /*
89
+ Dequeue based frame acquisition policy.
90
+
91
+ Deprecated: Use only for compatibility reasons.
92
+ */
93
+ class LegacyFrameAcquisitionPolicy : public FrameAcquisitionPolicy {
94
+ public:
95
+
96
+ using deque_t = TsQueue<frame_t>;
97
+
98
+ LegacyFrameAcquisitionPolicy(const std::optional<filter_t>& filter_out) : FrameAcquisitionPolicy(filter_out) {
99
+
100
+ m_queue_map[FrameCategory::CMD] = std::make_shared<deque_t>();
101
+ m_queue_map[FrameCategory::RES] = std::make_shared<deque_t>();
102
+ m_queue_map[FrameCategory::EV] = std::make_shared<deque_t>();
103
+ m_queue_map[FrameCategory::SERV] = std::make_shared<deque_t>();
104
+ m_queue_map[FrameCategory::DAQ] = std::make_shared<deque_t>();
105
+ m_queue_map[FrameCategory::META] = std::make_shared<deque_t>();
106
+ m_queue_map[FrameCategory::ERR] = std::make_shared<deque_t>();
107
+ m_queue_map[FrameCategory::STIM] = std::make_shared<deque_t>();
108
+
109
+ }
110
+
111
+ LegacyFrameAcquisitionPolicy() = delete;
112
+ LegacyFrameAcquisitionPolicy(const LegacyFrameAcquisitionPolicy&) = delete;
113
+ LegacyFrameAcquisitionPolicy(LegacyFrameAcquisitionPolicy&&) = delete;
114
+
115
+ void feed(FrameCategory frame_category, std::uint32_t counter, std::uint64_t timestamp, const payload_t& payload) override {
116
+ if (m_filter_out && (!(*m_filter_out).contains(frame_category))) {
117
+ m_queue_map[frame_category]->put({counter, timestamp, payload});
118
+ }
119
+ }
120
+
121
+ std::shared_ptr<deque_t> get_req_queue() {
122
+ return m_queue_map.at(FrameCategory::CMD);
123
+ }
124
+
125
+ std::shared_ptr<deque_t> get_res_queue() {
126
+ return m_queue_map.at(FrameCategory::RES);
127
+ }
128
+
129
+ std::shared_ptr<deque_t> get_daq_queue() {
130
+ return m_queue_map.at(FrameCategory::DAQ);
131
+ }
132
+
133
+ std::shared_ptr<deque_t> get_ev_queue() {
134
+ return m_queue_map.at(FrameCategory::EV);
135
+ }
136
+
137
+ std::shared_ptr<deque_t> get_serv_queue() {
138
+ return m_queue_map.at(FrameCategory::SERV);
139
+ }
140
+
141
+ std::shared_ptr<deque_t> get_meta_queue() {
142
+ return m_queue_map.at(FrameCategory::META);
143
+ }
144
+
145
+ std::shared_ptr<deque_t> get_error_queue() {
146
+ return m_queue_map.at(FrameCategory::ERR);
147
+ }
148
+
149
+ std::shared_ptr<deque_t> get_stim_queue() {
150
+ return m_queue_map.at(FrameCategory::STIM);
151
+ }
152
+
153
+ void finalize() override {}
154
+
155
+ private:
156
+
157
+ std::map<FrameCategory, std::shared_ptr<deque_t>> m_queue_map{};
158
+ };
159
+
160
+ std::string hex_bytes(std::string_view payload) {
161
+ std::stringstream ss;
162
+
163
+ for (auto ch: payload) {
164
+ ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(static_cast<unsigned char>(ch)) << " ";
165
+ }
166
+ return ss.str();
167
+ }
168
+
169
+ /*
170
+ Frame acquisition policy that prints frames to stdout.
171
+ */
172
+ class StdoutPolicy : public FrameAcquisitionPolicy {
173
+ public:
174
+ StdoutPolicy(const std::optional<filter_t>& filter_out) : FrameAcquisitionPolicy(filter_out) {}
175
+
176
+ StdoutPolicy() = delete;
177
+ StdoutPolicy(const StdoutPolicy&) = delete;
178
+ StdoutPolicy(StdoutPolicy&&) = delete;
179
+
180
+ void feed(FrameCategory frame_category, std::uint32_t counter, std::uint64_t timestamp, const payload_t& payload) override {
181
+ if (m_filter_out && (!(*m_filter_out).contains(frame_category))) {
182
+ std::cout << std::left << std::setw(8) << FrameCategoryName.at(frame_category) << " " << std::right <<
183
+ std::setw(6) << counter << " " << std::setw(8) << timestamp << " [ " << std::left << hex_bytes(payload) << "]" << std::endl;
184
+ }
185
+ }
186
+
187
+ void finalize() override { }
188
+
189
+ };
190
+
191
+ class FrameRecorderPolicy : public FrameAcquisitionPolicy {
192
+ public:
193
+
194
+ FrameRecorderPolicy(const std::string& file_name, const std::optional<filter_t>& filter_out, uint32_t prealloc = 10UL, uint32_t chunk_size = 1) : FrameAcquisitionPolicy(filter_out) {
195
+ m_writer = std::make_unique<XcpLogFileWriter>(file_name, prealloc, chunk_size);
196
+ }
197
+
198
+ FrameRecorderPolicy() = delete;
199
+ FrameRecorderPolicy(const FrameRecorderPolicy&) = delete;
200
+ FrameRecorderPolicy(FrameRecorderPolicy&&) = delete;
201
+
202
+ void feed(FrameCategory frame_category, std::uint32_t counter, std::uint64_t timestamp, const payload_t& payload) override {
203
+ if (m_filter_out && (!(*m_filter_out).contains(frame_category))) {
204
+ m_writer->add_frame(static_cast<std::uint8_t>(frame_category), counter, timestamp, payload.size(), std::bit_cast<const char *>(payload.data()));
205
+ }
206
+ }
207
+
208
+ void finalize() override {
209
+ m_writer->finalize();
210
+ }
211
+
212
+ private:
213
+ std::unique_ptr<XcpLogFileWriter> m_writer;
214
+ };
@@ -0,0 +1,249 @@
1
+
2
+
3
+ #include <pybind11/chrono.h>
4
+ #include <pybind11/functional.h>
5
+ #include <pybind11/numpy.h>
6
+ #include <pybind11/pybind11.h>
7
+ #include <pybind11/stl.h>
8
+
9
+ #include <cstdint>
10
+
11
+ #include "transport_ext.hpp"
12
+ #include "framing.hpp"
13
+ #include "sxi_framing.hpp"
14
+
15
+
16
+ namespace py = pybind11;
17
+ using namespace pybind11::literals;
18
+
19
+ using SxiFrLBCN = SxiReceiver< SxiHeaderFormat::LenByte, SxiChecksumType::None>;
20
+ using SxiFrLBC8 = SxiReceiver< SxiHeaderFormat::LenByte, SxiChecksumType::Sum8>;
21
+ using SxiFrLBC16 = SxiReceiver< SxiHeaderFormat::LenByte, SxiChecksumType::Sum16>;
22
+
23
+ using SxiFrLCBCN = SxiReceiver< SxiHeaderFormat::LenCtrByte, SxiChecksumType::None>;
24
+ using SxiFrLCBC8 = SxiReceiver< SxiHeaderFormat::LenCtrByte, SxiChecksumType::Sum8>;
25
+ using SxiFrLCBC16 = SxiReceiver< SxiHeaderFormat::LenCtrByte, SxiChecksumType::Sum16>;
26
+
27
+ using SxiFrLFBCN = SxiReceiver< SxiHeaderFormat::LenFillByte, SxiChecksumType::None>;
28
+ using SxiFrLFBC8 = SxiReceiver< SxiHeaderFormat::LenFillByte, SxiChecksumType::Sum8>;
29
+ using SxiFrLFBC16 = SxiReceiver< SxiHeaderFormat::LenFillByte, SxiChecksumType::Sum16>;
30
+
31
+ using SxiFrLWCN = SxiReceiver< SxiHeaderFormat::LenWord, SxiChecksumType::None>;
32
+ using SxiFrLWC8 = SxiReceiver< SxiHeaderFormat::LenWord, SxiChecksumType::Sum8>;
33
+ using SxiFrLWC16 = SxiReceiver< SxiHeaderFormat::LenWord, SxiChecksumType::Sum16>;
34
+
35
+ using SxiFrLCWCN = SxiReceiver< SxiHeaderFormat::LenCtrWord, SxiChecksumType::None>;
36
+ using SxiFrLCWC8 = SxiReceiver< SxiHeaderFormat::LenCtrWord, SxiChecksumType::Sum8>;
37
+ using SxiFrLCWC16 = SxiReceiver< SxiHeaderFormat::LenCtrWord, SxiChecksumType::Sum16>;
38
+
39
+ using SxiFrLFWCN = SxiReceiver< SxiHeaderFormat::LenFillWord, SxiChecksumType::None>;
40
+ using SxiFrLFWC8 = SxiReceiver< SxiHeaderFormat::LenFillWord, SxiChecksumType::Sum8>;
41
+ using SxiFrLFWC16 = SxiReceiver< SxiHeaderFormat::LenFillWord, SxiChecksumType::Sum16>;
42
+
43
+
44
+ class PyFrameAcquisitionPolicy : public FrameAcquisitionPolicy {
45
+ public:
46
+
47
+ using FrameAcquisitionPolicy::FrameAcquisitionPolicy;
48
+
49
+ void feed(
50
+ FrameCategory frame_category, std::uint32_t counter, std::uint64_t timestamp, const payload_t& payload
51
+ ) override {
52
+ PYBIND11_OVERRIDE_PURE(void, FrameAcquisitionPolicy, feed, frame_category, counter, timestamp, payload);
53
+ }
54
+
55
+
56
+ void finalize() override {
57
+ PYBIND11_OVERRIDE_PURE(void, FrameAcquisitionPolicy, finalize);
58
+ }
59
+ };
60
+
61
+
62
+ PYBIND11_MODULE(transport_ext, m) {
63
+ m.doc() = "pyXCP transport-layer base classes.";
64
+
65
+ py::enum_<FrameCategory>(m, "FrameCategory")
66
+ .value("METADATA", FrameCategory::META)
67
+ .value("CMD", FrameCategory::CMD)
68
+ .value("RESPONSE", FrameCategory::RES)
69
+ .value("ERROR", FrameCategory::ERR)
70
+ .value("EVENT", FrameCategory::EV)
71
+ .value("SERV", FrameCategory::SERV)
72
+ .value("DAQ", FrameCategory::DAQ)
73
+ .value("STIM", FrameCategory::STIM)
74
+ ;
75
+
76
+ py::class_<FrameAcquisitionPolicy, PyFrameAcquisitionPolicy>(m, "FrameAcquisitionPolicy", py::dynamic_attr())
77
+ .def(py::init<const std::optional<FrameAcquisitionPolicy::filter_t>&>(), py::arg("filtered_out") = std::nullopt)
78
+ .def("feed", &FrameAcquisitionPolicy::feed)
79
+ .def("finalize", &FrameAcquisitionPolicy::finalize)
80
+ .def_property_readonly("filtered_out", &FrameAcquisitionPolicy::get_filtered_out)
81
+ ;
82
+
83
+ py::class_<LegacyFrameAcquisitionPolicy>(m, "LegacyFrameAcquisitionPolicy", py::dynamic_attr())
84
+ .def(py::init<const std::optional<FrameAcquisitionPolicy::filter_t>&>(), py::arg("filtered_out") = std::nullopt)
85
+ .def("feed", &FrameAcquisitionPolicy::feed)
86
+ .def("finalize", &FrameAcquisitionPolicy::finalize)
87
+ .def_property_readonly("reqQueue", &LegacyFrameAcquisitionPolicy::get_req_queue)
88
+ .def_property_readonly("resQueue", &LegacyFrameAcquisitionPolicy::get_res_queue)
89
+ .def_property_readonly("daqQueue", &LegacyFrameAcquisitionPolicy::get_daq_queue)
90
+ .def_property_readonly("evQueue", &LegacyFrameAcquisitionPolicy::get_ev_queue)
91
+ .def_property_readonly("servQueue", &LegacyFrameAcquisitionPolicy::get_serv_queue)
92
+ .def_property_readonly("metaQueue", &LegacyFrameAcquisitionPolicy::get_meta_queue)
93
+ .def_property_readonly("errorQueue", &LegacyFrameAcquisitionPolicy::get_error_queue)
94
+ .def_property_readonly("stimQueue", &LegacyFrameAcquisitionPolicy::get_stim_queue)
95
+ ;
96
+
97
+ py::class_<NoOpPolicy>(m, "NoOpPolicy", py::dynamic_attr())
98
+ .def(py::init<const std::optional<FrameAcquisitionPolicy::filter_t>&>(), py::arg("filtered_out") = std::nullopt)
99
+ .def("feed", &FrameAcquisitionPolicy::feed)
100
+ .def("finalize", &FrameAcquisitionPolicy::finalize)
101
+ ;
102
+
103
+ py::class_<StdoutPolicy>(m, "StdoutPolicy", py::dynamic_attr())
104
+ .def(py::init<const std::optional<FrameAcquisitionPolicy::filter_t>&>(), py::arg("filtered_out") = std::nullopt)
105
+ .def("feed", &FrameAcquisitionPolicy::feed)
106
+ .def("finalize", &FrameAcquisitionPolicy::finalize)
107
+ ;
108
+
109
+ py::class_<FrameRecorderPolicy>(m, "FrameRecorderPolicy", py::dynamic_attr())
110
+ .def(py::init<const std::string&, const std::optional<FrameAcquisitionPolicy::filter_t>&, uint32_t, uint32_t>(),
111
+ py::arg("file_name"), py::arg("filtered_out") = std::nullopt, py::arg("prealloc") = 10UL, py::arg("chunk_size") = 1)
112
+ .def("feed", &FrameAcquisitionPolicy::feed)
113
+ .def("finalize", &FrameAcquisitionPolicy::finalize)
114
+ ;
115
+ // Transport layer type enum
116
+ py::enum_<XcpTransportLayerType>(m, "XcpTransportLayerType")
117
+ .value("CAN", XcpTransportLayerType::CAN)
118
+ .value("ETH", XcpTransportLayerType::ETH)
119
+ .value("SXI", XcpTransportLayerType::SXI)
120
+ .value("USB", XcpTransportLayerType::USB);
121
+
122
+ // XCP checksum type enum
123
+ py::enum_<ChecksumType>(m, "ChecksumType")
124
+ .value("NO_CHECKSUM", ChecksumType::NO_CHECKSUM)
125
+ .value("BYTE_CHECKSUM", ChecksumType::BYTE_CHECKSUM)
126
+ .value("WORD_CHECKSUM", ChecksumType::WORD_CHECKSUM);
127
+
128
+ // XCP framing configuration and helper
129
+ py::class_<XcpFramingConfig>(m, "XcpFramingConfig")
130
+ .def(py::init<>())
131
+ .def(py::init<XcpTransportLayerType, std::uint8_t, std::uint8_t, std::uint8_t, bool, ChecksumType>(),
132
+ "transport_layer_type"_a, "header_len"_a, "header_ctr"_a, "header_fill"_a, "tail_fill"_a = false, "tail_cs"_a = ChecksumType::NO_CHECKSUM)
133
+ .def_property_readonly("transport_layer_type", [](const XcpFramingConfig &self) { return self.transport_layer_type; })
134
+ .def_property_readonly("header_len", [](const XcpFramingConfig &self) { return self.header_len; })
135
+ .def_property_readonly("header_ctr", [](const XcpFramingConfig &self) { return self.header_ctr; })
136
+ .def_property_readonly("header_fill", [](const XcpFramingConfig &self) { return self.header_fill; })
137
+ .def_property_readonly("tail_fill", [](const XcpFramingConfig &self) { return self.tail_fill; })
138
+ .def_property_readonly("tail_cs", [](const XcpFramingConfig &self) { return self.tail_cs; });
139
+
140
+ py::class_<XcpFraming>(m, "XcpFraming")
141
+ .def(py::init<const XcpFramingConfig&>())
142
+ .def("prepare_request", [](XcpFraming &self, std::uint32_t cmd, py::bytes data) {
143
+ std::string s = data;
144
+ std::vector<uint8_t> data_vec(s.begin(), s.end());
145
+ return self.prepare_request(cmd, data_vec);
146
+ }, "cmd"_a, "data"_a)
147
+ .def("prepare_request", [](XcpFraming &self, std::uint32_t cmd, py::args data) {
148
+ std::vector<uint8_t> data_vec;
149
+ for (auto item : data) {
150
+ data_vec.push_back(py::cast<uint8_t>(item));
151
+ }
152
+ return self.prepare_request(cmd, data_vec);
153
+ }, "cmd"_a)
154
+ .def("unpack_header", &XcpFraming::unpack_header, py::arg("data"), py::arg("initial_offset") = 0)
155
+ .def("verify_checksum", &XcpFraming::verify_checksum)
156
+ .def_property("counter_send", &XcpFraming::get_counter_send, &XcpFraming::set_counter_send)
157
+ .def_property_readonly("header_size", &XcpFraming::get_header_size);
158
+
159
+ py::class_<SxiFrLBCN>(m, "SxiFrLBCN")
160
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
161
+ .def("feed_bytes", &SxiFrLBCN::feed_bytes, py::arg("data"))
162
+ ;
163
+
164
+ py::class_<SxiFrLBC8>(m, "SxiFrLBC8")
165
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
166
+ .def("feed_bytes", &SxiFrLBC8::feed_bytes, py::arg("data"))
167
+ ;
168
+
169
+ py::class_<SxiFrLBC16>(m, "SxiFrLBC16")
170
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
171
+ .def("feed_bytes", &SxiFrLBC16::feed_bytes, py::arg("data"))
172
+ ;
173
+
174
+ py::class_<SxiFrLCBCN>(m, "SxiFrLCBCN")
175
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
176
+ .def("feed_bytes", &SxiFrLCBCN::feed_bytes, py::arg("data"))
177
+ ;
178
+
179
+ py::class_<SxiFrLCBC8>(m, "SxiFrLCBC8")
180
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
181
+ .def("feed_bytes", &SxiFrLCBC8::feed_bytes, py::arg("data"))
182
+ ;
183
+
184
+ py::class_<SxiFrLCBC16>(m, "SxiFrLCBC16")
185
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
186
+ .def("feed_bytes", &SxiFrLCBC16::feed_bytes, py::arg("data"))
187
+ ;
188
+
189
+ py::class_<SxiFrLFBCN>(m, "SxiFrLFBCN")
190
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
191
+ .def("feed_bytes", &SxiFrLFBCN::feed_bytes, py::arg("data"))
192
+ ;
193
+
194
+ py::class_<SxiFrLFBC8>(m, "SxiFrLFBC8")
195
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
196
+ .def("feed_bytes", &SxiFrLFBC8::feed_bytes, py::arg("data"))
197
+ ;
198
+
199
+ py::class_<SxiFrLFBC16>(m, "SxiFrLFBC16")
200
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
201
+ .def("feed_bytes", &SxiFrLFBC16::feed_bytes, py::arg("data"))
202
+ ;
203
+
204
+ py::class_<SxiFrLWCN>(m, "SxiFrLWCN")
205
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
206
+ .def("feed_bytes", &SxiFrLWCN::feed_bytes, py::arg("data"))
207
+ ;
208
+
209
+ py::class_<SxiFrLWC8>(m, "SxiFrLWC8")
210
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
211
+ .def("feed_bytes", &SxiFrLWC8::feed_bytes, py::arg("data"))
212
+ ;
213
+
214
+ py::class_<SxiFrLWC16>(m, "SxiFrLWC16")
215
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
216
+ .def("feed_bytes", &SxiFrLWC16::feed_bytes, py::arg("data"))
217
+ ;
218
+
219
+ py::class_<SxiFrLCWCN>(m, "SxiFrLCWCN")
220
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
221
+ .def("feed_bytes", &SxiFrLCWCN::feed_bytes, py::arg("data"))
222
+ ;
223
+
224
+ py::class_<SxiFrLCWC8>(m, "SxiFrLCWC8")
225
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
226
+ .def("feed_bytes", &SxiFrLCWC8::feed_bytes, py::arg("data"))
227
+ ;
228
+
229
+ py::class_<SxiFrLCWC16>(m, "SxiFrLCWC16")
230
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
231
+ .def("feed_bytes", &SxiFrLCWC16::feed_bytes, py::arg("data"))
232
+ ;
233
+
234
+ py::class_<SxiFrLFWCN>(m, "SxiFrLFWCN")
235
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
236
+ .def("feed_bytes", &SxiFrLFWCN::feed_bytes, py::arg("data"))
237
+ ;
238
+
239
+ py::class_<SxiFrLFWC8>(m, "SxiFrLFWC8")
240
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
241
+ .def("feed_bytes", &SxiFrLFWC8::feed_bytes, py::arg("data"))
242
+ ;
243
+
244
+ py::class_<SxiFrLFWC16>(m, "SxiFrLFWC16")
245
+ .def(py::init<std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)>>(), py::arg("dispatch_handler"))
246
+ .def("feed_bytes", &SxiFrLFWC16::feed_bytes, py::arg("data"))
247
+ ;
248
+
249
+ }
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python
2
- import struct
2
+
3
3
  import threading
4
4
  from array import array
5
5
  from collections import deque
@@ -12,7 +12,13 @@ import usb.core
12
12
  import usb.util
13
13
  from usb.core import USBError, USBTimeoutError
14
14
 
15
- from pyxcp.transport.base import BaseTransport
15
+ from pyxcp.transport.base import (
16
+ BaseTransport,
17
+ ChecksumType,
18
+ XcpFramingConfig,
19
+ XcpTransportLayerType,
20
+ parse_header_format,
21
+ )
16
22
  from pyxcp.utils import short_sleep
17
23
 
18
24
 
@@ -23,21 +29,26 @@ FIVE_MS = 5_000_000 # Five milliseconds in nanoseconds.
23
29
  class Usb(BaseTransport):
24
30
  """"""
25
31
 
26
- HEADER = struct.Struct("<2H")
27
- HEADER_SIZE = HEADER.size
28
-
29
32
  def __init__(self, config=None, policy=None, transport_layer_interface: Optional[usb.core.Device] = None):
30
- super().__init__(config, policy, transport_layer_interface)
31
33
  self.load_config(config)
34
+ header_len, header_ctr, header_fill = parse_header_format(self.config.header_format)
35
+ framing_config = XcpFramingConfig(
36
+ transport_layer_type=XcpTransportLayerType.USB,
37
+ header_len=header_len,
38
+ header_ctr=header_ctr,
39
+ header_fill=header_fill,
40
+ tail_fill=False,
41
+ tail_cs=ChecksumType.NO_CHECKSUM,
42
+ )
32
43
  self.serial_number: str = self.config.serial_number
33
44
  self.vendor_id: int = self.config.vendor_id
34
45
  self.product_id: int = self.config.product_id
35
46
  self.configuration_number: int = self.config.configuration_number
36
47
  self.interface_number: int = self.config.interface_number
37
48
  self.library: str = self.config.library
38
- self.header_format: str = self.config.header_format
39
49
  self.library = self.config.get("library")
40
50
  self.device = None
51
+ super().__init__(config, framing_config, policy, transport_layer_interface)
41
52
 
42
53
  ## IN-EP (RES/ERR, DAQ, and EV/SERV) Parameters.
43
54
  self.in_ep_number: int = self.config.in_ep_number
@@ -58,6 +69,7 @@ class Usb(BaseTransport):
58
69
  target=self._packet_listen,
59
70
  args=(),
60
71
  kwargs={},
72
+ daemon=True,
61
73
  )
62
74
  self._packets = deque()
63
75
 
@@ -109,17 +121,23 @@ class Usb(BaseTransport):
109
121
  def start_listener(self):
110
122
  super().start_listener()
111
123
  if self._packet_listener.is_alive():
112
- self._packet_listener.join()
113
- self._packet_listener = threading.Thread(target=self._packet_listen)
124
+ self._packet_listener.join(timeout=2.0)
125
+ self._packet_listener = threading.Thread(target=self._packet_listen, daemon=True)
114
126
  self._packet_listener.start()
115
127
 
116
128
  def close(self):
117
129
  """Close the transport-layer connection and event-loop."""
118
130
  self.finish_listener()
119
- if self.listener.is_alive():
120
- self.listener.join()
121
- if self._packet_listener.is_alive():
122
- self._packet_listener.join()
131
+ try:
132
+ if self.listener.is_alive():
133
+ self.listener.join(timeout=2.0)
134
+ except Exception:
135
+ pass
136
+ try:
137
+ if self._packet_listener.is_alive():
138
+ self._packet_listener.join(timeout=2.0)
139
+ except Exception:
140
+ pass
123
141
  self.close_connection()
124
142
 
125
143
  def _packet_listen(self):
@@ -148,9 +166,19 @@ class Usb(BaseTransport):
148
166
  self.status = 0 # disconnected
149
167
  break
150
168
 
169
+ def send(self, frame):
170
+ self.pre_send_timestamp = self.timestamp.value
171
+ try:
172
+ self.out_ep.write(frame)
173
+ except (USBError, USBTimeoutError):
174
+ # sometimes usb.core.USBError: [Errno 5] Input/Output Error is raised
175
+ # even though the command is send and a reply is received from the device.
176
+ # Ignore this here since a Timeout error will be raised anyway if
177
+ # the device does not respond
178
+ pass
179
+ self.post_send_timestamp = self.timestamp.value
180
+
151
181
  def listen(self):
152
- HEADER_UNPACK_FROM = self.HEADER.unpack_from
153
- HEADER_SIZE = self.HEADER_SIZE
154
182
  popleft = self._packets.popleft
155
183
  process_response = self.process_response
156
184
  close_event_set = self.closeEvent.is_set
@@ -178,10 +206,10 @@ class Usb(BaseTransport):
178
206
  short_sleep()
179
207
  last_sleep = self.timestamp.value
180
208
  if length is None:
181
- if current_size >= HEADER_SIZE:
182
- length, counter = HEADER_UNPACK_FROM(data, current_position)
183
- current_position += HEADER_SIZE
184
- current_size -= HEADER_SIZE
209
+ if current_size >= self.framing.header_size:
210
+ length, counter = self.framing.unpack_header(bytes(data), initial_offset=current_position)
211
+ current_position += self.framing.header_size
212
+ current_size -= self.framing.header_size
185
213
  else:
186
214
  data = data[current_position:]
187
215
  break
@@ -196,18 +224,6 @@ class Usb(BaseTransport):
196
224
  data = data[current_position:]
197
225
  break
198
226
 
199
- def send(self, frame):
200
- self.pre_send_timestamp = self.timestamp.value
201
- try:
202
- self.out_ep.write(frame)
203
- except (USBError, USBTimeoutError):
204
- # sometimes usb.core.USBError: [Errno 5] Input/Output Error is raised
205
- # even though the command is send and a reply is received from the device.
206
- # Ignore this here since a Timeout error will be raised anyway if
207
- # the device does not respond
208
- pass
209
- self.post_send_timestamp = self.timestamp.value
210
-
211
227
  def close_connection(self):
212
228
  if self.device is not None:
213
229
  usb.util.dispose_resources(self.device)
pyxcp/types.py CHANGED
@@ -978,19 +978,6 @@ class XcpGetSeedMode(enum.IntEnum):
978
978
  REMAINING = 1
979
979
 
980
980
 
981
- class FrameCategory(enum.IntEnum):
982
- """XCP frame categories."""
983
-
984
- METADATA = 0
985
- CMD = 1
986
- RESPONSE = 2
987
- ERROR = 3
988
- EVENT = 4
989
- SERV = 5
990
- DAQ = 6
991
- STIM = 7
992
-
993
-
994
981
  class TryCommandResult(enum.IntEnum):
995
982
  """ """
996
983
 
@@ -6,7 +6,7 @@ import sys
6
6
  from binascii import hexlify
7
7
  from enum import IntEnum
8
8
  from time import perf_counter, sleep
9
- from typing import Any, List, Optional, Union
9
+ from typing import Union
10
10
 
11
11
  import chardet
12
12
  import pytz
@@ -86,7 +86,6 @@ def delay(amount: float):
86
86
 
87
87
 
88
88
  class CurrentDatetime(TimestampInfo):
89
-
90
89
  def __init__(self, timestamp_ns: int):
91
90
  TimestampInfo.__init__(self, timestamp_ns)
92
91
  timezone = pytz.timezone(self.timezone)
@@ -113,8 +112,8 @@ def enum_from_str(enum_class: IntEnum, enumerator: str) -> IntEnum:
113
112
 
114
113
  enumerator: str
115
114
 
116
- Example
117
- -------
115
+ Examples
116
+ --------
118
117
 
119
118
  class Color(enum.IntEnum):
120
119
  RED = 0