Files
eskin-finger-sdk/docs/ARCHITECTURE.md
Lenn Louis 79f4055959 update
2026-05-04 22:42:00 +08:00

55 KiB
Raw Blame History

Eskin Finger SDK — 架构设计文档

1. 概述

Eskin Finger SDK 是一款高性能压敏薄膜传感器数据采集 SDK用于采集手套式压力传感器阵列的分布力数据五指 + 掌心共 28 个传感器模组)。采用三层架构设计:

┌─────────────────────────────────────────────────┐
│              C++ Wrapper (RAII + Callback)       │
│          cpp/include/eskin_finger_sdk.hpp        │
│          cpp/src/eskin_finger_sdk.cpp            │
├─────────────────────────────────────────────────┤
│              C ABI Layer (FFI)                   │
│          include/eskin_finger_sdk.h              │
│          src/ffi/c_api.rs                        │
├─────────────────────────────────────────────────┤
│              Rust Core (Channel + Thread)        │
│          src/lib.rs, device.rs, stream.rs        │
│          src/channel.rs, types.rs, error.rs      │
│          src/protocol.rs, src/transport.rs       │
└─────────────────────────────────────────────────┘
  • Rust CoreUART 通信、协议解析、数据采集线程、Channel 通信、错误管理
  • C ABI Layer:通过 #[repr(C)]extern "C" 提供稳定的 FFI 接口
  • C++ WrapperRAII 封装、异常处理、回调机制

2. 通信协议概述

2.1 UART 物理层

参数
波特率 115200
数据位 8 bit
停止位 1 位
校验位 NONE
通信模式 请求-应答(主从模式)

2.2 帧结构

读请求帧

┌────────┬──────────┬────────────┬────────┬────────┬──────────┬──────────────┬───────────┬────────┐
│ 起始符 │ 数据长度  │  设备地址   │  预留  │功能码   │ 起始地址  │ 读取数据长度  │  CRC-8   │        │
│ 0x55AA │  2B(LE)  │   1B       │  0x00  │ 0xFB   │  4B(LE)  │   2B(LE)     │   1B     │        │
├────────┼──────────┼────────────┼────────┼────────┼──────────┼──────────────┼──────────┼────────┤
│data[0-1]│data[2-3]│  data[4]   │data[5] │data[6] │data[7-10]│ data[11-12]  │ data[13] │        │
└────────┴──────────┴────────────┴────────┴────────┴──────────┴──────────────┴──────────┘

读应答帧

┌────────┬──────────┬────────────┬────────┬────────────┬──────────┬───────────┬──────────┬────────┬────────┐
│ 起始符 │ 数据长度  │  设备地址   │  预留  │  功能码     │ 起始地址  │ 读取数据   │  CRC-8   │  状态  │        │
│ 0xAA55 │  2B(LE)  │   1B       │  0x00  │ 0x80+func  │  4B(LE)  │  2B(LE)   │   1B     │  1B   │        │
├────────┼──────────┼────────────┼────────┼────────────┼──────────┼───────────┼──────────┼────────┤
│data[0-1]│data[2-3]│  data[4]   │data[5] │  data[6]   │data[7-10]│data[11-12]│data[N+13]│data[13]│        │
└────────┴──────────┴────────────┴────────┴────────────┴──────────┴───────────┴──────────┴────────┘

写请求帧

┌────────┬──────────┬────────────┬────────┬────────┬──────────┬──────────────┬───────────┬────────┐
│ 起始符 │ 数据长度  │  设备地址   │  预留  │功能码   │ 起始地址  │ 写入数据长度  │ 写入数据  │  CRC-8 │
│ 0x55AA │  2B(LE)  │   1B       │  0x00  │ 0x79   │  4B(LE)  │   2B(LE)     │   NB     │   1B   │
├────────┼──────────┼────────────┼────────┼────────┼──────────┼──────────────┼──────────┼────────┤
│data[0-1]│data[2-3]│  data[4]   │data[5] │data[6] │data[7-10]│ data[11-12]  │  data[]  │data[N+13]│
└────────┴──────────┴────────────┴────────┴────────┴──────────┴──────────────┴──────────┴────────┘

写应答帧

┌────────┬──────────┬────────────┬────────┬────────────┬──────────┬───────────┬────────┬────────┐
│ 起始符 │ 数据长度  │  设备地址   │  预留  │  功能码     │ 起始地址  │ 返回字节数 │  CRC-8 │  状态  │
│ 0xAA55 │  2B(LE)  │   1B       │  0x00  │ 0x80+func  │  4B(LE)  │  2B(LE)   │   1B   │  1B   │
├────────┼──────────┼────────────┼────────┼────────────┼──────────┼───────────┼────────┼────────┤
│data[0-1]│data[2-3]│  data[4]   │data[5] │  data[6]   │data[7-10]│data[11-12]│data[14]│data[13]│
└────────┴──────────┴────────────┴────────┴────────────┴──────────┴───────────┴────────┴────────┘

2.3 状态码

含义
0x0000 正常(成功)
0x0001 读取长度超限
0x0002 长度错误
0x0003 无效地址
0x0004 只读寄存器

2.4 功能码

功能码 方向 说明
0xFB 请求 → 读请求
0x79 请求 → 写请求
0x80 + func ← 应答 读/写应答(0xFF = 读应答,0xF9 = 写应答)

3. 传感器模组布局

3.1 模组分布28 个传感器模组)

  ┌──────────────────────────────────────────────────────────────┐
  │                      传感器手套布局                           │
  │                                                              │
  │   大拇指          食指         中指        无名指      小拇指  │
  │  ┌──┐           ┌──┐        ┌──┐        ┌──┐       ┌──┐     │
  │  │指甲│          │指甲│       │指甲│       │指甲│      │指甲│    │
  │  ├──┤           ├──┤        ├──┤        ├──┤       ├──┤     │
  │  │指尖│          │指尖│       │指尖│       │指尖│      │指尖│    │
  │  ├──┤           ├──┤        ├──┤        ├──┤       ├──┤     │
  │  │中节│          │中节│       │中节│       │中节│      │中节│    │
  │  ├──┤           ├──┤        ├──┤        ├──┤       ├──┤     │
  │  │近节│          │近节│       │近节│       │近节│      │近节│    │
  │  └──┘           └──┘        └──┘        └──┘       └──┘     │
  │                                                              │
  │                    ┌─────────────────┐                       │
  │                    │  掌心 1-8       │                       │
  │                    │  (8个模组)      │                       │
  │                    └─────────────────┘                       │
  └──────────────────────────────────────────────────────────────┘

