Files
DataRouter/DESIGN.md
T
2026-07-26 01:40:05 +08:00

12 KiB
Raw Blame History

DataRouter 详细设计文档

1. 设计目标

轻量级 C 语言数据路由库。核心思想:

  • 报文类型(Channel):编译期通过宏声明的数据类型(如 sensor / status / control)
  • 端口(Port):运行期创建的连接点,绑定一个报文类型 + 一个物理外设发送 handler
  • 路由(Routing):将逻辑报文类型连接到实际的物理外设发送函数
应用层  send_sensor(port, data)
              │
              ▼
         库校验端口
              │
              ▼
         调用注册的 handler(data)  ← 用户实现:UART / TCP / CAN / 文件发送
              │
              ▼
         物理外设

设计原则

原则 说明
编译期确定类型 所有报文类型在 datarouter_config.h 中声明,库自动生成代码
运行期绑定外设 端口创建后,用户注册具体的发送 handler(函数指针)
不关心物理外设 库不依赖任何外设 API,只调用用户提供的 handler
零动态内存 纯静态数组,无 malloc/free

2. 整体架构

2.1 模块依赖

@startuml
left to right direction

rectangle "用户代码" as User {
  component "应用逻辑\n调用 datarouter_send_<type>()" as App
  component "datarouter_config.h\n声明报文类型" as Cfg
  component "外设 handler\nUART / TCP / CAN / 文件" as Handlers
  component "接收回调\n处理收到的报文" as Callbacks
}

database "DataRouter 库" as Lib {
  component "datarouter.h\n公共 API + 代码生成" as Hdr
  component "datarouter.c\n端口表 + handler 调用" as Impl
}

App --> Hdr
Cfg --> Hdr
Impl --> Cfg
Handlers --> Impl : 注册为 send_handler
Callbacks --> Impl : 实现 on_recv_<type>

@enduml

2.2 文件结构

文件 职责
datarouter_config.h 用户在此声明所有报文类型
datarouter.h 公共 API + X-macro 展开(enum + 发送函数声明 + 回调声明)
datarouter.c 发送函数实现 + handler 调用 + 端口管理
example.c 使用示例
tests/test_datarouter.c 31 个单元测试

3. 核心概念

3.1 报文类型(Channel)

@startuml
object "DATAROUTER_CHANNEL(sensor, \"Sensor data\")" as C1
object "DATAROUTER_CHANNEL(status, \"Status report\")" as C2
object "DATAROUTER_CHANNEL(control, \"Control command\")" as C3

note right of C1
  编译期展开产物:
  · DATAROUTER_CHANNEL_sensor = 0
  · int datarouter_send_sensor(port, data, len)
  · void datarouter_on_recv_sensor(port, data, len)
  · channel_names[0] = "sensor"
  · channel_descs[0] = "Sensor data"
end note

C1 --> C2
C2 --> C3
@enduml

3.2 端口与 handler

@startuml
skinparam objectBorderColor black

object "端口 A" as PA {
  type = sensor
  handler = uart_send
}

object "端口 B" as PB {
  type = sensor
  handler = tcp_send
}

object "端口 C" as PC {
  type = status
  handler = uart_send
}

object "UART发送函数" as UART
object "TCP发送函数" as TCP

PA --> UART : 注册
PB --> TCP : 注册
PC --> UART : 注册

note right of PA
  同一报文类型 sensor
  可路由到不同外设
end note

note right of PC
  不同报文类型 status
  可复用同一外设 UART
end note

@enduml

4. X-macro 代码生成

4.1 展开流程

@startuml

== datarouter.h: 3 次展开 ==

-> datarouter_config.h: #include 获得 DATAROUTER_CHANNELS

datarouter.h -> "第 1 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_CHANNEL_##id,
note right: 生成 enum 通道枚举

datarouter.h -> "第 2 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(...);
note right: 生成发送触发函数声明

datarouter.h -> "第 3 遍": #define DATAROUTER_CHANNEL(id,desc) void datarouter_on_recv_##id(...);
note right: 生成接收回调声明

== datarouter.c: 3 次展开 ==

datarouter.c -> "第 4 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_WEAK void ...
note right: 弱符号回调默认空实现

datarouter.c -> "第 5 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(…) {…}
note right: 发送函数实现 → 调用handler

