piper-can 0.0.3

CAN bus adapter abstraction layer supporting SocketCAN and GS-USB
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# piper-can

CAN 硬件抽象层,为 Piper 机械臂 SDK 提供统一的 CAN 接口。

## 概述

`piper-can` 是 Piper SDK 的底层 CAN 抽象层,提供:

- **统一的 `CanAdapter` trait**:跨平台的 CAN 接口抽象
- **多后端支持**:SocketCAN(Linux)和 GS-USB(跨平台)
- **自动平台选择**:默认通过 `auto-backend` feature 自动选择合适的后端
- **灵活的 feature 控制**:支持显式选择后端和 mock 测试模式
- **类型安全**:基于 `PiperFrame` 的强类型 CAN 帧接口

## Features

`piper-can` 使用 Cargo features 来控制后端选择和功能:

| Feature | 说明 | 默认 |
|---------|------|------|
| `auto-backend` | 根据平台自动选择后端(Linux: SocketCAN + GS-USB,其他: GS-USB) ||
| `socketcan` | 强制启用 SocketCAN(仅 Linux) | - |
| `gs_usb` | 强制启用 GS-USB(所有平台) | - |
| `mock` | 禁用所有硬件依赖(用于 CI 测试) | - |
| `serde` | 启用 Serde 序列化支持 | - |

### Feature 组合示例

| Feature 组合 | 行为 | 使用场景 |
|------------|------|---------|
| `default` | Linux: SocketCAN + GS-USB<br>其他: GS-USB | 生产环境(推荐) |
| `gs_usb`, `default-features = false` | 仅 GS-USB | 交叉平台测试 |
| `socketcan`, `gs_usb` | Linux: 两者都启用<br>其他: 编译失败(仅 Linux) | 高级用例 |
| `mock`, `default-features = false` | 仅 Mock Adapter | CI 测试 |

### Feature 优先级

**Mock 优先级最高**:
- `mock` feature 会禁用所有硬件依赖(socketcan 和 gs_usb)
- 即使同时启用 `auto-backend``mock`,也只会编译 Mock Adapter
- 用于 CI 测试和无硬件的开发环境

**显式 Feature 优先于自动推导**:
- 用户显式指定的 features(如 `socketcan`)优先于 `auto-backend`
- 例如:`features = ["auto-backend", "socketcan"]` 等同于只启用 `socketcan`

**优先级顺序**:
```
mock > 显式 features (socketcan, gs_usb) > auto-backend
```

### ⚠️ 平台限制

**`socketcan` feature 仅在 Linux 上可用**:

```toml
# ❌ 错误:在 Windows/macOS 上启用 socketcan 会失败
piper-can = { features = ["socketcan"] }  # 编译失败(nix crate 不支持)

# ✅ 正确做法:依赖 auto-backend 的自动选择
piper-can = "0.0.3"  # 自动选择平台合适的后端
```

## 平台支持

### Linux

- **默认后端**:SocketCAN(通过 `cfg(target_os = "linux")` 自动启用)
- **可选后端**:GS-USB(可通过 `PiperBuilder` 在运行时切换)
- **特性**  - 内核级性能,支持硬件时间戳
  - 通过 `ip link` 工具配置接口和波特率
  - 支持标准帧和扩展帧

**使用示例**:
```rust
use piper_can::SocketCanAdapter;

// 打开 can0 接口
let mut adapter = SocketCanAdapter::new("can0")?;

// 发送帧
let frame = PiperFrame::new_standard(0x123, &[1, 2, 3, 4]);
adapter.send(frame)?;

// 接收帧
let rx_frame = adapter.receive()?;
```

### macOS / Windows

- **唯一后端**:GS-USB(通过 `cfg(not(target_os = "linux"))` 自动启用)
- **特性**  - 通过 USB 连接的 CAN 适配器(如 candleLight)
  - 跨平台支持(Linux/macOS/Windows)
  - 应用层配置波特率和模式

**使用示例**:
```rust
use piper_can::GsUsbCanAdapter;

// 自动打开第一个可用的 GS-USB 设备
let mut adapter = GsUsbCanAdapter::new()?;

// 或通过序列号指定设备
let mut adapter = GsUsbCanAdapter::new_with_serial("ABC123456")?;

// 配置波特率并启动
adapter.configure(1_000_000)?;

// 发送帧
let frame = PiperFrame::new_standard(0x123, &[1, 2, 3, 4]);
adapter.send(frame)?;

// 接收帧
let rx_frame = adapter.receive()?;
```