3.2 模组编号0-27

编号 模组 编号 模组
0 大拇指-近节 14 无名指-近节
1 大拇指-中节 15 无名指-中节
2 大拇指-指尖 16 无名指-指尖
3 大拇指-指甲 17 无名指-指甲
4 食指-近节 18 小拇指-近节
5 食指-中节 19 小拇指-中节
6 食指-指尖 20 小拇指-指尖
7 食指-指甲 21 小拇指-指甲
8 中指-近节 22 掌心 1
9 中指-中节 23 掌心 2
10 中指-指尖 24 掌心 3
11 中指-指甲 25 掌心 4
12 无名指-近节 26 掌心 5
13 无名指-中节 27 掌心 6

注:掌心 7、掌心 8 为扩展编号 28-29部分配置可能不包含


4. 寄存器地址映射

4.1 系统信息寄存器

地址 名称 权限 数据类型 说明
0x0000-0x0001 系列号 RO u32 设备唯一序列号
0x000F 固件版本 RO u16 0x0000 = 系列号,0x0001 = 软件版本号
0x0010 标定组 RO u16 标定参数组,用于确认产品状态
0x0011 模组有效状态 RO u16 bit0-3: 大拇指近/中/指/甲bit4-7: 食指近/中/指/甲
0x0012 传感器尺寸 L_LINE RO u16 宽度阵列点数
0x0013 传感器尺寸 H_LINE RO u16 长度阵列点数
0x0014 分布力点数(大拇指) RO u16 分布力字节数 = 该值 × 3
0x0015 分布力点数(食指) RO u16 同上
0x0016 分布力点数(中指) RO u16 同上
0x0017 分布力点数(无名指) RO u16 同上
0x0018 分布力点数(小拇指) RO u16 同上
0x0023 当前模块位置 RO u16 0-3: 大拇指4-7: 食指,... 20-27: 掌心

4.2 配置寄存器

地址 名称 权限 数据类型 说明
0x0030-0x0031 产品配置 1 RW u32 bit0: 自动回传使能1=使能0=不使能默认0
0x0032-0x0033 产品配置 2 RW u32 保留
0x0034-0x003F 大拇指分布力点数 RO u16[6] 近节、中节、指尖、指甲各模组的分布力点数
0x0040-0x004B 食指分布力点数 RO u16[6] 同上
0x004C-0x0057 中指分布力点数 RO u16[6] 同上
0x0058-0x0059 无名指分布力点数 RO u16[2] 近节、中节
0x005A-0x005B 无名指分布力点数 RO u16[2] 指尖、指甲
0x005C-0x0067 掌心分布力点数 RO u16[6] 掌心8-1倒序

4.3 数据寄存器

地址 名称 权限 数据大小 说明
0x0500-0x05A7 合力数据 RO 168B 28个模组每个 3×2B (fx,fy,fz 为 i16)
0x0700-0x0737 模组错误码 RO 56B 28个模组每个 2B
0x1000-0x11FF 大拇指分布力 RO 变长 三维分布力 (fx,fy,fz 每点 3B)
0x1200-0x13FF 食指分布力 RO 变长 同上
0x1400-0x15FF 中指分布力 RO 变长 同上
0x1600-0x17FF 无名指分布力 RO 变长 同上
0x1800-0x19FF 小拇指分布力 RO 变长 同上
0x1A00-0x1BFF 掌心分布力1-4 RO 变长 同上
0x1C00-0x1DFF 掌心分布力5-8 RO 变长 同上
0x2000-0x2FFF 处理后数值 RO 变长 原始地址 + 0x3000
0x8000-0x8FFF 标定参数 RW 变长 每点 32 参数,最多 128 点

5. 目录结构

eskin-finger-sdk/
├── Cargo.toml                    # Rust crate 配置
├── CMakeLists.txt                # C++ wrapper 构建配置
├── docs/
│   └── ARCHITECTURE.md           # 本文档
├── include/
│   └── eskin_finger_sdk.h        # C 头文件(公共 API
├── cpp/
│   ├── include/
│   │   └── eskin_finger_sdk.hpp  # C++ wrapper 头文件
│   └── src/
│       └── eskin_finger_sdk.cpp  # C++ wrapper 实现
├── src/
│   ├── lib.rs                    # Rust 库入口
│   ├── main.rs                   # 示例/测试入口
│   ├── types.rs                  # 数据建模(传感器结构体、枚举)
│   ├── error.rs                  # 错误类型定义
│   ├── protocol.rs               # UART 协议解析帧编解码、CRC-8-ITU
│   ├── transport.rs              # 串口传输层(读写抽象)
│   ├── register.rs               # 寄存器地址映射与读写封装
│   ├── device.rs                 # 设备管理核心
│   ├── channel.rs                # Channel 设计与管理
│   ├── stream.rs                 # 数据流控制(分布力采集)
│   ├── config.rs                 # 设备配置管理
│   └── ffi/
│       ├── mod.rs                # FFI 模块声明
│       └── c_api.rs              # C ABI 实现
└── examples/
    └── basic_usage.rs            # Rust 使用示例

6. Crate 依赖 (Cargo.toml)

[package]
name = "eskin-finger-sdk"
version = "0.1.0"
edition = "2024"

[lib]
name = "eskin_finger_sdk"
crate-type = ["lib", "cdylib", "staticlib"]

[dependencies]
# Channel 通信 — 高性能多生产者多消费者通道
crossbeam-channel = "0.5"

# 错误处理 — 结构化错误类型
thiserror = "2"

# 日志 — 可选的日志输出
log = "0.4"

# 日志前端
fern = "0.7"

# 时间处理 — 高精度时间戳
chrono = "0.4"

# 序列化 — 配置和数据的序列化支持
serde = { version = "1", features = ["derive"] }
serde_json = "1"

# 串口传输 — UART/USB-Serial 通信
serialport = "4"

# FFI 类型支持C 类型兼容)
libc = "0.2"

# UUID — 设备序列号等唯一标识
uuid = { version = "1", features = ["v4"] }

[dev-dependencies]
# 测试工具
env_logger = "0.11"

依赖说明

