pyxcp 0.25.4__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.c +116 -0
- pyxcp/asamkeydll.exe +0 -0
- pyxcp/asamkeydll.sh +2 -0
- pyxcp/checksum.py +732 -0
- pyxcp/cmdline.py +71 -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.cp310-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp311-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp312-win_amd64.pyd +0 -0
- pyxcp/cpp_ext/cpp_ext.cp313-win_amd64.pyd +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 +291 -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.cp310-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cp311-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cp312-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cp313-win_amd64.pyd +0 -0
- pyxcp/daq_stim/stim.cpp +13 -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 +36 -0
- pyxcp/examples/xcp_user_supplied_driver.py +43 -0
- pyxcp/examples/xcphello.py +65 -0
- pyxcp/examples/xcphello_recorder.py +107 -0
- pyxcp/master/__init__.py +10 -0
- pyxcp/master/errorhandler.py +677 -0
- pyxcp/master/master.py +2641 -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 +444 -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.cp310-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cp311-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cp312-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cp313-win_amd64.pyd +0 -0
- pyxcp/recorder/rekorder.cpp +59 -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 +159 -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/hdf5_policy.py +129 -0
- pyxcp/transport/sxi.py +209 -0
- pyxcp/transport/transport_ext.cp310-win_amd64.pyd +0 -0
- pyxcp/transport/transport_ext.cp311-win_amd64.pyd +0 -0
- pyxcp/transport/transport_ext.cp312-win_amd64.pyd +0 -0
- pyxcp/transport/transport_ext.cp313-win_amd64.pyd +0 -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/__init__.py +127 -0
- pyxcp/utils/cli.py +78 -0
- pyxcp/vector/__init__.py +0 -0
- pyxcp/vector/map.py +82 -0
- pyxcp-0.25.4.dist-info/METADATA +341 -0
- pyxcp-0.25.4.dist-info/RECORD +157 -0
- pyxcp-0.25.4.dist-info/WHEEL +4 -0
- pyxcp-0.25.4.dist-info/entry_points.txt +9 -0
- pyxcp-0.25.4.dist-info/licenses/LICENSE +165 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
|
|
2
|
+
#if !defined(__MC_OBJECT_HPP)
|
|
3
|
+
#define __MC_OBJECT_HPP
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <map>
|
|
7
|
+
#include <optional>
|
|
8
|
+
#include <ranges>
|
|
9
|
+
#include <string>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
#include "helper.hpp"
|
|
13
|
+
|
|
14
|
+
const std::map<const std::string, std::tuple<std::uint16_t, std::uint16_t>> TYPE_MAP = {
|
|
15
|
+
{ "U8", { 0, 1 } },
|
|
16
|
+
{ "I8", { 1, 1 } },
|
|
17
|
+
{ "U16", { 2, 2 } },
|
|
18
|
+
{ "I16", { 3, 2 } },
|
|
19
|
+
{ "U32", { 4, 4 } },
|
|
20
|
+
{ "I32", { 5, 4 } },
|
|
21
|
+
{ "U64", { 6, 8 } },
|
|
22
|
+
{ "I64", { 7, 8 } },
|
|
23
|
+
{ "F32", { 8, 4 } },
|
|
24
|
+
{ "F64", { 9, 8 } },
|
|
25
|
+
#if HAS_FLOAT16
|
|
26
|
+
{ "F16", { 10, 2 } },
|
|
27
|
+
#endif
|
|
28
|
+
#if HAS_BFLOAT16
|
|
29
|
+
{ "BF16", { 11, 2 } },
|
|
30
|
+
#endif
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
enum class TypeCode : std::uint8_t {
|
|
34
|
+
U8,
|
|
35
|
+
I8,
|
|
36
|
+
U16,
|
|
37
|
+
I16,
|
|
38
|
+
U32,
|
|
39
|
+
I32,
|
|
40
|
+
U64,
|
|
41
|
+
I64,
|
|
42
|
+
F32,
|
|
43
|
+
F64,
|
|
44
|
+
F16,
|
|
45
|
+
BF16,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const std::map<const std::string, TypeCode> TYPE_TO_TYPE_CODE_MAP = {
|
|
49
|
+
{ "U8", TypeCode::U8 },
|
|
50
|
+
{ "I8", TypeCode::I8 },
|
|
51
|
+
{ "U16", TypeCode::U16 },
|
|
52
|
+
{ "I16", TypeCode::I16 },
|
|
53
|
+
{ "U32", TypeCode::U32 },
|
|
54
|
+
{ "I32", TypeCode::I32 },
|
|
55
|
+
{ "U64", TypeCode::U64 },
|
|
56
|
+
{ "I64", TypeCode::I64 },
|
|
57
|
+
{ "F32", TypeCode::F32 },
|
|
58
|
+
{ "F64", TypeCode::F64 },
|
|
59
|
+
#if HAS_FLOAT16
|
|
60
|
+
{ "F16", TypeCode::F16 },
|
|
61
|
+
#endif
|
|
62
|
+
#if HAS_BFLOAT16
|
|
63
|
+
{ "BF16", TypeCode::BF16 },
|
|
64
|
+
#endif
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const std::map<std::uint16_t, const std::string> TYPE_MAP_REV = {
|
|
68
|
+
{ 0, "U8" },
|
|
69
|
+
{ 1, "I8" },
|
|
70
|
+
{ 2, "U16" },
|
|
71
|
+
{ 3, "I16" },
|
|
72
|
+
{ 4, "U32" },
|
|
73
|
+
{ 5, "I32" },
|
|
74
|
+
{ 6, "U64" },
|
|
75
|
+
{ 7, "I64" },
|
|
76
|
+
{ 8, "F32" },
|
|
77
|
+
{ 9, "F64" },
|
|
78
|
+
#if HAS_FLOAT16
|
|
79
|
+
{ 10, "F16" },
|
|
80
|
+
#endif
|
|
81
|
+
#if HAS_BFLOAT16
|
|
82
|
+
{ 11, "BF16" },
|
|
83
|
+
#endif
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
inline std::vector<std::string> get_data_types() {
|
|
87
|
+
std::vector<std::string> result;
|
|
88
|
+
|
|
89
|
+
for (const auto& [k, v] : TYPE_MAP) {
|
|
90
|
+
result.emplace_back(k);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class McObject {
|
|
97
|
+
public:
|
|
98
|
+
|
|
99
|
+
explicit McObject(
|
|
100
|
+
std::string_view name, std::uint32_t address, std::uint8_t ext, std::uint16_t length, const std::string& data_type,
|
|
101
|
+
const std::vector<McObject>& components = std::vector<McObject>()
|
|
102
|
+
) :
|
|
103
|
+
m_name(name),
|
|
104
|
+
m_address(address),
|
|
105
|
+
m_ext(ext),
|
|
106
|
+
m_length(length),
|
|
107
|
+
m_data_type(data_type),
|
|
108
|
+
m_type_index(-1),
|
|
109
|
+
m_components(components) {
|
|
110
|
+
if (data_type != "") {
|
|
111
|
+
std::string dt_toupper;
|
|
112
|
+
|
|
113
|
+
dt_toupper.resize(data_type.size());
|
|
114
|
+
|
|
115
|
+
std::transform(data_type.begin(), data_type.end(), dt_toupper.begin(), [](unsigned char c) -> unsigned char {
|
|
116
|
+
return std::toupper(c);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (!TYPE_MAP.contains(dt_toupper)) {
|
|
120
|
+
throw std::runtime_error("Invalid data type: " + data_type);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const auto [ti, len] = TYPE_MAP.at(dt_toupper);
|
|
124
|
+
m_type_index = ti;
|
|
125
|
+
m_length = len;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
McObject(const McObject& obj) = default;
|
|
130
|
+
McObject(McObject&& obj) = default;
|
|
131
|
+
McObject& operator=(const McObject&) = default;
|
|
132
|
+
McObject& operator=(McObject&&) = default;
|
|
133
|
+
|
|
134
|
+
const std::string& get_name() const {
|
|
135
|
+
return m_name;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
void set_name(std::string_view name) {
|
|
139
|
+
m_name = name;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
std::uint32_t get_address() const {
|
|
143
|
+
return m_address;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
void set_address(std::uint32_t address) {
|
|
147
|
+
m_address = address;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
std::uint8_t get_ext() const {
|
|
151
|
+
return m_ext;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
void set_ext(std::uint8_t ext) {
|
|
155
|
+
m_ext = ext;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const std::string& get_data_type() const {
|
|
159
|
+
return m_data_type;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
void set_data_type(const std::string& value) {
|
|
163
|
+
m_data_type = value;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
std::uint16_t get_length() const {
|
|
167
|
+
return m_length;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
void set_length(std::uint16_t length) {
|
|
171
|
+
m_length = length;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
std::int32_t get_type_index() const {
|
|
175
|
+
return m_type_index;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const std::vector<McObject>& get_components() const {
|
|
179
|
+
return m_components;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
void add_component(const McObject& obj) {
|
|
183
|
+
m_components.emplace_back(obj);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
bool operator==(const McObject& other) const {
|
|
187
|
+
return (m_name == other.m_name) && (m_address == other.m_address) && (m_ext == other.m_ext) &&
|
|
188
|
+
(m_length == other.m_length) && (m_data_type == other.m_data_type) &&
|
|
189
|
+
(std::equal(m_components.begin(), m_components.end(), other.m_components.begin(), other.m_components.end()));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
std::string dumps() const noexcept {
|
|
193
|
+
std::stringstream ss;
|
|
194
|
+
|
|
195
|
+
ss << to_binary(m_name);
|
|
196
|
+
ss << to_binary(m_address);
|
|
197
|
+
ss << to_binary(m_ext);
|
|
198
|
+
ss << to_binary(m_length);
|
|
199
|
+
ss << to_binary(m_data_type);
|
|
200
|
+
ss << to_binary(m_type_index);
|
|
201
|
+
|
|
202
|
+
std::size_t ccount = m_components.size();
|
|
203
|
+
ss << to_binary(ccount);
|
|
204
|
+
for (const auto& obj : m_components) {
|
|
205
|
+
ss << obj.dumps();
|
|
206
|
+
}
|
|
207
|
+
return ss.str();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
auto get_hash() const noexcept {
|
|
211
|
+
std::hash<std::string> hash_fn;
|
|
212
|
+
return hash_fn(dumps());
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private:
|
|
216
|
+
|
|
217
|
+
std::string m_name;
|
|
218
|
+
std::uint32_t m_address;
|
|
219
|
+
std::uint8_t m_ext;
|
|
220
|
+
std::uint16_t m_length;
|
|
221
|
+
std::string m_data_type;
|
|
222
|
+
std::int16_t m_type_index;
|
|
223
|
+
std::vector<McObject> m_components{};
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
std::string mc_components_to_string(const std::vector<McObject>& components);
|
|
227
|
+
|
|
228
|
+
std::string to_string(const McObject& obj) {
|
|
229
|
+
std::stringstream ss;
|
|
230
|
+
|
|
231
|
+
ss << "McObject(name='" << obj.get_name() << "', address=" << obj.get_address()
|
|
232
|
+
<< ", ext=" << static_cast<std::uint16_t >(obj.get_ext()) << ", data_type='" << obj.get_data_type()
|
|
233
|
+
<< "', length=" << obj.get_length() << ", components=[" << mc_components_to_string(obj.get_components()) << "])";
|
|
234
|
+
return ss.str();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
std::string mc_components_to_string(const std::vector<McObject>& components) {
|
|
238
|
+
std::stringstream ss;
|
|
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
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return ss.str();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#endif // __MC_OBJECT_HPP
|
|
@@ -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
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
#ifndef __TSQUEUE_HPP
|
|
3
|
+
#define __TSQUEUE_HPP
|
|
4
|
+
|
|
5
|
+
#include <condition_variable>
|
|
6
|
+
#include <mutex>
|
|
7
|
+
#include <queue>
|
|
8
|
+
|
|
9
|
+
template<typename T>
|
|
10
|
+
class TsQueue {
|
|
11
|
+
public:
|
|
12
|
+
|
|
13
|
+
TsQueue() = default;
|
|
14
|
+
|
|
15
|
+
TsQueue(const TsQueue& other) noexcept {
|
|
16
|
+
std::scoped_lock lock(other.m_mtx);
|
|
17
|
+
m_queue = other.m_queue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
void put(T value) noexcept {
|
|
21
|
+
std::scoped_lock lock(m_mtx);
|
|
22
|
+
m_queue.push(value);
|
|
23
|
+
m_cond.notify_one();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
std::shared_ptr<T> get() noexcept {
|
|
27
|
+
std::unique_lock lock(m_mtx);
|
|
28
|
+
m_cond.wait(lock, [this] { return !m_queue.empty(); });
|
|
29
|
+
std::shared_ptr<T> result(std::make_shared<T>(m_queue.front()));
|
|
30
|
+
m_queue.pop();
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
bool empty() const noexcept {
|
|
35
|
+
std::scoped_lock lock(m_mtx);
|
|
36
|
+
return m_queue.empty();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private:
|
|
40
|
+
|
|
41
|
+
mutable std::mutex m_mtx;
|
|
42
|
+
std::queue<T> m_queue;
|
|
43
|
+
std::condition_variable m_cond;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
#endif // __TSQUEUE_HPP
|