From e8ee92110be42644a9ebc637309fc2ffbf893743 Mon Sep 17 00:00:00 2001 From: 7iwind <7wind@7iwind.cn> Date: Sun, 26 Jul 2026 22:29:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8F=98=E6=9B=B4=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E4=B8=BA=E5=B9=BF=E6=92=AD=EF=BC=8C=E5=8F=91=E9=80=81=E5=88=B0?= =?UTF-8?q?=E6=89=80=E6=9C=89=E7=BB=91=E5=AE=9A=E7=9A=84=E7=AB=AF=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/editor.xml | 247 +++++++++++++++++++++++++++++++++ DESIGN.md | 287 ++++++++++++++++----------------------- README.md | 83 +++++------- TEST_CASES.md | 72 ++++------ build/example | Bin 17512 -> 17440 bytes build/test_datarouter | Bin 39744 -> 31224 bytes datarouter.c | 85 +++--------- datarouter.h | 38 ++---- example.c | 84 +++++------- tests/test_datarouter.c | 292 +++++++++++++++++----------------------- 10 files changed, 614 insertions(+), 574 deletions(-) diff --git a/.idea/editor.xml b/.idea/editor.xml index f81c5eb..27a89fb 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -1,6 +1,253 @@ + \ No newline at end of file diff --git a/DESIGN.md b/DESIGN.md index 0a411b9..15cfa8d 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -2,7 +2,7 @@ ## 1. 设计目标 -轻量级 C 语言数据路由库。发送与接收能力分开声明,每个端口独立设置发送类型和接收类型,支持多外设一对多广播路由。 +轻量级 C 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `datarouter_send_(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。 ### 核心设计 @@ -12,27 +12,25 @@ DATAROUTER_SEND_CAPABILITIES DATAROUTER_RECV_CAPABILITIES DATAROUTER_SEND(status, ...) DATAROUTER_RECV(command, ...) │ │ ▼ ▼ - send_sensor(port, data) on_recv_rtcm(port, data) - send_status(port, data) on_recv_command(port, data) - │ (用户实现回调) + send_sensor(data, len) on_recv_rtcm(port, data, len) + │ (用户实现回调) ▼ - 端口校验 → for each handler(port, data) - │ - ┌────────┼────────┐ - ▼ ▼ ▼ - UART TCP CAN + 遍历端口表 → send_type 匹配? + │ + ▼ + for each handler(port, data) ─── UART / TCP / CAN ``` -**关键区别**:sensor 只声明发送,不生成接收回调;rtcm 只声明接收,不生成发送函数。端口可独立设置 send_type 和 recv_type,互不干扰。 +**关键**:send 无 port_id 参数,自动路由到所有匹配端口。 ### 设计原则 | 原则 | 说明 | |------|------| -| 收发分离 | 发送能力和接收能力在配置中分开声明,按需组合 | +| 收发分离 | 发送/接收能力在配置中分开声明,按需组合 | | 编译期确定能力 | 所有能力在 `datarouter_config.h` 中声明,库自动生成代码 | | 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 | -| 一对多广播 | 一个端口可注册多个 handler,发送时逐一调用 | +| 自动广播 | `send_(data, len)` 遍历所有端口,自动匹配 send_type | | 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler | | 零动态内存 | 纯静态数组,无 malloc/free | @@ -47,23 +45,22 @@ DATAROUTER_SEND_CAPABILITIES DATAROUTER_RECV_CAPABILITIES left to right direction rectangle "用户代码" as User { - component "应用逻辑" as App - component "datarouter_config.h\n声明发送+接收能力" as Cfg + component "应用逻辑\n调 send_(data, len)" as App + component "datarouter_config.h\n声明能力" as Cfg component "外设 handler\nUART / TCP / CAN" as Handlers - component "接收回调\n处理收到的报文" as Callbacks + component "接收回调\non_recv_()" as Callbacks } database "DataRouter 库" as Lib { - component "datarouter.h\n公共 API + X-macro 展开" as Hdr - component "datarouter.c\n端口表 + handler 遍历" as Impl + component "datarouter.h\n公共 API + X-macro" as Hdr + component "datarouter.c\n端口表+遍历路由" as Impl } App --> Hdr Cfg --> Hdr Impl --> Cfg -Handlers --> Impl : 注册 handler +Handlers --> Impl : add_handler Callbacks --> Impl : 实现 on_recv_ - @enduml ``` @@ -71,11 +68,11 @@ Callbacks --> Impl : 实现 on_recv_ | 文件 | 职责 | |------|------| -| `datarouter_config.h` | 分别声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` | -| `datarouter.h` | 公共 API + X-macro 展开(两组枚举 + 声明) | -| `datarouter.c` | 端口表 + handler 列表遍历 + 弱符号回调 | -| `example.c` | 收发分离示例 | -| `tests/test_datarouter.c` | 27 个单元测试 | +| `datarouter_config.h` | 声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` | +| `datarouter.h` | 公共 API + X-macro 展开 | +| `datarouter.c` | 端口表 + send 遍历 + 弱符号回调 | +| `example.c` | 使用示例 | +| `tests/test_datarouter.c` | 22 个单元测试 | --- @@ -84,12 +81,10 @@ Callbacks --> Impl : 实现 on_recv_ ### 3.1 配置声明 ```c -// 发送能力 —— 只生成发送触发函数 #define DATAROUTER_SEND_CAPABILITIES \ DATAROUTER_SEND(sensor, "Sensor data report") \ DATAROUTER_SEND(status, "Device status report") -// 接收能力 —— 只生成接收回调声明 #define DATAROUTER_RECV_CAPABILITIES \ DATAROUTER_RECV(rtcm, "RTCM correction data") \ DATAROUTER_RECV(command, "Remote control command") @@ -99,75 +94,58 @@ Callbacks --> Impl : 实现 on_recv_ ``` DATAROUTER_SEND_CAPABILITIES 展开: - enum: DATAROUTER_SEND_sensor = 0, - DATAROUTER_SEND_status = 1, - DATAROUTER_SEND_CAP_COUNT = 2 - 发送函数: int datarouter_send_sensor(port, data, len); - int datarouter_send_status(port, data, len); + enum: sensor = 0, status = 1, SEND_CAP_COUNT = 2 + 发送函数: int datarouter_send_sensor(const void *data, size_t len); + int datarouter_send_status(const void *data, size_t len); 元数据: send_names[] = {"sensor", "status"} send_descs[] = {"Sensor data report", "Device status report"} DATAROUTER_RECV_CAPABILITIES 展开: - enum: DATAROUTER_RECV_rtcm = 0, - DATAROUTER_RECV_command = 1, - DATAROUTER_RECV_CAP_COUNT = 2 - 接收回调: void datarouter_on_recv_rtcm(port, data, len); - void datarouter_on_recv_command(port, data, len); - 弱符号默认: 每个回调提供空的 weak 实现,用户覆盖 + enum: rtcm = 0, command = 1, RECV_CAP_COUNT = 2 + 接收回调: void datarouter_on_recv_rtcm(uint32_t port, const void*, size_t); + void datarouter_on_recv_command(uint32_t port, const void*, size_t); + 弱符号默认: 空实现,用户覆盖 元数据: recv_names[] = {"rtcm", "command"} recv_descs[] = {"RTCM correction data", "Remote control command"} ``` -### 3.3 端口组合示例 +### 3.3 端口示例 ``` -端口 A: send_type = sensor, recv_type = -1 → 只发送 -端口 B: send_type = -1, recv_type = rtcm → 只接收 -端口 C: send_type = status, recv_type = command → 同时收发 -端口 D: send_type = -1, recv_type = -1 → 无功能 +端口 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 代码生成 -### 4.1 展开流程 - ```plantuml @startuml -== datarouter.h:4 次展开 == +== datarouter.h == -> datarouter_config.h: 获得两组宏列表 -datarouter.h -> "第 1 遍": - #define DATAROUTER_SEND(id,desc) DATAROUTER_SEND_##id, - DATAROUTER_SEND_CAPABILITIES → 发送 enum +datarouter.h -> "1": #define DATAROUTER_SEND(id,desc) DATAROUTER_SEND_##id, +note right: 发送 enum -datarouter.h -> "第 2 遍": - #define DATAROUTER_RECV(id,desc) DATAROUTER_RECV_##id, - DATAROUTER_RECV_CAPABILITIES → 接收 enum +datarouter.h -> "2": #define DATAROUTER_RECV(id,desc) DATAROUTER_RECV_##id, +note right: 接收 enum -datarouter.h -> "第 3 遍": - #define DATAROUTER_SEND(id,desc) int datarouter_send_##id(...); - DATAROUTER_SEND_CAPABILITIES → 发送函数声明 +datarouter.h -> "3": #define DATAROUTER_SEND(id,desc) int send_##id(data, len); +note right: 发送函数声明 -datarouter.h -> "第 4 遍": - #define DATAROUTER_RECV(id,desc) void datarouter_on_recv_##id(...); - DATAROUTER_RECV_CAPABILITIES → 接收回调声明 +datarouter.h -> "4": #define DATAROUTER_RECV(id,desc) void on_recv_##id(...); +note right: 接收回调声明 -== datarouter.c:4 次展开 == - -datarouter.c -> "第 5 遍": - 弱符号回调默认实现(DATAROUTER_RECV 展开) - -datarouter.c -> "第 6 遍": - 发送函数实现 body(DATAROUTER_SEND 展开) - 校验 → 遍历 handler 列表 - -datarouter.c -> "第 7 ~ 8 遍": - send_names[] / send_descs[] - recv_names[] / recv_descs[] +== datarouter.c == +datarouter.c -> "5": 弱符号回调默认实现 +datarouter.c -> "6": 发送函数实现(遍历端口表) +datarouter.c -> "7~8": send_names[] / send_descs[] / recv_names[] / recv_descs[] @enduml ``` @@ -181,21 +159,11 @@ datarouter.c -> "第 7 ~ 8 遍": @startuml skinparam classAttributeIconSize 0 -enum "datarouter_send_cap" as S { - SENSOR - STATUS - SEND_CAP_COUNT -} - -enum "datarouter_recv_cap" as R { - RTCM - COMMAND - RECV_CAP_COUNT -} +enum "datarouter_send_cap" as S { SENSOR, STATUS, SEND_CAP_COUNT } +enum "datarouter_recv_cap" as R { RTCM, COMMAND, RECV_CAP_COUNT } class "port_entry_t" as Entry { - int send_type - - int recv_type - datarouter_send_handler_t handlers[] - int count - uint8_t in_use @@ -206,8 +174,8 @@ class "datarouter_send_handler_t" as Handler { } class "发送函数" as Send { - + int send_sensor(port, data, len) - + int send_status(port, data, len) + + int send_sensor(data, len) + + int send_status(data, len) } class "接收回调" as Recv { @@ -215,10 +183,9 @@ class "接收回调" as Recv { + void on_recv_command(port, data, len) } -Entry --> S : send_type ∈ S enum / -1 -Entry --> R : recv_type ∈ R enum / -1 -Entry --> Handler : handlers[] 数组 -Send --> Entry : 校验 send_type + 遍历 handlers +Entry --> S : send_type +Entry --> Handler : handlers[] +Send --> Entry : 遍历端口表 → 匹配 send_type → 遍历 handlers Recv --> Entry : 用户根据业务调对应回调 @enduml ``` @@ -226,13 +193,12 @@ Recv --> Entry : 用户根据业务调对应回调 ### 5.2 `port_entry_t` ```c -#define DATAROUTER_MAX_HANDLERS_PER_PORT 4 // 每端口最多 4 个外设 +#define DATAROUTER_MAX_HANDLERS_PER_PORT 4 typedef struct { - int send_type; // DATAROUTER_SEND_xxx / -1 - int recv_type; // DATAROUTER_RECV_xxx / -1 + int send_type; datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT]; - int count; // 当前 handler 数量 + int count; uint8_t in_use; } port_entry_t; ``` @@ -259,28 +225,26 @@ void datarouter_reset(void); // 重置全部状态 ### 6.2 端口管理 ```c -datarouter_port_t datarouter_port_create(void); // 创建 -void datarouter_port_delete(datarouter_port_t port); // 删除 +datarouter_port_t datarouter_port_create(void); +void datarouter_port_delete(datarouter_port_t port); -void datarouter_port_set_send_type(port, int cap_id); // 设置发送类型 -int datarouter_port_get_send_type(port); // -1 表示未设置 -void datarouter_port_set_recv_type(port, int cap_id); // 设置接收类型 -int datarouter_port_get_recv_type(port); // -1 表示未设置 +void datarouter_port_set_send_type(port, int cap_id); +int datarouter_port_get_send_type(port); -int datarouter_port_add_handler(port, handler); // 注册外设 handler -int datarouter_port_remove_handler(port, handler); // 移除 -void datarouter_port_clear_handlers(port); // 清除全部 -int datarouter_port_handler_count(port); // 数量 +int datarouter_port_add_handler(port, handler); +int datarouter_port_remove_handler(port, handler); +void datarouter_port_clear_handlers(port); +int datarouter_port_handler_count(port); ``` ### 6.3 发送触发函数(每个 SEND 能力生成一个) ```c -int datarouter_send_sensor(port, data, len); -int datarouter_send_status(port, data, len); +int datarouter_send_sensor(const void *data, size_t len); +int datarouter_send_status(const void *data, size_t len); ``` -返回值:0 成功 / -1 未初始化 / -2 端口无效 / -3 send_type 不匹配 / -4 无 handler +返回值:0 成功 / -1 未初始化 / -2 无端口匹配该 send_type ### 6.4 接收回调(每个 RECV 能力生成一个声明) @@ -289,17 +253,15 @@ void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len); void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len); ``` -库提供弱符号默认空实现,用户定义同名函数覆盖。 +库提供弱符号默认空实现。 -### 6.5 能力元数据(两组独立) +### 6.5 能力元数据 ```c -// 发送能力 int datarouter_send_cap_count(void); const char *datarouter_send_cap_name(int cap_id); const char *datarouter_send_cap_desc(int cap_id); -// 接收能力 int datarouter_recv_cap_count(void); const char *datarouter_recv_cap_name(int cap_id); const char *datarouter_recv_cap_desc(int cap_id); @@ -315,33 +277,35 @@ const char *datarouter_recv_cap_desc(int cap_id); @startuml actor "应用" as App participant "datarouter_send_sensor()" as Send -participant "端口表" as Table +participant "端口表 ports[]" as Table participant "handlers[]" as HList participant "外设" as Dev -App -> Send : send_sensor(port=2, data, len) +App -> Send : send_sensor("temp:25.3", 9) -group 校验 +group 初始化检查 Send -> Send : initialized? alt 否 → return -1 end - Send -> Table : ports[2].in_use? - alt 否 → return -2 - end - Send -> Table : ports[2].send_type == SENSOR? - alt 否 → return -3 - end - Send -> Table : ports[2].count > 0? - alt 否 → return -4 +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 -loop for each handler in ports[2].handlers[] - Send -> HList : handlers[i](port, data, len) - HList -> Dev : 实际外设写入 +alt 至少调了一次 handler + Send --> App : return 0 +else 无匹配端口 + Send --> App : return -2 end - -Send --> App : return 0 @enduml ``` @@ -353,7 +317,7 @@ actor "外设" as Dev participant "用户分发代码" as Dispatch participant "on_recv_rtcm()" as Callback -Dev -> Dispatch : 收到数据,识别为 rtcm 类型 +Dev -> Dispatch : 收到数据,识别为 rtcm Dispatch -> Callback : on_recv_rtcm(port_id, data, len) Callback --> Dispatch : void @enduml @@ -364,19 +328,14 @@ Callback --> Dispatch : void ```plantuml @startuml state "空闲" as Idle -state "已创建\nsend=-1 recv=-1" as Created +state "已创建\nsend_type=-1\ncount=0" as Created state "已配置" as Config [*] --> Idle : reset / 启动 - Idle --> Created : port_create() - -Created --> Config : set_send_type / set_recv_type / add_handler - -Config --> Config : 独立切换 send_type / recv_type\n增删 handler - -Config --> Created : set_send_type(-1) / set_recv_type(-1)\nclear_handlers - +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 @@ -387,42 +346,38 @@ Idle --> Created : 复用 ID ## 8. 路由场景 -### 场景 1:传感器只发送(sensor → UART + TCP) +### 场景 1:sensor 广播到 UART + TCP + CAN ```c -datarouter_port_t p = datarouter_port_create(); -datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); -datarouter_port_add_handler(p, uart_send); -datarouter_port_add_handler(p, tcp_send); +datarouter_port_t p1 = datarouter_port_create(); +datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor); +datarouter_port_add_handler(p1, uart_send); +datarouter_port_add_handler(p1, tcp_send); -datarouter_send_sensor(p, "temp:25.3", 9); -// → uart_send(p, "temp:25.3", 9) -// → tcp_send(p, "temp:25.3", 9) +datarouter_port_t p2 = datarouter_port_create(); +datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor); +datarouter_port_add_handler(p2, can_send); + +datarouter_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:RTCM 只接收 +### 场景 2:仅接收 ```c -datarouter_port_t p = datarouter_port_create(); -datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm); -// 无 send_type,不注册 handler - -// 外设收到数据后由用户调回调: -datarouter_on_recv_rtcm(p, data, len); +void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { + apply_rtcm_correction(data, len); +} +// 外设收到后:datarouter_on_recv_rtcm(port, data, len); ``` -### 场景 3:双向端口(status 发送 + command 接收) +### 场景 3:运行时移除 handler ```c -datarouter_port_t p = datarouter_port_create(); -datarouter_port_set_send_type(p, DATAROUTER_SEND_status); -datarouter_port_set_recv_type(p, DATAROUTER_RECV_command); -datarouter_port_add_handler(p, uart_send); - -// 发送 -datarouter_send_status(p, "status:OK", 9); -// 接收 -datarouter_on_recv_command(p, "reboot", 6); +datarouter_port_remove_handler(p1, tcp_send); +// 下次 send_sensor 不再触发 tcp ``` --- @@ -451,30 +406,24 @@ Weak ..> Result : 后备 | 返回值 | 含义 | |:------:|------| -| 0 | 成功,所有 handler 被调用 | +| 0 | 成功,至少有一个 handler 被调用 | | -1 | 库未初始化(未调 init) | -| -2 | 端口无效(越界/未创建/已删除) | -| -3 | 端口 send_type 与调用的发送函数不匹配 | -| -4 | 端口 handler 列表为空(count == 0) | +| -2 | 无端口匹配该 send_type(或匹配端口均无 handler) | --- ## 11. 配置扩展 -### 增加能力 - ```c #define DATAROUTER_SEND_CAPABILITIES \ DATAROUTER_SEND(sensor, "Sensor data") \ - DATAROUTER_SEND(debug, "Debug log") /* 新增发送能力 */ + DATAROUTER_SEND(debug, "Debug log") /* 新增 */ #define DATAROUTER_RECV_CAPABILITIES \ DATAROUTER_RECV(rtcm, "RTCM data") \ - DATAROUTER_RECV(ota, "OTA firmware") /* 新增接收能力 */ + DATAROUTER_RECV(ota, "OTA firmware") /* 新增 */ ``` -### 自定义限制 - ```bash cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8" ``` diff --git a/README.md b/README.md index 882d3fb..ddd54a1 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # DataRouter -轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口独立设置 send_type 和 recv_type,支持多外设一对多广播路由。 +轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口绑定 `send_type` 和外设 handler 列表。调用 `datarouter_send_(data, len)` 时自动广播到所有匹配端口。 ## 概念 | 术语 | 说明 | |------|------| -| **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `datarouter_send_()` | -| **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `datarouter_on_recv_()` | -| **端口 (Port)** | 运行期连接点,独立设置 send_type + recv_type + handler 列表 | +| **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `datarouter_send_(data, len)` | +| **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `datarouter_on_recv_(port, data, len)` | +| **端口 (Port)** | 运行期创建的连接点,包含 `send_type` + handler 列表 | | **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) | ## 快速开始 @@ -27,14 +27,11 @@ DATAROUTER_RECV(command, "Remote control command") ``` -> 库默认自带一份示例配置。将你的 `datarouter_config.h` 放到项目目录, -> 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源代码。 +> 库自带默认配置。将你的 `datarouter_config.h` 放到项目目录, +> 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源码。 > -> **注意**:确保你的配置目录在包含路径中出现在库目录之前,否则编译 `datarouter.c` -> 时仍会使用库的默认配置。在 CMake 中可参考: > ```cmake -> # 你的 CMakeLists.txt -> include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) # 全局优先 +> include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) > add_subdirectory(path/to/DataRouter) > target_link_libraries(my_app DataRouter) > ``` @@ -56,26 +53,17 @@ static void tcp_send(uint32_t port_id, const void *data, size_t len) { ```c datarouter_init(); -/* —— 只发送:传感器上报 —— */ +/* —— 传感器上报:sensor 报文走 UART + TCP —— */ datarouter_port_t tx = datarouter_port_create(); datarouter_port_set_send_type(tx, DATAROUTER_SEND_sensor); datarouter_port_add_handler(tx, uart_send); datarouter_port_add_handler(tx, tcp_send); -datarouter_send_sensor(tx, "temperature:25.3", 16); // → UART + TCP -/* —— 只接收:RTCM 修正 —— */ -datarouter_port_t rx = datarouter_port_create(); -datarouter_port_set_recv_type(rx, DATAROUTER_RECV_rtcm); -// 外设收到数据后由用户调回调: -datarouter_on_recv_rtcm(rx, data, len); +/* 不指定端口,库自动广播到所有 send_type=sensor 的端口 */ +datarouter_send_sensor("temperature:25.3", 16); // → UART + TCP -/* —— 同时收发:状态上报 + 命令接收 —— */ -datarouter_port_t bi = datarouter_port_create(); -datarouter_port_set_send_type(bi, DATAROUTER_SEND_status); -datarouter_port_set_recv_type(bi, DATAROUTER_RECV_command); -datarouter_port_add_handler(bi, uart_send); -datarouter_send_status(bi, "status:OK", 9); -datarouter_on_recv_command(bi, "reboot", 6); +/* —— 接收:实现回调,外设数据到达后自行调用 —— */ +datarouter_on_recv_rtcm(rx_port, data, len); ``` ### 4. 实现接收回调 @@ -84,10 +72,6 @@ datarouter_on_recv_command(bi, "reboot", 6); void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { apply_rtcm_correction(data, len); } - -void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len) { - execute_command(data, len); -} ``` ## API 一览 @@ -107,8 +91,6 @@ void datarouter_port_delete(datarouter_port_t port); // 删除 void datarouter_port_set_send_type(port, int cap_id); // 设置发送类型 int datarouter_port_get_send_type(port); // -1 = 未设置 -void datarouter_port_set_recv_type(port, int cap_id); // 设置接收类型 -int datarouter_port_get_recv_type(port); // -1 = 未设置 int datarouter_port_add_handler(port, handler); // 注册外设 handler int datarouter_port_remove_handler(port, handler); // 移除 @@ -119,12 +101,13 @@ int datarouter_port_handler_count(port); // 当前数量 ### 发送触发(每个 SEND 能力生成一个) ```c -int datarouter_send_sensor(port, data, len); // 0 成功 / -1 未初始化 -int datarouter_send_status(port, data, len); // -2 端口无效 - // -3 send_type 不匹配 - // -4 无 handler +int datarouter_send_sensor(const void *data, size_t len); // 0 成功 +int datarouter_send_status(const void *data, size_t len); // -1 未初始化 + // -2 无匹配端口 ``` +不指定端口,库遍历所有端口,`send_type` 匹配的逐一调用其全部 handler。 + ### 接收回调(每个 RECV 能力生成一个声明) ```c @@ -149,17 +132,18 @@ const char *datarouter_recv_cap_desc(int cap_id); ## 路由模型 ``` -send_sensor(port, data, len) +datarouter_send_sensor("temperature:25.3", 16) │ - ├─→ handler[0] = uart_send → UART - ├─→ handler[1] = tcp_send → TCP - └─→ handler[2] = can_send → CAN + ├─→ 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 ``` -- 一个端口可注册多个 handler(一对多广播) +- 调用 `send_(data, len)` 时,库遍历所有端口,自动匹配 `send_type` +- 每个端口可注册多个 handler(一对多广播) - 同一 handler 可注册到多个端口(多路复用) -- 运行时通过 `add_handler` / `remove_handler` 动态改变路由 -- 清空 handler 列表后发送返回 -4 +- 运行时通过 `add_handler` / `remove_handler` / `port_delete` 动态改变路由 +- 无任何端口匹配该 `send_type` 时返回 -2 ## 编译 @@ -183,18 +167,19 @@ ctest --test-dir build ./build/test_datarouter ``` -当前 27 个测试用例,覆盖端口管理、send_type/recv_type 独立设置、handler 增删、一对多广播、接收回调、能力元数据、错误边界。 +当前 22 个测试用例,覆盖端口管理、send_type 与 handler 设置、自动广播路由、接收回调、能力元数据。 ## 文件结构 ``` -datarouter_config.h 发送 + 接收能力配置(用户编辑) -datarouter.h 公共 API + X-macro 展开 -datarouter.c 实现(端口表 + handler 遍历) -example.c 收发分离使用示例 -tests/test_datarouter.c 单元测试(27 用例) -DESIGN.md 详细设计文档(含 PlantUML 图) -TEST_CASES.md 测试用例文档 +datarouter_config.h 发送 + 接收能力配置(用户可覆盖) +datarouter.h 公共 API + X-macro 展开 +datarouter.c 实现(端口表 + send 遍历) +datarouter_config_template.h 外部配置模板 +example.c 使用示例 +tests/test_datarouter.c 单元测试(22 用例) +DESIGN.md 详细设计文档(含 PlantUML 图) +TEST_CASES.md 测试用例文档 ``` ## 依赖 diff --git a/TEST_CASES.md b/TEST_CASES.md index 28b2b9b..95e185e 100644 --- a/TEST_CASES.md +++ b/TEST_CASES.md @@ -17,7 +17,6 @@ cmake -S . -B build cmake --build build ctest --test-dir build -# 或直接运行: ./build/test_datarouter ``` @@ -27,68 +26,53 @@ ctest --test-dir build | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 1 | `error_uninit` | 未 init 时发送 | 不调 init 直接 `send_sensor(0, ...)` | 返回 -1 | +| 1 | `error_uninit` | 未 init 时发送 | `send_sensor("d", 1)` | 返回 -1 | ### 2. 端口管理(4 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| | 2 | `port_create_basic` | 基本创建 | `port_create()` | 返回 ≥ 0 | -| 3 | `port_create_max` | 满容量(64) | 循环创建到满再尝试 | 64 个成功,第 65 个 INVALID | +| 3 | `port_create_max` | 满容量 64 | 循环创建到满再尝试 | 64 个成功,第 65 个 INVALID | | 4 | `port_delete_invalid` | 删除无效 ID | `delete(-1)` + `delete(999)` | 不崩溃 | -| 5 | `port_default_state` | 默认状态 | 创建端口,查 send/recv/handler | send_type=-1, recv_type=-1, count=0 | +| 5 | `port_default_state` | 默认状态 | 新建端口,查 send_type / handler count | `send_type=-1`, `count=0` | -### 3. send_type / recv_type 独立设置(6 个) +### 3. send_type 与 handler(3 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 6 | `send_type_set_get` | 设置发送类型 | `set_send_type(sensor)` → `get_send_type()` | 返回 sensor | -| 7 | `recv_type_set_get` | 设置接收类型 | `set_recv_type(rtcm)` → `get_recv_type()` | 返回 rtcm | -| 8 | `types_independent` | 收发互不干扰 | 同时 `set_send_type(sensor)` + `set_recv_type(command)` | get_send=sensor, get_recv=command | -| 9 | `send_type_clear` | 清除发送类型 | `set_send_type(sensor)` → `set_send_type(-1)` | `get_send_type()` == -1 | -| 10 | `recv_type_clear` | 清除接收类型 | `set_recv_type(rtcm)` → `set_recv_type(-1)` | `get_recv_type()` == -1 | -| 11 | `types_from_invalid` | 无效端口查类型 | `get_send_type(-1/999)` / `get_recv_type(-1/999)` | 均返回 -1 | +| 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 | -### 4. handler 增删(2 个) +### 4. 发送 — 自动广播(11 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 12 | `handler_add_remove` | 添加后移除 | ① add(a) add(b) count=2 ② remove(a) count=1 | count 正确 | -| 13 | `handler_clear` | 清除全部 | add 后 clear | count == 0 | +| 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 | 各触发匹配的端口 | -### 5. 发送触发(9 个) +### 5. 接收回调(1 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 14 | `send_one_handler` | 单 handler 发送 | send_type + add_handler → send_sensor | 返回 0;handler 被调用 1 次 | -| 15 | `send_multi_handlers` | 三个 handler 广播 | 注册 3 个 handler 后 send | 返回 0;handler 被调用 3 次 | -| 16 | `send_type_mismatch` | 发送类型不匹配 | set_send_type(sensor) 但调用 send_status | 返回 -3;handler 不被调用 | -| 17 | `send_no_handler` | 未注册 handler | 只 set_send_type 不 add_handler | 返回 -4 | -| 18 | `send_no_send_type` | 没有 send_type | 端口只有 recv_type,调 send_sensor | 返回 -3 | -| 19 | `send_remove_handler` | 移除后少广播 | 注册 2 handler → send count=2 → remove 1 → send count=1 | 数量正确 | -| 20 | `send_clear_handlers` | 清空后无法发送 | add → clear → send | 返回 -4 | -| 21 | `send_zero_length` | 零长度数据 | send(port, "x", 0) | 返回 0;handler 被调用 | -| 22 | `send_null_data` | NULL 数据 | send(port, NULL, 0) | 返回 0;handler 被调用 | +| 20 | `recv_callback` | 用户实现接收回调 | `on_recv_rtcm(0, "d", 1)` | 回调被触发 | -### 6. 接收回调(1 个) +### 6. 能力元数据(2 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 23 | `recv_callbacks` | 用户实现接收回调 | ① `on_recv_rtcm(0, ...)` ② `on_recv_command(1, ...)` | 各自触发,互不干扰 | - -### 7. 能力元数据(2 个) - -| # | 测试名 | 描述 | 步骤 | 预期 | -|---|--------|------|------|------| -| 24 | `send_cap_meta` | 发送能力元数据 | 查 count / name / desc / 边界 | SEND 两组正确 | -| 25 | `recv_cap_meta` | 接收能力元数据 | 查 count / name / desc / 边界 | RECV 两组正确 | - -### 8. 错误处理(2 个) - -| # | 测试名 | 描述 | 步骤 | 预期 | -|---|--------|------|------|------| -| 26 | `error_invalid_port` | 无效端口 | `send_sensor(999/-1, ...)` | 均返回 -2 | -| 27 | `error_deleted_port` | 已删除端口 | 创建→设好→删除→send | 返回 -2 | +| 21 | `send_cap_meta` | 发送能力元数据 | count / name / desc / 边界 | SEND 两组正确 | +| 22 | `recv_cap_meta` | 接收能力元数据 | count / name / desc / 边界 | RECV 两组正确 | ## 覆盖矩阵 @@ -96,11 +80,9 @@ ctest --test-dir build |----------|:------:|------------| | 未初始化 | 1 | send → -1 | | 端口管理 | 4 | 创建/满容量/删除异常/默认状态 | -| send_type / recv_type | 6 | 设置/清除/独立/无效端口 | -| handler 增删 | 2 | 添加/移除/清空 | -| 发送触发 | 9 | 单播/多播/类型不匹配/无handler/无send_type/移除后/清空后/零长/NULL | +| send_type & handler | 3 | 设置/清除/添加/移除/清空 | +| 发送自动广播 | 11 | 无匹配/单播/多 handler 单端口/多端口同类型/多端口多 handler/类型过滤/移除后/删除后/零长/NULL/多能力独立 | | 接收回调 | 1 | 用户实现覆盖弱符号 | | 能力元数据 | 2 | SEND 与 RECV 两组独立查询 | -| 错误处理 | 2 | 无效端口/已删除端口 | -**总计:27 个测试用例** +**总计:22 个测试用例** diff --git a/build/example b/build/example index 9499c22e4acb5e41fa65ac01262d984da0e0517e..6e019f228b50f331839ef91dbe52c629002e132b 100755 GIT binary patch delta 3897 zcma)93s6+o89w&_A_(#jySprpi?9ob3-T6f?5?oRGNIO(n3zeepsXXUuw+?`(_q(j zG2<4snqyv$+Sp0QPHW=WwbuH!F<{#0$c$;5Hq+V`GwVPV11Z#EZohNySst6w=FGr% z&-eZRfByeJ=RP*LLwa+Ev?ot$*qi^i*az`M9S`DJvQ!w;IYXOv2I{0DY2J-2Z5oav z8cjwB+fVOs~B^`}C0gsGahKqa;O?{c zOTn=iLaOZjoo9msSio-&cH4vJxd{S(7D))Ej$L!T&7UiZ!~X}6SEEAYYZKgWzy*)0 z{)652!I1w+)qfEF?@XBPhAEQKi!#Q>73F*)mIGHUxU1UqF){rRCSENK^F){n*y@m< zO&Gqa`PGQ&A^4rAKhvdrHB$eFvt!cJ*}i+Q;}(xA}tItHxMT#+*413J6I! z8A-b?CUm_r$*2DUe0L<>ezC*TYai!s19jc@amV^A-1Bf+YJMjy>S5yOUUk#a{dH{7 zn8Sa~-)mE@V?7BTKKr;N)@fQXjc&*wKtZ}3n5QIwvjOXY+}1Ja%~6FumOdK(-$P2zA4 zaKTT`0par&e5RNp<_};yx%t0@x!Atjui={QO*O>50B3E>J6bt0b0vtLNGkCOjml?r z`gt@apS9@q#<(sY^O(_)O=ssZt06DhF{LUtp)fI4%Sl#xvr=P+|dCLJzf4Tcgr zkgaBWCYv3}Fn>$-Z5@}qsr=l(?%0*`p6Gu6JZu7v5RNNnrnLqhWXnVR&QAAZM>Db> zw#4@7reAuAHPijr&oiG6G(?}3UY6s%a+}NTkxd2|tS+f2ELN9Q-UUkMXfAc$W~^#y+krbe02yh((zY+M^SR{u-gf&2Oa=60Y3o70e^K@ zQ96O6zyrXlAw@Y0d=dCD@EmX)`0S^MLkV#KYk}K=O~6CLiqZuuUcqKZ4IlzO! zTHsM&6R;oH1xyi5lLCG7W=SSq3$f2me2Lb?&#?O5qi_d(+RCluc&MDSSe`9 zP3VtL)0a-@Wzc6a$&~4BKxbc=X4N`j<$=|&xjrqU)`^VvL7$Dr@1LgcozPD}-+_&2 zpBDey6Y+0Q;w689!NU?5L4XqMV8YY@%T#}&mWiFqf{s`HWE)(QdKvVpN9a#Q)DrT< zBNp%=fb|gp_Ca4g8u*EMMl@@Pwz8jD7SW;fz&|X7v?}z4+MLC+BP8@q>J#(OSLi`8 z4}Eu@4%98|mgZ8DNaB+<*QFYetE{hBXZSN_5=kZ7tb)cXk&l~9D$yARaU~H0Nwji~ zAc{6dSus?Kt>WrJtHy@Zc-B}_6r)$w6tacAQIac}*(G=sfg@xN34N(+euOM#_n@wZ znv6AGDzjxOMSILcOR{MWP;F zTn-6-n_lBXYs_YUFV)qdQECP|zoMz3m?qBCBr+t{(o{kUg#D?o9BNr#nXd5}p>7LP z+XVlOupQ|Z{AIB^zVFm=K=3m~U({Ow${D^&-8HE5&^3#AoCs&~iSU{wD(w)_B5}-k zm)&zULVZNw(#VR-by0axwJuk(#&TU>(fL1ple%i+X2+|D)ia z6&+C@lHl)vPof60!ive1aUZ;lFS!6sGNhnD-ks+~U7?tT?0AJPKa=;A;uZ-VbrGdb zc-8Wejl;Ev6Y>y9A>?XHUu88Iv5eSwfLVAbVWS-owYDFbd-kD8MQ5w`dFQ< xC^>Xrgb=@6e`s4God~>dQ>6F?@w1nwmc~A;DTujQp(W73VrnZTE3?(+{tseT8WaEk delta 4326 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 diff --git a/build/test_datarouter b/build/test_datarouter index edf203e20c042e688b7270e2b6a53844124c0a85..4615576eedddbfd7ab4979f688cb875e414a3532 100755 GIT binary patch literal 31224 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 Z>?$nXzDOqDnKot=bzerb6603w{{jCk;mZI3 literal 39744 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 diff --git a/datarouter.c b/datarouter.c index fd93586..b3064b3 100644 --- a/datarouter.c +++ b/datarouter.c @@ -1,6 +1,5 @@ /** * @file datarouter.c - * @brief 数据路由库 —— 实现 */ #include "datarouter.h" @@ -18,7 +17,6 @@ typedef struct { int send_type; - int recv_type; datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT]; int count; uint8_t in_use; @@ -27,7 +25,7 @@ typedef struct { static port_entry_t ports[DATAROUTER_MAX_PORTS]; static uint8_t initialized; -/* ---- 发送能力元数据 ---- */ +/* ---- 元数据 ---- */ static const char *send_names[] = { #define DATAROUTER_SEND(id, desc) #id, @@ -41,8 +39,6 @@ static const char *send_descs[] = { #undef DATAROUTER_SEND }; -/* ---- 接收能力元数据 ---- */ - static const char *recv_names[] = { #define DATAROUTER_RECV(id, desc) #id, DATAROUTER_RECV_CAPABILITIES @@ -55,29 +51,31 @@ static const char *recv_descs[] = { #undef DATAROUTER_RECV }; -/* ---- 弱符号接收回调默认实现 ---- */ +/* ---- 弱符号接收回调 ---- */ #define DATAROUTER_RECV(id, desc) \ DATAROUTER_WEAK void datarouter_on_recv_##id( \ uint32_t port_id, const void *data, size_t len) \ - { \ - (void)port_id; (void)data; (void)len; \ - } + { (void)port_id; (void)data; (void)len; } DATAROUTER_RECV_CAPABILITIES #undef DATAROUTER_RECV -/* ---- 发送触发函数实现 ---- */ +/* ---- 发送触发函数 ---- */ #define DATAROUTER_SEND(id, desc) \ - int datarouter_send_##id(uint32_t port_id, const void *data, size_t len) \ + int datarouter_send_##id(const void *data, size_t len) \ { \ if (!initialized) return -1; \ - if (port_id >= DATAROUTER_MAX_PORTS || !ports[port_id].in_use) return -2; \ - if (ports[port_id].send_type != DATAROUTER_SEND_##id) return -3; \ - if (ports[port_id].count == 0) return -4; \ - for (int _i = 0; _i < ports[port_id].count; _i++) \ - ports[port_id].handlers[_i](port_id, data, len); \ - return 0; \ + int called = 0; \ + for (uint32_t _p = 0; _p < DATAROUTER_MAX_PORTS; _p++) { \ + if (!ports[_p].in_use) continue; \ + if (ports[_p].send_type != DATAROUTER_SEND_##id) continue; \ + for (int _h = 0; _h < ports[_p].count; _h++) { \ + ports[_p].handlers[_h](_p, data, len); \ + called = 1; \ + } \ + } \ + return called ? 0 : -2; \ } DATAROUTER_SEND_CAPABILITIES #undef DATAROUTER_SEND @@ -104,7 +102,6 @@ datarouter_port_t datarouter_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 (datarouter_port_t)i; } @@ -133,20 +130,6 @@ int datarouter_port_get_send_type(datarouter_port_t port) return ports[port].send_type; } -void datarouter_port_set_recv_type(datarouter_port_t port, int cap_id) -{ - if (!initialized) return; - if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return; - ports[port].recv_type = cap_id; -} - -int datarouter_port_get_recv_type(datarouter_port_t port) -{ - if (!initialized) return -1; - if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return -1; - return ports[port].recv_type; -} - int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler) { if (!initialized) return -1; @@ -187,36 +170,10 @@ int datarouter_port_handler_count(datarouter_port_t port) /* ---- 元数据查询 ---- */ -int datarouter_send_cap_count(void) -{ - return DATAROUTER_SEND_CAP_COUNT; -} +int datarouter_send_cap_count(void) { return DATAROUTER_SEND_CAP_COUNT; } +const char *datarouter_send_cap_name(int c) { return (c<0||c>=DATAROUTER_SEND_CAP_COUNT)?NULL:send_names[c]; } +const char *datarouter_send_cap_desc(int c) { return (c<0||c>=DATAROUTER_SEND_CAP_COUNT)?NULL:send_descs[c]; } -const char *datarouter_send_cap_name(int cap_id) -{ - if (cap_id < 0 || cap_id >= DATAROUTER_SEND_CAP_COUNT) return NULL; - return send_names[cap_id]; -} - -const char *datarouter_send_cap_desc(int cap_id) -{ - if (cap_id < 0 || cap_id >= DATAROUTER_SEND_CAP_COUNT) return NULL; - return send_descs[cap_id]; -} - -int datarouter_recv_cap_count(void) -{ - return DATAROUTER_RECV_CAP_COUNT; -} - -const char *datarouter_recv_cap_name(int cap_id) -{ - if (cap_id < 0 || cap_id >= DATAROUTER_RECV_CAP_COUNT) return NULL; - return recv_names[cap_id]; -} - -const char *datarouter_recv_cap_desc(int cap_id) -{ - if (cap_id < 0 || cap_id >= DATAROUTER_RECV_CAP_COUNT) return NULL; - return recv_descs[cap_id]; -} +int datarouter_recv_cap_count(void) { return DATAROUTER_RECV_CAP_COUNT; } +const char *datarouter_recv_cap_name(int c) { return (c<0||c>=DATAROUTER_RECV_CAP_COUNT)?NULL:recv_names[c]; } +const char *datarouter_recv_cap_desc(int c) { return (c<0||c>=DATAROUTER_RECV_CAP_COUNT)?NULL:recv_descs[c]; } diff --git a/datarouter.h b/datarouter.h index 26aa877..ba1163b 100644 --- a/datarouter.h +++ b/datarouter.h @@ -4,20 +4,16 @@ * * ## 概念 * - * - **发送能力 (Send Capability)**:编译期声明的可发送报文类型 - * - **接收能力 (Recv Capability)**:编译期声明的可接收报文类型 - * - **端口 (Port)**:运行期连接点,独立设置 send_type + recv_type + 外设 handler 列表 + * - **发送能力**:编译期声明的可发送报文类型,生成 datarouter_send_(data, len) + * - **接收能力**:编译期声明的可接收报文类型,生成 datarouter_on_recv_(port, data, len) + * - **端口**:运行期创建的连接点,包含 send_type + handler 列表 * - * ## 使用流程 + * ## 路由 * - * 1. 创建 datarouter_config.h 声明发送和接收能力(见 datarouter_config_template.h) - * 2. 编译时通过 -I 指向你的配置目录;若无,库使用内置默认配置 - * 3. datarouter_init() 初始化 - * 4. port_create() 创建端口 - * 5. port_set_send_type() 设置发送类型 / port_set_recv_type() 设置接收类型 - * 6. port_add_handler() 注册外设发送函数 - * 7. 调 datarouter_send_() 触发发送(库调用所有 handler) - * 8. 实现 datarouter_on_recv_() 处理接收数据 + * 调用 datarouter_send_(data, len) 时,库遍历所有端口, + * 找到 send_type 匹配的端口,逐一调用其全部 handler。无需指定从哪个端口发送。 + * + * 接收侧由用户自行调用 datarouter_on_recv_(port, data, len) 处理外设收到的数据。 */ #ifndef DATAROUTER_H @@ -26,14 +22,6 @@ #include #include -/* - * 配置加载策略: - * 1. 优先从 include path 搜索 (用户提供的版本) - * 2. 找不到则回退到库自带的 "datarouter_config.h"(默认配置) - * - * 用户只需将自己的 datarouter_config.h 放到项目目录, - * 编译时添加 -I /path/to/your/config 即可覆盖内置配置。 - */ #if defined(__has_include) # if __has_include() # include @@ -66,7 +54,7 @@ enum datarouter_recv_cap { DATAROUTER_RECV_CAP_COUNT }; -/* ========== 外设发送 handler 类型 ========== */ +/* ========== 外设发送 handler ========== */ typedef void (*datarouter_send_handler_t)(uint32_t port_id, const void *data, size_t len); @@ -74,14 +62,14 @@ typedef void (*datarouter_send_handler_t)(uint32_t port_id, const void *data, si #define DATAROUTER_MAX_HANDLERS_PER_PORT 4 #endif -/* ========== 发送触发函数(每个发送能力生成一个) ========== */ +/* ========== 发送触发函数 ========== */ #define DATAROUTER_SEND(id, desc) \ - int datarouter_send_##id(uint32_t port_id, const void *data, size_t len); + int datarouter_send_##id(const void *data, size_t len); DATAROUTER_SEND_CAPABILITIES #undef DATAROUTER_SEND -/* ========== 接收回调声明(每个接收能力生成一个,用户实现) ========== */ +/* ========== 接收回调声明 ========== */ #define DATAROUTER_RECV(id, desc) \ void datarouter_on_recv_##id(uint32_t port_id, const void *data, size_t len); @@ -101,8 +89,6 @@ void datarouter_port_delete(datarouter_port_t port); void datarouter_port_set_send_type(datarouter_port_t port, int cap_id); int datarouter_port_get_send_type(datarouter_port_t port); -void datarouter_port_set_recv_type(datarouter_port_t port, int cap_id); -int datarouter_port_get_recv_type(datarouter_port_t port); int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler); int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handler_t handler); diff --git a/example.c b/example.c index 2760478..5895b5e 100644 --- a/example.c +++ b/example.c @@ -1,93 +1,73 @@ /** * @file example.c - * @brief 数据路由库使用示例 * - * 展示: - * - 发送能力和接收能力分开声明(sensor 只发送,rtcm 只接收) - * - 端口独立设置 send_type 和 recv_type - * - 一个端口可同时拥有发送和接收能力 + * 调用 datarouter_send_sensor(data, len) 无需指定端口, + * 库自动广播到所有 send_type=sensor 的端口。 */ #include "datarouter.h" #include -#include - -/* ---- 外设发送 handler ---- */ static void uart_send(uint32_t port_id, const void *data, size_t len) { - printf("[UART] port %u: %.*s\n", port_id, (int)len, (const char *)data); + 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) { - printf("[TCP] port %u: %.*s\n", port_id, (int)len, (const char *)data); + printf("[TCP port %u] %.*s\n", port_id, (int)len, (const char *)data); } -/* ---- 接收回调实现 ---- */ - -void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) +static void can_send(uint32_t port_id, const void *data, size_t len) { - printf("[RX:rtcm] port %u: %.*s\n", port_id, (int)len, (const char *)data); + printf("[CAN port %u] %.*s\n", port_id, (int)len, (const char *)data); } void datarouter_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); } int main(void) { datarouter_init(); - /* 查询能力 */ - printf("Send capabilities: %d\n", datarouter_send_cap_count()); + printf("Capabilities:\n"); for (int i = 0; i < datarouter_send_cap_count(); i++) printf(" SEND [%d] %s - %s\n", i, datarouter_send_cap_name(i), datarouter_send_cap_desc(i)); - printf("Recv capabilities: %d\n", datarouter_recv_cap_count()); - for (int i = 0; i < datarouter_recv_cap_count(); i++) - printf(" RECV [%d] %s - %s\n", i, - datarouter_recv_cap_name(i), datarouter_recv_cap_desc(i)); - /* - * 端口 A:只有发送能力 —— 上报传感器数据 + * 创建两个端口,都设 send_type=sensor,各绑不同外设。 + * 调用 datarouter_send_sensor() 时两个端口同时触发。 */ - datarouter_port_t tx_port = datarouter_port_create(); - datarouter_port_set_send_type(tx_port, DATAROUTER_SEND_sensor); - datarouter_port_add_handler(tx_port, uart_send); - datarouter_port_add_handler(tx_port, tcp_send); + datarouter_port_t p1 = datarouter_port_create(); + datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor); + datarouter_port_add_handler(p1, uart_send); + datarouter_port_add_handler(p1, tcp_send); - printf("\n--- send sensor (UART + TCP) ---\n"); - datarouter_send_sensor(tx_port, "temperature:25.3", 16); - /* 该端口没有 recv_type,不能接收任何报文 */ + datarouter_port_t p2 = datarouter_port_create(); + datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor); + datarouter_port_add_handler(p2, can_send); - /* - * 端口 B:只有接收能力 —— 接收 RTCM 修正 - */ - datarouter_port_t rtcm_port = datarouter_port_create(); - datarouter_port_set_recv_type(rtcm_port, DATAROUTER_RECV_rtcm); - /* 没有 send_type,也没有 handler —— 不能发送 */ + /* 不指定端口,自动广播到所有 sensor 端口 */ + printf("\n--- send_sensor (auto-route to ALL sensor ports) ---\n"); + datarouter_send_sensor("temperature:25.3", 16); - printf("\n--- simulate receive RTCM ---\n"); - datarouter_on_recv_rtcm(rtcm_port, "rtcm_data_v3.2", 14); + /* 运行时移除一个 handler,下次发送不再触发它 */ + printf("\n--- remove TCP handler from p1, resend ---\n"); + datarouter_port_remove_handler(p1, tcp_send); + datarouter_send_sensor("temperature:26.1", 16); - /* - * 端口 C:同时有收发能力 —— 接收命令 + 上报状态 - */ - datarouter_port_t bi_port = datarouter_port_create(); - datarouter_port_set_send_type(bi_port, DATAROUTER_SEND_status); - datarouter_port_set_recv_type(bi_port, DATAROUTER_RECV_command); - datarouter_port_add_handler(bi_port, uart_send); + /* 接收侧:用户自行调用回调 */ + printf("\n--- recv command ---\n"); + datarouter_on_recv_command(0, "reboot", 6); - printf("\n--- bidirectional port ---\n"); - datarouter_send_status(bi_port, "status:OK", 9); - datarouter_on_recv_command(bi_port, "reboot", 6); + /* 删除一个端口,触发时不再走它 */ + printf("\n--- delete p2 (CAN), resend ---\n"); + datarouter_port_delete(p2); + datarouter_send_sensor("temperature:27.0", 16); - /* 清理 */ - datarouter_port_delete(tx_port); - datarouter_port_delete(rtcm_port); - datarouter_port_delete(bi_port); + datarouter_port_delete(p1); return 0; } diff --git a/tests/test_datarouter.c b/tests/test_datarouter.c index 8f895fa..9e81b7b 100644 --- a/tests/test_datarouter.c +++ b/tests/test_datarouter.c @@ -1,15 +1,5 @@ /** * @file test_datarouter.c - * @brief 数据路由库单元测试 - * - * 覆盖: - * - 端口管理 - * - send_type / recv_type 独立设置 - * - 多 handler 增删 - * - 发送调用(一对多广播) - * - 接收回调 - * - 发送/接收能力元数据各自独立 - * - 错误处理 */ #include "datarouter.h" @@ -17,7 +7,7 @@ #include /* ================================================================ - * 测试框架 + * 框架 * ================================================================ */ static int _total_run, _total_pass, _total_fail; @@ -66,19 +56,19 @@ cleanup: \ * ================================================================ */ static int _h_cnt; -static void _h(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } -static void _h_a(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } -static void _h_b(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } -static void _h_c(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _h_cnt++; } -static void _h_d(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 _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; } /* ================================================================ * 1. 未初始化 * ================================================================ */ TEST_START(error_uninit) - CHECK(datarouter_send_sensor(0, "d", 1) == -1, "expected -1"); + CHECK(datarouter_send_sensor("d", 1) == -1, "expected -1"); TEST_END() /* ================================================================ @@ -86,7 +76,7 @@ TEST_END() * ================================================================ */ TEST_START(port_create_basic) - CHECK(FIXTURE_CREATE(&_fix) >= 0, "create failed"); + CHECK(FIXTURE_CREATE(&_fix) >= 0, NULL); TEST_END() TEST_START(port_create_max) @@ -106,68 +96,28 @@ TEST_END() TEST_START(port_default_state) datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_get_send_type(p) == -1, "send_type should be -1"); - CHECK(datarouter_port_get_recv_type(p) == -1, "recv_type should be -1"); - CHECK(datarouter_port_handler_count(p) == 0, "handler count 0"); + CHECK(datarouter_port_get_send_type(p) == -1, NULL); + CHECK(datarouter_port_handler_count(p) == 0, NULL); TEST_END() /* ================================================================ - * 3. send_type / recv_type 独立设置 + * 3. send_type 与 handler * ================================================================ */ TEST_START(send_type_set_get) datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); CHECK(datarouter_port_get_send_type(p) == DATAROUTER_SEND_sensor, NULL); -TEST_END() - -TEST_START(recv_type_set_get) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm); - CHECK(datarouter_port_get_recv_type(p) == DATAROUTER_RECV_rtcm, NULL); -TEST_END() - -TEST_START(types_independent) - /* 一个端口同时设置 send_type 和 recv_type,互不干扰 */ - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); - datarouter_port_set_recv_type(p, DATAROUTER_RECV_command); - CHECK(datarouter_port_get_send_type(p) == DATAROUTER_SEND_sensor, NULL); - CHECK(datarouter_port_get_recv_type(p) == DATAROUTER_RECV_command, NULL); -TEST_END() - -TEST_START(send_type_clear) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_set_send_type(p, -1); CHECK(datarouter_port_get_send_type(p) == -1, NULL); TEST_END() -TEST_START(recv_type_clear) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm); - datarouter_port_set_recv_type(p, -1); - CHECK(datarouter_port_get_recv_type(p) == -1, NULL); -TEST_END() - -TEST_START(types_from_invalid) - CHECK(datarouter_port_get_send_type(-1) == -1, NULL); - CHECK(datarouter_port_get_recv_type(-1) == -1, NULL); - CHECK(datarouter_port_get_send_type(999) == -1, NULL); - CHECK(datarouter_port_get_recv_type(999) == -1, NULL); -TEST_END() - -/* ================================================================ - * 4. 多 handler - * ================================================================ */ - TEST_START(handler_add_remove) datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_add_handler(p, _h_a) == 0, NULL); - CHECK(datarouter_port_add_handler(p, _h_b) == 0, NULL); - CHECK(datarouter_port_handler_count(p) == 2, NULL); - CHECK(datarouter_port_remove_handler(p, _h_a) == 0, NULL); + CHECK(datarouter_port_add_handler(p, _h) == 0, NULL); CHECK(datarouter_port_handler_count(p) == 1, NULL); + CHECK(datarouter_port_remove_handler(p, _h) == 0, NULL); + CHECK(datarouter_port_handler_count(p) == 0, NULL); TEST_END() TEST_START(handler_clear) @@ -178,67 +128,93 @@ TEST_START(handler_clear) TEST_END() /* ================================================================ - * 5. 发送触发 + * 4. 发送 — 自动广播 * ================================================================ */ -TEST_START(send_one_handler) +TEST_START(send_no_match_port) + /* 没有端口配置该 send_type */ + CHECK(datarouter_send_sensor("d", 1) == -2, "expected -2"); +TEST_END() + +TEST_START(send_one_port_one_handler) datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h); reset_h(); - CHECK(datarouter_send_sensor(p, "d", 1) == 0, NULL); - CHECK(_h_cnt == 1, "handler called %d times", _h_cnt); + CHECK(datarouter_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_multi_handlers) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); - datarouter_port_add_handler(p, _h_a); - datarouter_port_add_handler(p, _h_b); - datarouter_port_add_handler(p, _h_c); - reset_h(); - CHECK(datarouter_send_sensor(p, "d", 1) == 0, NULL); - CHECK(_h_cnt == 3, "handler called %d times", _h_cnt); -TEST_END() - -TEST_START(send_type_mismatch) +TEST_START(send_one_port_multi_handler) datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h); + datarouter_port_add_handler(p, _h2); reset_h(); - CHECK(datarouter_send_status(p, "d", 1) == -3, "expected -3"); - CHECK(_h_cnt == 0, NULL); + CHECK(datarouter_send_sensor("d", 1) == 0, NULL); + CHECK(_h_cnt == 2, "cnt=%d", _h_cnt); TEST_END() -TEST_START(send_no_handler) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); - CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4"); +TEST_START(send_multi_ports_same_type) + /* 两个端口同类型,各一个 handler */ + datarouter_port_t p1 = FIXTURE_CREATE(&_fix); + datarouter_port_t p2 = FIXTURE_CREATE(&_fix); + datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor); + datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor); + datarouter_port_add_handler(p1, _h); + datarouter_port_add_handler(p2, _h); + reset_h(); + CHECK(datarouter_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_no_send_type) - /* 端口只有 recv_type,没有 send_type */ - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm); - CHECK(datarouter_send_sensor(p, "d", 1) == -3, "expected -3 (send_type unset)"); +TEST_START(send_multi_port_multi_handler) + datarouter_port_t p1 = FIXTURE_CREATE(&_fix); + datarouter_port_t p2 = FIXTURE_CREATE(&_fix); + datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor); + datarouter_port_set_send_type(p2, DATAROUTER_SEND_sensor); + datarouter_port_add_handler(p1, _h); + datarouter_port_add_handler(p1, _h2); /* p1 两个 */ + datarouter_port_add_handler(p2, _h3); /* p2 一个 */ + reset_h(); + CHECK(datarouter_send_sensor("d", 1) == 0, NULL); + CHECK(_h_cnt == 3, "cnt=%d", _h_cnt); TEST_END() -TEST_START(send_remove_handler) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); - datarouter_port_add_handler(p, _h_a); - datarouter_port_add_handler(p, _h_b); - reset_h(); datarouter_send_sensor(p, "d", 1); CHECK(_h_cnt == 2, NULL); - datarouter_port_remove_handler(p, _h_a); - reset_h(); datarouter_send_sensor(p, "d", 1); CHECK(_h_cnt == 1, NULL); +TEST_START(send_type_filter) + /* 两个端口不同类型,只触发匹配的 */ + datarouter_port_t p1 = FIXTURE_CREATE(&_fix); + datarouter_port_t p2 = FIXTURE_CREATE(&_fix); + datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor); + datarouter_port_set_send_type(p2, DATAROUTER_SEND_status); + datarouter_port_add_handler(p1, _h); + datarouter_port_add_handler(p2, _h); + reset_h(); + CHECK(datarouter_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_clear_handlers) +TEST_START(send_after_remove_handler) datarouter_port_t p = FIXTURE_CREATE(&_fix); datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h); - datarouter_port_clear_handlers(p); - CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4"); + reset_h(); datarouter_send_sensor("d", 1); CHECK(_h_cnt == 1, NULL); + datarouter_port_remove_handler(p, _h); + CHECK(datarouter_send_sensor("d", 1) == -2, "expected -2 after remove"); +TEST_END() + +TEST_START(send_after_delete_port) + datarouter_port_t p = FIXTURE_CREATE(&_fix); + datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); + datarouter_port_add_handler(p, _h); + datarouter_port_delete(p); + CHECK(datarouter_send_sensor("d", 1) == -2, "expected -2"); + _fix.count--; TEST_END() TEST_START(send_zero_length) @@ -246,7 +222,7 @@ TEST_START(send_zero_length) datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h); reset_h(); - CHECK(datarouter_send_sensor(p, "x", 0) == 0, NULL); + CHECK(datarouter_send_sensor("x", 0) == 0, NULL); CHECK(_h_cnt == 1, NULL); TEST_END() @@ -255,38 +231,47 @@ TEST_START(send_null_data) datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h); reset_h(); - CHECK(datarouter_send_sensor(p, NULL, 0) == 0, NULL); + CHECK(datarouter_send_sensor(NULL, 0) == 0, NULL); CHECK(_h_cnt == 1, NULL); TEST_END() -/* ================================================================ - * 6. 接收回调 - * ================================================================ */ - -static int _rx_r, _rx_c; -void datarouter_on_recv_rtcm(uint32_t p, const void *d, size_t l) - { (void)p; (void)d; (void)l; _rx_r++; } -void datarouter_on_recv_command(uint32_t p, const void *d, size_t l) - { (void)p; (void)d; (void)l; _rx_c++; } - -TEST_START(recv_callbacks) - _rx_r = _rx_c = 0; - datarouter_on_recv_rtcm(0, "d", 1); - CHECK(_rx_r == 1 && _rx_c == 0, "rtcm=%d cmd=%d", _rx_r, _rx_c); - datarouter_on_recv_command(1, "d", 1); - CHECK(_rx_r == 1 && _rx_c == 1, "rtcm=%d cmd=%d", _rx_r, _rx_c); +TEST_START(send_multi_cap) + /* 发送 status 不影响 sensor 端口 */ + datarouter_port_t p1 = FIXTURE_CREATE(&_fix); + datarouter_port_t p2 = FIXTURE_CREATE(&_fix); + datarouter_port_set_send_type(p1, DATAROUTER_SEND_sensor); + datarouter_port_set_send_type(p2, DATAROUTER_SEND_status); + datarouter_port_add_handler(p1, _h); + datarouter_port_add_handler(p2, _h); + reset_h(); + CHECK(datarouter_send_sensor("d", 1) == 0, NULL); + CHECK(_h_cnt == 1, "sensor: cnt=%d", _h_cnt); + CHECK(datarouter_send_status("d", 1) == 0, NULL); + CHECK(_h_cnt == 2, "status: cnt=%d", _h_cnt); TEST_END() /* ================================================================ - * 7. 能力元数据(两组独立) + * 5. 接收回调 + * ================================================================ */ + +static int _rx_r; +void datarouter_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; + datarouter_on_recv_rtcm(0, "d", 1); + CHECK(_rx_r == 1, "cnt=%d", _rx_r); +TEST_END() + +/* ================================================================ + * 6. 能力元数据 * ================================================================ */ TEST_START(send_cap_meta) CHECK(datarouter_send_cap_count() == 2, NULL); CHECK(strcmp(datarouter_send_cap_name(0), "sensor") == 0, NULL); CHECK(strcmp(datarouter_send_cap_desc(0), "Sensor data report") == 0, NULL); - CHECK(strcmp(datarouter_send_cap_name(1), "status") == 0, NULL); - CHECK(strcmp(datarouter_send_cap_desc(1), "Device status report") == 0, NULL); CHECK(datarouter_send_cap_name(-1) == NULL, NULL); CHECK(datarouter_send_cap_name(99) == NULL, NULL); TEST_END() @@ -295,35 +280,15 @@ TEST_START(recv_cap_meta) CHECK(datarouter_recv_cap_count() == 2, NULL); CHECK(strcmp(datarouter_recv_cap_name(0), "rtcm") == 0, NULL); CHECK(strcmp(datarouter_recv_cap_desc(0), "RTCM correction data") == 0, NULL); - CHECK(strcmp(datarouter_recv_cap_name(1), "command") == 0, NULL); - CHECK(strcmp(datarouter_recv_cap_desc(1), "Remote control command") == 0, NULL); CHECK(datarouter_recv_cap_name(-1) == NULL, NULL); CHECK(datarouter_recv_cap_desc(99) == NULL, NULL); TEST_END() -/* ================================================================ - * 8. 错误 - * ================================================================ */ - -TEST_START(error_invalid_port) - CHECK(datarouter_send_sensor(999, "d", 1) == -2, NULL); - CHECK(datarouter_send_sensor(-1, "d", 1) == -2, NULL); -TEST_END() - -TEST_START(error_deleted_port) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); - datarouter_port_add_handler(p, _h); - datarouter_port_delete(p); - CHECK(datarouter_send_sensor(p, "d", 1) == -2, NULL); - _fix.count--; -TEST_END() - /* ================================================================ * 主函数 * ================================================================ */ -#define RUN(name) do { reset_h(); test_##name(); } while (0) +#define RUN(name) do { reset_h(); _rx_r = 0; test_##name(); } while (0) int main(void) { @@ -341,35 +306,29 @@ int main(void) RUN(port_default_state); printf("\n"); - datarouter_reset(); datarouter_init(); - printf("[send_type / recv_type]\n"); + datarouter_reset(); datarouter_init(); + printf("[send_type & handler]\n"); RUN(send_type_set_get); - RUN(recv_type_set_get); - RUN(types_independent); - RUN(send_type_clear); - RUN(recv_type_clear); - RUN(types_from_invalid); - printf("\n"); - - printf("[handler add/remove]\n"); RUN(handler_add_remove); RUN(handler_clear); printf("\n"); - printf("[send trigger]\n"); - RUN(send_one_handler); - RUN(send_multi_handlers); - RUN(send_type_mismatch); - RUN(send_no_handler); - RUN(send_no_send_type); - RUN(send_remove_handler); - RUN(send_clear_handlers); + 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_type_filter); + RUN(send_after_remove_handler); + RUN(send_after_delete_port); RUN(send_zero_length); RUN(send_null_data); + RUN(send_multi_cap); printf("\n"); printf("[receive callback]\n"); - RUN(recv_callbacks); + RUN(recv_callback); printf("\n"); printf("[capability metadata]\n"); @@ -377,11 +336,6 @@ int main(void) RUN(recv_cap_meta); printf("\n"); - printf("[error handling]\n"); - RUN(error_invalid_port); - RUN(error_deleted_port); - printf("\n"); - printf("==============================\n"); printf(" total: %d | pass: %d | fail: %d\n\n", _total_run, _total_pass, _total_fail); -- 2.54.0