依赖 用途 必要性
crossbeam-channel 高性能 bounded/unbounded channel支持 try_sendrecv_timeout 核心
thiserror 派生 std::error::Error,简化错误类型定义 核心
log / fern 日志抽象层 + 日志前端,方便调试 推荐
chrono 高精度时间戳(微秒/纳秒级) 推荐
serde / serde_json 配置文件序列化、调试输出 推荐
serialport 串口打开、读写、清空缓冲区,承载设备 UART 协议 核心
libc FFI 类型兼容(c_char, c_int 等) FFI 必需
uuid 设备唯一标识符生成 可选

编译产物

crate-type = ["lib", "cdylib", "staticlib"]
  • libRust 内部使用
  • cdylib:生成 .dylib/.so/.dll,供 C/C++ 动态链接
  • staticlib:生成 .a/.lib,供 C/C++ 静态链接

7. 数据建模

7.1 核心数据类型

// src/types.rs

/// 三维力向量(分布力中的单个测力点)
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub struct Force3D {
    pub fx: i16,  // X 方向力 (原始值)
    pub fy: i16,  // Y 方向力 (原始值)
    pub fz: i16,  // Z 方向力 (原始值)
}

/// 三维力向量(浮点版本,用于合力)
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub struct Force3F {
    pub fx: f32,  // X 方向力 (N)
    pub fy: f32,  // Y 方向力 (N)
    pub fz: f32,  // Z 方向力 (N)
}

/// 传感器模组标识
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SensorModule {
    // 大拇指
    ThumbProximal  = 0,   // 大拇指近节
    ThumbMiddle    = 1,   // 大拇指中节
    ThumbTip       = 2,   // 大拇指指尖
    ThumbNail      = 3,   // 大拇指指甲

    // 食指
    IndexProximal  = 4,
    IndexMiddle    = 5,
    IndexTip       = 6,
    IndexNail      = 7,

    // 中指
    MiddleProximal = 8,
    MiddleMiddle   = 9,
    MiddleTip      = 10,
    MiddleNail     = 11,

    // 无名指
    RingProximal   = 12,
    RingMiddle     = 13,
    RingTip        = 14,
    RingNail       = 15,

    // 小拇指
    PinkyProximal  = 16,
    PinkyMiddle    = 17,
    PinkyTip       = 18,
    PinkyNail      = 19,

    // 掌心
    Palm1          = 20,
    Palm2          = 21,
    Palm3          = 22,
    Palm4          = 23,
    Palm5          = 24,
    Palm6          = 25,
    Palm7          = 26,
    Palm8          = 27,
}

/// 单个传感器模组的分布力数据
#[repr(C)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DistributionForce {
    pub module: SensorModule,
    /// 分布力点数(每个点 3 字节: fx, fy, fz
    pub point_count: u16,
    /// 分布力数据(原始 i8 值,长度 = point_count * 3
    pub points: Vec<Force3D>,
}

/// 单个传感器模组的合力数据
#[repr(C)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct CombinedForce {
    pub module: SensorModule,
    /// X 方向合力 (i16 原始值)
    pub fx: i16,
    /// Y 方向合力 (i16 原始值)
    pub fy: i16,
    /// Z 方向合力 (i16 原始值)
    pub fz: i16,
}

/// 一帧完整的传感器采样数据
#[repr(C)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FingerSample {
    /// 时间戳微秒SDK 侧采集时间)
    pub timestamp_us: u64,
    /// 采样序列号SDK 侧递增)
    pub sequence: u32,
    /// 28 个模组的合力数据
    pub combined_forces: [CombinedForce; 28],
    /// 28 个模组的分布力数据(可选,根据需求拉取)
    pub distribution_forces: Vec<DistributionForce>,
    /// 28 个模组的错误码(每个模组 2 字节)
    pub module_errors: [u16; 28],
}

/// 单个测力点的简化数据(用于 C ABI 传递分布力)
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub struct ForcePoint {
    pub fx: i8,
    pub fy: i8,
    pub fz: i8,
}

7.2 设备配置

// src/config.rs

/// 设备配置
#[repr(C)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceConfig {
    /// 设备地址(默认 0x34
    pub device_addr: u8,

    /// 是否启用自动回传模式
    /// bit0: 1=使能, 0=不使能默认0
    pub auto_transmit: bool,

    /// 是否读取分布力数据(合力数据始终读取)
    pub read_distribution: bool,

    /// 样本丢弃策略
    pub drop_policy: DropPolicy,
}

/// 样本丢弃策略
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DropPolicy {
    /// 丢弃新数据 — 通道满时丢弃当前新样本,保留旧数据
    DropNewest,
    /// 丢弃旧数据 — 通道满时丢弃队列头部的旧数据,保留最新样本
    DropOldest,
}

7.3 系统信息

/// 设备系统信息(从只读寄存器读取)
#[repr(C)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceInfo {
    /// 系列号
    pub serial_number: u32,
    /// 固件版本
    pub firmware_version: u16,
    /// 标定组
    pub calibration_group: u16,
    /// 模组有效状态bit 掩码)
    pub module_active_status: u16,
    /// 传感器宽度阵列点数 (L_LINE)
    pub l_line: u16,
    /// 传感器长度阵列点数 (H_LINE)
    pub h_line: u16,
    /// 产品配置 1
    pub product_config_1: u32,
    /// 产品配置 2
    pub product_config_2: u32,
}

7.4 协议帧类型

// src/protocol.rs

/// 帧起始符
pub const FRAME_START_REQUEST: u16 = 0x55AA;
pub const FRAME_START_RESPONSE: u16 = 0xAA55;

/// 功能码
pub const FUNC_READ: u8 = 0xFB;
pub const FUNC_WRITE: u8 = 0x79;
pub const FUNC_RESPONSE_READ: u8 = 0xFF;   // 0x80 + 0xFB
pub const FUNC_RESPONSE_WRITE: u8 = 0xF9;  // 0x80 + 0x79

/// 设备状态码(应答帧中的 status 字段)
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeviceStatus {
    Success          = 0x0000,  // 正常
    ReadLenExceeded  = 0x0001,  // 读取长度超限
    LengthError      = 0x0002,  // 长度错误
    InvalidAddress   = 0x0003,  // 无效地址
    ReadOnlyRegister = 0x0004,  // 只读寄存器
}

