diff --git a/DESIGN.md b/DESIGN.md index c671a88..668fe85 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -2,208 +2,57 @@ ## 1. 设计目标 -轻量级 C 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。 +轻量级 C 语言数据路由库。发送能力与接收能力分开声明,端口可独立设置 `send_type` 和 `recv_type`。发送时自动广播到所有匹配端口,接收时通过 `dr_port_dispatch()` 查表自动路由到回调。 ### 核心设计 ``` -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) - │ (用户实现回调) - ▼ +发送: dr_send_sensor(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 +接收: dr_port_dispatch(port, data, len) + │ + ▼ + 查 recv_table[port.recv_type] + │ + ▼ + dr_on_recv_rtcm(port, data, len) ← 用户实现 ``` --- -## 5. 数据结构 +## 2. 数据结构 -### 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` +### 2.1 `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; + int send_type; // DR_SEND_xxx / -1 + int recv_type; // DR_RECV_xxx / -1 + dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT]; + int count; + uint8_t in_use; } port_entry_t; ``` -### 5.3 端口表 +### 2.2 接收回调函数指针表 + +```c +// 编译期由 X-macro 生成,DR_RECV_rtcm 的枚举值即为数组下标 +static void (*const recv_table[DR_RECV_CAP_COUNT])(uint32_t, const void*, size_t) = { + dr_on_recv_rtcm, + dr_on_recv_command, +}; +``` + +### 2.3 端口表 ```c #define DR_MAX_PORTS 64 @@ -213,224 +62,121 @@ static uint8_t initialized; --- -## 6. API 参考 +## 3. API -### 6.1 初始化 - -```c -void dr_init(void); // 幂等初始化 -void dr_reset(void); // 重置全部状态 -``` - -### 6.2 端口管理 +### 3.1 端口管理 ```c dr_port_t dr_port_create(void); -void dr_port_delete(dr_port_t port); +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); +void dr_port_set_send_type(port, int cap_id); +int dr_port_get_send_type(port); +void dr_port_set_recv_type(port, int cap_id); +int dr_port_get_recv_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); +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); + +void dr_port_dispatch(port, data, len); // ← 自动路由接收 ``` -### 6.3 发送触发函数(每个 SEND 能力生成一个) +### 3.2 发送触发 ```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 +返回值:0 成功 / -1 未初始化 / -2 无匹配端口 -### 6.4 接收回调(每个 RECV 能力生成一个声明) +### 3.3 接收回调 ```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. 核心流程 +## 4. 核心流程 -### 7.1 发送流程 +### 4.1 发送 ```plantuml @startuml -actor "应用" as App -participant "dr_send_sensor()" as Send -participant "端口表 ports[]" as Table -participant "handlers[]" as HList -participant "外设" as Dev +actor App +participant dr_send_sensor as Send +participant ports[] as Table +participant handlers[] as H -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 +App -> Send : send_sensor("temp", 9) +loop for p in 0..MAX_PORTS-1 + Send -> Table : in_use && send_type == SENSOR ? + alt 匹配 + loop for h in handlers + Send -> H : handlers[h](p, data, len) end end end - -alt 至少调了一次 handler +alt 至少调了一次 Send --> App : return 0 -else 无匹配端口 +else 无匹配 Send --> App : return -2 end @enduml ``` -### 7.2 接收流程 +### 4.2 接收(dispatch) ```plantuml @startuml -actor "外设" as Dev -participant "用户分发代码" as Dispatch -participant "on_recv_rtcm()" as Callback +actor 外设ISR as Dev +participant dr_port_dispatch as Dispatch +participant recv_table[] as Table +participant dr_on_recv_rtcm as Cb -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 +Dev -> Dispatch : dispatch(port, data, len) +Dispatch -> Table : 查 recv_table[port.recv_type] +Dispatch -> Cb : recv_table[rid](port, data, len) +Cb --> Dev : void @enduml ``` --- -## 8. 路由场景 +## 5. Dispatch 实现 -### 场景 1:sensor 广播到 UART + TCP + CAN +`dr_port_dispatch` 是零分支的查表路由: ```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); +void dr_port_dispatch(dr_port_t port, const void *data, size_t len) +{ + if (!initialized) return; + if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return; + int rid = ports[port].recv_type; + if (rid < 0 || rid >= DR_RECV_CAP_COUNT) return; + recv_table[rid]((uint32_t)port, data, len); // 直接函数指针调用 } -// 外设收到后:dr_on_recv_rtcm(port, data, len); ``` -### 场景 3:运行时移除 handler - -```c -dr_port_remove_handler(p1, tcp_send); -// 下次 send_sensor 不再触发 tcp -``` +`recv_table[]` 是 `const` 数组,编译期由 X-macro 生成。`DR_RECV_rtcm` 的枚举值等于数组下标,查表时间为 O(1),无分支预测开销,适合 ISR。 --- -## 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. 错误码 +## 6. 错误码 | 返回值 | 含义 | |:------:|------| -| 0 | 成功,至少有一个 handler 被调用 | -| -1 | 库未初始化(未调 init) | -| -2 | 无端口匹配该 send_type(或匹配端口均无 handler) | +| 0 | 成功 | +| -1 | 库未初始化 | +| -2 | 无端口匹配该 send_type | --- -## 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. 约束 +## 7. 约束 | 约束 | 说明 | |------|------| diff --git a/README.md b/README.md index 960516d..594bcc2 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,22 @@ # DataRouter -轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_(data, len)` 时自动广播到所有匹配端口。 +轻量级 C 语言数据路由库。发送能力和接收能力分开声明,端口绑定 `send_type` + `recv_type` + handler 列表。发送时自动广播到所有匹配端口,接收时 `dr_port_dispatch()` 自动路由到对应回调。 ## 概念 | 术语 | 说明 | |------|------| -| **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `dr_send_(data, len)` | -| **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `dr_on_recv_(port, data, len)` | -| **端口 (Port)** | 运行期创建的连接点,包含 `send_type` + handler 列表 | +| **发送能力** | 编译期声明的可发送报文类型,生成 `dr_send_(data, len)` | +| **接收能力** | 编译期声明的可接收报文类型,生成 `dr_on_recv_(port, data, len)` | +| **端口** | 运行期连接点,包含 `send_type` + `recv_type` + handler 列表 | | **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) | +| **Dispatch** | `dr_port_dispatch(port, data, len)` 根据端口的 `recv_type` 自动路由到回调 | ## 快速开始 ### 1. 声明能力 -创建自己的 `dr_config.h`(可参考 `dr_config_template.h`): +创建自己的 `datarouter_config.h`(可参考 `datarouter_config_template.h`): ```c #define DR_SEND_CAPABILITIES \ @@ -27,24 +28,15 @@ DR_RECV(command, "Remote control command") ``` -> 库自带默认配置。将你的 `dr_config.h` 放到项目目录, -> 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源码。 -> -> ```cmake -> include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) -> add_subdirectory(path/to/DataRouter) -> target_link_libraries(my_app DataRouter) -> ``` - -### 2. 实现外设发送 handler +### 2. 实现 handler 和回调 ```c -static void uart_send(uint32_t port_id, const void *data, size_t len) { +static void uart_write(uint32_t port_id, const void *data, size_t len) { HAL_UART_Transmit(&huart1, data, len, 1000); } -static void tcp_send(uint32_t port_id, const void *data, size_t len) { - send(tcp_socket, data, len, 0); +void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { + apply_rtcm_correction(data, len); } ``` @@ -53,25 +45,17 @@ static void tcp_send(uint32_t port_id, const void *data, size_t len) { ```c dr_init(); -/* —— 传感器上报:sensor 报文走 UART + TCP —— */ -dr_port_t tx = dr_port_create(); -dr_port_set_send_type(tx, DR_SEND_sensor); -dr_port_add_handler(tx, uart_send); -dr_port_add_handler(tx, tcp_send); +dr_port_t uart = dr_port_create(); +dr_port_set_send_type(uart, DR_SEND_sensor); +dr_port_set_recv_type(uart, DR_RECV_rtcm); +dr_port_add_handler(uart, uart_write); +dr_port_add_handler(uart, tcp_write); -/* 不指定端口,库自动广播到所有 send_type=sensor 的端口 */ -dr_send_sensor("temperature:25.3", 16); // → UART + TCP +/* 发送:自动广播到所有 send_type=sensor 的端口 */ +dr_send_sensor("temperature:25.3", 16); -/* —— 接收:实现回调,外设数据到达后自行调用 —— */ -dr_on_recv_rtcm(rx_port, data, len); -``` - -### 4. 实现接收回调 - -```c -void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { - apply_rtcm_correction(data, len); -} +/* 接收:外设中断中一行 dispatch,自动调 dr_on_recv_rtcm() */ +dr_port_dispatch(uart, data, len); ``` ## API 一览 @@ -79,45 +63,45 @@ void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { ### 初始化 ```c -void dr_init(void); // 幂等初始化 -void dr_reset(void); // 重置全部状态 +void dr_init(void); +void dr_reset(void); ``` ### 端口管理 ```c -dr_port_t dr_port_create(void); // 创建端口 -void dr_port_delete(dr_port_t port); // 删除 +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); // -1 = 未设置 +void dr_port_set_send_type(port, int cap_id); +int dr_port_get_send_type(port); +void dr_port_set_recv_type(port, int cap_id); +int dr_port_get_recv_type(port); -int dr_port_add_handler(port, handler); // 注册外设 handler -int dr_port_remove_handler(port, handler); // 移除 -void dr_port_clear_handlers(port); // 清除全部 -int dr_port_handler_count(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); + +void dr_port_dispatch(port, data, len); /* 自动路由接收 */ ``` -### 发送触发(每个 SEND 能力生成一个) +### 发送触发 ```c int dr_send_sensor(const void *data, size_t len); // 0 成功 int dr_send_status(const void *data, size_t len); // -1 未初始化 - // -2 无匹配端口 + // -2 无匹配端口 ``` -不指定端口,库遍历所有端口,`send_type` 匹配的逐一调用其全部 handler。 - -### 接收回调(每个 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); ``` -库提供弱符号默认空实现,用户定义同名函数覆盖。 - -### 能力元数据(两组独立) +### 能力元数据 ```c int dr_send_cap_count(void); @@ -132,114 +116,23 @@ const char *dr_recv_cap_desc(int cap_id); ## 路由模型 ``` -dr_send_sensor("temperature:25.3", 16) +发送:dr_send_sensor("temp:25.3", 9) │ - ├─→ port 0 (send_type=sensor) handlers[0]=uart_send → UART - │ handlers[1]=tcp_send → TCP - └─→ port 1 (send_type=sensor) handlers[0]=can_send → CAN + ├─→ port A (send_type=sensor) handlers = [uart_write, tcp_write] + └─→ port B (send_type=sensor) handlers = [can_write] + +接收:dr_port_dispatch(uart1, data, len) + │ + └─→ uart1.recv_type = DR_RECV_rtcm + └─→ dr_on_recv_rtcm(uart1, data, len) ← 查表调用 ``` -- 调用 `send_(data, len)` 时,库遍历所有端口,自动匹配 `send_type` -- 每个端口可注册多个 handler(一对多广播) -- 同一 handler 可注册到多个端口(多路复用) -- 运行时通过 `add_handler` / `remove_handler` / `port_delete` 动态改变路由 -- 无任何端口匹配该 `send_type` 时返回 -2 - -## 编译 - -```bash -cmake -S . -B build -cmake --build build -``` - -可选配置: - -```bash -cmake -DCMAKE_C_FLAGS="-DDR_MAX_PORTS=128 -DDR_MAX_HANDLERS_PER_PORT=8" -``` - -## 嵌入式 / Cortex-M 构建 - -库支持 `arm-none-eabi-gcc` 交叉编译,提供 **7 个 CMake preset** 覆盖常见 Cortex-M 目标: - -```bash -# 列出所有 preset -cmake --list-presets - -# Cortex-M3(软浮点) -cmake --preset cm3 -cmake --build --preset cm3 - -# Cortex-M4F(硬件浮点,单精度) -cmake --preset cm4-hard -cmake --build --preset cm4-hard - -# Cortex-M7F(硬件浮点,双精度) -cmake --preset cm7-hard-dp -cmake --build --preset cm7-hard-dp -``` - -> 使用 preset 编译需要 `arm-none-eabi-gcc` 在 PATH 中。 -> 各 preset 均启用 `DR_SOURCE_ONLY` 模式——只提供源文件路径, -> 你需要将 `datarouter.c` 加入自己的 target 以继承项目编译标志: - -```cmake -# 你的 CMakeLists.txt -set(DR_SOURCE_ONLY ON) -FetchContent_MakeAvailable(datarouter) -target_sources(my_app PRIVATE ${datarouter_SOURCE_DIR}/datarouter.c) -target_include_directories(my_app PRIVATE ${datarouter_SOURCE_DIR}) -``` - -### 编译兼容性矩阵 - -启用 `DR_COMPILE_CHECK` 可自动检测 `arm-none-eabi-gcc` 并对 CM3/CM4/CM7 -各变体执行编译验证: - -```bash -cmake -B build-check -DDR_COMPILE_CHECK=ON -cmake --build build-check # 生成 check_cm3, check_cm4-hard, … 等 target -# 或单独检查某个变体: -make check_cm7-hard-dp -``` - -### 内置 CMake Preset 列表 - -| Preset | 目标 | FPU | ABI | -|--------|------|-----|-----| -| `cm3` | Cortex-M3 | 无 | soft | -| `cm4-soft` | Cortex-M4 | 无 | soft | -| `cm4-hard` | Cortex-M4F | FPv4-SP-D16 | hard | -| `cm7-soft` | Cortex-M7 | 无 | soft | -| `cm7-hard-sp` | Cortex-M7F | FPv5-SP-D16 | hard | -| `cm7-hard-dp` | Cortex-M7F | FPv5-D16 | hard | - ## 测试 ```bash cmake --build build ctest --test-dir build -# 或直接运行 ./build/test_datarouter ``` -当前 22 个测试用例,覆盖端口管理、send_type 与 handler 设置、自动广播路由、接收回调、能力元数据。 - -## 文件结构 - -``` -dr_config.h 发送 + 接收能力配置(用户可覆盖) -datarouter.h 公共 API + X-macro 展开 -datarouter.c 实现(端口表 + send 遍历) -dr_config_template.h 外部配置模板 -example.c 使用示例 -tests/test_datarouter.c 单元测试(22 用例) -DESIGN.md 详细设计文档(含 PlantUML 图) -TEST_CASES.md 测试用例文档 -``` - -## 依赖 - -- C11 编译器 -- CMake ≥ 3.10 -- 无第三方库依赖 +当前 24 个测试用例,覆盖端口管理、send_type/recv_type、handler、自动广播、dispatch、能力元数据。 diff --git a/TEST_CASES.md b/TEST_CASES.md index 95e185e..1e7c763 100644 --- a/TEST_CASES.md +++ b/TEST_CASES.md @@ -26,63 +26,65 @@ ctest --test-dir build | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 1 | `error_uninit` | 未 init 时发送 | `send_sensor("d", 1)` | 返回 -1 | +| 1 | `error_uninit` | 未 init 时发送 | `dr_send_sensor("d", 1)` | 返回 -1 | ### 2. 端口管理(4 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 2 | `port_create_basic` | 基本创建 | `port_create()` | 返回 ≥ 0 | -| 3 | `port_create_max` | 满容量 64 | 循环创建到满再尝试 | 64 个成功,第 65 个 INVALID | +| 2 | `port_create_basic` | 基本创建 | `dr_port_create()` | 返回 ≥ 0 | +| 3 | `port_create_max` | 满容量 64 | 循环到满 | 第 65 个 INVALID | | 4 | `port_delete_invalid` | 删除无效 ID | `delete(-1)` + `delete(999)` | 不崩溃 | -| 5 | `port_default_state` | 默认状态 | 新建端口,查 send_type / handler count | `send_type=-1`, `count=0` | +| 5 | `port_default_state` | 默认状态 | 查 send_type / recv_type / count | 均为 -1 / 0 | -### 3. send_type 与 handler(3 个) +### 3. send_type / recv_type / handler(4 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 6 | `send_type_set_get` | 设置并清除 send_type | `set_send_type(sensor)` → `get` → `set(-1)` → `get` | sensor → -1 | -| 7 | `handler_add_remove` | 添加后移除 handler | add(h) count=1 → remove(h) count=0 | count 正确 | -| 8 | `handler_clear` | 清除全部 handler | add → clear | count == 0 | +| 6 | `send_type_set_clear` | 设置并清除 send_type | set(sensor) → get → set(-1) → get | sensor → -1 | +| 7 | `recv_type_set_clear` | 设置并清除 recv_type | set(rtcm) → get → set(-1) → get | rtcm → -1 | +| 8 | `types_independent` | 收发类型独立设置 | 同时 set_send(sensor) + set_recv(command) | 各自返回正确值 | +| 9 | `handler_add_remove` | 添加后移除 handler | add(h) → count=1 → remove(h) → count=0 | 正确 | -### 4. 发送 — 自动广播(11 个) +### 4. 发送 — 自动广播(7 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 9 | `send_no_match_port` | 无匹配端口 | 不创建任何端口,直接 `send_sensor` | 返回 -2 | -| 10 | `send_one_port_one_handler` | 单端口单 handler | 创建端口中 send_type + add(h) → send | 返回 0;handler 1 次 | -| 11 | `send_one_port_multi_handler` | 单端口双 handler | add(h) + add(h2) → send | handler 2 次 | -| 12 | `send_multi_ports_same_type` | 两端口同类型 | p1(sensor,h) + p2(sensor,h) → send | handler 2 次,各端口一次 | -| 13 | `send_multi_port_multi_handler` | 多端口多 handler | p1(h,h2) + p2(h3) → send | handler 3 次 | -| 14 | `send_type_filter` | 类型过滤 | p1(sensor,h) + p2(status,h) → `send_sensor` | 只触发 p1 | -| 15 | `send_after_remove_handler` | 移除 handler 后 | add(h) → send count=1 → remove(h) → send | 第二次返回 -2 | -| 16 | `send_after_delete_port` | 删除端口后 | 创建→设好→删除→send | 返回 -2 | -| 17 | `send_zero_length` | 零长度数据 | send("x", 0) | 返回 0;handler 被调 | -| 18 | `send_null_data` | NULL 数据 | send(NULL, 0) | 返回 0;handler 被调 | -| 19 | `send_multi_cap` | 多能力独立 | p1(sensor) + p2(status) → 分别 send_sensor + send_status | 各触发匹配的端口 | +| 10 | `send_no_match_port` | 无匹配端口 | 不创建端口直接 send | 返回 -2 | +| 11 | `send_one_port_one_handler` | 单端口单 handler | send_type + add → send | 0,handler 1 次 | +| 12 | `send_multi_ports` | 两端口同类型 | p1(sensor) + p2(sensor) → send | handler 2 次 | +| 13 | `send_type_filter` | 类型过滤 | p1(sensor) + p2(status) → send_sensor | 只触发 p1 | +| 14 | `send_after_delete` | 删除端口后 send | 创建→设好→删除→send | 返回 -2 | +| 15 | `send_zero_length` | 零长度数据 | send("x", 0) | 0,handler 被调 | +| 16 | `send_null_data` | NULL 数据 | send(NULL, 0) | 0,handler 被调 | -### 5. 接收回调(1 个) +### 5. 接收回调 + dispatch(6 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 20 | `recv_callback` | 用户实现接收回调 | `on_recv_rtcm(0, "d", 1)` | 回调被触发 | +| 17 | `recv_callback_direct` | 直接调用回调 | `dr_on_recv_rtcm()` + `dr_on_recv_command()` | 各自触发 | +| 18 | `dispatch_valid` | dispatch 自动路由 rtcm | set_recv(rtcm) → dispatch(port, data) | dr_on_recv_rtcm 触发 | +| 19 | `dispatch_command` | dispatch 自动路由 command | set_recv(command) → dispatch(port, data) | dr_on_recv_command 触发 | +| 20 | `dispatch_unset` | recv_type 未设置 | recv_type=-1 → dispatch | 无回调触发 | +| 21 | `dispatch_invalid_port` | 无效端口 dispatch | dispatch(999/-1, ...) | 无回调触发 | +| 22 | `dispatch_deleted_port` | 已删端口 dispatch | 创建→设置→删除→dispatch | 无回调触发 | ### 6. 能力元数据(2 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 21 | `send_cap_meta` | 发送能力元数据 | count / name / desc / 边界 | SEND 两组正确 | -| 22 | `recv_cap_meta` | 接收能力元数据 | count / name / desc / 边界 | RECV 两组正确 | +| 23 | `send_cap_meta` | 发送能力元数据 | count / name / 边界 | 正确 | +| 24 | `recv_cap_meta` | 接收能力元数据 | count / name / 边界 | 正确 | ## 覆盖矩阵 | 功能模块 | 用例数 | 主要验证点 | |----------|:------:|------------| | 未初始化 | 1 | send → -1 | -| 端口管理 | 4 | 创建/满容量/删除异常/默认状态 | -| send_type & handler | 3 | 设置/清除/添加/移除/清空 | -| 发送自动广播 | 11 | 无匹配/单播/多 handler 单端口/多端口同类型/多端口多 handler/类型过滤/移除后/删除后/零长/NULL/多能力独立 | -| 接收回调 | 1 | 用户实现覆盖弱符号 | -| 能力元数据 | 2 | SEND 与 RECV 两组独立查询 | +| 端口管理 | 4 | 创建/满容量/删除/默认状态 | +| 收发类型 & handler | 4 | send_type/recv_type 独立设置、handler 增删 | +| 发送自动广播 | 7 | 单播/多播/过滤/删除后/零长/NULL | +| 接收 dispatch | 6 | 直接回调/有效路由/无效路由/未设置/无效端口/已删除 | +| 能力元数据 | 2 | 两组独立查询 | -**总计:22 个测试用例** +**总计:24 个测试用例** diff --git a/build/DataRouterConfig.cmake b/build/DataRouterConfig.cmake index a68423a..8de9700 100644 --- a/build/DataRouterConfig.cmake +++ b/build/DataRouterConfig.cmake @@ -3,7 +3,7 @@ ####### Any changes to this file will be overwritten by the next CMake run #### ####### The input file was DataRouterConfig.cmake.in ######## -get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) macro(set_and_check _var _file) set(${_var} "${_file}") @@ -24,12 +24,23 @@ endmacro() #################################################################################### -set_and_check(DataRouter_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include") -set_and_check(DataRouter_LIBRARY_DIR "${PACKAGE_PREFIX_DIR}/lib") +get_filename_component(DataRouter_INCLUDE_DIR + "${CMAKE_CURRENT_LIST_DIR}/../../../include" ABSOLUTE) +get_filename_component(DataRouter_LIBRARY_DIR + "${CMAKE_CURRENT_LIST_DIR}/../../../lib" ABSOLUTE) include("${CMAKE_CURRENT_LIST_DIR}/DataRouterTargets.cmake" OPTIONAL) -if(NOT TARGET DataRouter::DataRouter) +# 无论 targets 文件是否加载,都覆盖 include 路径为正确值 +# (targets 文件中的 _IMPORT_PREFIX 在某些平台/工具链下可能计算异常) +if(TARGET DataRouter) + set_target_properties(DataRouter PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${DataRouter_INCLUDE_DIR}" + ) + if(NOT TARGET DataRouter::DataRouter) + add_library(DataRouter::DataRouter ALIAS DataRouter) + endif() +else() add_library(DataRouter::DataRouter STATIC IMPORTED) set_target_properties(DataRouter::DataRouter PROPERTIES IMPORTED_LOCATION "${DataRouter_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}DataRouter${CMAKE_STATIC_LIBRARY_SUFFIX}" diff --git a/build/example b/build/example index c2ee587..330e15a 100755 Binary files a/build/example and b/build/example differ diff --git a/build/test_datarouter b/build/test_datarouter index 2a205d0..c263390 100755 Binary files a/build/test_datarouter and b/build/test_datarouter differ diff --git a/datarouter.c b/datarouter.c index 8cc2c76..6e95fec 100644 --- a/datarouter.c +++ b/datarouter.c @@ -17,7 +17,8 @@ typedef struct { int send_type; - dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT]; + int recv_type; + dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT]; int count; uint8_t in_use; } port_entry_t; @@ -51,6 +52,14 @@ static const char *recv_descs[] = { #undef DR_RECV }; +/* ---- 接收回调函数指针表(dispatch 查表用) ---- */ + +static void (*const recv_table[DR_RECV_CAP_COUNT])(uint32_t, const void*, size_t) = { +#define DR_RECV(id, desc) dr_on_recv_##id, + DR_RECV_CAPABILITIES +#undef DR_RECV +}; + /* ---- 弱符号接收回调 ---- */ #define DR_RECV(id, desc) \ @@ -102,6 +111,7 @@ dr_port_t dr_port_create(void) if (!ports[i].in_use) { ports[i].in_use = 1; ports[i].send_type = -1; + ports[i].recv_type = -1; ports[i].count = 0; return (dr_port_t)i; } @@ -130,6 +140,29 @@ int dr_port_get_send_type(dr_port_t port) return ports[port].send_type; } +void dr_port_set_recv_type(dr_port_t port, int cap_id) +{ + if (!initialized) return; + if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return; + ports[port].recv_type = cap_id; +} + +int dr_port_get_recv_type(dr_port_t port) +{ + if (!initialized) return -1; + if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return -1; + return ports[port].recv_type; +} + +void dr_port_dispatch(dr_port_t port, const void *data, size_t len) +{ + if (!initialized) return; + if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return; + int rid = ports[port].recv_type; + if (rid < 0 || rid >= DR_RECV_CAP_COUNT) return; + recv_table[rid]((uint32_t)port, data, len); +} + int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler) { if (!initialized) return -1; diff --git a/datarouter.h b/datarouter.h index ff89a33..fa867ec 100644 --- a/datarouter.h +++ b/datarouter.h @@ -6,14 +6,17 @@ * * - **发送能力**:编译期声明的可发送报文类型,生成 dr_send_(data, len) * - **接收能力**:编译期声明的可接收报文类型,生成 dr_on_recv_(port, data, len) - * - **端口**:运行期创建的连接点,包含 send_type + handler 列表 + * - **端口**:运行期连接点,包含 send_type + recv_type + handler 列表 * - * ## 路由 + * ## 发送路由 * - * 调用 dr_send_(data, len) 时,库遍历所有端口, - * 找到 send_type 匹配的端口,逐一调用其全部 handler。无需指定从哪个端口发送。 + * dr_send_(data, len) 遍历所有端口,找到 send_type 匹配的端口, + * 逐一调用其全部 handler,无需指定从哪个端口发送。 * - * 接收侧由用户自行调用 dr_on_recv_(port, data, len) 处理外设收到的数据。 + * ## 接收路由 + * + * 外设(UART / SPI / CAN)收到数据后,调用 dr_port_dispatch(port, data, len), + * 库根据端口的 recv_type 自动查表调用对应的 dr_on_recv_(),无需手动 if/else。 */ #ifndef DR_H @@ -89,12 +92,26 @@ void dr_port_delete(dr_port_t port); void dr_port_set_send_type(dr_port_t port, int cap_id); int dr_port_get_send_type(dr_port_t port); +void dr_port_set_recv_type(dr_port_t port, int cap_id); +int dr_port_get_recv_type(dr_port_t port); int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler); int dr_port_remove_handler(dr_port_t port, dr_send_handler_t handler); void dr_port_clear_handlers(dr_port_t port); int dr_port_handler_count(dr_port_t port); +/** + * @brief 按端口绑定的 recv_type 自动路由到对应的 dr_on_recv_() 回调 + * + * 外设中断中收到数据后调用此函数,无需自己写 if/else 判断报文类型。 + * 库通过 X-macro 生成函数指针表,直接查表调用,零分支预测开销。 + * + * @param[in] port 接收端口 ID + * @param[in] data 数据缓冲区 + * @param[in] len 数据长度 + */ +void dr_port_dispatch(dr_port_t port, const void *data, size_t len); + /* ========== 能力元数据查询 ========== */ int dr_send_cap_count(void); diff --git a/example.c b/example.c index 6bca696..bcf1ce1 100644 --- a/example.c +++ b/example.c @@ -1,73 +1,76 @@ /** * @file example.c * - * 调用 dr_send_sensor(data, len) 无需指定端口, - * 库自动广播到所有 send_type=sensor 的端口。 + * 展示: + * 1. send_type + 多 handler 发送广播 + * 2. recv_type + dr_port_dispatch 自动路由接收 + * 3. 外设中断中只需一行 dr_port_dispatch() */ #include "datarouter.h" #include -static void uart_send(uint32_t port_id, const void *data, size_t len) +/* ---- 发送 handler ---- */ + +static void uart_write(uint32_t port_id, const void *data, size_t len) { printf("[UART port %u] %.*s\n", port_id, (int)len, (const char *)data); } -static void tcp_send(uint32_t port_id, const void *data, size_t len) +static void tcp_write(uint32_t port_id, const void *data, size_t len) { printf("[TCP port %u] %.*s\n", port_id, (int)len, (const char *)data); } -static void can_send(uint32_t port_id, const void *data, size_t len) +/* ---- 接收回调 ---- */ + +void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { - printf("[CAN port %u] %.*s\n", port_id, (int)len, (const char *)data); + printf("[RX:rtcm port %u] %.*s\n", port_id, (int)len, (const char *)data); } void dr_on_recv_command(uint32_t port_id, const void *data, size_t len) { - printf("[RX:cmd port %u] %.*s\n", port_id, (int)len, (const char *)data); + printf("[RX:cmd port %u] %.*s\n", port_id, (int)len, (const char *)data); } +/* ---- main ---- */ + int main(void) { dr_init(); - printf("Capabilities:\n"); - for (int i = 0; i < dr_send_cap_count(); i++) - printf(" SEND [%d] %s - %s\n", i, - dr_send_cap_name(i), dr_send_cap_desc(i)); - /* - * 创建两个端口,都设 send_type=sensor,各绑不同外设。 - * 调用 dr_send_sensor() 时两个端口同时触发。 + * 创建端口,配置发送和接收能力。 + * 一个端口可以同时有 send_type 和 recv_type。 */ - 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 uart1 = dr_port_create(); + dr_port_set_send_type(uart1, DR_SEND_sensor); + dr_port_set_recv_type(uart1, DR_RECV_rtcm); + dr_port_add_handler(uart1, uart_write); + dr_port_add_handler(uart1, tcp_write); - dr_port_t p2 = dr_port_create(); - dr_port_set_send_type(p2, DR_SEND_sensor); - dr_port_add_handler(p2, can_send); + dr_port_t uart2 = dr_port_create(); + dr_port_set_send_type(uart2, DR_SEND_status); + dr_port_set_recv_type(uart2, DR_RECV_command); + dr_port_add_handler(uart2, uart_write); - /* 不指定端口,自动广播到所有 sensor 端口 */ - printf("\n--- send_sensor (auto-route to ALL sensor ports) ---\n"); + /* ---- 发送 ---- */ + printf("--- send sensor (auto-route to uart1) ---\n"); dr_send_sensor("temperature:25.3", 16); - /* 运行时移除一个 handler,下次发送不再触发它 */ - printf("\n--- remove TCP handler from p1, resend ---\n"); - dr_port_remove_handler(p1, tcp_send); - dr_send_sensor("temperature:26.1", 16); + printf("\n--- send status (auto-route to uart2) ---\n"); + dr_send_status("status:OK", 9); - /* 接收侧:用户自行调用回调 */ - printf("\n--- recv command ---\n"); - dr_on_recv_command(0, "reboot", 6); + /* ---- 接收(模拟外设中断) ---- */ + printf("\n--- UART1 received data → dispatch to rtcm ---\n"); + dr_port_dispatch(uart1, "rtcm_data_v3.2", 14); - /* 删除一个端口,触发时不再走它 */ - printf("\n--- delete p2 (CAN), resend ---\n"); - dr_port_delete(p2); - dr_send_sensor("temperature:27.0", 16); + printf("\n--- UART2 received data → dispatch to command ---\n"); + dr_port_dispatch(uart2, "reboot", 6); - dr_port_delete(p1); + /* 清理 */ + dr_port_delete(uart1); + dr_port_delete(uart2); return 0; } diff --git a/tests/test_datarouter.c b/tests/test_datarouter.c index facb0c2..55ca9d5 100644 --- a/tests/test_datarouter.c +++ b/tests/test_datarouter.c @@ -52,16 +52,20 @@ cleanup: \ } while (0) /* ================================================================ - * 测试用 handler + * 测试用 handler / 回调 * ================================================================ */ static int _h_cnt; -static int _h_port[16]; -static int _h_pos; -static void _h(uint32_t p, const void *d, size_t l) { (void)d; (void)l; _h_cnt++; if (_h_pos<16) _h_port[_h_pos++]=p; } -static void _h2(uint32_t p, const void *d, size_t l) { (void)d; (void)l; _h_cnt++; if (_h_pos<16) _h_port[_h_pos++]=p; } -static void _h3(uint32_t p, const void *d, size_t l) { (void)d; (void)l; _h_cnt++; if (_h_pos<16) _h_port[_h_pos++]=p; } -static void reset_h(void) { _h_cnt = 0; _h_pos = 0; } +static void _h(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } +static void reset_h(void) { _h_cnt = 0; } + +static int _rx_rtcm; +static int _rx_cmd; +void dr_on_recv_rtcm(uint32_t p, const void *d, size_t l) + { (void)p; (void)d; (void)l; _rx_rtcm++; } +void dr_on_recv_command(uint32_t p, const void *d, size_t l) + { (void)p; (void)d; (void)l; _rx_cmd++; } +static void reset_rx(void) { _rx_rtcm = _rx_cmd = 0; } /* ================================================================ * 1. 未初始化 @@ -97,14 +101,15 @@ TEST_END() TEST_START(port_default_state) dr_port_t p = FIXTURE_CREATE(&_fix); CHECK(dr_port_get_send_type(p) == -1, NULL); + CHECK(dr_port_get_recv_type(p) == -1, NULL); CHECK(dr_port_handler_count(p) == 0, NULL); TEST_END() /* ================================================================ - * 3. send_type 与 handler + * 3. send_type / recv_type 与 handler * ================================================================ */ -TEST_START(send_type_set_get) +TEST_START(send_type_set_clear) dr_port_t p = FIXTURE_CREATE(&_fix); dr_port_set_send_type(p, DR_SEND_sensor); CHECK(dr_port_get_send_type(p) == DR_SEND_sensor, NULL); @@ -112,6 +117,22 @@ TEST_START(send_type_set_get) CHECK(dr_port_get_send_type(p) == -1, NULL); TEST_END() +TEST_START(recv_type_set_clear) + dr_port_t p = FIXTURE_CREATE(&_fix); + dr_port_set_recv_type(p, DR_RECV_rtcm); + CHECK(dr_port_get_recv_type(p) == DR_RECV_rtcm, NULL); + dr_port_set_recv_type(p, -1); + CHECK(dr_port_get_recv_type(p) == -1, NULL); +TEST_END() + +TEST_START(types_independent) + dr_port_t p = FIXTURE_CREATE(&_fix); + dr_port_set_send_type(p, DR_SEND_sensor); + dr_port_set_recv_type(p, DR_RECV_command); + CHECK(dr_port_get_send_type(p) == DR_SEND_sensor, NULL); + CHECK(dr_port_get_recv_type(p) == DR_RECV_command, NULL); +TEST_END() + TEST_START(handler_add_remove) dr_port_t p = FIXTURE_CREATE(&_fix); CHECK(dr_port_add_handler(p, _h) == 0, NULL); @@ -120,19 +141,11 @@ TEST_START(handler_add_remove) CHECK(dr_port_handler_count(p) == 0, NULL); TEST_END() -TEST_START(handler_clear) - dr_port_t p = FIXTURE_CREATE(&_fix); - dr_port_add_handler(p, _h); - dr_port_clear_handlers(p); - CHECK(dr_port_handler_count(p) == 0, NULL); -TEST_END() - /* ================================================================ * 4. 发送 — 自动广播 * ================================================================ */ TEST_START(send_no_match_port) - /* 没有端口配置该 send_type */ CHECK(dr_send_sensor("d", 1) == -2, "expected -2"); TEST_END() @@ -143,21 +156,9 @@ TEST_START(send_one_port_one_handler) reset_h(); CHECK(dr_send_sensor("d", 1) == 0, NULL); CHECK(_h_cnt == 1, "cnt=%d", _h_cnt); - CHECK(_h_port[0] == (int)p, "port=%d", _h_port[0]); TEST_END() -TEST_START(send_one_port_multi_handler) - dr_port_t p = FIXTURE_CREATE(&_fix); - dr_port_set_send_type(p, DR_SEND_sensor); - dr_port_add_handler(p, _h); - dr_port_add_handler(p, _h2); - reset_h(); - CHECK(dr_send_sensor("d", 1) == 0, NULL); - CHECK(_h_cnt == 2, "cnt=%d", _h_cnt); -TEST_END() - -TEST_START(send_multi_ports_same_type) - /* 两个端口同类型,各一个 handler */ +TEST_START(send_multi_ports) dr_port_t p1 = FIXTURE_CREATE(&_fix); dr_port_t p2 = FIXTURE_CREATE(&_fix); dr_port_set_send_type(p1, DR_SEND_sensor); @@ -167,26 +168,9 @@ TEST_START(send_multi_ports_same_type) reset_h(); CHECK(dr_send_sensor("d", 1) == 0, NULL); CHECK(_h_cnt == 2, "cnt=%d", _h_cnt); - /* p1 和 p2 各触发一次 */ - CHECK((_h_port[0]==p1 && _h_port[1]==p2) || - (_h_port[0]==p2 && _h_port[1]==p1), "ports %d,%d", _h_port[0], _h_port[1]); -TEST_END() - -TEST_START(send_multi_port_multi_handler) - dr_port_t p1 = FIXTURE_CREATE(&_fix); - dr_port_t p2 = FIXTURE_CREATE(&_fix); - dr_port_set_send_type(p1, DR_SEND_sensor); - dr_port_set_send_type(p2, DR_SEND_sensor); - dr_port_add_handler(p1, _h); - dr_port_add_handler(p1, _h2); /* p1 两个 */ - dr_port_add_handler(p2, _h3); /* p2 一个 */ - reset_h(); - CHECK(dr_send_sensor("d", 1) == 0, NULL); - CHECK(_h_cnt == 3, "cnt=%d", _h_cnt); TEST_END() TEST_START(send_type_filter) - /* 两个端口不同类型,只触发匹配的 */ dr_port_t p1 = FIXTURE_CREATE(&_fix); dr_port_t p2 = FIXTURE_CREATE(&_fix); dr_port_set_send_type(p1, DR_SEND_sensor); @@ -196,19 +180,9 @@ TEST_START(send_type_filter) reset_h(); CHECK(dr_send_sensor("d", 1) == 0, NULL); CHECK(_h_cnt == 1, "cnt=%d", _h_cnt); - CHECK(_h_port[0] == (int)p1, "only p1 should fire"); TEST_END() -TEST_START(send_after_remove_handler) - dr_port_t p = FIXTURE_CREATE(&_fix); - dr_port_set_send_type(p, DR_SEND_sensor); - dr_port_add_handler(p, _h); - reset_h(); dr_send_sensor("d", 1); CHECK(_h_cnt == 1, NULL); - dr_port_remove_handler(p, _h); - CHECK(dr_send_sensor("d", 1) == -2, "expected -2 after remove"); -TEST_END() - -TEST_START(send_after_delete_port) +TEST_START(send_after_delete) dr_port_t p = FIXTURE_CREATE(&_fix); dr_port_set_send_type(p, DR_SEND_sensor); dr_port_add_handler(p, _h); @@ -235,33 +209,65 @@ TEST_START(send_null_data) CHECK(_h_cnt == 1, NULL); TEST_END() -TEST_START(send_multi_cap) - /* 发送 status 不影响 sensor 端口 */ - dr_port_t p1 = FIXTURE_CREATE(&_fix); - dr_port_t p2 = FIXTURE_CREATE(&_fix); - dr_port_set_send_type(p1, DR_SEND_sensor); - dr_port_set_send_type(p2, DR_SEND_status); - dr_port_add_handler(p1, _h); - dr_port_add_handler(p2, _h); - reset_h(); - CHECK(dr_send_sensor("d", 1) == 0, NULL); - CHECK(_h_cnt == 1, "sensor: cnt=%d", _h_cnt); - CHECK(dr_send_status("d", 1) == 0, NULL); - CHECK(_h_cnt == 2, "status: cnt=%d", _h_cnt); -TEST_END() - /* ================================================================ - * 5. 接收回调 + * 5. 接收回调 + dispatch * ================================================================ */ -static int _rx_r; -void dr_on_recv_rtcm(uint32_t p, const void *d, size_t l) - { (void)p; (void)d; (void)l; _rx_r++; } - -TEST_START(recv_callback) - _rx_r = 0; +TEST_START(recv_callback_direct) + /* 直接调用回调(旧方式) */ + reset_rx(); dr_on_recv_rtcm(0, "d", 1); - CHECK(_rx_r == 1, "cnt=%d", _rx_r); + CHECK(_rx_rtcm == 1, "rtcm=%d", _rx_rtcm); + dr_on_recv_command(1, "d", 1); + CHECK(_rx_cmd == 1, "cmd=%d", _rx_cmd); +TEST_END() + +TEST_START(dispatch_valid) + /* dr_port_dispatch 自动路由到对应回调 */ + dr_port_t p = FIXTURE_CREATE(&_fix); + dr_port_set_recv_type(p, DR_RECV_rtcm); + + reset_rx(); + dr_port_dispatch(p, "data", 4); + CHECK(_rx_rtcm == 1, "rtcm should fire: %d", _rx_rtcm); + CHECK(_rx_cmd == 0, "cmd should not fire: %d", _rx_cmd); +TEST_END() + +TEST_START(dispatch_command) + dr_port_t p = FIXTURE_CREATE(&_fix); + dr_port_set_recv_type(p, DR_RECV_command); + + reset_rx(); + dr_port_dispatch(p, "data", 4); + CHECK(_rx_cmd == 1, "cmd should fire: %d", _rx_cmd); + CHECK(_rx_rtcm == 0, "rtcm should not fire: %d", _rx_rtcm); +TEST_END() + +TEST_START(dispatch_unset) + /* recv_type 为 -1 时 dispatch 不应调用任何回调 */ + dr_port_t p = FIXTURE_CREATE(&_fix); + reset_rx(); + dr_port_dispatch(p, "data", 4); + CHECK(_rx_rtcm == 0, NULL); + CHECK(_rx_cmd == 0, NULL); +TEST_END() + +TEST_START(dispatch_invalid_port) + reset_rx(); + dr_port_dispatch(999, "data", 4); + dr_port_dispatch(-1, "data", 4); + CHECK(_rx_rtcm == 0, NULL); + CHECK(_rx_cmd == 0, NULL); +TEST_END() + +TEST_START(dispatch_deleted_port) + dr_port_t p = FIXTURE_CREATE(&_fix); + dr_port_set_recv_type(p, DR_RECV_rtcm); + dr_port_delete(p); + reset_rx(); + dr_port_dispatch(p, "data", 4); + CHECK(_rx_rtcm == 0, "deleted should not fire"); + _fix.count--; TEST_END() /* ================================================================ @@ -271,24 +277,20 @@ TEST_END() TEST_START(send_cap_meta) CHECK(dr_send_cap_count() == 2, NULL); CHECK(strcmp(dr_send_cap_name(0), "sensor") == 0, NULL); - CHECK(strcmp(dr_send_cap_desc(0), "Sensor data report") == 0, NULL); CHECK(dr_send_cap_name(-1) == NULL, NULL); - CHECK(dr_send_cap_name(99) == NULL, NULL); TEST_END() TEST_START(recv_cap_meta) CHECK(dr_recv_cap_count() == 2, NULL); CHECK(strcmp(dr_recv_cap_name(0), "rtcm") == 0, NULL); - CHECK(strcmp(dr_recv_cap_desc(0), "RTCM correction data") == 0, NULL); CHECK(dr_recv_cap_name(-1) == NULL, NULL); - CHECK(dr_recv_cap_desc(99) == NULL, NULL); TEST_END() /* ================================================================ * 主函数 * ================================================================ */ -#define RUN(name) do { reset_h(); _rx_r = 0; test_##name(); } while (0) +#define RUN(name) do { reset_h(); reset_rx(); test_##name(); } while (0) int main(void) { @@ -307,28 +309,30 @@ int main(void) printf("\n"); dr_reset(); dr_init(); - printf("[send_type & handler]\n"); - RUN(send_type_set_get); + printf("[send_type / recv_type / handler]\n"); + RUN(send_type_set_clear); + RUN(recv_type_set_clear); + RUN(types_independent); RUN(handler_add_remove); - RUN(handler_clear); printf("\n"); printf("[send — auto broadcast]\n"); RUN(send_no_match_port); RUN(send_one_port_one_handler); - RUN(send_one_port_multi_handler); - RUN(send_multi_ports_same_type); - RUN(send_multi_port_multi_handler); + RUN(send_multi_ports); RUN(send_type_filter); - RUN(send_after_remove_handler); - RUN(send_after_delete_port); + RUN(send_after_delete); RUN(send_zero_length); RUN(send_null_data); - RUN(send_multi_cap); printf("\n"); - printf("[receive callback]\n"); - RUN(recv_callback); + printf("[receive — dispatch]\n"); + RUN(recv_callback_direct); + RUN(dispatch_valid); + RUN(dispatch_command); + RUN(dispatch_unset); + RUN(dispatch_invalid_port); + RUN(dispatch_deleted_port); printf("\n"); printf("[capability metadata]\n");