Files
eskin-finger-sdk/docs/PROGRESS.md
2026-05-05 16:48:16 +08:00

347 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Eskin Finger SDK Progress
本文件记录当前代码骨架进度、已完成设计决策、已知问题和后续实现顺序。
## 当前状态
当前 SDK 已经形成如下分层:
```text
Device API
-> Stream Runtime / Channel
-> Register Access
-> Protocol Codec
-> Serial Transport
-> Hardware
```
当前 `cargo check` 可以通过,但还有 `stream.rs` 中的两个 warning 需要清理。
## 已完成
### 1. Protocol Layer
文件:`src/protocol.rs`
已完成:
- 读请求编码:`encode_read_request`
- 写请求编码:`encode_write_request`
- 读应答解码:`decode_read_response`
- 写应答解码:`decode_write_response`
- stream frame 解码入口:`decode_stream_frame`
- CRC 校验
- 设备状态码转换
- response 帧长度校验
当前协议约定:
```text
response frame = header + payload/status data + status(1B) + crc(1B)
crc 是最后 1 字节
status 是 crc 前 1 字节
```
注意:
- `FRAME_START_RESPONSE = 0xAA55`
- 当前 `device.rs``stream.rs` 读取 response 起始符时使用 `u16::from_be_bytes([header[0], header[1]])`
- 如果真实设备返回字节序为 `55 AA`,这里需要改为小端;如果真实返回 `AA 55`,当前逻辑正确
### 2. Transport Layer
文件:`src/transport.rs`
已完成:
- `SerialTransport` trait
- `SerialPortTransport`
- 串口 open/close/is_open
- write/read/flush_rx
- timeout 转换
- serialport error 到 `SdkError` 的转换
- `SharedSerialTransport = Arc<Mutex<Box<dyn SerialTransport>>>`
设计决策:
- `SerialTransport: Send`,不要求 `Sync`
- 跨线程共享通过 `Arc<Mutex<...>>` 完成
- 串口 request/response 应在同一把 mutex lock 内完成,避免多线程串帧
### 3. Device Layer
文件:`src/device.rs`
已完成:
- `DeviceState`
- `EskinDeviceInner`
- `EskinDevice` trait
- `open/close`
- `start_stream/stop_stream`
- `read_sample/read_event`
- 同步 `read_register/write_register`
- `ensure_open`
- 共享 channel`Arc<ChannelManager>`
- 共享 transport`SharedSerialTransport`
- `create_stream_runtime`
- `shared_transport`
当前行为:
- `read_register/write_register` 会:
- 检查设备状态
- protocol encode request
- lock transport
- flush rx
- write request
- read full response frame
- protocol decode response
注意:
- `device.start_stream()``StreamRuntime::start()` 当前都会发送 `StreamStarted`
- 后续需要明确 stream 状态由谁统一管理,避免重复事件
### 4. Channel Layer
文件:`src/channel.rs`
已完成:
- `DeviceCommand`
- `DeviceEvent`
- `ChannelManager`
- sample/cmd/event 三类 channel
- `send_sample/recv_sample`
- `send_cmd/recv_cmd`
- `send_event/recv_event`
- `dropped_count/reset_dropped_count`
- sample drop policy
- `DropNewest`
- `DropOldest`
设计决策:
- `dropped_samples` 只统计 sample drop不统计 command/event
- channel timeout 和 disconnected 分别映射为:
- `SdkError::Timeout`
- `SdkError::ChannelClosed`
- sample channel 满时根据 drop policy 处理,不作为硬错误
### 5. Stream Layer
文件:`src/stream.rs`
已完成:
- `StreamMode`
- `StreamConfig`
- `StreamController`
- `StreamRuntime`
- `StreamWorker`
- worker thread 生命周期:
- `start()` spawn worker
- `stop()` stop flag + join
- `SampleCollector` trait
- `NoopSampleCollector`
- `PollingSampleCollector` 骨架
- polling collector 已具备同步 `read_register` 能力
- polling collector 当前会尝试读取:
- `REG_COMBINED_FORCE`
- `REG_MODULE_ERROR`
当前行为:
```text
StreamRuntime::start()
-> make_collector()
-> spawn StreamWorker
-> worker loop
-> collector.collect_once()
-> if Some(sample), send_sample(sample)
```
当前 `PollingSampleCollector::collect_once()` 只读取 raw bytes尚未解析为 `FingerSample`,因此返回 `Ok(None)`
已知 warning
- `src/stream.rs` unused import: `transport::{self, ...}` 中的 `self`
- `StreamWorker::new` 参数 `transport` 未使用
### 6. Register Layer
文件:`src/register.rs`
已完成:
- 寄存器地址常量
- `RegisterSpec`
- `RegisterAccess`
- `RegisterValueType`
- `DEVICE_INFO_REGISTERS`
- `RegisterMap` trait
- `EskinRegisterMap`
- `parse_distribution_force`
暂未完成:
- `distribution_register`
- `parse_device_info`
- combined force 解析
- module error 解析
## 当前主要设计决策
### Transport 共享模型
当前使用:
```rust
Arc<Mutex<Box<dyn SerialTransport>>>
```
原因:
- device 和 stream worker 需要共享同一个串口
- 串口读写需要 `&mut self`
- mutex 保证一次 request/response 不被其他线程打断
长期建议:
- stream running 时,尽量由 worker 独占串口访问
- 主线程通过 command channel 请求 worker 操作设备
- 避免主线程同步 `read_register` 和 worker polling 同时抢 transport
### Stream 职责拆分
当前拆分:
```text
StreamRuntime
管理 start/stop、worker handle、对外读取 sample/event
StreamWorker
管理 loop、running flag、sleep、错误事件
SampleCollector
管理一次采集,后续负责协议读写和 sample 构建
```
这是推荐方向。worker 不应该直接塞满协议和寄存器解析逻辑。
## 明确下一步
### Step 1: 清理当前 warning
文件:`src/stream.rs`
处理:
- 删除 unused import 中的 `self`
```rust
transport::{SerialTransport, SharedSerialTransport}
```
- `StreamWorker::new` 当前参数 `transport` 未使用。二选一:
- 删除 `StreamWorker` 中的 transport 参数,因为 collector 已持有 transport
- 或者让 worker 持有 transport但不推荐职责重复
推荐:删除 `StreamWorker::new``transport` 参数。
### Step 2: 完善 Register 解析接口
文件:`src/register.rs`
新增解析函数:
- `parse_combined_forces(raw: &[u8]) -> Result<Vec<CombinedForce>, SdkError>`
- `parse_module_errors(raw: &[u8]) -> Result<Vec<ModuleError>, SdkError>`
依据当前寄存器表:
```text
REG_COMBINED_FORCE = 0x0500
长度 168B = 28 modules * 6B
每个 module = fx:i16 + fy:i16 + fz:i16
REG_MODULE_ERROR = 0x0700
长度 56B = 28 modules * 2B
每个 module = error_code:u16
```
### Step 3: 让 PollingSampleCollector 产出 FingerSample
文件:`src/stream.rs`
`PollingSampleCollector::collect_once()` 中:
```text
1. sequence = next_sequence()
2. read REG_COMBINED_FORCE
3. read REG_MODULE_ERROR
4. register parse raw bytes
5. build FingerSample
6. return Ok(Some(sample))
```
先不处理 distribution force。
### Step 4: 补 distribution force
文件:`src/register.rs``src/stream.rs`
前置条件:
- `distribution_register(module)` 能根据 `SensorModule` 返回地址和长度
- 需要确认每个 module 的分布力长度来源
实现策略:
- `StreamConfig.read_distribution == false` 时跳过
- `StreamConfig.modules` 为空时默认读所有模块,或者默认不读;需要明确语义
### Step 5: 统一 stream 状态入口
文件:`src/device.rs``src/stream.rs`
当前重复点:
- `device.start_stream()``StreamStarted`
- `StreamRuntime::start()` 也发 `StreamStarted`
需要选择一个主入口:
推荐:
```text
device.open()
let mut stream = device.create_stream_runtime()
stream.start(config)
stream.next_sample()
stream.stop()
```
如果最终 SDK 希望用户只调用 `device.start_stream()`,则 `EskinDeviceInner` 需要持有 `StreamRuntime` 或 worker handle。
### Step 6: 增加基础测试
建议先加这些测试:
- protocol encode read request golden bytes
- protocol encode write request golden bytes
- CRC 校验
- register parse combined force
- register parse module error
- ChannelManager drop policy
## 当前风险点
1. response 起始符字节序仍需真实设备帧确认。
2. stream worker 和 device 同步 read/write 共享同一 transport虽然 mutex 安全,但业务上仍可能抢响应。
3. `PollingSampleCollector` 已读取 raw bytes但还未构建 sample。
4. `register.rs``parse_device_info``distribution_register` 仍是 `todo!()`
5. `StreamStarted/StreamStopped` 事件存在重复来源,需要统一入口。