/// 读请求帧结构
pub struct ReadRequest {
    pub device_addr: u8,
    pub start_addr: u32,
    pub read_byte_count: u16,
}

/// 写请求帧结构
pub struct WriteRequest {
    pub device_addr: u8,
    pub start_addr: u32,
    pub data: Vec<u8>,
}

/// 读应答帧结构
pub struct ReadResponse {
    pub device_addr: u8,
    pub start_addr: u32,
    pub data: Vec<u8>,
    pub status: DeviceStatus,
}

/// 写应答帧结构
pub struct WriteResponse {
    pub device_addr: u8,
    pub start_addr: u32,
    pub return_byte_count: u16,
    pub status: DeviceStatus,
}

8. 错误处理

8.1 SDK 错误码C ABI

// src/error.rs

/// C ABI 兼容的错误码
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SdkErrorCode {
    Success           = 0,
    InvalidPointer    = 1,
    DeviceNotFound    = 2,
    DeviceAlreadyOpen = 3,
    NotInitialized    = 4,
    AlreadyStreaming  = 5,
    NotStreaming      = 6,
    ConfigError       = 7,
    IoError           = 8,
    Timeout           = 9,
    ChannelClosed     = 10,
    InternalError     = 11,
    BufferOverflow    = 12,
    InvalidParameter  = 13,
    CrcError          = 14,
    FrameError        = 15,
    ProtocolError     = 16,
    DeviceError       = 17,  // 设备返回非零状态码
}

/// Rust 内部错误类型
#[derive(Debug, thiserror::Error)]
pub enum SdkError {
    #[error("Device not found: {0}")]
    DeviceNotFound(String),

    #[error("Device already open")]
    DeviceAlreadyOpen,

    #[error("SDK not initialized")]
    NotInitialized,

    #[error("Already streaming")]
    AlreadyStreaming,

    #[error("Not streaming")]
    NotStreaming,

    #[error("Configuration error: {0}")]
    ConfigError(String),

    #[error("I/O error: {0}")]
    IoError(#[from] std::io::Error),

    #[error("Read timeout")]
    Timeout,

    #[error("Channel closed")]
    ChannelClosed,

    #[error("Internal error: {0}")]
    InternalError(String),

    #[error("Buffer overflow — dropped {0} samples")]
    BufferOverflow(u64),

    #[error("Invalid parameter: {0}")]
    InvalidParameter(String),

    #[error("CRC error: expected 0x{expected:02X}, got 0x{actual:02X}")]
    CrcError { expected: u8, actual: u8 },

    #[error("Frame error: {0}")]
    FrameError(String),

    #[error("Protocol error: {0}")]
    ProtocolError(String),

    #[error("Device error: status 0x{0:04X}")]
    DeviceError(u16),
}

8.2 设备侧错误码

/// 设备应答帧中的状态码
impl DeviceStatus {
    pub fn to_error(&self) -> Option<SdkError> {
        match self {
            DeviceStatus::Success => None,
            DeviceStatus::ReadLenExceeded => Some(SdkError::DeviceError(0x0001)),
            DeviceStatus::LengthError => Some(SdkError::DeviceError(0x0002)),
            DeviceStatus::InvalidAddress => Some(SdkError::DeviceError(0x0003)),
            DeviceStatus::ReadOnlyRegister => Some(SdkError::DeviceError(0x0004)),
        }
    }
}

9. CRC-8-ITU 校验

// src/protocol.rs

/// CRC-8-ITU 多项式: x^8 + x^2 + x + 1 (0x07)
/// 初始值: 0x00
pub fn crc8_itu(data: &[u8]) -> u8 {
    let mut crc: u8 = 0x00;
    for &byte in data {
        crc ^= byte;
        for _ in 0..8 {
            if crc & 0x80 != 0 {
                crc = (crc << 1) ^ 0x07;
            } else {
                crc <<= 1;
            }
        }
    }
    crc
}

10. Channel 设计

10.1 通道架构

                    ┌──────────────────────┐
                    │  UART I/O Thread      │
                    │  (串口数据采集线程)    │
                    └──────┬───────┬───────┘
                           │       │
                    sample_tx    event_tx
                           │       │
              ┌────────────▼─┐  ┌──▼───────────┐
              │  Data Channel │  │ Event Channel │
              │  (bounded)    │  │  (bounded)    │
              │  capacity: 64 │  │  capacity: 64 │
              └────────────┬─┘  └──┬────────────┘
                           │       │
                    sample_rx    event_rx
                           │       │
              ┌────────────▼───────▼────────────┐
              │         User-facing API          │
              │  read_sample() / callback        │
              └──────────────────────────────────┘

    ┌──────────────────────┐
    │   User Thread         │
    └──────┬───────────────┘
           │
      cmd_tx
           │
    ┌──────▼──────────────┐
    │   Command Channel    │
    │   (bounded)          │
    │   capacity: 16       │
    └──────┬───────────────┘
           │
      cmd_rx
    ┌──────▼──────────────┐
    │  UART I/O Thread     │
    └──────────────────────┘

10.2 通道定义

// src/channel.rs

use crossbeam_channel::{bounded, Sender, Receiver};
use std::sync::atomic::{AtomicU64, Ordering};

/// 设备命令
#[derive(Debug, Clone)]
pub enum DeviceCommand {
    StartStream,
    StopStream,
    SetConfig(DeviceConfig),
    /// 读取指定寄存器
    ReadRegister { addr: u32, length: u16 },
    /// 写入指定寄存器
    WriteRegister { addr: u32, data: Vec<u8> },
    Shutdown,
}

/// 设备事件
#[derive(Debug, Clone)]
pub enum DeviceEvent {
    Disconnected(String),
    IoError(String),
    ProtocolError(String),
    ConfigApplied,
    StreamStarted,
    StreamStopped,
    SampleDropped { count: u64 },
    ModuleError { module: SensorModule, error_code: u16 },
}

/// 通道管理器
pub struct ChannelManager {
    // 数据通道 — 传感器采样数据
    pub sample_tx: Sender<FingerSample>,
    pub sample_rx: Receiver<FingerSample>,

    // 命令通道 — 控制命令
    pub cmd_tx: Sender<DeviceCommand>,
    pub cmd_rx: Receiver<DeviceCommand>,

    // 事件通道 — 事件通知
    pub event_tx: Sender<DeviceEvent>,
    pub event_rx: Receiver<DeviceEvent>,

