Skip to content

Commit 93e6214

Browse files
committed
Добавлено сообщение version_check
1 parent 9f34ad6 commit 93e6214

4 files changed

Lines changed: 99 additions & 37 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class Protocol;
4747

4848
## Установка
4949

50-
CSUP это **заголовочная библиотека**, не требующая сборки.
50+
CSUP - это **заголовочная библиотека**, не требующая сборки.
5151

5252
Для установки достаточно скопировать папку `csup` в каталог с заголовочными файлами вашего проекта или в стандартный путь поиска компилятора.
5353

csup/definitions.hpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@
77

88
namespace csup {
99

10+
/**
11+
* @brief CSUP protocol version definition.
12+
*
13+
* Semantic versioning format: MAJOR.MINOR.PATCH
14+
*
15+
* | Component | Description |
16+
* |------------|-------------------------------------------------|
17+
* | MAJOR | Incompatible protocol changes. |
18+
* | MINOR | Backward-compatible feature additions. |
19+
* | PATCH | Backward-compatible bug fixes and refinements. |
20+
*/
21+
static constexpr types::u8 VERSION[3] = {1, 0, 0};
22+
23+
/** @brief Result codes returned by CSUP protocol operations. */
1024
enum class Result : types::u8
1125
{
1226
// 0x0X — Success
@@ -29,8 +43,21 @@ enum class Result : types::u8
2943
// 0x3X — Protocol / logical errors
3044
MSG_COLLISION = 0x30, // Message collision detected
3145
MAX_ATTEMPT = 0x31, // Maximum retransmission attempts reached
46+
VERSION_MISMATCH = 0x32, // Protocol version mismatch
3247
};
3348

49+
50+
/**
51+
* @brief Identifiers for system-level messages used by CSUP.
52+
*
53+
* Reserved range: 0xE0–0xEF.
54+
*
55+
* | Type | Value | Description |
56+
* |----------------|--------|-------------------------------------------------------------|
57+
* | ACK | 0xE0 | Confirms successful frame reception. |
58+
* | HEARTBEAT | 0xE1 | Keep-alive signal indicating active connection. |
59+
* | VERSION_CHECK | 0xE2 | Used to exchange and verify protocol version compatibility. |
60+
*/
3461
enum class SystemMsgType : types::u8
3562
{
3663
ACK = 0xE0,
@@ -44,18 +71,11 @@ enum class SystemMsgType : types::u8
4471
* Used when two nodes send frames simultaneously and both require acknowledgment.
4572
* Determines which side should resend or yield based on the selected policy.
4673
*
47-
* | Mode | Description |
48-
* |-------------------|------------------------------------------------------------------|
49-
* | BALANCED | Both sides decide based on frame ID (lower ID has priority). |
50-
* | PRIORITY_SEND | Always prioritizes sending; ignores incoming conflicting frame. |
51-
* | NON_PRIORITY_SEND | Yields to the other sender when a collision is detected. |
52-
*
53-
* @note Compatibility Table:
54-
* | Mode | Compatible with Modes |
55-
* |-------------------|------------------------|
56-
* | BALANCED | BALANCED |
57-
* | PRIORITY_SEND | NON_PRIORITY_SEND |
58-
* | NON_PRIORITY_SEND | PRIORITY_SEND |
74+
* | Mode | Compatible mode | Description |
75+
* |-------------------|-------------------|------------------------------------------------------------------|
76+
* | BALANCED | BALANCED | Both sides decide based on frame ID (lower ID has priority). |
77+
* | PRIORITY_SEND | NON_PRIORITY_SEND | Always prioritizes sending; ignores incoming conflicting frame. |
78+
* | NON_PRIORITY_SEND | PRIORITY_SEND | Yields to the other sender when a collision is detected. |
5979
*/
6080
enum class CollisionBehavior : uint8_t
6181
{

csup/protocol/protocol.hpp

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ template <typename Uart = hal::DefaultUart,
3939
types::size MaxMsgSize = 256>
4040
class Protocol
4141
{
42+
static_assert(MaxMsgSize >= sizeof(VERSION), "MaxMsgSize must be >= VERSION size");
43+
4244
public:
4345
Protocol() = default;
4446
~Protocol() = default;
@@ -100,11 +102,13 @@ class Protocol
100102
if (result != Result::SUCCESS)
101103
continue;
102104

105+
const SystemMsgType msg_type = static_cast<SystemMsgType>(frame.header.type.value);
106+
103107
if (frame.header.ack.ack_required)
104-
send_ack(frame.header.ack.frame_id);
108+
send_ack(frame.header.ack.frame_id, msg_type == SystemMsgType::VERSION_CHECK);
105109

106-
// Skip HEARTBEAT frames
107-
if (frame.header.type.value == static_cast<types::u8>(SystemMsgType::HEARTBEAT))
110+
// Skip HEARTBEAT and VERSION_CHECK frames
111+
if (msg_type == SystemMsgType::HEARTBEAT || msg_type == SystemMsgType::VERSION_CHECK)
108112
{
109113
result = Result::SYS_MSG_HANDLED;
110114
continue;
@@ -148,9 +152,6 @@ class Protocol
148152
const types::u8& retries = 3,
149153
const types::u32& timeout = 200)
150154
{
151-
if (type >= 0xE0)
152-
return Result::WRONG_MSG_TYPE;
153-
154155
tp::DataFrame frame;
155156
frame.header.type = type;
156157
frame.header.ack.ack_required = ack;
@@ -203,24 +204,51 @@ class Protocol
203204
return send(static_cast<types::u8>(SystemMsgType::HEARTBEAT), BufferView(), true);
204205
}
205206

207+
/**
208+
* @brief Checks protocol version compatibility.
209+
*
210+
* @return Result::SUCCESS if compatible, otherwise error code.
211+
*/
212+
Result version_check()
213+
{
214+
Result result;
215+
result = send(static_cast<types::u8>(SystemMsgType::VERSION_CHECK), BufferView(), true);
216+
if (result != Result::SUCCESS)
217+
return result;
218+
219+
tp::DataFrame frame;
220+
result = frame.unpack(buffer_.subview(0, tp::FieldHeader::SIZE + sizeof(VERSION)));
221+
if (result != Result::SUCCESS)
222+
return result;
223+
224+
if (frame.data[0] != VERSION[0])
225+
return Result::VERSION_MISMATCH;
226+
227+
return Result::SUCCESS;
228+
}
229+
206230
private:
207231
/**
208232
* @brief Sends an acknowledgment frame for the specified message ID.
209233
*
210234
* @param frame_id Identifier of the frame being acknowledged.
235+
* @param version Whether to include protocol version info in the acknowledgment.
211236
* @return Result::SUCCESS if acknowledgment received, otherwise error code.
212237
*/
213-
Result send_ack(const tp::FieldID& frame_id)
238+
Result send_ack(const tp::FieldID& frame_id, bool version = false)
214239
{
215-
Result result;
240+
tp::DataFrame ack_frame;
241+
ack_frame.header.type = static_cast<types::u8>(SystemMsgType::ACK);
242+
ack_frame.header.ack.ack_required = false;
243+
ack_frame.header.ack.frame_id = frame_id;
216244

217-
tp::FieldHeader ack_frame;
218-
ack_frame.type = static_cast<types::u8>(SystemMsgType::ACK);
219-
ack_frame.ack.ack_required = false;
220-
ack_frame.ack.frame_id = frame_id;
245+
if (version)
246+
{
247+
ack_frame.data = BufferView(const_cast<types::u8*>(VERSION), sizeof(VERSION));
248+
}
221249

222-
BufferHandler<tp::FieldHeader::SIZE + Crc::SIZE> ack_buf;
223-
result = ack_frame.pack(ack_buf);
250+
BufferHandler<tp::FieldHeader::SIZE + Crc::SIZE + sizeof(VERSION)> ack_buf;
251+
Result result = ack_frame.pack(ack_buf);
224252
if (result != Result::SUCCESS)
225253
return result;
226254

@@ -236,7 +264,7 @@ class Protocol
236264
*
237265
* @param frame_id ID of the sent frame awaiting acknowledgment.
238266
* @param type Type of the sent frame (used for collision handling).
239-
* @param timeout Maximum waiting time for acknowledgment, in milliseconds.
267+
* @param timeout Maximum waiting time for acknowledgment, in milliseconds.
240268
* @return Result::SUCCESS if acknowledgment received, otherwise error code.
241269
*/
242270
Result wait_ack(const tp::FieldID& frame_id, types::u8 type, const types::u32& timeout)
@@ -256,23 +284,29 @@ class Protocol
256284
if (result != Result::SUCCESS)
257285
return result;
258286

259-
if (data_header.type.value == static_cast<types::u8>(SystemMsgType::ACK) &&
260-
data_header.ack.frame_id == frame_id)
287+
const SystemMsgType rcv_type = static_cast<SystemMsgType>(data_header.type.value);
288+
289+
if (rcv_type == SystemMsgType::ACK && data_header.ack.frame_id == frame_id)
261290
{
262291
return Result::SUCCESS;
263292
}
264-
else if (data_header.type.value == static_cast<types::u8>(SystemMsgType::HEARTBEAT))
293+
294+
const bool send_sys_msg = type == static_cast<types::u8>(SystemMsgType::HEARTBEAT) ||
295+
type == static_cast<types::u8>(SystemMsgType::VERSION_CHECK);
296+
297+
if (rcv_type == SystemMsgType::HEARTBEAT || rcv_type == SystemMsgType::VERSION_CHECK)
265298
{
266-
send_ack(data_header.ack.frame_id);
267-
if (type == static_cast<types::u8>(SystemMsgType::HEARTBEAT))
299+
send_ack(data_header.ack.frame_id, rcv_type == SystemMsgType::VERSION_CHECK);
300+
if (send_sys_msg)
268301
continue;
269302
return Result::SYS_MSG_HANDLED;
270303
}
271-
else if (data_header.ack.ack_required) // Collision
272-
{
273-
if (type == static_cast<types::u8>(SystemMsgType::HEARTBEAT))
274-
continue;
275304

305+
if (send_sys_msg)
306+
continue;
307+
308+
if (data_header.ack.ack_required) // Collision
309+
{
276310
switch (Collision)
277311
{
278312
case CollisionBehavior::PRIORITY_SEND:

examples/linux/src/main.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,14 @@ void test_heartbeat(csup::Protocol<>& csup)
190190
<< ", code=" << static_cast<int>(res) << "\n";
191191
++err_count;
192192
}
193+
194+
res = csup.version_check();
195+
if (res != csup::Result::SUCCESS)
196+
{
197+
std::cerr << "[ERROR] Version check failed at message " << i
198+
<< ", code=" << static_cast<int>(res) << "\n";
199+
++err_count;
200+
}
193201
}
194202

195203
auto end = std::chrono::steady_clock::now();

0 commit comments

Comments
 (0)