GKD.RoboCtrl
RoboMaster Linux 电控:异步 IO、设备驱动与机器人控制
载入中...
搜索中...
未找到
ui.hpp
1#pragma once
2
3#include "device/referee/protocol.hpp"
4#include <array>
5#include <bit>
6#include <cmath>
7#include <optional>
8#include <string_view>
9#include <utility>
10
11namespace roboctrl::device::referee_protocol {
12
13enum class graphic_type : std::uint8_t { line, rectangle, circle, ellipse, arc, floating, integer, text };
14struct graphic {
15 std::array<std::uint8_t, 3> name {};
16 std::uint8_t operation {1};
17 graphic_type type {graphic_type::line};
18 std::uint8_t layer {}, color {2};
19 std::uint16_t start_angle {}, end_angle {}, width {2}, x {}, y {}, radius {}, end_x {}, end_y {};
20 // Numeric bits are explicit: legacy floating UI used IEEE754 float, integer uses int32.
21 // Callers must verify their client protocol revision before using numeric graphics.
22 std::optional<std::uint32_t> numeric_bits {};
23};
24
25inline std::array<std::byte, 15> encode_graphic(const graphic& item) {
26 if (item.operation < 1 || item.operation > 3 || std::uint8_t(item.type) > 7 ||
27 item.layer > 9 || item.color > 8 || item.start_angle > 511 || item.end_angle > 511 ||
28 item.width > 1023 || item.x > 2047 || item.y > 2047 || item.radius > 1023 ||
29 item.end_x > 2047 || item.end_y > 2047)
30 throw std::invalid_argument("referee graphic field exceeds wire width/range");
31 std::vector<std::byte> result;
32 for (auto ch : item.name) result.push_back(std::byte{ch});
33 append_u32(result, std::uint32_t(item.operation) | (std::uint32_t(item.type) << 3) |
34 (std::uint32_t(item.layer) << 6) | (std::uint32_t(item.color) << 10) |
35 (std::uint32_t(item.start_angle) << 14) | (std::uint32_t(item.end_angle) << 23));
36 append_u32(result, std::uint32_t(item.width) | (std::uint32_t(item.x) << 10) |
37 (std::uint32_t(item.y) << 21));
38 append_u32(result, item.numeric_bits.value_or(std::uint32_t(item.radius) |
39 (std::uint32_t(item.end_x) << 10) | (std::uint32_t(item.end_y) << 21)));
40 std::array<std::byte, 15> out{};
41 std::copy(result.begin(), result.end(), out.begin());
42 return out;
43}
44
45inline std::optional<std::uint16_t> client_id(std::uint16_t robot_id) {
46 if ((robot_id >= 1 && robot_id <= 6) || (robot_id >= 101 && robot_id <= 106))
47 return robot_id + 0x0100;
48 return std::nullopt; // Sentry has no operator client in this source profile.
49}
50inline std::vector<std::byte> interactive_header(std::uint16_t content_id,
51 std::uint16_t sender, std::uint16_t receiver) {
52 if (sender == 0 || receiver == 0) throw std::invalid_argument("referee UI IDs must be nonzero");
53 std::vector<std::byte> out;
54 append_u16(out, content_id);
55 append_u16(out, sender);
56 append_u16(out, receiver);
57 return out;
58}
59inline std::vector<std::byte> encode_graphics(std::span<const graphic> items,
60 std::uint16_t sender, std::uint16_t receiver, std::uint8_t sequence) {
61 std::uint16_t content_id{};
62 switch (items.size()) {
63 case 1: content_id = 0x0101; break;
64 case 2: content_id = 0x0102; break;
65 case 5: content_id = 0x0103; break;
66 case 7: content_id = 0x0104; break;
67 default: throw std::invalid_argument("referee graphic count must be 1, 2, 5 or 7");
68 }
69 auto payload = interactive_header(content_id, sender, receiver);
70 for (const auto& item : items) {
71 if (item.type == graphic_type::text) throw std::invalid_argument("text uses its own frame");
72 auto encoded = encode_graphic(item);
73 payload.insert(payload.end(), encoded.begin(), encoded.end());
74 }
75 return encode_frame(0x0301, payload, sequence);
76}
77inline std::vector<std::byte> encode_text(graphic item, std::string_view text,
78 std::uint16_t sender, std::uint16_t receiver, std::uint8_t sequence) {
79 if (text.size() > 30) throw std::invalid_argument("referee UI text exceeds 30 bytes");
80 item.type = graphic_type::text;
81 item.end_angle = static_cast<std::uint16_t>(text.size());
82 auto payload = interactive_header(0x0110, sender, receiver);
83 auto encoded = encode_graphic(item);
84 payload.insert(payload.end(), encoded.begin(), encoded.end());
85 for (char ch : text) payload.push_back(std::byte(static_cast<unsigned char>(ch)));
86 payload.resize(51, std::byte{0});
87 return encode_frame(0x0301, payload, sequence);
88}
89inline std::vector<std::byte> encode_delete(std::uint8_t operation, std::uint8_t layer,
90 std::uint16_t sender, std::uint16_t receiver, std::uint8_t sequence) {
91 if (operation < 1 || operation > 2 || layer > 9)
92 throw std::invalid_argument("referee UI delete operation/layer invalid");
93 auto payload = interactive_header(0x0100, sender, receiver);
94 payload.push_back(std::byte{operation});
95 payload.push_back(std::byte{layer});
96 return encode_frame(0x0301, payload, sequence);
97}
98
99using ui_address = std::pair<std::uint16_t, std::uint16_t>;
100
103public:
104 [[nodiscard]] bool add_required(ui_address address) const {
105 return !address_ || *address_ != address || refresh_ == 0;
106 }
107 void submitted(ui_address address) {
108 if (!address_ || *address_ != address) refresh_ = 0;
109 address_ = address;
110 refresh_ = (refresh_ + 1) % 50;
111 }
112 void reset() { address_.reset(); refresh_ = 0; }
113private:
114 std::optional<ui_address> address_;
115 unsigned refresh_ {};
116};
117
118struct ui_status {
119 bool friction_ready {}, auto_aim {}, spinning {}, fire_permitted {};
120 float capacitor_percent {};
121};
122inline std::array<graphic, 5> status_graphics(const ui_status& status, bool add) {
123 std::array<graphic, 5> out{};
124 const std::array<bool, 4> flags{status.friction_ready, status.auto_aim, status.spinning, status.fire_permitted};
125 for (std::size_t i = 0; i < flags.size(); ++i) {
126 out[i] = {.name = {'S', 'T', static_cast<std::uint8_t>('0' + i)},
127 .operation = static_cast<std::uint8_t>(add ? 1 : 2), .type = graphic_type::circle,
128 .layer = 8, .color = static_cast<std::uint8_t>(flags[i] ? 2 : 1), .width = 4,
129 .x = static_cast<std::uint16_t>(800 + i * 70), .y = 500, .radius = 16};
130 }
131 const auto percent = std::isfinite(status.capacitor_percent) ?
132 std::clamp(status.capacitor_percent, 0.0f, 100.0f) : 0.0f;
133 out[4] = {.name = {'C', 'A', 'P'}, .operation = static_cast<std::uint8_t>(add ? 1 : 2),
134 .type = graphic_type::line, .layer = 8, .color = 2, .width = 8, .x = 800, .y = 450,
135 .end_x = static_cast<std::uint16_t>(800 + percent * 2), .end_y = 450};
136 return out;
137}
138} // namespace roboctrl::device::referee_protocol