    // 丢弃计数器
    pub dropped_samples: AtomicU64,

    // 丢弃策略
    pub drop_policy: DropPolicy,
}

impl ChannelManager {
    pub fn new(
        sample_capacity: usize,   // 建议 64传感器帧率较低
        cmd_capacity: usize,      // 建议 16
        event_capacity: usize,    // 建议 64
        drop_policy: DropPolicy,
    ) -> Self {
        let (sample_tx, sample_rx) = bounded(sample_capacity);
        let (cmd_tx, cmd_rx) = bounded(cmd_capacity);
        let (event_tx, event_rx) = bounded(event_capacity);

        Self {
            sample_tx, sample_rx,
            cmd_tx, cmd_rx,
            event_tx, event_rx,
            dropped_samples: AtomicU64::new(0),
            drop_policy,
        }
    }

    /// 非阻塞发送样本,根据策略处理溢出
    pub fn send_sample(&self, sample: FingerSample) {
        match self.drop_policy {
            DropPolicy::DropNewest => {
                if self.sample_tx.try_send(sample).is_err() {
                    self.dropped_samples.fetch_add(1, Ordering::Relaxed);
                }
            }
            DropPolicy::DropOldest => {
                if let Err(crossbeam_channel::TrySendError::Full(_)) =
                    self.sample_tx.try_send(sample)
                {
                    let _ = self.sample_rx.try_recv();
                    if self.sample_tx.try_send(sample).is_err() {
                        self.dropped_samples.fetch_add(1, Ordering::Relaxed);
                    }
                }
            }
        }
    }

    /// 带超时的样本读取
    pub fn recv_sample(&self, timeout_ms: u32) -> Result<FingerSample, SdkError> {
        let timeout = std::time::Duration::from_millis(timeout_ms as u64);
        self.sample_rx
            .recv_timeout(timeout)
            .map_err(|_| SdkError::Timeout)
    }

    /// 获取丢弃的样本数量
    pub fn dropped_count(&self) -> u64 {
        self.dropped_samples.load(Ordering::Relaxed)
    }
}

10.3 通道容量配置

通道 容量 频率 说明
数据通道 (sample) 64 低频 (~10-100Hz) bounded传感器帧率决定
命令通道 (cmd) 16 低频 (~1Hz) bounded固定
事件通道 (event) 64 低频 (~1Hz) bounded固定

11. 设备管理

11.1 设备状态机

