piper_sdk/can/
mod.rs

1//! CAN 适配层核心定义
2//!
3//! 提供统一的 CAN 接口抽象,支持 SocketCAN(Linux)和 GS-USB(Linux/macOS/Windows)两种后端。
4
5use std::time::Duration;
6use thiserror::Error;
7
8#[cfg(target_os = "linux")]
9pub mod socketcan;
10
11#[cfg(target_os = "linux")]
12pub use socketcan::SocketCanAdapter;
13
14#[cfg(target_os = "linux")]
15pub use socketcan::split::{SocketCanRxAdapter, SocketCanTxAdapter};
16
17pub mod gs_usb;
18
19// Re-export gs_usb 类型
20pub use gs_usb::GsUsbCanAdapter;
21
22// GS-USB 守护进程客户端库(UDS/UDP)
23pub mod gs_usb_udp;
24
25// 导出 split 相关的类型(如果可用)
26pub use gs_usb::split::{GsUsbRxAdapter, GsUsbTxAdapter};
27
28/// SDK 通用的 CAN 帧定义(只针对 CAN 2.0)
29///
30/// 设计要点:
31/// - Copy trait:零成本复制,适合高频场景
32/// - 固定 8 字节数据:避免堆分配
33/// - 无生命周期:简化 API
34/// - 支持硬件时间戳:用于精确的时间测量(实时控制场景)
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct PiperFrame {
37    /// CAN ID(标准帧或扩展帧)
38    pub id: u32,
39
40    /// 帧数据(固定 8 字节,未使用部分为 0)
41    pub data: [u8; 8],
42
43    /// 有效数据长度 (0-8)
44    pub len: u8,
45
46    /// 是否为扩展帧(29-bit ID)
47    pub is_extended: bool,
48
49    /// 硬件时间戳(微秒),0 表示不可用
50    ///
51    /// 当启用硬件时间戳模式(GS_CAN_MODE_HW_TIMESTAMP)时,
52    /// 此字段包含设备硬件提供的时间戳,用于精确测量帧收发时间。
53    /// 对于力控机械臂等实时控制系统,这是关键信息。
54    ///
55    /// **类型说明**:使用 `u64` 而非 `u32`,原因:
56    /// - 支持绝对时间戳(Unix 纪元开始),无需基准时间管理
57    /// - 支持相对时间戳(从适配器启动开始),可覆盖更长的时间范围(584,000+ 年)
58    /// - 与状态层设计一致(`JointPositionState.hardware_timestamp_us: u64`)
59    /// - 内存对齐后大小相同(24 字节),无额外开销
60    pub timestamp_us: u64,
61}
62
63impl PiperFrame {
64    /// 创建标准帧
65    pub fn new_standard(id: u16, data: &[u8]) -> Self {
66        Self::new(id as u32, data, false)
67    }
68
69    /// 创建扩展帧
70    pub fn new_extended(id: u32, data: &[u8]) -> Self {
71        Self::new(id, data, true)
72    }
73
74    /// 通用构造器
75    fn new(id: u32, data: &[u8], is_extended: bool) -> Self {
76        let mut fixed_data = [0u8; 8];
77        let len = data.len().min(8);
78        fixed_data[..len].copy_from_slice(&data[..len]);
79
80        Self {
81            id,
82            data: fixed_data,
83            len: len as u8,
84            is_extended,
85            timestamp_us: 0, // 默认无时间戳
86        }
87    }
88
89    /// 获取数据切片(只包含有效数据)
90    pub fn data_slice(&self) -> &[u8] {
91        &self.data[..self.len as usize]
92    }
93}
94
95/// CAN 适配层统一错误类型
96#[derive(Error, Debug)]
97pub enum CanError {
98    /// USB/IO 底层错误
99    #[error("IO Error: {0}")]
100    Io(#[from] std::io::Error),
101
102    /// 设备相关错误(设备未找到、未启动、配置失败等)
103    #[error("Device Error: {0}")]
104    Device(#[from] CanDeviceError),
105
106    /// 读取超时(非致命,可以重试)
107    #[error("Read timeout")]
108    Timeout,
109
110    /// 缓冲区溢出(致命错误)
111    #[error("Buffer overflow")]
112    BufferOverflow,
113
114    /// 总线关闭(致命错误,需要重启)
115    #[error("Bus off")]
116    BusOff,
117
118    /// 设备未启动
119    #[error("Device not started")]
120    NotStarted,
121}
122
123/// 设备/后端错误的结构化分类(不绑定具体后端实现)
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum CanDeviceErrorKind {
126    Unknown,
127    /// 设备未找到/不存在(热拔插或枚举不到)
128    NotFound,
129    /// 设备已断开
130    NoDevice,
131    /// 权限不足/被拒绝
132    AccessDenied,
133    /// 资源忙/被占用
134    Busy,
135    /// 不支持的波特率/配置
136    UnsupportedConfig,
137    /// 设备返回无效响应
138    InvalidResponse,
139    /// 解析到无效帧
140    InvalidFrame,
141    /// 其他 IO/后端错误
142    Backend,
143}
144
145/// 结构化设备错误:kind + message(保留人类可读信息,供日志/上层策略判断)
146#[derive(Error, Debug, Clone)]
147#[error("{kind:?}: {message}")]
148pub struct CanDeviceError {
149    pub kind: CanDeviceErrorKind,
150    pub message: String,
151}
152
153impl CanDeviceError {
154    pub fn new(kind: CanDeviceErrorKind, message: impl Into<String>) -> Self {
155        Self {
156            kind,
157            message: message.into(),
158        }
159    }
160
161    /// 判断是否为致命错误
162    ///
163    /// 致命错误表示设备已不可用,需要重新初始化或停止操作。
164    /// 非致命错误可以重试或忽略。
165    ///
166    /// # 返回
167    /// - `true`:致命错误(设备不可用)
168    /// - `false`:非致命错误(可以重试)
169    pub fn is_fatal(&self) -> bool {
170        matches!(
171            self.kind,
172            CanDeviceErrorKind::NoDevice
173                | CanDeviceErrorKind::AccessDenied
174                | CanDeviceErrorKind::NotFound
175        )
176    }
177}
178
179impl From<String> for CanDeviceError {
180    fn from(message: String) -> Self {
181        Self::new(CanDeviceErrorKind::Unknown, message)
182    }
183}
184
185impl From<&str> for CanDeviceError {
186    fn from(message: &str) -> Self {
187        Self::new(CanDeviceErrorKind::Unknown, message)
188    }
189}
190
191/// CAN 适配器 Trait
192///
193/// 语义:
194/// - `send()`: Fire-and-Forget,USB 写入成功即返回
195/// - `receive()`: 阻塞直到收到有效数据帧或超时
196///
197/// 增加了超时和非阻塞方法,提高 API 一致性和灵活性。
198pub trait CanAdapter {
199    /// 发送一帧
200    ///
201    /// # 语义
202    /// - **Fire-and-Forget**:将帧放入发送缓冲区即返回
203    /// - **不等待 Echo**:不阻塞等待 USB echo 确认
204    /// - **返回条件**:USB Bulk OUT 写入成功
205    ///
206    /// # 错误处理
207    /// - 设备未启动 → `CanError::NotStarted`
208    /// - USB 写入失败 → `CanError::Io` 或 `CanError::Device`
209    fn send(&mut self, frame: PiperFrame) -> Result<(), CanError>;
210
211    /// 接收一帧
212    ///
213    /// # 语义
214    /// - **阻塞读取**:直到收到有效数据帧或超时
215    /// - **自动过滤**:内部过滤 Echo 帧和瞬态错误
216    /// - **只返回有效数据**:过滤后的 CAN 总线数据
217    ///
218    /// # 错误处理
219    /// - 超时 → `CanError::Timeout`(可重试)
220    /// - 缓冲区溢出 → `CanError::BufferOverflow`(致命)
221    /// - 总线关闭 → `CanError::BusOff`(致命)
222    /// - 设备未启动 → `CanError::NotStarted`
223    fn receive(&mut self) -> Result<PiperFrame, CanError>;
224
225    /// 设置接收超时
226    ///
227    /// 设置后续 `receive()` 调用的超时时间。
228    /// 如果适配器不支持动态设置超时,此方法可能无效。
229    ///
230    /// # 参数
231    /// - `timeout`: 超时时间
232    ///
233    /// # 默认实现
234    /// 默认实现为空操作(no-op),适配器可以使用默认超时或初始化时设置的超时。
235    fn set_receive_timeout(&mut self, _timeout: Duration) {
236        // 默认实现:空操作
237        // 具体适配器可以覆盖此方法以实现动态超时设置
238    }
239
240    /// 带超时的接收
241    ///
242    /// 使用指定的超时时间接收一帧,不影响后续 `receive()` 调用的超时设置。
243    ///
244    /// # 参数
245    /// - `timeout`: 超时时间
246    ///
247    /// # 返回
248    /// - `Ok(frame)`: 成功接收到帧
249    /// - `Err(CanError::Timeout)`: 超时
250    /// - `Err(e)`: 其他错误
251    ///
252    /// # 默认实现
253    /// 默认实现临时设置超时,调用 `receive()`,然后恢复原超时。
254    /// 如果适配器不支持动态超时,使用默认超时。
255    fn receive_timeout(&mut self, timeout: Duration) -> Result<PiperFrame, CanError> {
256        // 默认实现:临时设置超时,调用 receive,然后恢复
257        // 注意:这个实现可能不够精确,具体适配器应该覆盖此方法
258        self.set_receive_timeout(timeout);
259        // 恢复原超时(如果适配器支持)
260        // 注意:这里无法恢复,因为不知道原超时值
261        // 具体适配器应该覆盖此方法以实现精确的超时控制
262        self.receive()
263    }
264
265    /// 非阻塞接收
266    ///
267    /// 尝试接收一帧,如果当前没有可用数据,立即返回 `Ok(None)`。
268    ///
269    /// # 返回
270    /// - `Ok(Some(frame))`: 成功接收到帧
271    /// - `Ok(None)`: 当前没有可用数据(非阻塞)
272    /// - `Err(e)`: 错误(设备错误、总线错误等)
273    ///
274    /// # 默认实现
275    /// 默认实现使用 `receive_timeout(Duration::ZERO)` 模拟非阻塞行为。
276    /// 如果适配器支持真正的非阻塞模式,应该覆盖此方法。
277    fn try_receive(&mut self) -> Result<Option<PiperFrame>, CanError> {
278        // 默认实现:使用零超时模拟非阻塞
279        match self.receive_timeout(Duration::ZERO) {
280            Ok(frame) => Ok(Some(frame)),
281            Err(CanError::Timeout) => Ok(None),
282            Err(e) => Err(e),
283        }
284    }
285
286    /// 带超时的发送
287    ///
288    /// 使用指定的超时时间发送一帧。
289    ///
290    /// # 参数
291    /// - `frame`: 要发送的帧
292    /// - `timeout`: 超时时间
293    ///
294    /// # 返回
295    /// - `Ok(())`: 成功发送
296    /// - `Err(CanError::Timeout)`: 超时(发送缓冲区满或设备无响应)
297    /// - `Err(e)`: 其他错误
298    ///
299    /// # 默认实现
300    /// 默认实现直接调用 `send()`,忽略超时参数。
301    /// 如果适配器支持发送超时,应该覆盖此方法。
302    fn send_timeout(&mut self, frame: PiperFrame, _timeout: Duration) -> Result<(), CanError> {
303        // 默认实现:直接调用 send,忽略超时
304        // 具体适配器可以覆盖此方法以实现发送超时
305        self.send(frame)
306    }
307}
308
309/// RX 适配器 Trait(用于双线程模式)
310///
311/// 只读适配器,专门用于接收 CAN 帧。
312/// 在双线程模式下,RX 线程使用此 trait 接收反馈帧。
313pub trait RxAdapter {
314    /// 接收一帧
315    ///
316    /// # 语义
317    /// - **阻塞读取**:直到收到有效数据帧或超时
318    /// - **自动过滤**:内部过滤 Echo 帧和瞬态错误
319    /// - **只返回有效数据**:过滤后的 CAN 总线数据
320    ///
321    /// # 错误处理
322    /// - 超时 → `CanError::Timeout`(可重试)
323    /// - 缓冲区溢出 → `CanError::BufferOverflow`(致命)
324    /// - 总线关闭 → `CanError::BusOff`(致命)
325    fn receive(&mut self) -> Result<PiperFrame, CanError>;
326}
327
328/// TX 适配器 Trait(用于双线程模式)
329///
330/// 只写适配器,专门用于发送 CAN 帧。
331/// 在双线程模式下,TX 线程使用此 trait 发送控制命令。
332pub trait TxAdapter {
333    /// 发送一帧
334    ///
335    /// # 语义
336    /// - **Fire-and-Forget**:将帧放入发送缓冲区即返回
337    /// - **不等待 Echo**:不阻塞等待 USB echo 确认
338    /// - **返回条件**:USB Bulk OUT 写入成功
339    ///
340    /// # 错误处理
341    /// - USB 写入失败 → `CanError::Io` 或 `CanError::Device`
342    fn send(&mut self, frame: PiperFrame) -> Result<(), CanError>;
343}
344
345/// 可分离适配器 Trait
346///
347/// 支持将适配器分离为独立的 RX 和 TX 适配器,实现双线程并发访问。
348///
349/// # 使用场景
350/// - 双线程 IO 架构:RX 和 TX 线程物理隔离,避免相互阻塞
351/// - 实时控制:RX 线程不受 TX 阻塞影响,保证状态更新及时性
352///
353/// # 实现要求
354/// - 设备必须已启动(`started == true`)才能分离
355/// - 分离后,原适配器不再可用(消费 `self`)
356/// - RX 和 TX 适配器可以在不同线程中并发使用
357pub trait SplittableAdapter: CanAdapter {
358    /// RX 适配器类型
359    type RxAdapter: RxAdapter;
360
361    /// TX 适配器类型
362    type TxAdapter: TxAdapter;
363
364    /// 分离为独立的 RX 和 TX 适配器
365    ///
366    /// # 前置条件
367    /// - 设备必须已启动
368    ///
369    /// # 返回
370    /// - `Ok((rx_adapter, tx_adapter))`:成功分离
371    /// - `Err(CanError::NotStarted)`:设备未启动
372    ///
373    /// # 注意
374    /// 此方法会消费 `self`,分离后不能再使用原适配器。
375    fn split(self) -> Result<(Self::RxAdapter, Self::TxAdapter), CanError>;
376}
377
378#[cfg(test)]
379mod tests {
380    use super::*;
381
382    #[test]
383    fn test_can_device_error_is_fatal() {
384        // 测试致命错误
385        let fatal_errors = vec![
386            CanDeviceError::new(CanDeviceErrorKind::NoDevice, "Device not found"),
387            CanDeviceError::new(CanDeviceErrorKind::AccessDenied, "Access denied"),
388            CanDeviceError::new(CanDeviceErrorKind::NotFound, "Device not found"),
389        ];
390
391        for error in fatal_errors {
392            assert!(error.is_fatal(), "Error should be fatal: {:?}", error);
393        }
394
395        // 测试非致命错误
396        let non_fatal_errors = vec![
397            CanDeviceError::new(CanDeviceErrorKind::Backend, "Backend error"),
398            CanDeviceError::new(CanDeviceErrorKind::Busy, "Device busy"),
399            CanDeviceError::new(CanDeviceErrorKind::InvalidFrame, "Invalid frame"),
400            CanDeviceError::new(CanDeviceErrorKind::InvalidResponse, "Invalid response"),
401            CanDeviceError::new(CanDeviceErrorKind::UnsupportedConfig, "Unsupported config"),
402            CanDeviceError::new(CanDeviceErrorKind::Unknown, "Unknown error"),
403        ];
404
405        for error in non_fatal_errors {
406            assert!(!error.is_fatal(), "Error should not be fatal: {:?}", error);
407        }
408    }
409
410    #[test]
411    fn test_piper_frame_new_standard() {
412        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
413        let frame = PiperFrame::new_standard(0x123, &data[..4]);
414
415        assert_eq!(frame.id, 0x123);
416        assert_eq!(frame.len, 4);
417        assert_eq!(frame.data[..4], data[..4]);
418        assert!(!frame.is_extended);
419    }
420
421    #[test]
422    fn test_piper_frame_new_extended() {
423        let data = [0xFF; 8];
424        let frame = PiperFrame::new_extended(0x12345678, &data);
425
426        assert_eq!(frame.id, 0x12345678);
427        assert_eq!(frame.len, 8);
428        assert!(frame.is_extended);
429    }
430
431    #[test]
432    fn test_piper_frame_data_truncation() {
433        // 超过 8 字节的数据应该被截断
434        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A];
435        let frame = PiperFrame::new_standard(0x123, &data);
436
437        assert_eq!(frame.len, 8); // 应该截断到 8
438        assert_eq!(frame.data[7], 0x08);
439    }
440
441    #[test]
442    fn test_piper_frame_data_slice() {
443        let data = [0x01, 0x02, 0x03];
444        let frame = PiperFrame::new_standard(0x123, &data);
445
446        let slice = frame.data_slice();
447        assert_eq!(slice.len(), 3);
448        assert_eq!(slice, &[0x01, 0x02, 0x03]);
449    }
450
451    #[test]
452    fn test_piper_frame_copy_trait() {
453        // 验证 Copy trait(零成本复制)
454        let frame1 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
455        let frame2 = frame1; // 应该复制,不是移动
456
457        assert_eq!(frame1.id, frame2.id); // frame1 仍然可用
458    }
459
460    #[test]
461    fn test_can_error_display() {
462        let err = CanError::Timeout;
463        assert!(err.to_string().contains("timeout"));
464    }
465
466    #[test]
467    fn test_can_error_from_io_error() {
468        let io_err = std::io::Error::new(std::io::ErrorKind::TimedOut, "test");
469        let can_err: CanError = io_err.into();
470
471        match can_err {
472            CanError::Io(_) => {},
473            _ => panic!("Expected Io variant"),
474        }
475    }
476
477    // Mock 实现用于测试 trait 定义
478    struct MockCanAdapter {
479        sent_frames: Vec<PiperFrame>,
480        received_frames: Vec<PiperFrame>,
481        receive_index: usize,
482    }
483
484    impl CanAdapter for MockCanAdapter {
485        fn send(&mut self, frame: PiperFrame) -> Result<(), CanError> {
486            self.sent_frames.push(frame);
487            Ok(())
488        }
489
490        fn receive(&mut self) -> Result<PiperFrame, CanError> {
491            if self.receive_index < self.received_frames.len() {
492                let frame = self.received_frames[self.receive_index];
493                self.receive_index += 1;
494                Ok(frame)
495            } else {
496                Err(CanError::Timeout)
497            }
498        }
499    }
500
501    #[test]
502    fn test_can_adapter_send() {
503        let mut adapter = MockCanAdapter {
504            sent_frames: Vec::new(),
505            received_frames: Vec::new(),
506            receive_index: 0,
507        };
508
509        let frame = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
510        adapter.send(frame).unwrap();
511
512        assert_eq!(adapter.sent_frames.len(), 1);
513        assert_eq!(adapter.sent_frames[0].id, 0x123);
514    }
515
516    #[test]
517    fn test_piper_frame_timestamp_default() {
518        let frame = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
519        assert_eq!(frame.timestamp_us, 0); // 默认无时间戳
520    }
521
522    #[test]
523    fn test_piper_frame_timestamp_preservation() {
524        // 测试时间戳字段的保留(通过手动构造)
525        let frame = PiperFrame {
526            id: 0x123,
527            data: [0x01, 0x02, 0, 0, 0, 0, 0, 0],
528            len: 2,
529            is_extended: false,
530            timestamp_us: 12345,
531        };
532        assert_eq!(frame.timestamp_us, 12345);
533    }
534
535    #[test]
536    fn test_piper_frame_eq_trait() {
537        let frame1 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
538        let frame2 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
539        assert_eq!(frame1, frame2); // 相同内容应该相等
540
541        // 时间戳不同时,应该不相等(PartialEq 会比较所有字段)
542        let frame3 = PiperFrame {
543            timestamp_us: 1000,
544            ..frame1
545        };
546        assert_ne!(frame1, frame3);
547    }
548
549    #[test]
550    fn test_piper_frame_with_timestamp() {
551        let frame = PiperFrame {
552            id: 0x123,
553            data: [0x01, 0x02, 0x03, 0x04, 0, 0, 0, 0],
554            len: 4,
555            is_extended: false,
556            timestamp_us: 12345678, // 12.345678 秒
557        };
558
559        assert_eq!(frame.id, 0x123);
560        assert_eq!(frame.len, 4);
561        assert_eq!(frame.timestamp_us, 12345678);
562    }
563
564    #[test]
565    fn test_piper_frame_empty_data() {
566        let frame = PiperFrame::new_standard(0x123, &[]);
567        assert_eq!(frame.len, 0);
568        assert_eq!(frame.data, [0u8; 8]);
569        assert_eq!(frame.timestamp_us, 0);
570    }
571
572    #[test]
573    fn test_piper_frame_data_slice_empty() {
574        let frame = PiperFrame::new_standard(0x123, &[]);
575        let slice = frame.data_slice();
576        assert_eq!(slice.len(), 0);
577    }
578
579    #[test]
580    fn test_piper_frame_data_slice_full() {
581        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
582        let frame = PiperFrame::new_standard(0x123, &data);
583        let slice = frame.data_slice();
584        assert_eq!(slice.len(), 8);
585        assert_eq!(slice, &data);
586    }
587
588    #[test]
589    fn test_can_adapter_receive() {
590        let frame1 = PiperFrame::new_standard(0x123, &[0x01, 0x02]);
591        let frame2 = PiperFrame::new_extended(0x456, &[0x03, 0x04]);
592
593        let mut adapter = MockCanAdapter {
594            sent_frames: Vec::new(),
595            received_frames: vec![frame1, frame2],
596            receive_index: 0,
597        };
598
599        let rx_frame1 = adapter.receive().unwrap();
600        assert_eq!(rx_frame1.id, 0x123);
601
602        let rx_frame2 = adapter.receive().unwrap();
603        assert_eq!(rx_frame2.id, 0x456);
604
605        // 第三次接收应该超时
606        assert!(adapter.receive().is_err());
607    }
608
609    #[test]
610    fn test_can_error_variants() {
611        // 测试所有错误变体
612        let timeout = CanError::Timeout;
613        assert!(timeout.to_string().to_lowercase().contains("timeout"));
614
615        let buffer_overflow = CanError::BufferOverflow;
616        assert!(buffer_overflow.to_string().to_lowercase().contains("overflow"));
617
618        let bus_off = CanError::BusOff;
619        assert!(bus_off.to_string().to_lowercase().contains("bus"));
620
621        let not_started = CanError::NotStarted;
622        assert!(not_started.to_string().to_lowercase().contains("start"));
623
624        let device = CanError::Device("test error".into());
625        assert!(device.to_string().contains("test error"));
626    }
627}