GKD.RoboCtrl
RoboMaster Linux 电控:异步 IO、设备驱动与机器人控制
载入中...
搜索中...
未找到
base.hpp
浏览该文件的文档.
1
13#pragma once
14
15#include <algorithm>
16#include <array>
17#include <concepts>
18#include <cstddef>
19#include <cstring>
20#include <functional>
21#include <span>
22#include <tuple>
23#include <type_traits>
24#include <map>
25#include <memory>
26#include <optional>
27#include <stdexcept>
28#include <utility>
29
30#include "core/async.hpp"
31#include "utils/callback.hpp"
32#include "utils/utils.hpp"
33#include "utils/concepts.hpp"
34
35namespace roboctrl::io{
36
40using data_ptr = std::shared_ptr<const std::vector<std::byte>>;
41
45using byte_span = std::span<const std::byte>;
46
47namespace detail {
48
49template<typename Fn>
50awaitable<void> invoke_raw_callback(Fn& fn, data_ptr data) {
51 const byte_span bytes{data->data(), data->size()};
52 if constexpr (std::same_as<std::invoke_result_t<Fn&, byte_span>, awaitable<void>>) {
53 co_await fn(bytes);
54 } else {
55 fn(bytes);
56 }
57}
58
59template<typename Fn>
60using typed_callback_arg_t = utils::function_arg_t<Fn>;
61
62template<typename Fn>
65 (std::same_as<std::invoke_result_t<Fn&, typed_callback_arg_t<Fn>>, void> ||
66 std::same_as<std::invoke_result_t<Fn&, typed_callback_arg_t<Fn>>, awaitable<void>>);
67
75template<typename Arg, typename Fn>
77 using package_type = std::remove_cvref_t<Arg>;
78 auto package = utils::from_bytes<package_type>(bytes);
79
80 if constexpr (std::same_as<std::invoke_result_t<Fn&, Arg>, awaitable<void>>) {
81 co_await std::invoke(fn, static_cast<Arg>(package));
82 } else {
83 std::invoke(fn, static_cast<Arg>(package));
84 }
85}
86
87} // namespace detail
88
93template<utils::byte_container T>
95 auto res = std::make_shared<std::vector<std::byte>>(t.size());
96 if (t.size() > 0)
97 std::memcpy(res->data(), t.data(), t.size());
98 return res;
99}
100
108 :public utils::immovable_base,
110{
111public:
112 inline bare_io_base(){}
113
117 inline void on_data(callback_fn<byte_span> auto fn){
118 callback_.add([fn](data_ptr data) mutable -> auto {
119 return detail::invoke_raw_callback(fn, std::move(data));
120 });
121 }
122
123 template<detail::typed_callback_fn Fn>
124 requires (!std::same_as<std::remove_cvref_t<utils::function_arg_t<Fn>>,byte_span>)
125 inline void on_data(Fn&& fn)
126 {
127 using Arg = utils::function_arg_t<Fn>;
129
130 on_data([fn = std::forward<Fn>(fn)](byte_span bytes) mutable {
131 return detail::invoke_typed_callback<Arg>(fn, bytes);
132 });
133 }
134
135protected:
140 inline void dispatch(byte_span bytes){
141
142 callback_(make_shared_from(bytes));
143 }
144
145private:
146 callback<data_ptr> callback_;
147};
148
153template<typename TK>
155 :public utils::immovable_base,
157{
158public:
159 inline keyed_io_base(){};
160
164 void on_data(const TK& key,callback_fn<byte_span> auto fn,size_t size = 0){
165 const auto [size_it, inserted] = sizes_.try_emplace(key, size);
166 if (!inserted) {
167 if (size_it->second != 0 && size != 0 && size_it->second != size) {
168 throw std::invalid_argument("conflicting payload sizes registered for IO key");
169 }
170 if (size_it->second == 0) {
171 size_it->second = size;
172 }
173 }
174 callbacks_[key].add([fn](data_ptr data) mutable -> auto{
175 return detail::invoke_raw_callback(fn, std::move(data));
176 });
177 }
178
182 template<detail::typed_callback_fn Fn>
183 requires (!std::same_as<std::remove_cvref_t<utils::function_arg_t<Fn>>,byte_span>)
184 inline void on_data(const TK& key,Fn&& fn)
185 {
186 using Arg = utils::function_arg_t<Fn>;
188
189 on_data(key,[fn = std::forward<Fn>(fn)](byte_span bytes) mutable {
190 return detail::invoke_typed_callback<Arg>(fn, bytes);
191 },sizeof(Arg));
192 }
193protected:
197 inline void dispatch(const TK& key,byte_span data){
198 const auto callback_it = callbacks_.find(key);
199 if (callback_it == callbacks_.end()) {
200 return;
201 }
202 const auto size_it = sizes_.find(key);
203 if (size_it != sizes_.end() && size_it->second != 0 && size_it->second != data.size()) {
204 return;
205 }
206 callback_it->second(make_shared_from(data));
207 }
208
209 inline std::optional<size_t> package_size(const TK& key) const {
210 const auto it = sizes_.find(key);
211 if (it == sizes_.end() || it->second == 0) {
212 return std::nullopt;
213 }
214 return it->second;
215 }
216
217private:
218 std::map<TK,callback<data_ptr>> callbacks_;
219 std::map<TK,size_t> sizes_;
220};
221
225template<typename T>
226concept bare_io = std::is_base_of_v<bare_io_base, T> && requires (T t){
227 {t.task()} -> std::same_as<awaitable<void>>;
228 {t.send(std::declval<byte_span>())} -> std::same_as<awaitable<void>>;
229};
230
234template<typename T>
235concept keyed_io = std::is_base_of_v<keyed_io_base<typename T::key_type>, T> && requires (T t) {
236 typename T::key_type;
237 {t.task()} -> std::same_as<awaitable<void>>;
238 {t.send(std::declval<typename T::key_type>(),std::declval<byte_span>())} -> std::same_as<awaitable<void>>;
239};
240
244template<typename T>
245concept data_parser = requires (T t,byte_span bytes){
246 typename T::data_type;
247 {t.parse(bytes)} -> std::same_as<std::size_t>;
248 {t.data()} -> std::same_as<typename T::data_type>;
249};
250
254template<data_parser... parser_types>
256 template<int N>
257 using parser_type = std::tuple_element_t<N,std::tuple<parser_types...>>;
258
259 template<int N>
260 using data_type = typename parser_type<N>::data_type;
261
266 size_t parse(byte_span data){
267 size_t pos = 0;
268 bool valid = true;
269 std::apply([&](auto&... parser) {
270 ([&] {
271 if (!valid) {
272 return;
273 }
274 const size_t consumed = parser.parse(data.subspan(pos));
275 if (consumed == 0 || consumed > data.size() - pos) {
276 valid = false;
277 return;
278 }
279 pos += consumed;
280 }(), ...);
281 }, parsers);
282
283 return valid ? pos : 0;
284 }
285
289 template<int N>
290 auto data() -> data_type<N>{
291 return std::get<N>(parsers).data();
292 }
293
294 std::tuple<parser_types...> parsers;
295};
296
301template<int N>
302struct nbytes{
303 using data_type = std::array<std::byte,N>;
304
305 std::size_t parse(byte_span bytes){
306 if(bytes.size() < N){
307 return 0;
308 }
309 std::copy_n(bytes.begin(), N, data_.begin());
310 return N;
311 }
312
313 data_type data(){
314 return data_;
315 }
316
317 data_type data_{};
318};
319
323template<roboctrl::utils::package T>
325 using data_type = T;
326
327 std::size_t parse(byte_span bytes){
328 if(bytes.size() < sizeof(T)){
329 return 0;
330 }
331 std::memcpy(static_cast<void*>(&data_), static_cast<const void*>(bytes.data()), sizeof(T));
332 return sizeof(T);
333 }
334
335 data_type data(){
336 return data_;
337 }
338
339 data_type data_{};
340};
341
345template<std::byte... bytes>
347 using data_type = std::array<std::byte,sizeof...(bytes)>;
348
349 constexpr static std::array<std::byte,sizeof...(bytes)> bytes_data{bytes...};
350
351 std::size_t parse(byte_span data){
352 if(data.size() < sizeof...(bytes)){
353 return 0;
354 }
355
356 for(size_t i = 0; i < sizeof...(bytes); ++i)
357 if(data[i] != bytes_data[i])
358 return 0;
359
360 return sizeof...(bytes);
361 }
362
363 data_type data(){
364 return bytes_data;
365 }
366};
367
372 using data_type = byte_span;
373
374 size_t parse(byte_span data){
375 data_ = data;
376 return data.size();
377 }
378
379 data_type data(){
380 return data_;
381 }
382
383 data_type data_{};
384};
385
394template<bare_io io_type>
396 co_await io.send(data);
397}
398
407template<bare_io io_type>
408awaitable<void> send(const typename io_type::info_type::key_type& key,byte_span data){
409 auto& io = roboctrl::get<io_type>(key);
410 co_await io.send(data);
411}
412
421template<bare_io io_type,utils::package T>
422requires (!std::same_as<T, byte_span>)
423awaitable<void> send(io_type& io,T pkg){
424 const auto bytes = utils::to_bytes(pkg);
425 co_await io.send(byte_span{bytes});
426}
427
436template<bare_io io_type,utils::package T>
437requires (!std::same_as<T, byte_span>)
438awaitable<void> send(typename io_type::info_type::key_type key,T pkg){
439 const auto bytes = utils::to_bytes(pkg);
440 co_await send<io_type>(key,byte_span{bytes});
441}
442}
443
异步任务上下文组件。
协程回调收集器。
管理一组协程回调的容器。
Definition callback.hpp:36
void dispatch(byte_span bytes)
分发收到的字节流。
Definition base.hpp:140
void on_data(callback_fn< byte_span > auto fn)
注册字节级别的回调。
Definition base.hpp:117
带 key 的 IO 基类,根据键值派发数据。
Definition base.hpp:157
void dispatch(const TK &key, byte_span data)
将数据派发给对应 key 的回调。
Definition base.hpp:197
void on_data(const TK &key, callback_fn< byte_span > auto fn, size_t size=0)
注册指定 key 的回调。
Definition base.hpp:164
void on_data(const TK &key, Fn &&fn)
注册平凡类型包的回调。
Definition base.hpp:184
约束回调类型可被调用且返回 void 或 awaitable<void>。
Definition callback.hpp:27
裸 IO 概念,要求具备 send/task 协程接口。
Definition base.hpp:226
数据解析器概念,负责按需消费字节并返回结果。
Definition base.hpp:245
带 key 的 IO 概念,要求具备 send/task 协程接口。
Definition base.hpp:235
用于约束“可安全作为报文搬运”的平凡类型。
Definition concepts.hpp:78
公共Concept与元类型工具。
std::shared_ptr< const std::vector< std::byte > > data_ptr
只读共享数据缓冲。
Definition base.hpp:40
awaitable< void > invoke_typed_callback(Fn &fn, byte_span bytes)
解析 typed payload,并让解析结果覆盖异步回调的完整执行期。
Definition base.hpp:76
std::span< const std::byte > byte_span
只读 byte span。
Definition base.hpp:45
awaitable< void > send(io_type &io, byte_span data)
发送原始字节流,适配任意裸 IO。
Definition base.hpp:395
data_ptr make_shared_from(const T &t)
将任意满足 byte_container 的数据拷贝到共享缓冲。
Definition base.hpp:94
asio::awaitable< T > awaitable
协程任务类型。
Definition async.hpp:46
组合式解析器,可顺序堆叠多个数据解析单元。
Definition base.hpp:255
auto data() -> data_type< N >
获取指定序号解析器的解析结果。
Definition base.hpp:290
size_t parse(byte_span data)
依次执行所有解析器。
Definition base.hpp:266
固定内容匹配解析器。
Definition base.hpp:346
固定长度字节解析器。
Definition base.hpp:302
吃掉剩余全部数据的解析器。
Definition base.hpp:371
平凡结构体解析器。
Definition base.hpp:324
快速继承获得“不可移动”约束的基类。
Definition concepts.hpp:59
快速继承获得“不可复制”约束的基类。
Definition concepts.hpp:68
常用数值与字节工具集合。