  ┌──────────┐     init()     ┌──────────────┐
  │   Idle   │ ──────────────►│  Initialized │
  └──────────┘                └──────┬───────┘
       ▲                             │
       │                        open(port)
       │                             │
       │                        ┌────▼───────┐
       │                        │ Connected  │
       │                        └────┬───────┘
       │                             │
       │                       start_stream()
       │                             │
       │                        ┌────▼───────┐
       │                        │ Streaming  │
       │                        └────┬───────┘
       │                             │
       │                       stop_stream()
       │                             │
       │                        ┌────▼───────┐
       │            close()     │ Connected  │
       │◄───────────────────────└────────────┘
       │
  shutdown()

11.2 核心设备结构

// src/device.rs

pub struct EskinDeviceInner {
    pub info: DeviceInfo,
    pub config: DeviceConfig,
    pub channels: ChannelManager,
    pub state: DeviceState,
    pub transport: Box<dyn SerialTransport>,
    pub stream_handle: Option<JoinHandle<()>>,
    pub cmd_handle: Option<JoinHandle<()>>,
    pub shutdown_flag: Arc<AtomicBool>,
    pub sequence_counter: AtomicU32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeviceState {
    Idle,
    Initialized,
    Connected,
    Streaming,
    Error,
}

11.3 传输层抽象

// src/transport.rs

/// 串口传输抽象(方便测试时 mock
pub trait SerialTransport: Send + Sync {
    /// 发送原始字节
    fn write(&mut self, data: &[u8]) -> Result<usize, SdkError>;
    /// 接收原始字节(带超时)
    fn read(&mut self, buf: &mut [u8], timeout: Duration) -> Result<usize, SdkError>;
    /// 清空接收缓冲区
    fn flush_rx(&mut self) -> Result<(), SdkError>;
}

/// 真实串口实现
pub struct SerialPortTransport {
    port: Box<dyn serialport::SerialPort>,
}

12. 数据采集流程

12.1 合力数据采集(标准模式)

用户调用 start_stream()
         │
         ▼
┌──────────────────────────────────────────┐
│          UART I/O Thread 启动             │
│                                          │
│  loop {                                  │
│    1. 检查 shutdown_flag                 │
│    2. 检查 cmd_rx 是否有新命令            │
│    3. 发送读请求: 0x0500, 长度 168 字节   │
│       (28 模组 × 6 字节合力)              │
│    4. 接收读应答帧                        │
│    5. 校验 CRC-8-ITU                     │
│    6. 解析 28 个 CombinedForce            │
│    7. 组装 FingerSample                   │
│    8. sample_tx.try_send(sample)          │
│    9. 如果配置了 read_distribution:        │
│       a. 依次发送读请求获取各指分布力      │
│       b. 填充 distribution_forces         │
│  }                                       │
└──────────────────────────────────────────┘

12.2 分布力数据采集

分布力数据按模组分段读取,每段地址范围不同:

传感器 地址范围 每点字节数
大拇指 0x1000-0x11FF 3 (fx, fy, fz)
食指 0x1200-0x13FF 3
中指 0x1400-0x15FF 3
无名指 0x1600-0x17FF 3
小拇指 0x1800-0x19FF 3
掌心 1-4 0x1A00-0x1BFF 3
掌心 5-8 0x1C00-0x1DFF 3

分布力字节数 = point_count × 3point_count 从 0x0014-0x0018 等寄存器读取。

12.3 命令处理流程

cmd_rx 接收到命令
       │
       ▼
match command {
    StartStream        → 设置 streaming 标志,开始采集循环
    StopStream         → 清除 streaming 标志
    SetConfig(cfg)     → 写入产品配置寄存器 (0x0030)
    ReadRegister       → 发送读请求,返回结果
    WriteRegister      → 发送写请求,检查状态码
    Shutdown           → 设置 shutdown_flag退出线程
}

13. C ABI 设计

13.1 Opaque Pointer 模式

// src/ffi/c_api.rs

/// 不透明指针C 端只能通过 API 函数操作
#[repr(C)]
pub struct EskinDevice {
    inner: Box<EskinDeviceInner>,
}

/// 全局 SDK 状态
static SDK_STATE: OnceLock<Mutex<SdkState>> = OnceLock::new();

struct SdkState {
    initialized: bool,
}

13.2 C API 函数签名

// include/eskin_finger_sdk.h

#ifndef ESKIN_FINGER_SDK_H
#define ESKIN_FINGER_SDK_H

#include <stdint.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

// ─── 错误码 ──────────────────────────────────────────
typedef enum {
    ESKIN_SUCCESS            = 0,
    ESKIN_INVALID_POINTER    = 1,
    ESKIN_DEVICE_NOT_FOUND   = 2,
    ESKIN_DEVICE_ALREADY_OPEN = 3,
    ESKIN_NOT_INITIALIZED    = 4,
    ESKIN_ALREADY_STREAMING  = 5,
    ESKIN_NOT_STREAMING      = 6,
    ESKIN_CONFIG_ERROR       = 7,
    ESKIN_IO_ERROR           = 8,
    ESKIN_TIMEOUT            = 9,
    ESKIN_CHANNEL_CLOSED     = 10,
    ESKIN_INTERNAL_ERROR     = 11,
    ESKIN_BUFFER_OVERFLOW    = 12,
    ESKIN_INVALID_PARAMETER  = 13,
    ESKIN_CRC_ERROR          = 14,
    ESKIN_FRAME_ERROR        = 15,
    ESKIN_PROTOCOL_ERROR     = 16,
    ESKIN_DEVICE_ERROR       = 17,
} EskinErrorCode;

// ─── 数据类型 ────────────────────────────────────────
typedef struct { int16_t fx, fy, fz; } EskinForce3D;

typedef struct {
    uint8_t module_index;  // 0-27
    int16_t fx, fy, fz;
} EskinCombinedForce;

typedef struct {
    uint8_t  module_index;
    uint16_t point_count;
    EskinForce3D* points;  // 长度 = point_count
} EskinDistributionForce;

typedef struct {
    uint64_t               timestamp_us;
    uint32_t               sequence;
    EskinCombinedForce     combined_forces[28];
    EskinDistributionForce* distribution_forces; // 可选NULL 表示不采集
    uint16_t               distribution_count;
    uint16_t               module_errors[28];
} EskinSample;

typedef struct {
    uint32_t serial_number;
    uint16_t firmware_version;
    uint16_t calibration_group;
    uint16_t module_active_status;
    uint16_t l_line;
    uint16_t h_line;
    uint32_t product_config_1;
    uint32_t product_config_2;
} EskinDeviceInfo;

typedef enum {
    ESKIN_DROP_NEWEST = 0,
    ESKIN_DROP_OLDEST = 1,
} EskinDropPolicy;

typedef struct {
    uint8_t         device_addr;      // 默认 0x34
    int             auto_transmit;    // 0=不使能, 1=使能
    int             read_distribution; // 0=只读合力, 1=也读分布力
    EskinDropPolicy drop_policy;
} EskinConfig;

// ─── 设备管理 API ────────────────────────────────────
EskinErrorCode eskin_initialize(void);
EskinErrorCode eskin_shutdown(void);

EskinErrorCode eskin_open(const char* port_name, EskinDevice** out_device);
EskinErrorCode eskin_open_by_serial(const char* serial, EskinDevice** out_device);
void           eskin_close(EskinDevice* device);

// ─── 设备信息 API ────────────────────────────────────
EskinErrorCode eskin_get_device_info(EskinDevice* device, EskinDeviceInfo* out_info);

// ─── 配置 API ────────────────────────────────────────
EskinErrorCode eskin_get_config(EskinDevice* device, EskinConfig* config);
EskinErrorCode eskin_set_config(EskinDevice* device, const EskinConfig* config);

// ─── 流控制 API ──────────────────────────────────────
EskinErrorCode eskin_start_stream(EskinDevice* device);
EskinErrorCode eskin_stop_stream(EskinDevice* device);

// ─── 数据读取 API ────────────────────────────────────
EskinErrorCode eskin_read_sample(EskinDevice* device, EskinSample* out_sample,
                                 uint32_t timeout_ms);
uint64_t       eskin_get_dropped_sample_count(EskinDevice* device);

// ─── 寄存器直接访问 API ─────────────────────────────
EskinErrorCode eskin_read_register(EskinDevice* device, uint32_t addr,
                                   uint16_t length, uint8_t* out_data);
EskinErrorCode eskin_write_register(EskinDevice* device, uint32_t addr,
                                    uint16_t length, const uint8_t* data);

// ─── 错误管理 API ────────────────────────────────────
const char*    eskin_error_string(EskinErrorCode code);
EskinErrorCode eskin_get_last_error(EskinDevice* device);

// ─── 内存管理 API ────────────────────────────────────
void           eskin_free_sample(EskinSample* sample);

#ifdef __cplusplus
}
#endif

#endif // ESKIN_FINGER_SDK_H

14. C++ Wrapper 设计

14.1 RAII 封装

// cpp/include/eskin_finger_sdk.hpp

#pragma once

#include "eskin_finger_sdk.h"
#include <functional>
#include <stdexcept>
#include <string>
#include <thread>
#include <atomic>
#include <vector>
#include <array>

namespace eskin {

// ─── 异常类型 ────────────────────────────────────────
class EskinException : public std::runtime_error {
public:
    EskinException(EskinErrorCode code, const std::string& msg)
        : std::runtime_error(msg), code_(code) {}
    EskinErrorCode code() const { return code_; }
private:
    EskinErrorCode code_;
};

// ─── 数据类型 ────────────────────────────────────────
struct Force3D { int16_t fx, fy, fz; };

struct CombinedForce {
    uint8_t module_index;
    int16_t fx, fy, fz;
};

struct DistributionForce {
    uint8_t module_index;
    std::vector<Force3D> points;
};

struct Sample {
    uint64_t timestamp_us;
    uint32_t sequence;
    std::array<CombinedForce, 28> combined_forces;
    std::vector<DistributionForce> distribution_forces;
    std::array<uint16_t, 28> module_errors;
};

struct DeviceInfo {
    uint32_t serial_number;
    uint16_t firmware_version;
    uint16_t calibration_group;
    uint16_t module_active_status;
    uint16_t l_line;
    uint16_t h_line;
};

// ─── 回调类型 ────────────────────────────────────────
using SampleCallback = std::function<void(const Sample&)>;
using ErrorCallback  = std::function<void(EskinErrorCode, const std::string&)>;

// ─── RAII Device 封装 ────────────────────────────────
class Device {
public:
    Device(const Device&) = delete;
    Device& operator=(const Device&) = delete;
    Device(Device&& other) noexcept;
    Device& operator=(Device&& other) noexcept;
    ~Device();