datarouter.c -> "第 6 遍": #define DATAROUTER_CHANNEL(id,desc) #id,  → channel_names[]
datarouter.c -> "第 7 遍": #define DATAROUTER_CHANNEL(id,desc) desc, → channel_descs[]

@enduml

4.2 展开示例(3 个通道)

用户配置:
    DATAROUTER_CHANNEL(sensor,  "Sensor data")
    DATAROUTER_CHANNEL(status,  "Status report")
    DATAROUTER_CHANNEL(control, "Control command")

展开产物:
    enum:   sensor = 0, status = 1, control = 2, CHANNEL_COUNT = 3

    发送:   int datarouter_send_sensor(uint32_t, const void*, size_t);
            int datarouter_send_status(…);
            int datarouter_send_control(…);

    回调:   void datarouter_on_recv_sensor(uint32_t, const void*, size_t);
            void datarouter_on_recv_status(…);
            void datarouter_on_recv_control(…);

    元数据: names[]  = {"sensor", "status", "control"}
            descs[]  = {"Sensor data", "Status report", "Control command"}

5. 数据结构

5.1 核心数据类型

@startuml
skinparam classAttributeIconSize 0

class "datarouter_channel (enum)" as Chan {
  + DATAROUTER_CHANNEL_sensor  = 0
  + DATAROUTER_CHANNEL_status  = 1
  + DATAROUTER_CHANNEL_control = 2
  + DATAROUTER_CHANNEL_COUNT   = 3
}

class "datarouter_send_handler_t" as Handler {
  + void (*)(uint32_t port_id, const void *data, size_t len)
}

class "端口表" as Table {
  - port_entry_t ports[64]
  - uint8_t initialized
}

class "port_entry_t" as Entry {
  - int    type
  - datarouter_send_handler_t handler
  - uint8_t in_use
}

class "发送函数" as Send {
  + int send_sensor(port, data, len)
  + int send_status(port, data, len)
  + int send_control(port, data, len)
}

class "接收回调" as Recv {
  + void on_recv_sensor(port, data, len)   ← 用户实现
  + void on_recv_status(port, data, len)
  + void on_recv_control(port, data, len)
}

Entry --> Chan : type ∈ enum
Entry --> Handler : handler 函数指针
Table --> Entry : ports[]
Send --> Table : 校验端口 + 调用 handler
Recv --> Send : 被用户从外设收数据时调用
@enduml

5.2 port_entry_t

typedef struct {
    int                       type;     // 报文类型(DATAROUTER_CHANNEL_xxx / -1)
    datarouter_send_handler_t handler;  // 外设发送函数(NULL 表示未注册)
    uint8_t                   in_use;   // 1=已分配,0=空闲
} port_entry_t;

5.3 端口表

#define DATAROUTER_MAX_PORTS 64        // 可由 -D 覆盖
static port_entry_t ports[64];         // 静态数组,无动态内存
static uint8_t      initialized;       // init/reset 标志

6. API 参考

6.1 初始化

void datarouter_init(void);   // 幂等初始化
void datarouter_reset(void);  // 重置所有状态(测试隔离/热重启)

6.2 端口管理

datarouter_port_t datarouter_port_create(void);    // 返回端口 ID 或 -1
void              datarouter_port_delete(port);     // 删除端口,ID 可复用
void              datarouter_port_set_type(port, channel_id);  // 指定报文类型
int               datarouter_port_get_type(port);   // 获取报文类型,-1 表示无
void              datarouter_port_set_handler(port, handler);  // 注册外设发送 handler
datarouter_send_handler_t datarouter_port_get_handler(port);   // 获取 handler

6.3 发送触发函数(每通道 1 个,X-macro 生成)

int datarouter_send_sensor(uint32_t port_id, const void *data, size_t len);
int datarouter_send_status(uint32_t port_id, const void *data, size_t len);
int datarouter_send_control(uint32_t port_id, const void *data, size_t len);

返回值:

值 含义
0 成功,handler 已被调用
-1 库未初始化
-2 端口无效或已删除
-3 端口类型与触发的报文类型不匹配
-4 端口未注册外设发送 handler

6.4 接收回调声明(每通道 1 个,用户实现)

void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len);
void datarouter_on_recv_status(uint32_t port_id, const void *data, size_t len);
void datarouter_on_recv_control(uint32_t port_id, const void *data, size_t len);

