# DataRouter 详细设计文档 ## 1. 设计目标 轻量级 C 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。 ### 核心设计 ``` DR_SEND_CAPABILITIES DR_RECV_CAPABILITIES DR_SEND(sensor, ...) DR_RECV(rtcm, ...) DR_SEND(status, ...) DR_RECV(command, ...) │ │ ▼ ▼ send_sensor(data, len) on_recv_rtcm(port, data, len) │ (用户实现回调) ▼ 遍历端口表 → send_type 匹配? │ ▼ for each handler(port, data) ─── UART / TCP / CAN ``` **关键**:send 无 port_id 参数,自动路由到所有匹配端口。 ### 设计原则 | 原则 | 说明 | |------|------| | 收发分离 | 发送/接收能力在配置中分开声明,按需组合 | | 编译期确定能力 | 所有能力在 `dr_config.h` 中声明,库自动生成代码 | | 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 | | 自动广播 | `send_(data, len)` 遍历所有端口,自动匹配 send_type | | 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler | | 零动态内存 | 纯静态数组,无 malloc/free | --- ## 2. 整体架构 ### 2.1 模块依赖 ```plantuml @startuml left to right direction rectangle "用户代码" as User { component "应用逻辑\n调 send_(data, len)" as App component "dr_config.h\n声明能力" as Cfg component "外设 handler\nUART / TCP / CAN" as Handlers component "接收回调\non_recv_()" as Callbacks } database "DataRouter 库" as Lib { component "datarouter.h\n公共 API + X-macro" as Hdr component "datarouter.c\n端口表+遍历路由" as Impl } App --> Hdr Cfg --> Hdr Impl --> Cfg Handlers --> Impl : add_handler Callbacks --> Impl : 实现 on_recv_ @enduml ``` ### 2.2 文件结构 | 文件 | 职责 | |------|------| | `dr_config.h` | 声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` | | `datarouter.h` | 公共 API + X-macro 展开 | | `datarouter.c` | 端口表 + send 遍历 + 弱符号回调 | | `example.c` | 使用示例 | | `tests/test_datarouter.c` | 22 个单元测试 | --- ## 3. 收发分离设计 ### 3.1 配置声明 ```c #define DR_SEND_CAPABILITIES \ DR_SEND(sensor, "Sensor data report") \ DR_SEND(status, "Device status report") #define DR_RECV_CAPABILITIES \ DR_RECV(rtcm, "RTCM correction data") \ DR_RECV(command, "Remote control command") ``` ### 3.2 展开产物 ``` DR_SEND_CAPABILITIES 展开: enum: sensor = 0, status = 1, SEND_CAP_COUNT = 2 发送函数: int dr_send_sensor(const void *data, size_t len); int dr_send_status(const void *data, size_t len); 元数据: send_names[] = {"sensor", "status"} send_descs[] = {"Sensor data report", "Device status report"} DR_RECV_CAPABILITIES 展开: enum: rtcm = 0, command = 1, RECV_CAP_COUNT = 2 接收回调: void dr_on_recv_rtcm(uint32_t port, const void*, size_t); void dr_on_recv_command(uint32_t port, const void*, size_t); 弱符号默认: 空实现,用户覆盖 元数据: recv_names[] = {"rtcm", "command"} recv_descs[] = {"RTCM correction data", "Remote control command"} ``` ### 3.3 端口示例 ``` 端口 A: send_type = sensor, handlers=[uart_send, tcp_send] 端口 B: send_type = sensor, handlers=[can_send] 端口 C: send_type = status, handlers=[uart_send] ``` 调用 `send_sensor("temp", 4)` → A 和 B 同时触发。 --- ## 4. X-macro 代码生成 ```plantuml @startuml == datarouter.h == -> dr_config.h: 获得两组宏列表 datarouter.h -> "1": #define DR_SEND(id,desc) DR_SEND_##id, note right: 发送 enum datarouter.h -> "2": #define DR_RECV(id,desc) DR_RECV_##id, note right: 接收 enum datarouter.h -> "3": #define DR_SEND(id,desc) int send_##id(data, len); note right: 发送函数声明 datarouter.h -> "4": #define DR_RECV(id,desc) void on_recv_##id(...); note right: 接收回调声明 == datarouter.c == datarouter.c -> "5": 弱符号回调默认实现 datarouter.c -> "6": 发送函数实现(遍历端口表) datarouter.c -> "7~8": send_names[] / send_descs[] / recv_names[] / recv_descs[] @enduml ``` --- ## 5. 数据结构 ### 5.1 核心数据类型 ```plantuml @startuml skinparam classAttributeIconSize 0 enum "dr_send_cap" as S { SENSOR, STATUS, SEND_CAP_COUNT } enum "dr_recv_cap" as R { RTCM, COMMAND, RECV_CAP_COUNT } class "port_entry_t" as Entry { - int send_type - dr_send_handler_t handlers[] - int count - uint8_t in_use } class "dr_send_handler_t" as Handler { + void (*)(uint32_t port_id, const void *data, size_t len) } class "发送函数" as Send { + int send_sensor(data, len) + int send_status(data, len) } class "接收回调" as Recv { + void on_recv_rtcm(port, data, len) ← 用户实现 + void on_recv_command(port, data, len) } Entry --> S : send_type Entry --> Handler : handlers[] Send --> Entry : 遍历端口表 → 匹配 send_type → 遍历 handlers Recv --> Entry : 用户根据业务调对应回调 @enduml ``` ### 5.2 `port_entry_t` ```c #define DR_MAX_HANDLERS_PER_PORT 4 typedef struct { int send_type; dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT]; int count; uint8_t in_use; } port_entry_t; ``` ### 5.3 端口表 ```c #define DR_MAX_PORTS 64 static port_entry_t ports[DR_MAX_PORTS]; static uint8_t initialized; ``` --- ## 6. API 参考 ### 6.1 初始化 ```c void dr_init(void); // 幂等初始化 void dr_reset(void); // 重置全部状态 ``` ### 6.2 端口管理 ```c dr_port_t dr_port_create(void); void dr_port_delete(dr_port_t port); void dr_port_set_send_type(port, int cap_id); int dr_port_get_send_type(port); int dr_port_add_handler(port, handler); int dr_port_remove_handler(port, handler); void dr_port_clear_handlers(port); int dr_port_handler_count(port); ``` ### 6.3 发送触发函数(每个 SEND 能力生成一个) ```c int dr_send_sensor(const void *data, size_t len); int dr_send_status(const void *data, size_t len); ``` 返回值:0 成功 / -1 未初始化 / -2 无端口匹配该 send_type ### 6.4 接收回调(每个 RECV 能力生成一个声明) ```c void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len); void dr_on_recv_command(uint32_t port_id, const void *data, size_t len); ``` 库提供弱符号默认空实现。 ### 6.5 能力元数据 ```c int dr_send_cap_count(void); const char *dr_send_cap_name(int cap_id); const char *dr_send_cap_desc(int cap_id); int dr_recv_cap_count(void); const char *dr_recv_cap_name(int cap_id); const char *dr_recv_cap_desc(int cap_id); ``` --- ## 7. 核心流程 ### 7.1 发送流程 ```plantuml @startuml actor "应用" as App participant "dr_send_sensor()" as Send participant "端口表 ports[]" as Table participant "handlers[]" as HList participant "外设" as Dev App -> Send : send_sensor("temp:25.3", 9) group 初始化检查 Send -> Send : initialized? alt 否 → return -1 end end group 遍历端口表 loop for p in 0..MAX_PORTS-1 Send -> Table : ports[p].in_use && ports[p].send_type == SENSOR ? alt 匹配 loop for h in 0..count-1 Send -> HList : handlers[h](p, data, len) HList -> Dev : 实际外设写入 end end end end alt 至少调了一次 handler Send --> App : return 0 else 无匹配端口 Send --> App : return -2 end @enduml ``` ### 7.2 接收流程 ```plantuml @startuml actor "外设" as Dev participant "用户分发代码" as Dispatch participant "on_recv_rtcm()" as Callback Dev -> Dispatch : 收到数据,识别为 rtcm Dispatch -> Callback : on_recv_rtcm(port_id, data, len) Callback --> Dispatch : void @enduml ``` ### 7.3 端口生命周期 ```plantuml @startuml state "空闲" as Idle state "已创建\nsend_type=-1\ncount=0" as Created state "已配置" as Config [*] --> Idle : reset / 启动 Idle --> Created : port_create() Created --> Config : set_send_type / add_handler Config --> Config : 切换 send_type\n增删 handler Config --> Created : set_send_type(-1) / clear_handlers Created --> Idle : port_delete() Config --> Idle : port_delete() Idle --> Created : 复用 ID @enduml ``` --- ## 8. 路由场景 ### 场景 1:sensor 广播到 UART + TCP + CAN ```c dr_port_t p1 = dr_port_create(); dr_port_set_send_type(p1, DR_SEND_sensor); dr_port_add_handler(p1, uart_send); dr_port_add_handler(p1, tcp_send); dr_port_t p2 = dr_port_create(); dr_port_set_send_type(p2, DR_SEND_sensor); dr_port_add_handler(p2, can_send); dr_send_sensor("temp:25.3", 9); // → uart_send(p1, "temp:25.3", 9) // → tcp_send(p1, "temp:25.3", 9) // → can_send(p2, "temp:25.3", 9) ``` ### 场景 2:仅接收 ```c void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { apply_rtcm_correction(data, len); } // 外设收到后:dr_on_recv_rtcm(port, data, len); ``` ### 场景 3:运行时移除 handler ```c dr_port_remove_handler(p1, tcp_send); // 下次 send_sensor 不再触发 tcp ``` --- ## 9. 弱符号机制 ```plantuml @startuml package "libDataRouter.a" { component "弱符号\nvoid on_recv_rtcm(...){}" as Weak } package "用户代码" { component "强符号\nvoid on_recv_rtcm(...){...}" as Strong } package "最终链接产物" { component "使用用户版本" as Result } Strong --> Result Weak ..> Result : 后备 @enduml ``` --- ## 10. 错误码 | 返回值 | 含义 | |:------:|------| | 0 | 成功,至少有一个 handler 被调用 | | -1 | 库未初始化(未调 init) | | -2 | 无端口匹配该 send_type(或匹配端口均无 handler) | --- ## 11. 配置扩展 ```c #define DR_SEND_CAPABILITIES \ DR_SEND(sensor, "Sensor data") \ DR_SEND(debug, "Debug log") /* 新增 */ #define DR_RECV_CAPABILITIES \ DR_RECV(rtcm, "RTCM data") \ DR_RECV(ota, "OTA firmware") /* 新增 */ ``` ```bash cmake -DCMAKE_C_FLAGS="-DDR_MAX_PORTS=128 -DDR_MAX_HANDLERS_PER_PORT=8" ``` --- ## 12. 约束 | 约束 | 说明 | |------|------| | 能力声明 | 编译期固定,修改需重新编译 | | 最大端口数 | 默认 64,通过 `-D` 覆盖 | | 每端口 handler 上限 | 默认 4,通过 `-D` 覆盖 | | 线程安全 | 不支持(无锁),多线程需外部加锁 | | 弱符号 | GCC/Clang 原生支持 | | 内存分配 | 纯静态,无 malloc/free |