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.
- pyxcp/__init__.py +1 -1
- pyxcp/asamkeydll.exe +0 -0
- pyxcp/cmdline.py +15 -30
- pyxcp/config/__init__.py +73 -20
- pyxcp/cpp_ext/aligned_buffer.hpp +168 -0
- pyxcp/cpp_ext/bin.hpp +7 -6
- pyxcp/cpp_ext/cpp_ext.cp310-win_arm64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp311-win_arm64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp312-win_arm64.pyd +0 -0
- pyxcp/cpp_ext/daqlist.hpp +241 -73
- pyxcp/cpp_ext/extension_wrapper.cpp +123 -15
- pyxcp/cpp_ext/framing.hpp +360 -0
- pyxcp/cpp_ext/mcobject.hpp +5 -3
- pyxcp/cpp_ext/sxi_framing.hpp +332 -0
- pyxcp/daq_stim/__init__.py +182 -45
- pyxcp/daq_stim/optimize/binpacking.py +2 -2
- pyxcp/daq_stim/scheduler.cpp +8 -8
- pyxcp/daq_stim/stim.cp310-win_arm64.pyd +0 -0
- pyxcp/daq_stim/stim.cp311-win_arm64.pyd +0 -0
- pyxcp/daq_stim/stim.cp312-win_arm64.pyd +0 -0
- pyxcp/errormatrix.py +2 -2
- pyxcp/examples/run_daq.py +5 -3
- pyxcp/examples/xcp_policy.py +6 -6
- pyxcp/examples/xcp_read_benchmark.py +2 -2
- pyxcp/examples/xcp_skel.py +1 -2
- pyxcp/examples/xcp_unlock.py +10 -12
- pyxcp/examples/xcp_user_supplied_driver.py +1 -2
- pyxcp/examples/xcphello.py +2 -15
- pyxcp/examples/xcphello_recorder.py +2 -2
- pyxcp/master/__init__.py +1 -0
- pyxcp/master/errorhandler.py +248 -13
- pyxcp/master/master.py +838 -250
- 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 +5 -10
- pyxcp/recorder/converter/__init__.py +4 -10
- pyxcp/recorder/reader.hpp +0 -1
- pyxcp/recorder/reco.py +1 -0
- pyxcp/recorder/rekorder.cp310-win_arm64.pyd +0 -0
- pyxcp/recorder/rekorder.cp311-win_arm64.pyd +0 -0
- pyxcp/recorder/rekorder.cp312-win_arm64.pyd +0 -0
- pyxcp/recorder/unfolder.hpp +129 -107
- pyxcp/recorder/wrap.cpp +3 -8
- pyxcp/scripts/xcp_fetch_a2l.py +2 -2
- pyxcp/scripts/xcp_id_scanner.py +1 -2
- pyxcp/scripts/xcp_info.py +66 -51
- pyxcp/scripts/xcp_profile.py +1 -2
- pyxcp/tests/test_daq.py +1 -1
- pyxcp/tests/test_framing.py +262 -0
- pyxcp/tests/test_master.py +210 -100
- pyxcp/tests/test_transport.py +138 -42
- pyxcp/timing.py +1 -1
- pyxcp/transport/__init__.py +8 -5
- pyxcp/transport/base.py +187 -143
- pyxcp/transport/can.py +117 -13
- pyxcp/transport/eth.py +55 -20
- pyxcp/transport/hdf5_policy.py +167 -0
- pyxcp/transport/sxi.py +126 -52
- pyxcp/transport/transport_ext.cp310-win_arm64.pyd +0 -0
- pyxcp/transport/transport_ext.cp311-win_arm64.pyd +0 -0
- pyxcp/transport/transport_ext.cp312-win_arm64.pyd +0 -0
- pyxcp/transport/transport_ext.hpp +214 -0
- pyxcp/transport/transport_wrapper.cpp +249 -0
- pyxcp/transport/usb_transport.py +47 -31
- pyxcp/types.py +0 -13
- pyxcp/{utils.py → utils/__init__.py} +3 -4
- pyxcp/utils/cli.py +78 -0
- pyxcp-0.25.6.dist-info/METADATA +341 -0
- pyxcp-0.25.6.dist-info/RECORD +153 -0
- {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info}/WHEEL +1 -1
- pyxcp/examples/conf_sxi.json +0 -9
- pyxcp/examples/conf_sxi.toml +0 -7
- pyxcp-0.23.3.dist-info/METADATA +0 -219
- pyxcp-0.23.3.dist-info/RECORD +0 -131
- {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info}/entry_points.txt +0 -0
- {pyxcp-0.23.3.dist-info → pyxcp-0.25.6.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
|
|
2
|
+
#if !defined(__FRAMING_HPP)
|
|
3
|
+
#define __FRAMING_HPP
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
|
|
7
|
+
#include <atomic>
|
|
8
|
+
#include <bit>
|
|
9
|
+
#include <optional>
|
|
10
|
+
#include <iostream>
|
|
11
|
+
#include <map>
|
|
12
|
+
#include <mutex>
|
|
13
|
+
#include <optional>
|
|
14
|
+
#include <set>
|
|
15
|
+
#include <thread>
|
|
16
|
+
#include <tuple>
|
|
17
|
+
#include <variant>
|
|
18
|
+
#include <vector>
|
|
19
|
+
#include <cstring>
|
|
20
|
+
|
|
21
|
+
namespace py = pybind11;
|
|
22
|
+
|
|
23
|
+
using FrameType = py::bytes;
|
|
24
|
+
using CommandType = std::variant<std::monostate, std::uint8_t, std::uint16_t, std::uint32_t>;
|
|
25
|
+
|
|
26
|
+
std::uint8_t find_msb(std::uint32_t val) {
|
|
27
|
+
std::uint8_t position = 0;
|
|
28
|
+
|
|
29
|
+
if (val == 0) {
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
while ((val >>= 1)) {
|
|
33
|
+
++position;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return position + 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
std::uint8_t byte_count(std::uint32_t val) {
|
|
40
|
+
std::uint8_t count = 1;
|
|
41
|
+
|
|
42
|
+
auto high_bit = find_msb(val);
|
|
43
|
+
if (high_bit > 24) {
|
|
44
|
+
count = 4;
|
|
45
|
+
} else if (high_bit > 16) {
|
|
46
|
+
count = 3;
|
|
47
|
+
} else if (high_bit > 8) {
|
|
48
|
+
count = 2;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return count;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
std::vector<std::uint8_t> serialize_cmd_value(std::uint32_t value) {
|
|
55
|
+
std::vector<std::uint8_t> result;
|
|
56
|
+
|
|
57
|
+
auto bc = byte_count(value);
|
|
58
|
+
result.reserve(bc);
|
|
59
|
+
|
|
60
|
+
switch (bc) {
|
|
61
|
+
case 4:
|
|
62
|
+
result.push_back(static_cast<std::uint8_t>(static_cast<std::uint32_t>(((value & 0xff000000UL)) >> 24)));
|
|
63
|
+
[[fallthrough]];
|
|
64
|
+
case 3:
|
|
65
|
+
result.push_back(static_cast<std::uint8_t>(static_cast<std::uint32_t>(((value & 0xff0000UL)) >> 16)));
|
|
66
|
+
[[fallthrough]];
|
|
67
|
+
case 2:
|
|
68
|
+
result.push_back(static_cast<std::uint8_t>(static_cast<std::uint16_t>(((value & 0xff00UL)) >> 8)));
|
|
69
|
+
[[fallthrough]];
|
|
70
|
+
case 1:
|
|
71
|
+
result.push_back(static_cast<std::uint8_t>(value & 0xffUL));
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
std::vector<std::uint8_t> serialize_word_le(std::uint16_t value) {
|
|
79
|
+
std::vector<std::uint8_t> result;
|
|
80
|
+
result.reserve(2);
|
|
81
|
+
|
|
82
|
+
result.push_back(static_cast<std::uint8_t>(value & 0xffUL));
|
|
83
|
+
result.push_back(static_cast<std::uint8_t>(static_cast<std::uint16_t>(((value & 0xff00UL)) >> 8)));
|
|
84
|
+
result.resize(2);
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
std::string_view bytes_as_string_view(const py::bytes& data) {
|
|
89
|
+
// Zero-copy view into Python bytes; lifetime is tied to 'data' which
|
|
90
|
+
// outlives this function call.
|
|
91
|
+
char* buf = nullptr;
|
|
92
|
+
Py_ssize_t len = 0;
|
|
93
|
+
// PyBytes_AsStringAndSize returns 0 on success.
|
|
94
|
+
if (PyBytes_AsStringAndSize(data.ptr(), &buf, &len) != 0 || buf == nullptr || len < 0) {
|
|
95
|
+
// Fallback: empty view on error (should be rare)
|
|
96
|
+
return std::string_view{};
|
|
97
|
+
}
|
|
98
|
+
return std::string_view(buf, static_cast<std::size_t>(len));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
enum class ChecksumType : std::uint8_t {
|
|
102
|
+
NO_CHECKSUM = 0,
|
|
103
|
+
BYTE_CHECKSUM = 1,
|
|
104
|
+
WORD_CHECKSUM = 2
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
enum class XcpTransportLayerType : std::uint8_t {
|
|
108
|
+
CAN,
|
|
109
|
+
ETH,
|
|
110
|
+
SXI,
|
|
111
|
+
USB
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
struct XcpFramingConfig {
|
|
115
|
+
XcpTransportLayerType transport_layer_type;
|
|
116
|
+
std::uint8_t header_len;
|
|
117
|
+
std::uint8_t header_ctr;
|
|
118
|
+
std::uint8_t header_fill;
|
|
119
|
+
|
|
120
|
+
bool tail_fill;
|
|
121
|
+
ChecksumType tail_cs;
|
|
122
|
+
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
class XcpFraming {
|
|
126
|
+
public:
|
|
127
|
+
|
|
128
|
+
XcpFraming(const XcpFramingConfig& framing_type) : m_counter_send(0),
|
|
129
|
+
m_framing_type(framing_type) {
|
|
130
|
+
|
|
131
|
+
m_send_buffer = new std::uint8_t[0xff + 8];
|
|
132
|
+
reset_send_buffer_pointer();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
XcpFraming() = delete;
|
|
136
|
+
XcpFraming(const XcpFraming&) = delete;
|
|
137
|
+
XcpFraming(XcpFraming&&) = delete;
|
|
138
|
+
|
|
139
|
+
FrameType prepare_request(std::uint32_t cmd, py::args data) {
|
|
140
|
+
|
|
141
|
+
std::vector<std::uint8_t> command_bytes{};
|
|
142
|
+
std::uint8_t frame_header_size{0};
|
|
143
|
+
std::uint8_t frame_tail_size{0};
|
|
144
|
+
|
|
145
|
+
reset_send_buffer_pointer();
|
|
146
|
+
|
|
147
|
+
command_bytes = serialize_cmd_value(cmd);
|
|
148
|
+
|
|
149
|
+
auto xcp_packet_size = data.size() + command_bytes.size();
|
|
150
|
+
|
|
151
|
+
if (m_framing_type.header_len > 0) {
|
|
152
|
+
frame_header_size += m_framing_type.header_len;
|
|
153
|
+
if (m_framing_type.header_len == 1) {
|
|
154
|
+
set_send_buffer(static_cast<std::uint8_t>(xcp_packet_size & 0xff));
|
|
155
|
+
} else {
|
|
156
|
+
auto packet_size_bytes = serialize_word_le(xcp_packet_size);
|
|
157
|
+
set_send_buffer(packet_size_bytes);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (m_framing_type.header_ctr > 0) {
|
|
161
|
+
frame_header_size += m_framing_type.header_ctr;
|
|
162
|
+
if (m_framing_type.header_ctr == 1) {
|
|
163
|
+
set_send_buffer(static_cast<std::uint8_t>(m_counter_send & 0xff));
|
|
164
|
+
} else {
|
|
165
|
+
auto counter_bytes = serialize_word_le(m_counter_send);
|
|
166
|
+
set_send_buffer(counter_bytes);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (m_framing_type.header_fill > 0) {
|
|
170
|
+
fill_send_buffer(m_framing_type.header_fill);
|
|
171
|
+
frame_header_size += m_framing_type.header_fill;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
set_send_buffer(command_bytes);
|
|
175
|
+
set_send_buffer(data);
|
|
176
|
+
|
|
177
|
+
if (m_framing_type.transport_layer_type == XcpTransportLayerType::SXI) {
|
|
178
|
+
if (m_framing_type.tail_fill == true) {
|
|
179
|
+
// Align to a word boundary if word checksum is used
|
|
180
|
+
if (m_framing_type.tail_cs == ChecksumType::WORD_CHECKSUM && (current_send_buffer_pointer() % 2 != 0)) {
|
|
181
|
+
fill_send_buffer(1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (m_framing_type.tail_cs != ChecksumType::NO_CHECKSUM) {
|
|
186
|
+
if (m_framing_type.tail_cs == ChecksumType::BYTE_CHECKSUM) {
|
|
187
|
+
auto cs = checksum_byte(0, current_send_buffer_pointer());
|
|
188
|
+
set_send_buffer(cs);
|
|
189
|
+
frame_tail_size += 1;
|
|
190
|
+
}
|
|
191
|
+
else if (m_framing_type.tail_cs == ChecksumType::WORD_CHECKSUM) {
|
|
192
|
+
// Align to a word boundary before calculating word checksum
|
|
193
|
+
if (current_send_buffer_pointer() % 2 != 0) {
|
|
194
|
+
fill_send_buffer(1);
|
|
195
|
+
}
|
|
196
|
+
auto cs = checksum_word(0, current_send_buffer_pointer());
|
|
197
|
+
auto cs_bytes = serialize_word_le(cs);
|
|
198
|
+
set_send_buffer(cs_bytes);
|
|
199
|
+
frame_tail_size += 2;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
m_counter_send++;
|
|
205
|
+
py::bytes result(reinterpret_cast<const char*>(m_send_buffer), current_send_buffer_pointer());
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
FrameType prepare_request(std::uint32_t cmd, const std::vector<std::uint8_t>& data_vec) {
|
|
210
|
+
py::tuple data_tuple(data_vec.size());
|
|
211
|
+
for (size_t i = 0; i < data_vec.size(); ++i) {
|
|
212
|
+
data_tuple[i] = py::int_(data_vec[i]);
|
|
213
|
+
}
|
|
214
|
+
return prepare_request(cmd, data_tuple);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
std::optional<std::tuple<std::uint16_t, std::uint16_t>> unpack_header(const py::bytes& data, std::uint16_t initial_offset=0) const noexcept {
|
|
218
|
+
auto data_view = bytes_as_string_view(data);
|
|
219
|
+
if (std::size(data_view) >= (get_header_size() + initial_offset)) {
|
|
220
|
+
auto offset = initial_offset;
|
|
221
|
+
std::uint16_t length = 0U;
|
|
222
|
+
std::uint16_t counter = 0U;
|
|
223
|
+
|
|
224
|
+
// Read length field starting at current offset (if present)
|
|
225
|
+
if (m_framing_type.header_len > 0) {
|
|
226
|
+
if (m_framing_type.header_len == 1) {
|
|
227
|
+
length = static_cast<std::uint16_t>(static_cast<std::uint8_t>(data_view[offset]));
|
|
228
|
+
} else {
|
|
229
|
+
auto b0 = static_cast<std::uint8_t>(data_view[offset]);
|
|
230
|
+
auto b1 = static_cast<std::uint8_t>(data_view[offset + 1]);
|
|
231
|
+
length = static_cast<std::uint16_t>(static_cast<std::uint16_t>(b0) | (static_cast<std::uint16_t>(b1) << 8));
|
|
232
|
+
}
|
|
233
|
+
offset += m_framing_type.header_len;
|
|
234
|
+
}
|
|
235
|
+
// Read counter field starting after length (if present)
|
|
236
|
+
if (m_framing_type.header_ctr > 0) {
|
|
237
|
+
if (m_framing_type.header_ctr == 1) {
|
|
238
|
+
counter = static_cast<std::uint16_t>(static_cast<std::uint8_t>(data_view[offset]));
|
|
239
|
+
} else {
|
|
240
|
+
auto c0 = static_cast<std::uint8_t>(data_view[offset]);
|
|
241
|
+
auto c1 = static_cast<std::uint8_t>(data_view[offset + 1]);
|
|
242
|
+
counter = static_cast<std::uint16_t>(static_cast<std::uint16_t>(c0) | (static_cast<std::uint16_t>(c1) << 8));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return std::make_tuple(length, counter);
|
|
246
|
+
}
|
|
247
|
+
return std::nullopt;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
bool verify_checksum(const py::bytes& data) const noexcept {
|
|
251
|
+
if (m_framing_type.transport_layer_type != XcpTransportLayerType::SXI || m_framing_type.tail_cs == ChecksumType::NO_CHECKSUM) {
|
|
252
|
+
return true; // No checksum verification needed
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
auto data_view = bytes_as_string_view(data);
|
|
256
|
+
auto data_size = std::size(data_view);
|
|
257
|
+
|
|
258
|
+
if (m_framing_type.tail_cs == ChecksumType::BYTE_CHECKSUM) {
|
|
259
|
+
if (data_size < 1) return false;
|
|
260
|
+
std::uint8_t received_cs = static_cast<std::uint8_t>(data_view[data_size - 1]);
|
|
261
|
+
std::uint8_t calculated_cs = 0;
|
|
262
|
+
for (size_t i = 0; i < data_size - 1; ++i) {
|
|
263
|
+
calculated_cs += static_cast<std::uint8_t>(data_view[i]);
|
|
264
|
+
}
|
|
265
|
+
return received_cs == calculated_cs;
|
|
266
|
+
}
|
|
267
|
+
else if (m_framing_type.tail_cs == ChecksumType::WORD_CHECKSUM) {
|
|
268
|
+
if (data_size < 2 || data_size % 2 != 0) return false; // Must have even length for word checksum
|
|
269
|
+
|
|
270
|
+
std::uint16_t received_cs = static_cast<std::uint16_t>(
|
|
271
|
+
static_cast<std::uint8_t>(data_view[data_size - 2]) |
|
|
272
|
+
(static_cast<std::uint16_t>(static_cast<std::uint8_t>(data_view[data_size - 1])) << 8)
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
std::uint16_t calculated_cs = 0;
|
|
276
|
+
for (size_t i = 0; i < data_size - 2; i += 2) {
|
|
277
|
+
calculated_cs += static_cast<std::uint16_t>(
|
|
278
|
+
static_cast<std::uint8_t>(data_view[i]) |
|
|
279
|
+
(static_cast<std::uint16_t>(static_cast<std::uint8_t>(data_view[i + 1])) << 8)
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return received_cs == calculated_cs;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return true; // Should not be reached if NO_CHECKSUM is handled
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
std::uint16_t get_header_size() const noexcept {
|
|
289
|
+
return m_framing_type.header_len + m_framing_type.header_ctr + m_framing_type.header_fill;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
std::uint16_t get_counter_send() const noexcept {
|
|
293
|
+
return m_counter_send;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
void set_counter_send(std::uint16_t counter) noexcept {
|
|
297
|
+
m_counter_send = counter;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
private:
|
|
302
|
+
void set_send_buffer(std::uint8_t value) noexcept {
|
|
303
|
+
m_send_buffer[m_send_buffer_offset] = value;
|
|
304
|
+
m_send_buffer_offset++;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
void set_send_buffer(const std::vector<std::uint8_t>& values) noexcept {
|
|
308
|
+
if (!values.empty()) {
|
|
309
|
+
std::memcpy(m_send_buffer + m_send_buffer_offset, values.data(), values.size());
|
|
310
|
+
m_send_buffer_offset += static_cast<std::uint16_t>(values.size());
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
void set_send_buffer(const py::args& values) noexcept {
|
|
315
|
+
for (auto idx=0; idx < values.size(); ++idx) {
|
|
316
|
+
m_send_buffer[m_send_buffer_offset] = values[idx].cast<std::uint8_t>();
|
|
317
|
+
m_send_buffer_offset++;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
void fill_send_buffer(uint16_t n) noexcept {
|
|
322
|
+
for (auto idx=0; idx < n; ++idx) {
|
|
323
|
+
m_send_buffer[m_send_buffer_offset] = 0;
|
|
324
|
+
m_send_buffer_offset++;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
void reset_send_buffer_pointer() noexcept {
|
|
329
|
+
m_send_buffer_offset = 0UL;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
std::uint16_t current_send_buffer_pointer() const noexcept {
|
|
333
|
+
return m_send_buffer_offset;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
std::uint8_t checksum_byte(std::uint16_t begin, std::uint16_t end) const noexcept {
|
|
337
|
+
uint8_t cs = 0;
|
|
338
|
+
for (auto idx = begin; idx < end; ++idx) {
|
|
339
|
+
cs += m_send_buffer[idx];
|
|
340
|
+
}
|
|
341
|
+
return cs;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
std::uint16_t checksum_word(std::uint16_t begin, std::uint16_t end) const noexcept {
|
|
345
|
+
std::uint16_t cs = 0UL;
|
|
346
|
+
|
|
347
|
+
for (auto idx = begin; idx < end; idx+=2) {
|
|
348
|
+
cs += static_cast<std::uint16_t>(m_send_buffer[idx] | (m_send_buffer[idx + 1] << 8));
|
|
349
|
+
}
|
|
350
|
+
return cs;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private:
|
|
354
|
+
std::uint16_t m_counter_send;
|
|
355
|
+
XcpFramingConfig m_framing_type;
|
|
356
|
+
std::uint8_t * m_send_buffer = nullptr;
|
|
357
|
+
std::uint16_t m_send_buffer_offset = 0UL;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
#endif // __FRAMING_HPP
|
pyxcp/cpp_ext/mcobject.hpp
CHANGED
|
@@ -236,9 +236,11 @@ std::string to_string(const McObject& obj) {
|
|
|
236
236
|
|
|
237
237
|
std::string mc_components_to_string(const std::vector<McObject>& components) {
|
|
238
238
|
std::stringstream ss;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
for (std::size_t i = 0; i < components.size(); ++i) {
|
|
240
|
+
ss << to_string(components[i]);
|
|
241
|
+
if (i + 1 < components.size()) {
|
|
242
|
+
ss << ", ";
|
|
243
|
+
}
|
|
242
244
|
}
|
|
243
245
|
return ss.str();
|
|
244
246
|
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#if !defined (__SXI_FRAMING_HPP)
|
|
2
|
+
#define __SXI_FRAMING_HPP
|
|
3
|
+
|
|
4
|
+
#include <array>
|
|
5
|
+
#include <bit>
|
|
6
|
+
#include <chrono>
|
|
7
|
+
#include <condition_variable>
|
|
8
|
+
#include <cstdint>
|
|
9
|
+
#include <functional>
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <mutex>
|
|
12
|
+
#include <thread>
|
|
13
|
+
#include <vector>
|
|
14
|
+
#include <iomanip>
|
|
15
|
+
#include <iostream>
|
|
16
|
+
|
|
17
|
+
// Header format options
|
|
18
|
+
enum class SxiHeaderFormat {
|
|
19
|
+
LenByte,
|
|
20
|
+
LenCtrByte,
|
|
21
|
+
LenFillByte,
|
|
22
|
+
LenWord,
|
|
23
|
+
LenCtrWord,
|
|
24
|
+
LenFillWord,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Checksum type options
|
|
28
|
+
enum class SxiChecksumType {
|
|
29
|
+
None,
|
|
30
|
+
Sum8,
|
|
31
|
+
Sum16
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
namespace detail {
|
|
35
|
+
inline uint16_t make_word_le(const uint8_t* p) {
|
|
36
|
+
return p[0] | p[1] << 8;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
inline void put_word_le(uint8_t* p, uint16_t v) {
|
|
40
|
+
if constexpr (std::endian::native == std::endian::big) {
|
|
41
|
+
p[0] = static_cast<uint8_t>(v & 0xFF);
|
|
42
|
+
p[1] = static_cast<uint8_t>((v >> 8) & 0xFF);
|
|
43
|
+
} else {
|
|
44
|
+
std::memcpy(p, &v, sizeof(v));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} // namespace detail
|
|
48
|
+
|
|
49
|
+
class RestartableTimer {
|
|
50
|
+
public:
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @brief Constructs the timer.
|
|
54
|
+
* @param timeout The duration after which the timer expires.
|
|
55
|
+
* @param on_timeout The function to call upon timeout.
|
|
56
|
+
*/
|
|
57
|
+
RestartableTimer(std::chrono::milliseconds timeout, std::function<void()> on_timeout) :
|
|
58
|
+
m_timeout(timeout), m_on_timeout(std::move(on_timeout)), m_running(false) {
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
~RestartableTimer() {
|
|
62
|
+
stop();
|
|
63
|
+
if (m_thread.joinable()) {
|
|
64
|
+
m_thread.join();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Disable copy and move semantics
|
|
69
|
+
RestartableTimer(const RestartableTimer&) = delete;
|
|
70
|
+
RestartableTimer& operator=(const RestartableTimer&) = delete;
|
|
71
|
+
RestartableTimer(RestartableTimer&&) = delete;
|
|
72
|
+
RestartableTimer& operator=(RestartableTimer&&) = delete;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @brief Starts the timer. If already running, it resets the countdown.
|
|
76
|
+
*/
|
|
77
|
+
void start() {
|
|
78
|
+
if (m_timeout == std::chrono::milliseconds(0)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
std::unique_lock<std::mutex> lock(m_mutex);
|
|
82
|
+
if (!m_running) {
|
|
83
|
+
m_running = true;
|
|
84
|
+
if (m_thread.joinable()) {
|
|
85
|
+
m_thread.join(); // Ensure previous thread is finished
|
|
86
|
+
}
|
|
87
|
+
m_thread = std::thread(&RestartableTimer::run, this);
|
|
88
|
+
} else {
|
|
89
|
+
// Already running, just signal a reset
|
|
90
|
+
m_cv.notify_one();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @brief Stops the timer.
|
|
96
|
+
*/
|
|
97
|
+
void stop() {
|
|
98
|
+
if (m_timeout == std::chrono::milliseconds(0)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
std::unique_lock<std::mutex> lock(m_mutex);
|
|
102
|
+
if (!m_running) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
m_running = false;
|
|
106
|
+
m_cv.notify_one();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @brief Resets the timer's countdown.
|
|
111
|
+
*/
|
|
112
|
+
void reset_timeout() {
|
|
113
|
+
if (m_timeout == std::chrono::milliseconds(0)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
std::unique_lock<std::mutex> lock(m_mutex);
|
|
117
|
+
if (m_running) {
|
|
118
|
+
m_cv.notify_one();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private:
|
|
123
|
+
|
|
124
|
+
void run() {
|
|
125
|
+
std::unique_lock<std::mutex> lock(m_mutex);
|
|
126
|
+
while (m_running) {
|
|
127
|
+
// wait_for returns cv_status::timeout if the time expires without a notification
|
|
128
|
+
if (m_cv.wait_for(lock, m_timeout) == std::cv_status::timeout) {
|
|
129
|
+
// Timeout occurred. Check m_running again in case stop() was called
|
|
130
|
+
// while we were waiting for the lock.
|
|
131
|
+
if (m_running) {
|
|
132
|
+
m_on_timeout();
|
|
133
|
+
m_running = false; // Stop the timer thread after firing
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
std::thread m_thread;
|
|
140
|
+
std::mutex m_mutex;
|
|
141
|
+
std::condition_variable m_cv;
|
|
142
|
+
std::chrono::milliseconds m_timeout;
|
|
143
|
+
std::function<void()> m_on_timeout;
|
|
144
|
+
std::atomic<bool> m_running;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
template<SxiHeaderFormat Format, SxiChecksumType Checksum>
|
|
149
|
+
class SxiReceiver {
|
|
150
|
+
public:
|
|
151
|
+
|
|
152
|
+
explicit SxiReceiver(
|
|
153
|
+
std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)> dispatch_handler,
|
|
154
|
+
std::chrono::milliseconds /*timeout*/ = std::chrono::milliseconds(0)
|
|
155
|
+
) :
|
|
156
|
+
dispatch_(std::move(dispatch_handler)) {
|
|
157
|
+
reset();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
void feed_bytes(const std::string& data) {
|
|
161
|
+
for (const auto& c : data) {
|
|
162
|
+
//feed(static_cast<uint8_t>(c));
|
|
163
|
+
feed(c);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
void feed(uint8_t octet) {
|
|
168
|
+
if (index_ >= buffer_.size()) {
|
|
169
|
+
reset();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
buffer_[index_] = octet;
|
|
173
|
+
if (state_ == State::Idle) {
|
|
174
|
+
state_ = State::UntilLength;
|
|
175
|
+
fill_ = 0;
|
|
176
|
+
}
|
|
177
|
+
if (state_ == State::UntilLength) {
|
|
178
|
+
bool header_complete = false;
|
|
179
|
+
if constexpr (Format == SxiHeaderFormat::LenByte) {
|
|
180
|
+
if (index_ == 0) {
|
|
181
|
+
dlc_ = buffer_[0];
|
|
182
|
+
remaining_ = dlc_;
|
|
183
|
+
header_complete = true;
|
|
184
|
+
}
|
|
185
|
+
} else if constexpr (Format == SxiHeaderFormat::LenCtrByte || Format == SxiHeaderFormat::LenFillByte) {
|
|
186
|
+
if (index_ == 1) {
|
|
187
|
+
dlc_ = buffer_[0];
|
|
188
|
+
if constexpr (Format == SxiHeaderFormat::LenCtrByte) {
|
|
189
|
+
ctr_ = buffer_[1];
|
|
190
|
+
}
|
|
191
|
+
remaining_ = dlc_;
|
|
192
|
+
header_complete = true;
|
|
193
|
+
}
|
|
194
|
+
} else if constexpr (Format == SxiHeaderFormat::LenWord) {
|
|
195
|
+
if (index_ == 1) {
|
|
196
|
+
dlc_ = detail::make_word_le(&buffer_[0]);
|
|
197
|
+
remaining_ = dlc_;
|
|
198
|
+
header_complete = true;
|
|
199
|
+
}
|
|
200
|
+
} else if constexpr (Format == SxiHeaderFormat::LenCtrWord || Format == SxiHeaderFormat::LenFillWord) {
|
|
201
|
+
if (index_ == 3) {
|
|
202
|
+
dlc_ = detail::make_word_le(&buffer_[0]);
|
|
203
|
+
if constexpr (Format == SxiHeaderFormat::LenCtrWord) {
|
|
204
|
+
ctr_ = detail::make_word_le(&buffer_[2]);
|
|
205
|
+
}
|
|
206
|
+
remaining_ = dlc_;
|
|
207
|
+
header_complete = true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (header_complete) {
|
|
211
|
+
if constexpr (Checksum == SxiChecksumType::Sum8) {
|
|
212
|
+
remaining_ += 1;
|
|
213
|
+
} else if constexpr (Checksum == SxiChecksumType::Sum16) {
|
|
214
|
+
uint16_t header_size = 0;
|
|
215
|
+
if constexpr (Format == SxiHeaderFormat::LenByte) header_size = 1;
|
|
216
|
+
else if constexpr (Format == SxiHeaderFormat::LenCtrByte || Format == SxiHeaderFormat::LenFillByte) header_size = 2;
|
|
217
|
+
else if constexpr (Format == SxiHeaderFormat::LenWord) header_size = 2;
|
|
218
|
+
else if constexpr (Format == SxiHeaderFormat::LenCtrWord || Format == SxiHeaderFormat::LenFillWord) header_size = 4;
|
|
219
|
+
|
|
220
|
+
fill_ = ((header_size + dlc_) % 2 != 0) ? 1u : 0u;
|
|
221
|
+
remaining_ += (2 + fill_);
|
|
222
|
+
}
|
|
223
|
+
state_ = State::Remaining;
|
|
224
|
+
if (remaining_ != 0) {
|
|
225
|
+
index_++;
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (state_ == State::Remaining) {
|
|
231
|
+
if (remaining_ > 0) {
|
|
232
|
+
remaining_--;
|
|
233
|
+
}
|
|
234
|
+
if (remaining_ == 0) {
|
|
235
|
+
uint16_t payload_off = 0;
|
|
236
|
+
if constexpr (Format == SxiHeaderFormat::LenByte) {
|
|
237
|
+
payload_off = 1;
|
|
238
|
+
} else if constexpr (Format == SxiHeaderFormat::LenCtrByte || Format == SxiHeaderFormat::LenFillByte) {
|
|
239
|
+
payload_off = 2;
|
|
240
|
+
} else if constexpr (Format == SxiHeaderFormat::LenWord) {
|
|
241
|
+
payload_off = 2;
|
|
242
|
+
} else if constexpr (Format == SxiHeaderFormat::LenCtrWord || Format == SxiHeaderFormat::LenFillWord) {
|
|
243
|
+
payload_off = 4;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// verify checksum
|
|
247
|
+
if constexpr (Checksum == SxiChecksumType::Sum8) {
|
|
248
|
+
uint8_t sum = 0;
|
|
249
|
+
for (uint16_t i = 0; i < (payload_off + dlc_ + fill_); ++i) {
|
|
250
|
+
sum += buffer_[i];
|
|
251
|
+
}
|
|
252
|
+
uint8_t rx = buffer_[payload_off + dlc_];
|
|
253
|
+
if (sum != rx) {
|
|
254
|
+
log_checksum_error(sum, rx, payload_off + dlc_ + 1);
|
|
255
|
+
reset();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
} else if constexpr (Checksum == SxiChecksumType::Sum16) {
|
|
259
|
+
uint16_t count = (payload_off + dlc_ + fill_);
|
|
260
|
+
uint16_t sum = 0;
|
|
261
|
+
|
|
262
|
+
for (uint16_t idx = 0; idx < count; idx += 2) {
|
|
263
|
+
sum = static_cast<uint16_t>(sum + detail::make_word_le(&buffer_[idx]));
|
|
264
|
+
}
|
|
265
|
+
uint16_t rx = detail::make_word_le(&buffer_[payload_off + dlc_ + fill_]);
|
|
266
|
+
if (sum != rx) {
|
|
267
|
+
log_checksum_error(sum, rx, payload_off + dlc_ + fill_ + 2);
|
|
268
|
+
reset();
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (dispatch_) {
|
|
273
|
+
dispatch_({ buffer_.data() + payload_off, buffer_.data() + payload_off + dlc_ }, dlc_, ctr_);
|
|
274
|
+
#if defined(XCP_TL_TEST_HOOKS)
|
|
275
|
+
std::fill(buffer_.begin(), buffer_.end(), 0xcc);
|
|
276
|
+
#endif
|
|
277
|
+
}
|
|
278
|
+
reset();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
index_++;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private:
|
|
286
|
+
|
|
287
|
+
enum class State {
|
|
288
|
+
Idle,
|
|
289
|
+
UntilLength,
|
|
290
|
+
Remaining
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
template<typename T>
|
|
294
|
+
void log_checksum_error(T calculated, T received, uint16_t packet_len) {
|
|
295
|
+
std::cerr << "SXI checksum error: Calculated " << std::hex << "0x" << static_cast<int>(calculated)
|
|
296
|
+
<< ", but received " << "0x" << static_cast<int>(received) << "." << std::dec << std::endl;
|
|
297
|
+
std::cerr << "Packet dump (" << packet_len << " bytes):" << std::endl;
|
|
298
|
+
std::cerr << "[";
|
|
299
|
+
std::ios_base::fmtflags flags(std::cerr.flags()); // save flags
|
|
300
|
+
for (uint16_t i = 0; i < packet_len; ++i) {
|
|
301
|
+
std::cerr << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(buffer_[i]) << " ";
|
|
302
|
+
if ((i + 1) % 16 == 0) {
|
|
303
|
+
std::cerr << std::endl;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
std::cerr << "]" << std::endl;
|
|
307
|
+
std::cerr.flags(flags); // restore flags
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
void reset() {
|
|
311
|
+
state_ = State::Idle;
|
|
312
|
+
index_ = 0;
|
|
313
|
+
dlc_ = 0;
|
|
314
|
+
remaining_ = 0;
|
|
315
|
+
ctr_ = 0;
|
|
316
|
+
fill_ = 0;
|
|
317
|
+
#if defined(XCP_TL_TEST_HOOKS)
|
|
318
|
+
std::fill(buffer_.begin(), buffer_.end(), 0xcc);
|
|
319
|
+
#endif
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
std::array<uint8_t, 1024> buffer_{};
|
|
323
|
+
State state_{ State::Idle };
|
|
324
|
+
uint32_t index_{ 0 };
|
|
325
|
+
uint16_t dlc_{ 0 };
|
|
326
|
+
uint16_t ctr_{ 0 };
|
|
327
|
+
uint32_t remaining_{ 0 };
|
|
328
|
+
uint16_t fill_ {0};
|
|
329
|
+
std::function<void(const std::vector<uint8_t>&, uint16_t, uint16_t)> dispatch_;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
#endif // __SXI_FRAMING_HPP
|