    // 设备信息
    DeviceInfo getDeviceInfo();

    // 配置
    void setConfig(const EskinConfig& config);
    EskinConfig getConfig();

    // 流控制
    void start();
    void stop();

    // 同步数据读取
    Sample readSample(uint32_t timeout_ms = 1000);
    uint64_t getDroppedSampleCount() const;

    // 寄存器直接访问
    std::vector<uint8_t> readRegister(uint32_t addr, uint16_t length);
    void writeRegister(uint32_t addr, const std::vector<uint8_t>& data);

    // 回调模式
    void setCallback(SampleCallback on_sample);
    void setErrorCallback(ErrorCallback on_error);
    void startAsync();
    void stopAsync();

private:
    friend class Sdk;
    explicit Device(EskinDevice* handle);

    EskinDevice* handle_ = nullptr;
    SampleCallback sample_callback_;
    ErrorCallback error_callback_;
    std::thread callback_thread_;
    std::atomic<bool> callback_running_{false};
};

// ─── SDK 全局管理 ────────────────────────────────────
class Sdk {
public:
    Sdk();
    ~Sdk();

    Sdk(const Sdk&) = delete;
    Sdk& operator=(const Sdk&) = delete;

    Device open(const std::string& port_name);
    Device openBySerial(const std::string& serial);

    static std::string errorString(EskinErrorCode code);

private:
    bool initialized_ = false;
};

} // namespace eskin

15. 数据流示意

15.1 同步读取模式Pull

用户线程                      SDK 内部
   │                             │
   │  eskin_read_sample()        │
   │────────────────────────────►│  sample_rx.recv_timeout()
   │                             │
   │  内部循环:                    │
   │  1. 发送 UART 读请求         │
   │  2. 接收应答,校验 CRC        │
   │  3. 解析合力/分布力           │
   │  4. 组装 FingerSample        │
   │  5. 通过 channel 传递        │
   │                             │
   │◄────────────────────────────│  返回 FingerSample 或 Timeout
   │                             │
   │  eskin_get_dropped_count()  │
   │────────────────────────────►│  atomic load
   │◄────────────────────────────│  返回 u64

15.2 回调模式Push

SDK 内部                          用户回调
   │                                 │
   │  UART I/O 线程采集并解析         │
   │  sample_tx.try_send(sample)     │
   │                                 │
   │  回调线程 recv sample            │
   │────────────────────────────────►│  on_sample(sample)
   │                                 │
   │  如果采集出错                    │
   │────────────────────────────────►│  on_error(code, msg)

16. 线程模型

┌─────────────────────────────────────────────────────┐
│                    Rust SDK 进程                     │
│                                                     │
│  ┌───────────────────┐   ┌───────────────────┐      │
│  │  UART I/O Thread  │   │  Command Handler  │      │
│  │  (per device)     │   │  (same thread)    │      │
│  │                   │   │                   │      │
│  │ - 串口读写        │   │ - 接收 cmd_rx     │      │
│  │ - CRC 校验        │   │ - 执行寄存器操作   │      │
│  │ - 协议解析        │   │ - 写入设备        │      │
│  │ - 组装样本        │   │                   │      │
│  │ - sample_tx       │   │                   │      │
│  │ - event_tx        │   │                   │      │
│  └───────┬───────────┘   └───────┬───────────┘      │
│          │                       │                   │
│          ▼                       ▼                   │
│  ┌───────────────────────────────────────┐          │
│  │          Channel Manager              │          │
│  │  - sample channel (bounded 64)        │          │
│  │  - cmd channel    (bounded 16)        │          │
│  │  - event channel  (bounded 64)        │          │
│  │  - dropped_samples (AtomicU64)        │          │
│  └───────────────────┬───────────────────┘          │
│                      │                               │
│  ┌───────────────────▼───────────────────┐          │
│  │          User-facing API              │          │
│  │  - C ABI (extern "C")                 │          │
│  │  - C++ Wrapper (RAII + callback)      │          │
│  └───────────────────────────────────────┘          │
└─────────────────────────────────────────────────────┘

17. 关键设计决策

决策 选择 理由
Channel 库 crossbeam-channel std::sync::mpsc 更高性能,支持 MPMCtry_sendrecv_timeout
数据通道容量 64 (bounded) 传感器帧率较低 (~10-100Hz)64 帧缓冲足够
样本发送策略 try_send (非阻塞) 采集线程绝不阻塞,保证实时性
丢弃策略 可配置 (DropNewest/DropOldest) 不同场景需求不同
FFI 指针 Opaque pointer (EskinDevice*) 隐藏内部结构ABI 稳定
内存管理 Box + raw pointer Rust 侧 Box 管理C 侧只持有指针,close() 时归还
错误处理 错误码 + last_error C ABI 用错误码C++ wrapper 转为异常
时间戳 u64 微秒 SDK 侧采集时间,避免浮点精度问题
CRC 校验 CRC-8-ITU 协议规定,多项式 0x07初始值 0x00
传输抽象 SerialTransport trait 方便单元测试 mock可扩展支持其他传输方式
分布力采集 可选配置 分布力数据量大,按需采集避免影响帧率

18. 指令示例

18.1 读取固件版本信息

请求: 55 AA 09 00 34 00 FB 00 00 00 00 10 00 76
      ├─起始─┤├─长度─┤├地址┤├─功能─┤├─地址──┤├─长度──┤├CRC┤

应答: AA 55 09 00 34 00 FF 00 00 00 00 02 00 xx xx CRC 00

18.2 读取大拇指指尖分布力点数

请求: 55 AA 09 00 34 00 FB 14 00 00 00 02 00 31
      起始地址 = 0x0014, 读取 2 字节

18.3 读取食指指尖合力

请求: 55 AA 09 00 34 00 FB 00 1C 00 00 A8 00 35
      起始地址 = 0x1C00, 读取 0xA8 = 168 字节

18.4 修改食指指尖 L_LINE 值

写请求: 55 AA 0A 00 34 00 79 14 00 00 00 01 00 03 A4
        起始地址 = 0x0014, 写入 1 字节, 数据 = 0x03

19. 构建方式

Rust生成 C 共享库)

