# 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 模块依赖 ```plantuml @startuml left to right direction rectangle "用户代码" as User { component "应用逻辑\n调用 datarouter_send_()" 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_ @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) ```plantuml @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 ```plantuml @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 展开流程 ```plantuml @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 核心数据类型 ```plantuml @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` ```c 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 端口表 ```c #define DATAROUTER_MAX_PORTS 64 // 可由 -D 覆盖 static port_entry_t ports[64]; // 静态数组,无动态内存 static uint8_t initialized; // init/reset 标志 ``` --- ## 6. API 参考 ### 6.1 初始化 ```c void datarouter_init(void); // 幂等初始化 void datarouter_reset(void); // 重置所有状态(测试隔离/热重启) ``` ### 6.2 端口管理 ```c 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 生成) ```c 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 个,用户实现) ```c 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 元数据查询 ```c int datarouter_channel_count(void); const char *datarouter_channel_name(int channel_id); const char *datarouter_channel_desc(int channel_id); ``` --- ## 7. 核心流程 ### 7.1 发送流程 ```plantuml @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 接收流程 ```plantuml @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 端口生命周期 ```plantuml @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. 弱符号机制 ```plantuml @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`: ```c #define DATAROUTER_CHANNELS \ DATAROUTER_CHANNEL(sensor, "Sensor data") \ DATAROUTER_CHANNEL(status, "Status report") \ DATAROUTER_CHANNEL(control, "Control command") \ DATAROUTER_CHANNEL(debug, "Debug message") /* 新增 */ ``` 重新编译后自动生成:枚举、发送函数、回调声明、元数据数组。 ### 修改最大端口数 ```bash cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128" ... ``` --- ## 12. 约束 | 约束 | 说明 | |------|------| | 报文类型 | 编译期固定,修改需重新编译 | | 最大端口数 | 默认 64,可由 `-D` 覆盖 | | 线程安全 | 不支持(无锁),多线程需外部加锁 | | 弱符号 | GCC/Clang 原生支持 | | 内存分配 | 纯静态,无 malloc/free |