GKD.RoboCtrl
RoboMaster Linux 电控:异步 IO、设备驱动与机器人控制
载入中...
搜索中...
未找到
can.cpp
1#include "io/can.h"
2#include "asio/use_awaitable.hpp"
3#include "core/async.hpp"
4#include "io/base.hpp"
5#include "linux/can.h"
6#include "utils/utils.hpp"
7#include <cstddef>
8#include <stdexcept>
9#include <sys/socket.h>
10#include <unistd.h>
11
12using namespace roboctrl::io;
13
14template <>
15struct std::formatter<can_frame> : std::formatter<std::string> {
16 auto format(const can_frame& frame, std::format_context& ctx) const {
17 std::ostringstream oss;
18 oss << std::hex << std::uppercase;
19 oss << "CAN ID=0x" << (frame.can_id & CAN_EFF_MASK);
20
21 oss << " [";
22 if (frame.can_id & CAN_EFF_FLAG) oss << "EFF ";
23 if (frame.can_id & CAN_RTR_FLAG) oss << "RTR ";
24 if (frame.can_id & CAN_ERR_FLAG) oss << "ERR ";
25 oss << "]";
26
27 oss << " DLC=" << std::dec << static_cast<int>(frame.can_dlc)
28 << " DATA=[";
29
30 const auto length = std::min<int>(frame.can_dlc, CAN_MAX_DLEN);
31 for (int i = 0; i < length; ++i) {
32 oss << std::format("{:02X}", frame.data[i]);
33 if (i + 1 < length) oss << ' ';
34 }
35 oss << ']';
36
37 return std::formatter<std::string>::format(oss.str(), ctx);
38 }
39};
40
42 :info_{info},
44 stream_{roboctrl::io_context()},
45 write_queue_{[this](byte_span data) -> awaitable<void> {
46 co_await asio::async_write(stream_, asio::buffer(data), asio::use_awaitable);
47 }},
48 interface_name_{info.interface_name.data(),info.interface_name.length()}
49{
50 int fd = ::socket(PF_CAN,SOCK_RAW,CAN_RAW);
51 if(fd < 0)
52 throw std::runtime_error("socket() failed");
53
54 if (interface_name_.empty() || interface_name_.size() >= IFNAMSIZ) {
55 ::close(fd);
56 throw std::invalid_argument("invalid CAN interface name");
57 }
58
59 struct ifreq ifr{};
60 std::strncpy(ifr.ifr_name, interface_name_.c_str(), IFNAMSIZ);
61 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
62 ::close(fd);
63 throw std::runtime_error("ioctl(SIOCGIFINDEX) failed");
64 }
65
66 struct sockaddr_can addr{};
67 addr.can_family = AF_CAN;
68 addr.can_ifindex = ifr.ifr_ifindex;
69 if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
70 ::close(fd);
71 throw std::runtime_error("bind() failed");
72 }
73
74 stream_.assign(fd);
75
76 log_info("Can io {} created on {}", info.name, info.interface_name);
77}
78
79void can::start() {
80 if (started_) {
81 return;
82 }
83 started_ = true;
85}
86
88 while(true){
89 size_t pkg_size = co_await stream_.async_read_some(asio::buffer(buffer_),asio::use_awaitable);
90 if (pkg_size != sizeof(can_frame)) {
91 log_warn("drop malformed CAN frame with {} bytes", pkg_size);
92 continue;
93 }
94 can_frame cf = utils::from_bytes<can_frame>(std::span{buffer_.data(),pkg_size});
95
96 can_id_type id = cf.can_id;
97 size_t can_size= cf.can_dlc;
98
99 if (can_size > CAN_MAX_DLEN || (id & (CAN_RTR_FLAG | CAN_ERR_FLAG))) {
100 log_warn("drop invalid CAN frame: {}", cf);
101 continue;
102 }
103
104 //log_debug("recv can frame: {}",cf);
105
106 dispatch(id,std::span{(std::byte*)cf.data,can_size});
107 }
108}
109
111
112 if (data.size() > CAN_MAX_DLEN) {
113 throw std::invalid_argument("CAN payload cannot exceed 8 bytes");
114 }
115 if (id & (CAN_RTR_FLAG | CAN_ERR_FLAG)) {
116 throw std::invalid_argument("CAN data frame ID cannot contain RTR or ERR flags");
117 }
118
119 can_frame frame{};
120 frame.can_id = id;
121 frame.can_dlc = static_cast<decltype(frame.can_dlc)>(data.size());
122 std::memcpy(frame.data, data.data(), data.size());
123
124 log_debug("send can frame: {}", frame);
125
126 co_await write_queue_.send_latest(
127 can_write_key(id, data.size()),
128 std::span<const std::byte>{reinterpret_cast<const std::byte*>(&frame), sizeof(frame)});
129}
异步任务上下文组件。
基于 Linux SocketCAN 的 CAN 总线封装。
can(const info_type &info)
打开 CAN 设备。
Definition can.cpp:41
awaitable< void > task()
接收循环任务。
Definition can.cpp:87
void start()
启动接收协程;重复调用不会重复启动。
Definition can.cpp:79
awaitable< void > send(can_id_type id, byte_span data)
发送带 CAN ID 的帧。
Definition can.cpp:110
带 key 的 IO 基类,根据键值派发数据。
Definition base.hpp:157
void dispatch(const std::uint32_t &key, byte_span data)
将数据派发给对应 key 的回调。
Definition base.hpp:197
awaitable< void > send_latest(std::uint64_t key, std::span< const std::byte > data, std::shared_ptr< void > keepalive={})
按 key 入队,并用新数据替换同 key 的尚未发送项。
void log_debug(std::format_string< Args... > fmt, Args &&...args) const
输出debug日志
Definition logger.h:202
void log_warn(std::format_string< Args... > fmt, Args &&...args) const
输出warn日志
Definition logger.h:226
IO的基础组件。
std::span< const std::byte > byte_span
只读 byte span。
Definition base.hpp:45
auto spawn(task_context::task_type &&task)
添加一个协程任务到全局任务上下文中执行。
Definition async.hpp:191
asio::awaitable< T > awaitable
协程任务类型。
Definition async.hpp:46
auto & io_context()
获取全局任务上下文的io_context。
Definition async.hpp:278
CAN 初始化参数。
Definition can.h:33
常用数值与字节工具集合。