cargo build --release
# 产物: target/release/libeskin_finger_sdk.dylib  (macOS)
#       target/release/libeskin_finger_sdk.so      (Linux)
#       target/release/eskin_finger_sdk.dll        (Windows)

C++ WrapperCMake

mkdir build && cd build
cmake ..
make

Python 绑定(未来扩展)

可以通过 cffipybind11 直接加载 C 共享库,无需额外绑定层。


20. 当前 Rust Core 接口骨架

当前阶段只定义接口和数据边界具体读写、CRC、解析、线程生命周期后续实现。

20.1 模块职责

模块 文件 职责
数据类型 src/types.rs 力向量、模组枚举、合力、分布力、采样帧、模组错误
配置 src/config.rs 设备地址、采样队列容量、超时、丢弃策略、设备信息
错误 src/error.rs SDK 错误枚举和 C ABI 错误码
传输层 src/transport.rs SerialTransport trait屏蔽具体串口实现
协议层 src/protocol.rs 请求/应答帧结构、ProtocolCodec trait、CRC 接口
寄存器层 src/register.rs 地址常量、寄存器元信息、原始字节到业务类型的解析接口
通道层 src/channel.rs sample/command/event 三类 channel 和丢弃策略
数据流 src/stream.rs StreamController trait定义轮询/自动回传模式
设备层 src/device.rs EskinDevice trait聚合 transport、codec、channel
FFI 边界 src/ffi/mod.rs C ABI handle、版本和基础生命周期接口草案

20.2 核心接口

pub trait SerialTransport {
    fn open(&mut self) -> Result<(), SdkError>;
    fn close(&mut self) -> Result<(), SdkError>;
    fn is_open(&self) -> bool;
    fn write(&mut self, data: &[u8]) -> Result<usize, SdkError>;
    fn read(&mut self, buf: &mut [u8], timeout: chrono::Duration) -> Result<usize, SdkError>;
    fn flush_rx(&mut self) -> Result<(), SdkError>;
}

pub trait ProtocolCodec {
    fn encode_read_request(&self, request: &ReadRequest) -> Result<Vec<u8>, SdkError>;
    fn encode_write_request(&self, request: &WriteRequest) -> Result<Vec<u8>, SdkError>;
    fn decode_read_response(&self, frame: &[u8]) -> Result<ReadResponse, SdkError>;
    fn decode_write_response(&self, frame: &[u8]) -> Result<WriteResponse, SdkError>;
    fn decode_stream_frame(&self, frame: &[u8]) -> Result<ProtocolFrame, SdkError>;
    fn crc8(&self, data: &[u8]) -> u8;
}

pub trait RegisterMap {
    fn device_info_registers(&self) -> &'static [RegisterSpec];
    fn distribution_register(&self, module: SensorModule) -> Result<RegisterSpec, SdkError>;
    fn parse_device_info(&self, raw: &[u8]) -> Result<DeviceInfo, SdkError>;
    fn parse_distribution_force(
        &self,
        module: SensorModule,
        raw: &[u8],
    ) -> Result<DistributionForce, SdkError>;
}

pub trait EskinDevice {
    fn open(&mut self) -> Result<(), SdkError>;
    fn close(&mut self) -> Result<(), SdkError>;
    fn state(&self) -> DeviceState;
    fn device_info(&self) -> Result<DeviceInfo, SdkError>;
    fn apply_config(&mut self, config: DeviceConfig) -> Result<(), SdkError>;
    fn start_stream(&mut self) -> Result<(), SdkError>;
    fn stop_stream(&mut self) -> Result<(), SdkError>;
    fn read_sample(&self, timeout_ms: u32) -> Result<FingerSample, SdkError>;
    fn read_event(&self, timeout_ms: u32) -> Result<DeviceEvent, SdkError>;
    fn read_register(&mut self, addr: u32, length: u16) -> Result<Vec<u8>, SdkError>;
    fn write_register(&mut self, addr: u32, data: &[u8]) -> Result<u16, SdkError>;
}

20.3 数据流流程图

flowchart TD
    App[Rust/C/C++ App] --> Device[EskinDevice]
    Device --> Channel[ChannelManager]
    Device --> Codec[ProtocolCodec]
    Device --> Register[RegisterMap]
    Device --> Transport[SerialTransport]
    Transport --> Serial[UART / USB-Serial]
    Serial --> Hardware[Eskin Finger Device]

    App -->|open| Device
    Device -->|open port| Transport
    Device -->|read device info registers| Register
    Register -->|ReadRequest| Codec
    Codec -->|frame bytes| Transport
    Transport -->|response bytes| Codec
    Codec -->|ReadResponse| Register
    Register -->|DeviceInfo| Device

    App -->|start_stream| Device
    Device -->|DeviceCommand::StartStream| Channel
    Device -->|poll or auto receive| Transport
    Transport -->|raw frame| Codec
    Codec -->|ProtocolFrame| Register
    Register -->|FingerSample| Channel
    Channel -->|sample_rx| App
    Channel -->|event_rx| App

20.4 设备生命周期

stateDiagram-v2
    [*] --> Closed
    Closed --> Open: open()
    Open --> Streaming: start_stream()
    Streaming --> Open: stop_stream()
    Open --> Closed: close()
    Streaming --> Error: transport/protocol error
    Open --> Error: transport/protocol error
    Error --> Closed: close()

21. 安全性考虑

  1. 空指针检查:所有 C API 入口检查指针非空
  2. 状态检查:操作前检查设备状态(是否已连接、是否正在采集等)
  3. CRC 校验:每帧数据必须通过 CRC-8-ITU 校验
  4. 帧完整性:检查起始符、数据长度与实际数据一致性
  5. 线程安全:使用 AtomicBoolAtomicU64AtomicU32 管理共享状态
  6. 资源泄漏C++ RAII 保证析构时释放C API 提供 eskin_close()
  7. panic 安全Rust FFI 函数不 panic所有 panic 用 catch_unwind 捕获
  8. 超时保护:所有串口读操作带超时,避免永久阻塞