Skip to main content

piper_protocol/
lib.rs

1//! # Piper Protocol
2//!
3//! 机械臂 CAN 总线协议定义(无硬件依赖)
4//!
5//! ## 模块
6//!
7//! - `ids`: CAN ID 常量定义
8//! - `constants`: 协议常量定义
9//! - `feedback`: 反馈帧解析
10//! - `control`: 控制帧构建
11//! - `config`: 配置帧处理
12//!
13//! ## 字节序
14//!
15//! 协议使用 Motorola (MSB) 高位在前(大端字节序)。
16//! 本模块提供了字节序转换工具函数。
17
18pub mod config;
19pub mod constants;
20pub mod control;
21pub mod feedback;
22pub mod ids;
23
24// 重新导出常用类型
25pub use config::*;
26pub use constants::*;
27pub use control::*;
28pub use feedback::*;
29pub use ids::*;
30
31/// CAN 2.0 标准帧的统一抽象
32///
33/// # 设计目的
34///
35/// `PiperFrame` 是协议层和硬件层之间的中间抽象,提供:
36/// - **层次解耦**:协议层不依赖底层 CAN 实现(SocketCAN/GS-USB)
37/// - **统一接口**:上层通过 `CanAdapter` trait 使用统一的帧类型
38/// - **类型安全**:编译时保证帧格式正确,避免原始字节操作错误
39///
40/// # 在架构中的位置
41///
42/// ```text
43/// Protocol Layer (piper-protocol)
44///     ↓ TryFrom<PiperFrame> 解析 / new_standard() 构建
45/// PiperFrame (此类型)
46///     ↓ 转换逻辑在 CAN 层实现
47/// CAN Layer (piper-can)
48///     ↓ SocketCAN/GS-USB 适配器
49/// Hardware
50/// ```
51///
52/// # 设计特性
53///
54/// - **Copy trait**:零成本复制,适合高频 CAN 场景(~1kHz 帧率)
55/// - **固定 8 字节**:避免堆分配,减少内存碎片
56/// - **无生命周期**:自包含数据结构,简化 API
57/// - **时间戳支持**:`timestamp_us` 字段支持录制/回放功能
58///
59/// # 限制
60///
61/// - **仅支持 CAN 2.0**:固定 8 字节数据
62/// - **不支持 CAN FD**:最长 64 字节的帧需要使用 `PiperFrameFd`(未来扩展)
63///
64/// # 转换示例
65///
66/// ```rust
67/// use piper_protocol::PiperFrame;
68///
69/// // 创建标准帧
70/// let frame = PiperFrame::new_standard(0x123, &[1, 2, 3, 4]);
71///
72/// // 创建扩展帧
73/// let frame_ext = PiperFrame::new_extended(0x12345678, &[5, 6, 7, 8]);
74///
75/// // 访问数据
76/// assert_eq!(frame.id(), 0x123);
77/// assert_eq!(frame.data_slice(), &[1, 2, 3, 4]);
78/// ```
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81pub struct PiperFrame {
82    /// CAN ID(标准帧或扩展帧)
83    pub id: u32,
84
85    /// 帧数据(固定 8 字节,未使用部分为 0)
86    pub data: [u8; 8],
87
88    /// 有效数据长度 (0-8)
89    pub len: u8,
90
91    /// 是否为扩展帧(29-bit ID)
92    pub is_extended: bool,
93
94    /// 硬件时间戳(微秒),0 表示不可用
95    pub timestamp_us: u64,
96}
97
98impl PiperFrame {
99    /// 创建标准帧
100    pub fn new_standard(id: u16, data: &[u8]) -> Self {
101        Self::new(id as u32, data, false)
102    }
103
104    /// 创建扩展帧
105    pub fn new_extended(id: u32, data: &[u8]) -> Self {
106        Self::new(id, data, true)
107    }
108
109    /// 通用构造器
110    fn new(id: u32, data: &[u8], is_extended: bool) -> Self {
111        let mut fixed_data = [0u8; 8];
112        let len = data.len().min(8);
113        fixed_data[..len].copy_from_slice(&data[..len]);
114
115        Self {
116            id,
117            data: fixed_data,
118            len: len as u8,
119            is_extended,
120            timestamp_us: 0, // 默认无时间戳
121        }
122    }
123
124    /// 获取数据切片(只包含有效数据)
125    pub fn data_slice(&self) -> &[u8] {
126        &self.data[..self.len as usize]
127    }
128
129    /// 获取 CAN ID
130    pub fn id(&self) -> u32 {
131        self.id
132    }
133
134    /// 获取完整数据(8字节固定数组)
135    pub fn data(&self) -> &[u8; 8] {
136        &self.data
137    }
138}
139
140pub mod can {
141    pub use super::PiperFrame;
142}
143
144use thiserror::Error;
145
146/// 协议解析错误类型
147#[derive(Error, Debug)]
148pub enum ProtocolError {
149    #[error("Invalid frame length: expected {expected}, got {actual}")]
150    InvalidLength { expected: usize, actual: usize },
151
152    #[error("Invalid CAN ID: 0x{id:X}")]
153    InvalidCanId { id: u32 },
154
155    #[error("Parse error: {0}")]
156    ParseError(String),
157
158    #[error("Invalid value for field {field}: {value}")]
159    InvalidValue { field: String, value: u8 },
160}
161
162/// 字节序转换工具函数
163///
164/// 协议使用 Motorola (MSB) 高位在前(大端字节序),
165/// 这些函数用于在协议层进行字节序转换。
166///
167/// 大端字节序转 i32
168pub fn bytes_to_i32_be(bytes: [u8; 4]) -> i32 {
169    i32::from_be_bytes(bytes)
170}
171
172/// 大端字节序转 i16
173pub fn bytes_to_i16_be(bytes: [u8; 2]) -> i16 {
174    i16::from_be_bytes(bytes)
175}
176
177/// i32 转大端字节序
178pub fn i32_to_bytes_be(value: i32) -> [u8; 4] {
179    value.to_be_bytes()
180}
181
182/// i16 转大端字节序
183pub fn i16_to_bytes_be(value: i16) -> [u8; 2] {
184    value.to_be_bytes()
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    #[test]
192    fn test_bytes_to_i32_be() {
193        let bytes = [0x12, 0x34, 0x56, 0x78];
194        let value = bytes_to_i32_be(bytes);
195        assert_eq!(value, 0x12345678);
196    }
197
198    #[test]
199    fn test_bytes_to_i32_be_negative() {
200        let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
201        let value = bytes_to_i32_be(bytes);
202        assert_eq!(value, -1);
203    }
204
205    #[test]
206    fn test_bytes_to_i16_be() {
207        let bytes = [0x12, 0x34];
208        let value = bytes_to_i16_be(bytes);
209        assert_eq!(value, 0x1234);
210    }
211
212    #[test]
213    fn test_bytes_to_i16_be_negative() {
214        let bytes = [0xFF, 0xFF];
215        let value = bytes_to_i16_be(bytes);
216        assert_eq!(value, -1);
217    }
218
219    #[test]
220    fn test_i32_to_bytes_be() {
221        let value = 0x12345678;
222        let bytes = i32_to_bytes_be(value);
223        assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78]);
224    }
225
226    #[test]
227    fn test_i32_to_bytes_be_negative() {
228        let value = -1;
229        let bytes = i32_to_bytes_be(value);
230        assert_eq!(bytes, [0xFF, 0xFF, 0xFF, 0xFF]);
231    }
232
233    #[test]
234    fn test_i16_to_bytes_be() {
235        let value = 0x1234;
236        let bytes = i16_to_bytes_be(value);
237        assert_eq!(bytes, [0x12, 0x34]);
238    }
239
240    #[test]
241    fn test_i16_to_bytes_be_negative() {
242        let value = -1;
243        let bytes = i16_to_bytes_be(value);
244        assert_eq!(bytes, [0xFF, 0xFF]);
245    }
246
247    #[test]
248    fn test_roundtrip_i32() {
249        let original = 0x12345678;
250        let bytes = i32_to_bytes_be(original);
251        let decoded = bytes_to_i32_be(bytes);
252        assert_eq!(original, decoded);
253    }
254
255    #[test]
256    fn test_roundtrip_i16() {
257        let original = 0x1234;
258        let bytes = i16_to_bytes_be(original);
259        let decoded = bytes_to_i16_be(bytes);
260        assert_eq!(original, decoded);
261    }
262}