GKD.RoboCtrl
RoboMaster Linux 电控:异步 IO、设备驱动与机器人控制
载入中...
搜索中...
未找到
protocol.hpp
1#pragma once
2
3#include <algorithm>
4#include <cstddef>
5#include <cstdint>
6#include <span>
7#include <stdexcept>
8#include <vector>
9
10namespace roboctrl::device::referee_protocol {
11
12using bytes = std::span<const std::byte>;
13inline constexpr std::size_t max_payload_size = 256;
14inline std::uint8_t u8(std::byte value) { return std::to_integer<std::uint8_t>(value); }
15inline std::uint16_t u16(bytes value, std::size_t offset) {
16 return u8(value[offset]) | (std::uint16_t{u8(value[offset + 1])} << 8);
17}
18inline std::uint32_t u32(bytes value, std::size_t offset) {
19 return u16(value, offset) | (std::uint32_t{u16(value, offset + 2)} << 16);
20}
21inline void append_u16(std::vector<std::byte>& out, std::uint16_t value) {
22 out.push_back(std::byte(value & 0xff));
23 out.push_back(std::byte(value >> 8));
24}
25inline void append_u32(std::vector<std::byte>& out, std::uint32_t value) {
26 append_u16(out, value & 0xffff);
27 append_u16(out, value >> 16);
28}
29
30// Legacy RM referee wire CRCs: reflected x^8+x^5+x^4+1 and
31// x^16+x^12+x^5+1, initial all ones, no final XOR.
32inline std::uint8_t crc8(bytes data, std::uint8_t crc = 0xff) {
33 for (const auto value : data) {
34 crc ^= u8(value);
35 for (int i = 0; i < 8; ++i)
36 crc = (crc & 1) ? (crc >> 1) ^ 0x8c : crc >> 1;
37 }
38 return crc;
39}
40inline std::uint16_t crc16(bytes data, std::uint16_t crc = 0xffff) {
41 for (const auto value : data) {
42 crc ^= u8(value);
43 for (int i = 0; i < 8; ++i)
44 crc = (crc & 1) ? (crc >> 1) ^ 0x8408 : crc >> 1;
45 }
46 return crc;
47}
48
49struct frame {
50 std::uint16_t command {};
51 std::uint8_t sequence {};
52 std::vector<std::byte> payload;
53};
54
55inline std::vector<std::byte> encode_frame(std::uint16_t command, bytes payload, std::uint8_t sequence) {
56 if (payload.size() > max_payload_size)
57 throw std::invalid_argument("referee payload exceeds legacy profile maximum");
58 std::vector<std::byte> out{std::byte{0xa5}};
59 append_u16(out, static_cast<std::uint16_t>(payload.size()));
60 out.push_back(std::byte{sequence});
61 out.push_back(std::byte{crc8(out)});
62 append_u16(out, command);
63 out.insert(out.end(), payload.begin(), payload.end());
64 append_u16(out, crc16(out));
65 return out;
66}
67
70public:
71 template<typename Fn>
72 void feed(bytes input, Fn&& on_frame) {
73 for (const auto value : input) {
74 buffer_.push_back(value);
75 drain(on_frame);
76 }
77 }
78 [[nodiscard]] std::size_t buffered_size() const { return buffer_.size(); }
79 [[nodiscard]] std::size_t rejected_frames() const { return rejected_frames_; }
80private:
81 template<typename Fn>
82 void drain(Fn& on_frame) {
83 while (!buffer_.empty()) {
84 if (buffer_.front() != std::byte{0xa5}) {
85 buffer_.erase(buffer_.begin());
86 continue;
87 }
88 if (buffer_.size() < 5) return;
89 const bytes data{buffer_};
90 const auto length = u16(data, 1);
91 if (crc8(data.first(4)) != u8(data[4]) || length > max_payload_size) {
92 ++rejected_frames_;
93 buffer_.erase(buffer_.begin());
94 continue;
95 }
96 const std::size_t total = length + 9;
97 if (buffer_.size() < total) return;
98 if (crc16(data.first(total - 2)) != u16(data, total - 2)) {
99 ++rejected_frames_;
100 buffer_.erase(buffer_.begin());
101 continue;
102 }
103 frame parsed{u16(data, 5), u8(data[3]), {data.begin() + 7, data.begin() + 7 + length}};
104 buffer_.erase(buffer_.begin(), buffer_.begin() + total);
105 on_frame(parsed);
106 }
107 }
108 std::vector<std::byte> buffer_;
109 std::size_t rejected_frames_ {};
110};
111} // namespace roboctrl::device::referee_protocol