## 架构设计

### PiperFrame:通用 CAN 帧抽象

`PiperFrame` 是贯穿所有 SDK 层次的中央数据结构:

```rust
pub struct PiperFrame {
    pub id: u32,           // CAN ID(标准帧 11 位或扩展帧 29 位)
    pub data: [u8; 8],     // 数据载荷(最多 8 字节)
    pub len: u8,           // 实际数据长度
    pub is_extended: bool, // 是否为扩展帧
    pub timestamp_us: u64, // 硬件时间戳(微秒)
}
```

**特性**:
- **零成本抽象**`Copy` trait,固定大小数组,无堆分配
- **类型安全**:编译时检查帧格式
- **时间戳支持**:保留硬件时间戳,用于力控和时间敏感应用

### CanAdapter Trait

所有 CAN 适配器实现的统一接口:

```rust
pub trait CanAdapter {
    fn send(&mut self, frame: PiperFrame) -> Result<(), CanError>;
    fn receive(&mut self) -> Result<PiperFrame, CanError>;
    fn set_receive_timeout(&mut self, timeout: Duration);
    // ... 更多方法
}
```

**支持的适配器**:
- `SocketCanAdapter`:Linux SocketCAN 接口
- `GsUsbCanAdapter`:GS-USB 设备(跨平台)
- `GsUsbUdpAdapter`:GS-USB 守护进程客户端(网络)

### 分离适配器(Splittable Adapter)

对于需要高并发的场景,支持将适配器分离为独立的 RX 和 TX 部分:

```rust
use piper_can::SplittableAdapter;

// 分离为 RX 和 TX 适配器
let (rx_adapter, tx_adapter) = adapter.split()?;

// 可以在不同线程中并发使用
std::thread::spawn(move || {
    loop {
        let frame = rx_adapter.receive()?;
        // 处理接收
    }
});

// TX 线程
for frame in frames {
    tx_adapter.send(frame)?;
}
```

## Features

### `serde`(可选)

启用 `PiperFrame` 的 Serde 序列化支持,用于帧录制和回放:

```toml
# Cargo.toml
piper-can = { version = "0.0.3", features = ["serde"] }
```

**使用示例**:
```rust
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct CanFrameLog {
    timestamp: u64,
    frame: PiperFrame,  // 需要 serde feature
}
```

## 平台自动选择

`piper-can` 使用 **混合模式**(`target_cfg` + features)自动选择平台合适的后端:

| 目标平台 | 默认启用的后端 | 可用的后端 |
|---------|--------------|-----------|
| Linux | SocketCAN + GS-USB | SocketCAN, GS-USB, Mock |
| macOS | GS-USB | GS-USB, Mock |
| Windows | GS-USB | GS-USB, Mock |

### 使用默认配置

推荐使用默认配置(`auto-backend` feature),无需手动指定:

```toml
# ✅ 推荐:使用默认配置
[dependencies]
piper-can = "0.0.3"
```

### 显式选择后端

如果需要禁用某些后端或启用特定功能,可以手动配置 features:

```toml
# 只使用 GS-USB(移除 SocketCAN 依赖)
[dependencies]
piper-can = { version = "0.0.3", features = ["gs_usb"], default-features = false }

# CI 测试(无硬件依赖)
[dependencies]
piper-can = { version = "0.0.3", features = ["mock"], default-features = false }

# 启用 Serde 序列化
[dependencies]
piper-can = { version = "0.0.3", features = ["serde"] }
```

### 运行时后端选择

使用 `PiperBuilder` 在运行时选择后端:

```rust
use piper_driver::{PiperBuilder, DriverType};

// 自动选择(Linux 优先 SocketCAN)
let piper = PiperBuilder::new()
    .build()?;

// 强制使用 GS-USB(所有平台)
let piper = PiperBuilder::new()
    .with_driver_type(DriverType::GsUsb)
    .build()?;

// 强制使用 SocketCAN(仅 Linux)
let piper = PiperBuilder::new()
    .with_driver_type(DriverType::SocketCan)
    .build()?;
```

## Mock Adapter(测试模式)

`mock` feature 提供无硬件依赖的 `MockCanAdapter`,用于 CI 测试和单元测试。

### 启用 Mock 模式