库提供弱符号默认空实现,用户定义同名函数即可覆盖。

6.5 元数据查询

int         datarouter_channel_count(void);
const char *datarouter_channel_name(int channel_id);
const char *datarouter_channel_desc(int channel_id);

7. 核心流程

7.1 发送流程

@startuml
actor "应用" as App
participant "datarouter_send_sensor()" as Send
participant "端口表" as Table
participant "handler" as Handler
participant "外设" as Dev

App -> Send : send_sensor(port=2, data, len)

group 校验
  Send -> Send : initialized?
  alt 未初始化
    Send --> App : return -1
  end

  Send -> Table : ports[2].in_use?
  alt 端口无效
    Send --> App : return -2
  end

  Send -> Table : ports[2].type == sensor?
  alt 类型不匹配
    Send --> App : return -3
  end

  Send -> Table : ports[2].handler != NULL?
  alt 无 handler
    Send --> App : return -4
  end
end

Send -> Handler : handler(2, data, len)
Handler -> Dev : UART_SendData(data, len)
Handler --> Send : void
Send --> App : return 0
@enduml

7.2 接收流程

@startuml
actor "外设" as Dev
participant "用户分发代码" as Dispatch
participant "datarouter_on_recv_sensor()" as Callback
participant "用户处理逻辑" as Process

Dev -> Dispatch : 收到数据,识别为 sensor 类型
Dispatch -> Callback : on_recv_sensor(port_id, data, len)
Callback -> Process : 解析数据、更新状态等
Process --> Callback : void
Callback --> Dispatch : void
@enduml

7.3 端口生命周期

@startuml
state "空闲\nin_use=0" as Idle
state "已创建\nin_use=1\ntype=-1\nhandler=NULL" as Created
state "已配置\nin_use=1\ntype=sensor\nhandler=..." as Config

[*] --> Idle : datarouter_reset()

Idle --> Created : datarouter_port_create()

Created --> Config : set_type + set_handler
Config --> Config : set_type / set_handler\n可任意切换
Config --> Created : set_type(-1) / set_handler(NULL)\n清除能力

Created --> Idle : datarouter_port_delete()
Config --> Idle : datarouter_port_delete()
Idle --> Created : 复用 ID

@enduml

8. 路由示例

场景:传感器数据同时路由到 UART 和 TCP

应用
  │
  ├─ send_sensor(sensor_uart_port, data, len)
  │    │
  │    ▼
  │  handler = uart_send  → UART 外设
  │
  └─ send_sensor(sensor_tcp_port, data, len)
       │
       ▼
     handler = tcp_send   → TCP 外设

场景:运行时切换路由

端口 sensor_tcp 原 handler = tcp_send

datarouter_port_set_handler(sensor_tcp, file_send);

端口 sensor_tcp 现 handler = file_send  → 改为写入文件

9. 弱符号机制

@startuml
package "libDataRouter.a" {
  component "弱符号\nvoid on_recv_sensor(...){}" as Weak
}
package "用户代码" {
  component "强符号\nvoid on_recv_sensor(...){...}" as Strong
}
package "最终链接产物" {
  component "使用用户版本" as Result
}
Strong --> Result
Weak ..> Result : 后备
@enduml
  • 库为每个回调提供 __attribute__((weak)) 空实现
  • 用户定义同名函数自动覆盖
  • 未定义的回调在调用时静默空转

10. 错误码体系

返回值 含义
0 成功
-1 库未初始化
-2 端口无效(越界/未创建/已删除)
-3 端口类型不匹配
-4 端口未注册发送 handler

11. 配置扩展

增加报文类型

编辑 datarouter_config.h:

#define DATAROUTER_CHANNELS \
    DATAROUTER_CHANNEL(sensor,  "Sensor data")      \
    DATAROUTER_CHANNEL(status,  "Status report")    \
    DATAROUTER_CHANNEL(control, "Control command")  \
    DATAROUTER_CHANNEL(debug,   "Debug message")    /* 新增 */

重新编译后自动生成:枚举、发送函数、回调声明、元数据数组。

修改最大端口数

cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128" ...

12. 约束

约束 说明
报文类型 编译期固定,修改需重新编译
最大端口数 默认 64,可由 -D 覆盖
线程安全 不支持(无锁),多线程需外部加锁
弱符号 GCC/Clang 原生支持
内存分配 纯静态,无 malloc/free