GKD.RoboCtrl
RoboMaster Linux 电控:异步 IO、设备驱动与机器人控制
载入中...
搜索中...
未找到
network_protocol.hpp
1#pragma once
2
3#include <array>
4#include <bit>
5#include <chrono>
6#include <cmath>
7#include <cstddef>
8#include <cstdint>
9#include <limits>
10#include <optional>
11#include <span>
12#include <stdexcept>
13#include <string_view>
14#include <vector>
15
16namespace roboctrl::device {
17
18// The legacy Linux packed structure included a four-byte ROBOT_MODE enum.
19// compact_10 is an explicit peer compatibility setting, never auto-detected.
20enum class vision_wire_format { legacy_14, compact_10 };
21
22struct aim_target {
23 float yaw{}; // absolute radians, peer/IMU coordinate convention
24 float pitch{};
25 bool fire{};
26 std::uint32_t legacy_mode{}; // informational only; cannot arm the robot
27};
28
30 float vx{}; // m/s in the configured navigation/control coordinate frame
31 float vy{};
32};
33
34namespace network_protocol {
35using bytes = std::span<const std::byte>;
36static_assert(sizeof(float) == 4 && std::numeric_limits<float>::is_iec559);
37static_assert(sizeof(double) == 8 && std::numeric_limits<double>::is_iec559);
38
39inline std::uint32_t read_u32(bytes data, std::size_t offset) {
40 std::uint32_t result{};
41 for (std::size_t i = 0; i < 4; ++i) {
42 result |= std::uint32_t(std::to_integer<unsigned char>(data[offset + i])) << (8 * i);
43 }
44 return result;
45}
46
47inline float read_float(bytes data, std::size_t offset) {
48 return std::bit_cast<float>(read_u32(data, offset));
49}
50
51inline void write_le(std::span<std::byte> data, std::size_t offset,
52 std::uint64_t value, std::size_t count) {
53 for (std::size_t i = 0; i < count; ++i) {
54 data[offset + i] = std::byte((value >> (8 * i)) & 0xff);
55 }
56}
57
58inline void write_float(std::span<std::byte> data, std::size_t offset, float value) {
59 write_le(data, offset, std::bit_cast<std::uint32_t>(value), 4);
60}
61
62inline std::optional<aim_target> decode_aim(bytes data, std::uint8_t header,
63 vision_wire_format format) {
64 if (format != vision_wire_format::legacy_14 && format != vision_wire_format::compact_10) {
65 return std::nullopt;
66 }
67 const auto length = format == vision_wire_format::legacy_14 ? 14u : 10u;
68 if (data.size() != length || data[0] != std::byte{header} || header == 0x37 ||
69 std::to_integer<unsigned char>(data[9]) > 1) {
70 return std::nullopt;
71 }
72 aim_target result{read_float(data, 1), read_float(data, 5), data[9] == std::byte{1}, 0};
73 if (!std::isfinite(result.yaw) || !std::isfinite(result.pitch)) {
74 return std::nullopt;
75 }
76 if (format == vision_wire_format::legacy_14) {
77 result.legacy_mode = read_u32(data, 10);
78 if (result.legacy_mode > 5) {
79 return std::nullopt;
80 }
81 }
82 return result;
83}
84
85inline std::optional<navigation_command> decode_navigation(bytes data) {
86 if (data.size() != 9 || data[0] != std::byte{0x37}) {
87 return std::nullopt;
88 }
89 navigation_command result{read_float(data, 1), read_float(data, 5)};
90 if (!std::isfinite(result.vx) || !std::isfinite(result.vy)) {
91 return std::nullopt;
92 }
93 return result;
94}
95
96inline std::array<std::byte, 10> encode_posture(std::uint8_t header,
97 float yaw, float pitch, bool red) {
98 if (header == 0x37 || !std::isfinite(yaw) || !std::isfinite(pitch)) {
99 throw std::invalid_argument("invalid vision posture");
100 }
101 std::array<std::byte, 10> data{};
102 data[0] = std::byte{header};
103 write_float(data, 1, yaw);
104 write_float(data, 5, pitch);
105 data[9] = std::byte{red};
106 return data;
107}
108
109inline std::array<std::byte, 10> encode_navigation(float yaw, float hp_fraction,
110 bool match_started) {
111 if (!std::isfinite(yaw) || !std::isfinite(hp_fraction) || hp_fraction < 0 ||
112 hp_fraction > 1) {
113 throw std::invalid_argument("invalid navigation telemetry");
114 }
115 std::array<std::byte, 10> data{};
116 data[0] = std::byte{0x37};
117 write_float(data, 1, yaw);
118 write_float(data, 5, hp_fraction);
119 data[9] = std::byte{match_started};
120 return data;
121}
122
123// Local receive time is used: the legacy wire contains no sequence or timestamp.
124template<typename T>
126public:
127 using clock = std::chrono::steady_clock;
128 void update(T value, clock::time_point now = clock::now()) {
129 value_ = value;
130 received_ = now;
131 }
132 std::optional<T> get(clock::duration timeout, clock::time_point now = clock::now()) const {
133 if (!value_ || timeout <= clock::duration::zero() || now < received_ ||
134 now - received_ >= timeout) {
135 return std::nullopt;
136 }
137 return value_;
138 }
139 void reset() { value_.reset(); }
140private:
141 std::optional<T> value_;
142 clock::time_point received_{};
143};
144
145inline std::uint32_t log_name_id(std::string_view name) {
146 std::uint32_t value = 2166136261u;
147 for (const unsigned char c : name) {
148 value = (value ^ c) * 16777619u;
149 }
150 return value;
151}
152
153inline std::vector<std::byte> encode_log_name(std::string_view name) {
154 if (name.empty() || name.size() > 255) {
155 throw std::invalid_argument("remote log name must contain 1..255 bytes");
156 }
157 std::vector<std::byte> data(8 + name.size());
158 write_le(data, 0, data.size(), 2);
159 data[2] = std::byte{0};
160 write_le(data, 3, log_name_id(name), 4);
161 data[7] = std::byte(name.size());
162 for (std::size_t i = 0; i < name.size(); ++i) data[8 + i] = std::byte(name[i]);
163 return data;
164}
165
166inline std::array<std::byte, 15> encode_log_value(std::uint32_t id, double value) {
167 if (!std::isfinite(value)) throw std::invalid_argument("non-finite remote log value");
168 std::array<std::byte, 15> data{};
169 write_le(data, 0, data.size(), 2);
170 data[2] = std::byte{1};
171 write_le(data, 3, id, 4);
172 write_le(data, 7, std::bit_cast<std::uint64_t>(value), 8);
173 return data;
174}
175
176inline std::vector<std::byte> encode_log_text(std::string_view text, bool message_box = false) {
177 // Keep each complete datagram below IPv4's maximum UDP payload.
178 if (text.size() > 65502) throw std::invalid_argument("remote log text too long");
179 std::vector<std::byte> data(5 + text.size());
180 write_le(data, 0, data.size(), 2);
181 data[2] = std::byte(message_box ? 3 : 2);
182 write_le(data, 3, text.size(), 2);
183 for (std::size_t i = 0; i < text.size(); ++i) data[5 + i] = std::byte(text[i]);
184 return data;
185}
186} // namespace network_protocol
187} // namespace roboctrl::device
auto get() -> T &
获取单例实例
Definition multiton.hpp:230