```toml
[dependencies]
piper-can = { version = "0.0.3", features = ["mock"], default-features = false }
```

### 使用示例

```rust
use piper_can::{MockCanAdapter, CanAdapter, PiperFrame};

let mut adapter = MockCanAdapter::new();

// 注入测试帧
let frame = PiperFrame::new_standard(0x123, &[1, 2, 3, 4]);
adapter.inject(frame.clone())?;

// 接收帧
let rx_frame = adapter.receive()?;
assert_eq!(rx_frame.id, 0x123);

// 回环模式:发送的帧自动进入接收队列
adapter.send(frame)?;
let rx_frame2 = adapter.receive()?;
assert_eq!(rx_frame2.id, 0x123);
```

### Mock 特性

- **回环模式**:发送的帧自动进入接收队列
- **零延迟**:所有操作立即完成
- **FIFO 队列**:模拟 CAN 总线的帧顺序
- **超时模拟**:支持测试超时逻辑

### CI 测试示例

```yaml
# .github/workflows/test.yml
test-mock:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
    - uses: dtolnay/rust-toolchain@stable
    - name: Test mock backend
      run: cargo test --package piper-can --features mock,default-features=false
```

## 架构设计

### PiperFrame:通用 CAN 帧抽象

`PiperFrame` 是贯穿所有 SDK 层次的中央数据结构:

```rust
pub struct PiperFrame {
    pub id: u32,           // CAN ID(标准帧 11 位或扩展帧 29 位)
    pub data: [u8; 8],     // 数据载荷(最多 8 字节)
    pub len: u8,           // 实际数据长度
    pub is_extended: bool, // 是否为扩展帧
    pub timestamp_us: u64, // 硬件时间戳(微秒)
}
```

**特性**:
- **零成本抽象**`Copy` trait,固定大小数组,无堆分配
- **类型安全**:编译时检查帧格式
- **时间戳支持**:保留硬件时间戳,用于力控和时间敏感应用

### CanAdapter Trait

所有 CAN 适配器实现的统一接口,包括 `SocketCanAdapter`、`GsUsbCanAdapter` 和 `MockCanAdapter`:

```rust
pub trait CanAdapter {
    fn send(&mut self, frame: PiperFrame) -> Result<(), CanError>;
    fn receive(&mut self) -> Result<PiperFrame, CanError>;
    fn set_receive_timeout(&mut self, timeout: Duration);
    // ... 更多方法
}
```

## 错误处理

`piper-can` 提供结构化的错误类型:

```rust
use piper_can::{CanError, CanDeviceError, CanDeviceErrorKind};

match adapter.receive() {
    Ok(frame) => /* 处理帧 */,
    Err(CanError::Timeout) => /* 超时,可重试 */,
    Err(CanError::Device(err)) => {
        match err.kind {
            CanDeviceErrorKind::NotFound => /* 设备未找到 */,
            CanDeviceErrorKind::AccessDenied => /* 权限不足 */,
            CanDeviceErrorKind::Busy => /* 设备忙碌 */,
            _ => /* 其他错误 */,
        }
    },
    Err(e) => /* 其他错误 */,
}
```

## 权限要求

### Linux

- **SocketCAN**:通常需要 `dialout` 组权限或 `sudo`
- **GS-USB**:需要 udev 规则或 `sudo`

**安装 udev 规则**(推荐):
```bash
sudo cp scripts/99-piper-gs-usb.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
```

### macOS / Windows

- GS-USB:可能需要管理员权限(首次连接时安装驱动)

## 性能特性

### SocketCAN(Linux)

- **零拷贝**:内核级 CAN 处理
- **硬件时间戳**:支持硬件时间戳(微秒级精度)
- **高效轮询**:使用 `poll` + `recvmsg` 实现非阻塞接收

### GS-USB(跨平台)

- **批量读取**:一次 USB 传输可包含多个 CAN 帧
- **接收队列**:缓存 USB 包中的多帧,避免丢包
- **实时模式**:可配置写超时,实现快速失败(力控场景)

## 相关文档

- [架构概述]../../docs/v0/README.md(CLAUDE.md)
- [GS-USB 实现对比]../../docs/v0/gs_usb/gs_usb_implementation_comparison.md
- [硬件时间戳实现]../../docs/v0/gs_usb/hardware_timestamp_implementation_plan.md
- [Position Control 用户指南]../../docs/v0/position_control_user_guide.md

## License

MIT OR Apache-2.0