From 51e081118a7fb4cad9e589626f310a546db82681 Mon Sep 17 00:00:00 2001 From: 7iwind <7wind@7iwind.cn> Date: Sun, 26 Jul 2026 10:01:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8B=86=E5=88=86=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=92=8C=E6=8E=A5=E6=94=B6=E5=8A=9F=E8=83=BD=E5=A3=B0=E6=98=8E?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96config=E9=85=8D=E7=BD=AE=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 + DESIGN.md | 495 +++++++++++++++++------------------ README.md | 152 +++++++---- TEST_CASES.md | 87 +++--- build/example | Bin 17336 -> 17512 bytes build/test_datarouter | Bin 44104 -> 39744 bytes datarouter.c | 128 +++++---- datarouter.h | 174 +++++------- datarouter_config.h | 48 ++-- datarouter_config_template.h | 51 ++++ example.c | 102 ++++---- tests/test_datarouter.c | 446 ++++++++++++------------------- 12 files changed, 815 insertions(+), 871 deletions(-) create mode 100644 datarouter_config_template.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 36c1edd..9edaf0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,9 @@ project(DataRouter C) set(CMAKE_C_STANDARD 11) add_library(DataRouter STATIC datarouter.c) + +# 将库源码目录暴露给使用者,使其可通过 #include +# 从自身 include path 覆盖默认配置。如果使用者未提供,则回退到库自带版本。 target_include_directories(DataRouter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) if(BUILD_EXAMPLES OR NOT DEFINED BUILD_EXAMPLES) diff --git a/DESIGN.md b/DESIGN.md index 5785df0..0a411b9 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -2,31 +2,37 @@ ## 1. 设计目标 -轻量级 C 语言数据路由库。核心思想: +轻量级 C 语言数据路由库。发送与接收能力分开声明,每个端口独立设置发送类型和接收类型,支持多外设一对多广播路由。 -- **报文类型(Channel)**:编译期通过宏声明的数据类型(如 `sensor` / `status` / `control`) -- **端口(Port)**:运行期创建的连接点,绑定一个报文类型 + 一个物理外设发送 handler -- **路由(Routing)**:将逻辑报文类型连接到实际的物理外设发送函数 +### 核心设计 ``` -应用层 send_sensor(port, data) - │ - ▼ - 库校验端口 - │ - ▼ - 调用注册的 handler(data) ← 用户实现:UART / TCP / CAN / 文件发送 - │ - ▼ - 物理外设 +DATAROUTER_SEND_CAPABILITIES DATAROUTER_RECV_CAPABILITIES + DATAROUTER_SEND(sensor, ...) DATAROUTER_RECV(rtcm, ...) + DATAROUTER_SEND(status, ...) DATAROUTER_RECV(command, ...) + │ │ + ▼ ▼ + send_sensor(port, data) on_recv_rtcm(port, data) + send_status(port, data) on_recv_command(port, data) + │ (用户实现回调) + ▼ + 端口校验 → for each handler(port, data) + │ + ┌────────┼────────┐ + ▼ ▼ ▼ + UART TCP CAN ``` +**关键区别**:sensor 只声明发送,不生成接收回调;rtcm 只声明接收,不生成发送函数。端口可独立设置 send_type 和 recv_type,互不干扰。 + ### 设计原则 | 原则 | 说明 | |------|------| -| 编译期确定类型 | 所有报文类型在 `datarouter_config.h` 中声明,库自动生成代码 | -| 运行期绑定外设 | 端口创建后,用户注册具体的发送 handler(函数指针) | +| 收发分离 | 发送能力和接收能力在配置中分开声明,按需组合 | +| 编译期确定能力 | 所有能力在 `datarouter_config.h` 中声明,库自动生成代码 | +| 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 | +| 一对多广播 | 一个端口可注册多个 handler,发送时逐一调用 | | 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler | | 零动态内存 | 纯静态数组,无 malloc/free | @@ -41,22 +47,22 @@ left to right direction rectangle "用户代码" as User { - component "应用逻辑\n调用 datarouter_send_()" as App - component "datarouter_config.h\n声明报文类型" as Cfg - component "外设 handler\nUART / TCP / CAN / 文件" as Handlers + component "应用逻辑" as App + component "datarouter_config.h\n声明发送+接收能力" as Cfg + component "外设 handler\nUART / TCP / CAN" as Handlers component "接收回调\n处理收到的报文" as Callbacks } database "DataRouter 库" as Lib { - component "datarouter.h\n公共 API + 代码生成" as Hdr - component "datarouter.c\n端口表 + handler 调用" as Impl + component "datarouter.h\n公共 API + X-macro 展开" as Hdr + component "datarouter.c\n端口表 + handler 遍历" as Impl } App --> Hdr Cfg --> Hdr Impl --> Cfg -Handlers --> Impl : 注册为 send_handler -Callbacks --> Impl : 实现 on_recv_ +Handlers --> Impl : 注册 handler +Callbacks --> Impl : 实现 on_recv_ @enduml ``` @@ -65,77 +71,60 @@ Callbacks --> Impl : 实现 on_recv_ | 文件 | 职责 | |------|------| -| `datarouter_config.h` | 用户在此声明所有报文类型 | -| `datarouter.h` | 公共 API + X-macro 展开(enum + 发送函数声明 + 回调声明) | -| `datarouter.c` | 发送函数实现 + handler 调用 + 端口管理 | -| `example.c` | 使用示例 | -| `tests/test_datarouter.c` | 31 个单元测试 | +| `datarouter_config.h` | 分别声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` | +| `datarouter.h` | 公共 API + X-macro 展开(两组枚举 + 声明) | +| `datarouter.c` | 端口表 + handler 列表遍历 + 弱符号回调 | +| `example.c` | 收发分离示例 | +| `tests/test_datarouter.c` | 27 个单元测试 | --- -## 3. 核心概念 +## 3. 收发分离设计 -### 3.1 报文类型(Channel) +### 3.1 配置声明 -```plantuml -@startuml -object "DATAROUTER_CHANNEL(sensor, \"Sensor data\")" as C1 -object "DATAROUTER_CHANNEL(status, \"Status report\")" as C2 -object "DATAROUTER_CHANNEL(control, \"Control command\")" as C3 +```c +// 发送能力 —— 只生成发送触发函数 +#define DATAROUTER_SEND_CAPABILITIES \ + DATAROUTER_SEND(sensor, "Sensor data report") \ + DATAROUTER_SEND(status, "Device status report") -note right of C1 - 编译期展开产物: - · DATAROUTER_CHANNEL_sensor = 0 - · int datarouter_send_sensor(port, data, len) - · void datarouter_on_recv_sensor(port, data, len) - · channel_names[0] = "sensor" - · channel_descs[0] = "Sensor data" -end note - -C1 --> C2 -C2 --> C3 -@enduml +// 接收能力 —— 只生成接收回调声明 +#define DATAROUTER_RECV_CAPABILITIES \ + DATAROUTER_RECV(rtcm, "RTCM correction data") \ + DATAROUTER_RECV(command, "Remote control command") ``` -### 3.2 端口与 handler +### 3.2 展开产物 -```plantuml -@startuml -skinparam objectBorderColor black +``` +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); + 元数据: send_names[] = {"sensor", "status"} + send_descs[] = {"Sensor data report", "Device status report"} -object "端口 A" as PA { - type = sensor - handler = uart_send -} +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 实现,用户覆盖 + 元数据: recv_names[] = {"rtcm", "command"} + recv_descs[] = {"RTCM correction data", "Remote control command"} +``` -object "端口 B" as PB { - type = sensor - handler = tcp_send -} +### 3.3 端口组合示例 -object "端口 C" as PC { - type = status - handler = uart_send -} - -object "UART发送函数" as UART -object "TCP发送函数" as TCP - -PA --> UART : 注册 -PB --> TCP : 注册 -PC --> UART : 注册 - -note right of PA - 同一报文类型 sensor - 可路由到不同外设 -end note - -note right of PC - 不同报文类型 status - 可复用同一外设 UART -end note - -@enduml +``` +端口 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 → 无功能 ``` --- @@ -146,57 +135,42 @@ end note ```plantuml @startuml +== datarouter.h:4 次展开 == -== datarouter.h: 3 次展开 == +-> datarouter_config.h: 获得两组宏列表 --> datarouter_config.h: #include 获得 DATAROUTER_CHANNELS +datarouter.h -> "第 1 遍": + #define DATAROUTER_SEND(id,desc) DATAROUTER_SEND_##id, + DATAROUTER_SEND_CAPABILITIES → 发送 enum -datarouter.h -> "第 1 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_CHANNEL_##id, -note right: 生成 enum 通道枚举 +datarouter.h -> "第 2 遍": + #define DATAROUTER_RECV(id,desc) DATAROUTER_RECV_##id, + DATAROUTER_RECV_CAPABILITIES → 接收 enum -datarouter.h -> "第 2 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(...); -note right: 生成发送触发函数声明 +datarouter.h -> "第 3 遍": + #define DATAROUTER_SEND(id,desc) int datarouter_send_##id(...); + DATAROUTER_SEND_CAPABILITIES → 发送函数声明 -datarouter.h -> "第 3 遍": #define DATAROUTER_CHANNEL(id,desc) void datarouter_on_recv_##id(...); -note right: 生成接收回调声明 +datarouter.h -> "第 4 遍": + #define DATAROUTER_RECV(id,desc) void datarouter_on_recv_##id(...); + DATAROUTER_RECV_CAPABILITIES → 接收回调声明 -== datarouter.c: 3 次展开 == +== datarouter.c:4 次展开 == -datarouter.c -> "第 4 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_WEAK void ... -note right: 弱符号回调默认空实现 +datarouter.c -> "第 5 遍": + 弱符号回调默认实现(DATAROUTER_RECV 展开) -datarouter.c -> "第 5 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(…) {…} -note right: 发送函数实现 → 调用handler +datarouter.c -> "第 6 遍": + 发送函数实现 body(DATAROUTER_SEND 展开) + 校验 → 遍历 handler 列表 -datarouter.c -> "第 6 遍": #define DATAROUTER_CHANNEL(id,desc) #id, → channel_names[] -datarouter.c -> "第 7 遍": #define DATAROUTER_CHANNEL(id,desc) desc, → channel_descs[] +datarouter.c -> "第 7 ~ 8 遍": + send_names[] / send_descs[] + recv_names[] / recv_descs[] @enduml ``` -### 4.2 展开示例(3 个通道) - -``` -用户配置: - DATAROUTER_CHANNEL(sensor, "Sensor data") - DATAROUTER_CHANNEL(status, "Status report") - DATAROUTER_CHANNEL(control, "Control command") - -展开产物: - enum: sensor = 0, status = 1, control = 2, CHANNEL_COUNT = 3 - - 发送: int datarouter_send_sensor(uint32_t, const void*, size_t); - int datarouter_send_status(…); - int datarouter_send_control(…); - - 回调: void datarouter_on_recv_sensor(uint32_t, const void*, size_t); - void datarouter_on_recv_status(…); - void datarouter_on_recv_control(…); - - 元数据: names[] = {"sensor", "status", "control"} - descs[] = {"Sensor data", "Status report", "Control command"} -``` - --- ## 5. 数据结构 @@ -207,64 +181,68 @@ datarouter.c -> "第 7 遍": #define DATAROUTER_CHANNEL(id,desc) desc, → chann @startuml skinparam classAttributeIconSize 0 -class "datarouter_channel (enum)" as Chan { - + DATAROUTER_CHANNEL_sensor = 0 - + DATAROUTER_CHANNEL_status = 1 - + DATAROUTER_CHANNEL_control = 2 - + DATAROUTER_CHANNEL_COUNT = 3 +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 } class "datarouter_send_handler_t" as Handler { + void (*)(uint32_t port_id, const void *data, size_t len) } -class "端口表" as Table { - - port_entry_t ports[64] - - uint8_t initialized -} - -class "port_entry_t" as Entry { - - int type - - datarouter_send_handler_t handler - - uint8_t in_use -} - class "发送函数" as Send { + int send_sensor(port, data, len) + int send_status(port, data, len) - + int send_control(port, data, len) } class "接收回调" as Recv { - + void on_recv_sensor(port, data, len) ← 用户实现 - + void on_recv_status(port, data, len) - + void on_recv_control(port, data, len) + + void on_recv_rtcm(port, data, len) ← 用户实现 + + void on_recv_command(port, data, len) } -Entry --> Chan : type ∈ enum -Entry --> Handler : handler 函数指针 -Table --> Entry : ports[] -Send --> Table : 校验端口 + 调用 handler -Recv --> Send : 被用户从外设收数据时调用 +Entry --> S : send_type ∈ S enum / -1 +Entry --> R : recv_type ∈ R enum / -1 +Entry --> Handler : handlers[] 数组 +Send --> Entry : 校验 send_type + 遍历 handlers +Recv --> Entry : 用户根据业务调对应回调 @enduml ``` ### 5.2 `port_entry_t` ```c +#define DATAROUTER_MAX_HANDLERS_PER_PORT 4 // 每端口最多 4 个外设 + typedef struct { - int type; // 报文类型(DATAROUTER_CHANNEL_xxx / -1) - datarouter_send_handler_t handler; // 外设发送函数(NULL 表示未注册) - uint8_t in_use; // 1=已分配,0=空闲 + int send_type; // DATAROUTER_SEND_xxx / -1 + int recv_type; // DATAROUTER_RECV_xxx / -1 + datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT]; + int count; // 当前 handler 数量 + uint8_t in_use; } port_entry_t; ``` ### 5.3 端口表 ```c -#define DATAROUTER_MAX_PORTS 64 // 可由 -D 覆盖 -static port_entry_t ports[64]; // 静态数组,无动态内存 -static uint8_t initialized; // init/reset 标志 +#define DATAROUTER_MAX_PORTS 64 +static port_entry_t ports[DATAROUTER_MAX_PORTS]; +static uint8_t initialized; ``` --- @@ -274,54 +252,57 @@ static uint8_t initialized; // init/reset 标志 ### 6.1 初始化 ```c -void datarouter_init(void); // 幂等初始化 -void datarouter_reset(void); // 重置所有状态(测试隔离/热重启) +void datarouter_init(void); // 幂等初始化 +void datarouter_reset(void); // 重置全部状态 ``` ### 6.2 端口管理 ```c -datarouter_port_t datarouter_port_create(void); // 返回端口 ID 或 -1 -void datarouter_port_delete(port); // 删除端口,ID 可复用 -void datarouter_port_set_type(port, channel_id); // 指定报文类型 -int datarouter_port_get_type(port); // 获取报文类型,-1 表示无 -void datarouter_port_set_handler(port, handler); // 注册外设发送 handler -datarouter_send_handler_t datarouter_port_get_handler(port); // 获取 handler +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 表示未设置 + +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); // 数量 ``` -### 6.3 发送触发函数(每通道 1 个,X-macro 生成) +### 6.3 发送触发函数(每个 SEND 能力生成一个) ```c -int datarouter_send_sensor(uint32_t port_id, const void *data, size_t len); -int datarouter_send_status(uint32_t port_id, const void *data, size_t len); -int datarouter_send_control(uint32_t port_id, const void *data, size_t len); +int datarouter_send_sensor(port, data, len); +int datarouter_send_status(port, data, len); ``` -返回值: -| 值 | 含义 | -|:--:|------| -| 0 | 成功,handler 已被调用 | -| -1 | 库未初始化 | -| -2 | 端口无效或已删除 | -| -3 | 端口类型与触发的报文类型不匹配 | -| -4 | 端口未注册外设发送 handler | +返回值:0 成功 / -1 未初始化 / -2 端口无效 / -3 send_type 不匹配 / -4 无 handler -### 6.4 接收回调声明(每通道 1 个,用户实现) +### 6.4 接收回调(每个 RECV 能力生成一个声明) ```c -void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len); -void datarouter_on_recv_status(uint32_t port_id, const void *data, size_t len); -void datarouter_on_recv_control(uint32_t port_id, const void *data, size_t len); +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_channel_count(void); -const char *datarouter_channel_name(int channel_id); -const char *datarouter_channel_desc(int channel_id); +// 发送能力 +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); ``` --- @@ -335,36 +316,31 @@ const char *datarouter_channel_desc(int channel_id); actor "应用" as App participant "datarouter_send_sensor()" as Send participant "端口表" as Table -participant "handler" as Handler +participant "handlers[]" as HList participant "外设" as Dev App -> Send : send_sensor(port=2, data, len) group 校验 Send -> Send : initialized? - alt 未初始化 - Send --> App : return -1 + alt 否 → return -1 end - Send -> Table : ports[2].in_use? - alt 端口无效 - Send --> App : return -2 + alt 否 → return -2 end - - Send -> Table : ports[2].type == sensor? - alt 类型不匹配 - Send --> App : return -3 + Send -> Table : ports[2].send_type == SENSOR? + alt 否 → return -3 end - - Send -> Table : ports[2].handler != NULL? - alt 无 handler - Send --> App : return -4 + Send -> Table : ports[2].count > 0? + alt 否 → return -4 end end -Send -> Handler : handler(2, data, len) -Handler -> Dev : UART_SendData(data, len) -Handler --> Send : void +loop for each handler in ports[2].handlers[] + Send -> HList : handlers[i](port, data, len) + HList -> Dev : 实际外设写入 +end + Send --> App : return 0 @enduml ``` @@ -375,13 +351,10 @@ Send --> App : return 0 @startuml actor "外设" as Dev participant "用户分发代码" as Dispatch -participant "datarouter_on_recv_sensor()" as Callback -participant "用户处理逻辑" as Process +participant "on_recv_rtcm()" as Callback -Dev -> Dispatch : 收到数据,识别为 sensor 类型 -Dispatch -> Callback : on_recv_sensor(port_id, data, len) -Callback -> Process : 解析数据、更新状态等 -Process --> Callback : void +Dev -> Dispatch : 收到数据,识别为 rtcm 类型 +Dispatch -> Callback : on_recv_rtcm(port_id, data, len) Callback --> Dispatch : void @enduml ``` @@ -390,53 +363,66 @@ Callback --> Dispatch : void ```plantuml @startuml -state "空闲\nin_use=0" as Idle -state "已创建\nin_use=1\ntype=-1\nhandler=NULL" as Created -state "已配置\nin_use=1\ntype=sensor\nhandler=..." as Config +state "空闲" as Idle +state "已创建\nsend=-1 recv=-1" as Created +state "已配置" as Config -[*] --> Idle : datarouter_reset() +[*] --> Idle : reset / 启动 -Idle --> Created : datarouter_port_create() +Idle --> Created : port_create() -Created --> Config : set_type + set_handler -Config --> Config : set_type / set_handler\n可任意切换 -Config --> Created : set_type(-1) / set_handler(NULL)\n清除能力 +Created --> Config : set_send_type / set_recv_type / add_handler -Created --> Idle : datarouter_port_delete() -Config --> Idle : datarouter_port_delete() +Config --> Config : 独立切换 send_type / recv_type\n增删 handler + +Config --> Created : set_send_type(-1) / set_recv_type(-1)\nclear_handlers + +Created --> Idle : port_delete() +Config --> Idle : port_delete() Idle --> Created : 复用 ID - @enduml ``` --- -## 8. 路由示例 +## 8. 路由场景 -### 场景:传感器数据同时路由到 UART 和 TCP +### 场景 1:传感器只发送(sensor → UART + TCP) -``` -应用 - │ - ├─ send_sensor(sensor_uart_port, data, len) - │ │ - │ ▼ - │ handler = uart_send → UART 外设 - │ - └─ send_sensor(sensor_tcp_port, data, len) - │ - ▼ - handler = tcp_send → TCP 外设 +```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_send_sensor(p, "temp:25.3", 9); +// → uart_send(p, "temp:25.3", 9) +// → tcp_send(p, "temp:25.3", 9) ``` -### 场景:运行时切换路由 +### 场景 2:RTCM 只接收 +```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); ``` -端口 sensor_tcp 原 handler = tcp_send -datarouter_port_set_handler(sensor_tcp, file_send); +### 场景 3:双向端口(status 发送 + command 接收) -端口 sensor_tcp 现 handler = file_send → 改为写入文件 +```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); ``` --- @@ -446,10 +432,10 @@ datarouter_port_set_handler(sensor_tcp, file_send); ```plantuml @startuml package "libDataRouter.a" { - component "弱符号\nvoid on_recv_sensor(...){}" as Weak + component "弱符号\nvoid on_recv_rtcm(...){}" as Weak } package "用户代码" { - component "强符号\nvoid on_recv_sensor(...){...}" as Strong + component "强符号\nvoid on_recv_rtcm(...){...}" as Strong } package "最终链接产物" { component "使用用户版本" as Result @@ -459,44 +445,38 @@ Weak ..> Result : 后备 @enduml ``` -- 库为每个回调提供 `__attribute__((weak))` 空实现 -- 用户定义同名函数自动覆盖 -- 未定义的回调在调用时静默空转 - --- -## 10. 错误码体系 +## 10. 错误码 | 返回值 | 含义 | |:------:|------| -| 0 | 成功 | -| -1 | 库未初始化 | +| 0 | 成功,所有 handler 被调用 | +| -1 | 库未初始化(未调 init) | | -2 | 端口无效(越界/未创建/已删除) | -| -3 | 端口类型不匹配 | -| -4 | 端口未注册发送 handler | +| -3 | 端口 send_type 与调用的发送函数不匹配 | +| -4 | 端口 handler 列表为空(count == 0) | --- ## 11. 配置扩展 -### 增加报文类型 - -编辑 `datarouter_config.h`: +### 增加能力 ```c -#define DATAROUTER_CHANNELS \ - DATAROUTER_CHANNEL(sensor, "Sensor data") \ - DATAROUTER_CHANNEL(status, "Status report") \ - DATAROUTER_CHANNEL(control, "Control command") \ - DATAROUTER_CHANNEL(debug, "Debug message") /* 新增 */ +#define DATAROUTER_SEND_CAPABILITIES \ + DATAROUTER_SEND(sensor, "Sensor data") \ + DATAROUTER_SEND(debug, "Debug log") /* 新增发送能力 */ + +#define DATAROUTER_RECV_CAPABILITIES \ + DATAROUTER_RECV(rtcm, "RTCM data") \ + DATAROUTER_RECV(ota, "OTA firmware") /* 新增接收能力 */ ``` -重新编译后自动生成:枚举、发送函数、回调声明、元数据数组。 - -### 修改最大端口数 +### 自定义限制 ```bash -cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128" ... +cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8" ``` --- @@ -505,8 +485,9 @@ cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128" ... | 约束 | 说明 | |------|------| -| 报文类型 | 编译期固定,修改需重新编译 | -| 最大端口数 | 默认 64,可由 `-D` 覆盖 | +| 能力声明 | 编译期固定,修改需重新编译 | +| 最大端口数 | 默认 64,通过 `-D` 覆盖 | +| 每端口 handler 上限 | 默认 4,通过 `-D` 覆盖 | | 线程安全 | 不支持(无锁),多线程需外部加锁 | | 弱符号 | GCC/Clang 原生支持 | | 内存分配 | 纯静态,无 malloc/free | diff --git a/README.md b/README.md index e2f1084..882d3fb 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,48 @@ # DataRouter -轻量级 C 语言数据路由库。编译期声明报文类型,运行期将端口绑定到物理外设发送函数,实现一对多广播路由。 +轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口独立设置 send_type 和 recv_type,支持多外设一对多广播路由。 ## 概念 | 术语 | 说明 | |------|------| -| **通道 (Channel)** | 编译期声明的报文类型(如 `sensor` / `status` / `control`) | -| **端口 (Port)** | 运行期创建的连接点,绑定一个报文类型和 N 个外设发送 handler | -| **路由** | 将报文类型连接到物理外设发送函数的过程 | +| **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `datarouter_send_()` | +| **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `datarouter_on_recv_()` | +| **端口 (Port)** | 运行期连接点,独立设置 send_type + recv_type + handler 列表 | | **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) | ## 快速开始 -### 1. 声明报文类型 +### 1. 声明能力 -编辑 `datarouter_config.h`: +创建自己的 `datarouter_config.h`(可参考 `datarouter_config_template.h`): ```c -#define DATAROUTER_CHANNELS \ - DATAROUTER_CHANNEL(sensor, "Sensor data") \ - DATAROUTER_CHANNEL(status, "Status report") \ - DATAROUTER_CHANNEL(control, "Control command") +#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") ``` +> 库默认自带一份示例配置。将你的 `datarouter_config.h` 放到项目目录, +> 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源代码。 +> +> **注意**:确保你的配置目录在包含路径中出现在库目录之前,否则编译 `datarouter.c` +> 时仍会使用库的默认配置。在 CMake 中可参考: +> ```cmake +> # 你的 CMakeLists.txt +> include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) # 全局优先 +> add_subdirectory(path/to/DataRouter) +> target_link_libraries(my_app DataRouter) +> ``` + ### 2. 实现外设发送 handler ```c static void uart_send(uint32_t port_id, const void *data, size_t len) { - /* 调用具体外设 API 发送数据 */ HAL_UART_Transmit(&huart1, data, len, 1000); } @@ -37,27 +51,42 @@ static void tcp_send(uint32_t port_id, const void *data, size_t len) { } ``` -### 3. 创建端口并注册 handler +### 3. 创建端口 ```c datarouter_init(); -datarouter_port_t p = datarouter_port_create(); -datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); +/* —— 只发送:传感器上报 —— */ +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 -/* 一个端口同时路由到两个外设 */ -datarouter_port_add_handler(p, uart_send); -datarouter_port_add_handler(p, tcp_send); +/* —— 只接收:RTCM 修正 —— */ +datarouter_port_t rx = datarouter_port_create(); +datarouter_port_set_recv_type(rx, DATAROUTER_RECV_rtcm); +// 外设收到数据后由用户调回调: +datarouter_on_recv_rtcm(rx, data, len); -/* 发送 → 同时走 UART 和 TCP */ -datarouter_send_sensor(p, "temperature:25.3", 16); +/* —— 同时收发:状态上报 + 命令接收 —— */ +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); ``` ### 4. 实现接收回调 ```c -void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len) { - printf("received sensor data: %.*s\n", (int)len, (const char *)data); +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); } ``` @@ -66,48 +95,55 @@ void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len) { ### 初始化 ```c -void datarouter_init(void); // 初始化(幂等) -void datarouter_reset(void); // 重置全部状态 +void datarouter_init(void); // 幂等初始化 +void datarouter_reset(void); // 重置全部状态 ``` ### 端口管理 ```c -datarouter_port_t datarouter_port_create(void); // 创建端口 -void datarouter_port_delete(datarouter_port_t port); // 删除端口 -void datarouter_port_set_type(port, int channel_id); // 设置报文类型 -int datarouter_port_get_type(port); // 获取报文类型 -int datarouter_port_add_handler(port, handler); // 注册外设 handler -int datarouter_port_remove_handler(port, handler); // 移除 handler -void datarouter_port_clear_handlers(port); // 清除全部 handler -int datarouter_port_handler_count(port); // handler 数量 +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 = 未设置 + +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); // 当前数量 ``` -### 发送触发(每通道一个,X-macro 自动生成) +### 发送触发(每个 SEND 能力生成一个) ```c -int datarouter_send_sensor(port, data, len); // 返回值:0 成功 / -1 未初始化 -int datarouter_send_status(port, data, len); // -2 端口无效 -int datarouter_send_control(port, data, len); // -3 类型不匹配 - // -4 无 handler +int datarouter_send_sensor(port, data, len); // 0 成功 / -1 未初始化 +int datarouter_send_status(port, data, len); // -2 端口无效 + // -3 send_type 不匹配 + // -4 无 handler ``` -### 接收回调(每通道一个,用户实现) +### 接收回调(每个 RECV 能力生成一个声明) ```c -void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len); -void datarouter_on_recv_status(uint32_t port_id, const void *data, size_t len); -void datarouter_on_recv_control(uint32_t port_id, const void *data, size_t len); +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); ``` -库提供弱符号默认空实现,用户定义同名函数即可覆盖。 +库提供弱符号默认空实现,用户定义同名函数覆盖。 -### 通道元数据 +### 能力元数据(两组独立) ```c -int datarouter_channel_count(void); -const char *datarouter_channel_name(int channel_id); -const char *datarouter_channel_desc(int channel_id); +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); ``` ## 路由模型 @@ -115,15 +151,15 @@ const char *datarouter_channel_desc(int channel_id); ``` send_sensor(port, data, len) │ - ├─→ handler[0] = uart_send → UART 外设 - ├─→ handler[1] = tcp_send → TCP 外设 - └─→ handler[2] = can_send → CAN 外设 + ├─→ handler[0] = uart_send → UART + ├─→ handler[1] = tcp_send → TCP + └─→ handler[2] = can_send → CAN ``` - 一个端口可注册多个 handler(一对多广播) - 同一 handler 可注册到多个端口(多路复用) - 运行时通过 `add_handler` / `remove_handler` 动态改变路由 -- 清空 handler 后发送返回 -4 +- 清空 handler 列表后发送返回 -4 ## 编译 @@ -132,7 +168,7 @@ cmake -S . -B build cmake --build build ``` -可选配置项: +可选配置: ```bash cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8" @@ -147,18 +183,18 @@ ctest --test-dir build ./build/test_datarouter ``` -当前 36 个测试用例,覆盖端口管理、类型、handler 增删、一对多广播、接收回调、错误边界。 +当前 27 个测试用例,覆盖端口管理、send_type/recv_type 独立设置、handler 增删、一对多广播、接收回调、能力元数据、错误边界。 ## 文件结构 ``` -datarouter_config.h 报文类型配置(用户编辑) -datarouter.h 公共 API + X-macro 展开 -datarouter.c 实现(端口表 + handler 调用) -example.c 使用示例 -tests/test_datarouter.c 单元测试 -DESIGN.md 详细设计文档 -TEST_CASES.md 测试用例文档 +datarouter_config.h 发送 + 接收能力配置(用户编辑) +datarouter.h 公共 API + X-macro 展开 +datarouter.c 实现(端口表 + handler 遍历) +example.c 收发分离使用示例 +tests/test_datarouter.c 单元测试(27 用例) +DESIGN.md 详细设计文档(含 PlantUML 图) +TEST_CASES.md 测试用例文档 ``` ## 依赖 diff --git a/TEST_CASES.md b/TEST_CASES.md index 9b86942..28b2b9b 100644 --- a/TEST_CASES.md +++ b/TEST_CASES.md @@ -8,7 +8,8 @@ | 编译器 | GCC 11.4.0 | | 测试框架 | 自定义轻量框架(宏 + goto cleanup) | | 构建系统 | CMake ≥ 3.10 | -| 报文类型 | `sensor` / `status` / `control` 三个通道 | +| 发送能力 | `sensor` / `status` | +| 接收能力 | `rtcm` / `command` | ## 运行方式 @@ -26,78 +27,80 @@ ctest --test-dir build | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 1 | `error_uninit` | 未初始化时不能发送 | ① 不调 init 直接 `send_sensor(0, ...)` | 返回 -1 | +| 1 | `error_uninit` | 未 init 时发送 | 不调 init 直接 `send_sensor(0, ...)` | 返回 -1 | -### 2. 端口管理(7 个) +### 2. 端口管理(4 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 3 | `port_create_basic` | 基本创建 | ① `datarouter_port_create()` | 返回 ≥ 0 | -| 4 | `port_create_multiple` | 多端口唯一 ID | ① 连续创建 3 个端口 | ID 互不相同 | -| 5 | `port_create_reuse` | 删除后 ID 复用 | ① 创建 A ② 删除 A ③ 再创建 | 新 ID == A ID | -| 6 | `port_delete_invalid` | 删除无效 ID | ① `delete(-1)` ② `delete(9999)` | 不崩溃 | -| 7 | `port_delete_twice` | 重复删除 | ① 创建 ② 删除 ③ 再次删除 | 不崩溃 | -| 8 | `port_default_state` | 默认状态 | ① 创建端口 ② `get_type()` ③ `get_handler()` | type == -1, handler == NULL | -| 9 | `port_create_max` | 满容量边界 | ① 循环创建 64 个 ② 再创建一个 | 64 个成功,第 65 个返回 INVALID | +| 2 | `port_create_basic` | 基本创建 | `port_create()` | 返回 ≥ 0 | +| 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 | -### 3. 类型与 handler 设置获取(7 个) +### 3. send_type / recv_type 独立设置(6 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 10 | `type_set_get` | 设置并获取类型 | ① `set_type(sensor)` ② `get_type()` | 返回 sensor | -| 11 | `type_clear` | 清除类型 | ① `set_type(sensor)` ② `set_type(-1)` ③ `get_type()` | 返回 -1 | -| 12 | `type_switch` | 运行时切换类型 | ① `set_type(sensor)` ② `set_type(status)` ③ `get_type()` | 返回 status | -| 13 | `type_from_invalid_port` | 无效端口查询类型 | ① `get_type(-1)` ② `get_type(999)` | 均返回 -1 | -| 14 | `handler_set_get` | 设置并获取 handler | ① `set_handler(f)` ② `get_handler()` | 返回 f | -| 15 | `handler_clear` | 清除 handler | ① `set_handler(f)` ② `set_handler(NULL)` ③ `get_handler()` | 返回 NULL | -| 16 | `handler_from_invalid_port` | 无效端口查询 handler | ① `get_handler(-1)` ② `get_handler(999)` | 均返回 NULL | +| 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 | -### 4. 通道元数据查询(4 个) +### 4. handler 增删(2 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 17 | `channel_count` | 通道计数 | ① `channel_count()` | 3 | -| 18 | `channel_names` | 通道名称 | ① 依次 `channel_name(0/1/2)` | "sensor" / "status" / "control" | -| 19 | `channel_descs` | 通道描述 | ① 依次 `channel_desc(0/1/2)` | "Sensor data" / "Status report" / "Control command" | -| 20 | `channel_invalid_id` | 无效 ID | ① `channel_name(-1/99)` ② `channel_desc(-1/99)` | 均返回 NULL | +| 12 | `handler_add_remove` | 添加后移除 | ① add(a) add(b) count=2 ② remove(a) count=1 | count 正确 | +| 13 | `handler_clear` | 清除全部 | add 后 clear | count == 0 | ### 5. 发送触发(9 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 21 | `send_sensor` | sensor 类型发送 | ① 创建端口 ② set_type(sensor) + set_handler(test) ③ `send_sensor(port, "hello", 5)` | 返回 0;handler 被调用,参数正确 | -| 22 | `send_status` | status 类型发送 | 同上,用 status 通道 | 返回 0 | -| 23 | `send_control` | control 类型发送 | 同上,用 control 通道 | 返回 0 | -| 24 | `send_type_mismatch` | 类型不匹配 | ① set_type(sensor) ② `send_status(port, ...)` | 返回 -3;handler 不被调用 | -| 25 | `send_no_handler` | 未注册 handler | ① set_type(sensor) ② 不设 handler ③ `send_sensor(port, ...)` | 返回 -4 | -| 26 | `send_same_type_multiple_ports` | 同类型多端口 | ① 两端口均设 sensor + handler ② 依次发送 | 各调一次 handler | -| 27 | `send_switch_handler_runtime` | 运行时切换 handler | ① 注册 h1 发送 ② 改注册 h2 发送 | 第一次用 h1,第二次用 h2 | -| 28 | `send_zero_length` | 零长度数据 | ① 正常端口 ② `send_sensor(port, "x", 0)` | 返回 0;handler 被调用,len=0 | -| 29 | `send_null_data` | NULL 数据 | ① 正常端口 ② `send_sensor(port, NULL, 0)` | 返回 0;handler 被调用 | +| 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 被调用 | ### 6. 接收回调(1 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 30 | `recv_callback_user_impl` | 用户实现接收回调 | ① `on_recv_sensor(port, data, len)` ② `on_recv_status(port, data, len)` | sensor 回调触发,status 不触发;再触发 status 回调,sensor 不变 | +| 23 | `recv_callbacks` | 用户实现接收回调 | ① `on_recv_rtcm(0, ...)` ② `on_recv_command(1, ...)` | 各自触发,互不干扰 | -### 7. 错误处理(2 个) +### 7. 能力元数据(2 个) | # | 测试名 | 描述 | 步骤 | 预期 | |---|--------|------|------|------| -| 30 | `error_invalid_port` | 无效端口 ID | ① `send_sensor(999, ...)` ② `send_sensor(-1, ...)` | 均返回 -2 | -| 31 | `error_deleted_port` | 已删除端口 | ① 创建设好类型 + handler ② 删除 ③ `send_sensor(原ID, ...)` | 返回 -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 | ## 覆盖矩阵 | 功能模块 | 用例数 | 主要验证点 | |----------|:------:|------------| -| 未初始化 | 1 | 未 init 调用 send → -1 | -| 端口生命周期 | 7 | 创建/多创建/ID复用/删除异常/满容量 | -| 类型与 handler | 7 | set/get/clear/switch/无效端口 | -| 通道元数据 | 4 | count/names/descs/boundary | -| 发送触发 | 9 | 三通道/类型不匹配/无handler/多端口/切换handler/零长/NULL | +| 未初始化 | 1 | send → -1 | +| 端口管理 | 4 | 创建/满容量/删除异常/默认状态 | +| send_type / recv_type | 6 | 设置/清除/独立/无效端口 | +| handler 增删 | 2 | 添加/移除/清空 | +| 发送触发 | 9 | 单播/多播/类型不匹配/无handler/无send_type/移除后/清空后/零长/NULL | | 接收回调 | 1 | 用户实现覆盖弱符号 | +| 能力元数据 | 2 | SEND 与 RECV 两组独立查询 | | 错误处理 | 2 | 无效端口/已删除端口 | -**总计:31 个测试用例** +**总计:27 个测试用例** diff --git a/build/example b/build/example index 4be9e8b3876580d6255a70ad7ebc3aba007f1fc1..9499c22e4acb5e41fa65ac01262d984da0e0517e 100755 GIT binary patch delta 4425 zcmZu#3vg7`89w(Wn->XrkYuyFNp|yELPC-a33`zT$iN;(jy_YTC-%~GYb zoBAcS@MwWfMq{j`^RW``lFw!yTHgCgweyjJOSkOkh>ygbEy(#QXLxJO-Ew%92$18} z9TIe4cM9GS#rp(b8pSJucSZ37f^Uf8AJh4A=8jVQQaE^{_!kO>*T(fE?nryX@t(w( znl-D6vGMyVQ|%8j-n`ou_Ji?mGX?3Rw9HMZ&Pa3)iLNse@y;b9(K#d0%BhKL zTB37OQZY%rZH`maWUlfYpn z;`I!Xm#(TRO!lWCjCWf~hDQG#cBz>6$Y>1D6B+bGj6Cln58Rn>@73HR`)BIz36t*Q zVU!W+*Zju@J!1zEj@S7DDxcDQR4aQ7WxMsVVRA3_K2n^}rs#NNrj}xCf1;k^q(<`! z2_X?`KYvwF`r%2%8wfRM^Lz%I3n&29LyBn97^Jf?l#+El4P<9X{j@%-LRwFM%qmZ` zm%}txe&`UX*@bd$IenkCKt5PTK68!q90juH%B^+uB%q~?zA=|cSLjl9xqN93x08NF z_nK?v&2y+X+Y(NF&9rbnV-FtcE8Ndel{R-6QRy5MiUM2{s(Q?1fr<2mG_>Z(#nk*O zKlBd#`q69vI0oVDcG>Jd>3d#$#CpIt@Hvo(}%Etfx@ zO^d8{d8mfk(0hC~ZM7E3i)Pcy)_gg?hE7`N$=S1a*vo7Oo!XpE3p3Dn8Ihi$d#rP% zr)khyEZ^qTJm<~RJj$UGzoEzT?D9{FXm4JrTwO$`^XAGiMU;?VCVfXW`Nh)nv^sxo@^gj8EGl!fN!wIN zFXY?h`bw?fszMYzOD%{R>ZnW$?G{O*$L*Gc0%*P1UF5cB>s021L#M7y&QuTSFg;+; z(@o90rkUbgOYm+!ns9^);+O4k%ui@p-4s;XyVp>&BQxCI-Lb~u=nVuFM@e6kqolGd zl*Cr~dpaELzFyzj&aO_S(;tGOBZ)a2t30>f;JB*`>E+G{{W5yPh3*fOq_D& z@OKCRVJol|7=yDz0d4^91bzh^0p5270e}oA>^Lw4%*6TpA7CwT0@w=2i=5RBU#cqq!WfNvDEPU+y9^ST zi{DoKs;RozX4(U3l_s5l)Tc>fke;9iift9in6iDC+PN4y^UO?pSYDbE=h+++lP@R7 zi<=FP$JgXd+J|_pkcKshFUxaeDY2P$LVA%JOKfo?kVY=h#u97XDTpU7(5@1za~$Fa zSda-sn#>}9t!3cRGm(AoMLG?yT8JNAq$?0xA-cwCm(vyt=@FXXnIN4Zi_>NrfmB0{ zkWN9`LU%ZAapRC;FVjY+H7*lNarMUm5w+(HhW}m}$FE$t*SAfr8X{hOr5|d?2alPPYX6s?}rDyC` z@O-kETZ9*I7gg7p!}mj*E?Zbaly*Jmksqt9HJp)Jdst+7Gio7!uVt~aN5v~5%vr0j z|AW};#_oMa=fkXwA3F7H zLahD-JyrKN3fJ39_?@Gz$Jgx-@$Y^|TZcc?9>PtaeVt&rNW=BFQ&Gb*8fu#1;x~Y{ zkY8!jjFkRfKjU|UV4zR&2cbX^T#>XuthY}IQCee8spiz-@A50ttTp;ah^4t977aCI zW=yh^8E9{V**+zUrnr_R*x94p$Md92D)^A-sm9EBam%0&8&=St8WRe&Ys!>RlfyAe z@KbJME&aH0cE;pbcrez6kDeanUh&HdieRK$E4x91#4w# z6a9KoRg&?+NQ)nSZ_$mCluQ7|)YS7AJ0U>Bbw#va~+&a-H5Ix?^d5(%^M^&nnTK H3-bR5W@m4> delta 4298 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 literal 44104 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 -#include #if defined(__GNUC__) || defined(__clang__) #define DATAROUTER_WEAK __attribute__((weak)) @@ -20,17 +16,9 @@ #define DATAROUTER_MAX_PORTS 64 #endif -/** - * @struct port_entry_t - * @brief 端口表条目 - * - * @var type 报文类型(DATAROUTER_CHANNEL_xxx / -1) - * @var handlers 外设发送 handler 数组 - * @var count 当前注册的 handler 数量 - * @var in_use 1=已分配,0=空闲 - */ typedef struct { - int type; + int send_type; + int recv_type; datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT]; int count; uint8_t in_use; @@ -39,48 +27,62 @@ typedef struct { static port_entry_t ports[DATAROUTER_MAX_PORTS]; static uint8_t initialized; -/* ---- 通道元数据(X-macro 展开) ---- */ +/* ---- 发送能力元数据 ---- */ -static const char *channel_names[] = { -#define DATAROUTER_CHANNEL(id, desc) #id, - DATAROUTER_CHANNELS -#undef DATAROUTER_CHANNEL +static const char *send_names[] = { +#define DATAROUTER_SEND(id, desc) #id, + DATAROUTER_SEND_CAPABILITIES +#undef DATAROUTER_SEND }; -static const char *channel_descs[] = { -#define DATAROUTER_CHANNEL(id, desc) desc, - DATAROUTER_CHANNELS -#undef DATAROUTER_CHANNEL +static const char *send_descs[] = { +#define DATAROUTER_SEND(id, desc) desc, + DATAROUTER_SEND_CAPABILITIES +#undef DATAROUTER_SEND +}; + +/* ---- 接收能力元数据 ---- */ + +static const char *recv_names[] = { +#define DATAROUTER_RECV(id, desc) #id, + DATAROUTER_RECV_CAPABILITIES +#undef DATAROUTER_RECV +}; + +static const char *recv_descs[] = { +#define DATAROUTER_RECV(id, desc) desc, + DATAROUTER_RECV_CAPABILITIES +#undef DATAROUTER_RECV }; /* ---- 弱符号接收回调默认实现 ---- */ -#define DATAROUTER_CHANNEL(id, desc) \ +#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; \ } -DATAROUTER_CHANNELS -#undef DATAROUTER_CHANNEL +DATAROUTER_RECV_CAPABILITIES +#undef DATAROUTER_RECV /* ---- 发送触发函数实现 ---- */ -#define DATAROUTER_CHANNEL(id, desc) \ +#define DATAROUTER_SEND(id, desc) \ int datarouter_send_##id(uint32_t port_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].type != DATAROUTER_CHANNEL_##id) return -3; \ + 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; \ } -DATAROUTER_CHANNELS -#undef DATAROUTER_CHANNEL +DATAROUTER_SEND_CAPABILITIES +#undef DATAROUTER_SEND -/* ---- 端口管理 API ---- */ +/* ---- 端口管理 ---- */ void datarouter_init(void) { @@ -100,9 +102,10 @@ datarouter_port_t datarouter_port_create(void) if (!initialized) return DATAROUTER_INVALID_PORT; for (int i = 0; i < DATAROUTER_MAX_PORTS; i++) { if (!ports[i].in_use) { - ports[i].in_use = 1; - ports[i].type = -1; - ports[i].count = 0; + ports[i].in_use = 1; + ports[i].send_type = -1; + ports[i].recv_type = -1; + ports[i].count = 0; return (datarouter_port_t)i; } } @@ -116,18 +119,32 @@ void datarouter_port_delete(datarouter_port_t port) ports[port].in_use = 0; } -void datarouter_port_set_type(datarouter_port_t port, int channel_id) +void datarouter_port_set_send_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].type = channel_id; + ports[port].send_type = cap_id; } -int datarouter_port_get_type(datarouter_port_t port) +int datarouter_port_get_send_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].type; + 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) @@ -168,21 +185,38 @@ int datarouter_port_handler_count(datarouter_port_t port) return ports[port].count; } -/* ---- 通道元数据查询 ---- */ +/* ---- 元数据查询 ---- */ -int datarouter_channel_count(void) +int datarouter_send_cap_count(void) { - return DATAROUTER_CHANNEL_COUNT; + return DATAROUTER_SEND_CAP_COUNT; } -const char *datarouter_channel_name(int channel_id) +const char *datarouter_send_cap_name(int cap_id) { - if (channel_id < 0 || channel_id >= DATAROUTER_CHANNEL_COUNT) return NULL; - return channel_names[channel_id]; + if (cap_id < 0 || cap_id >= DATAROUTER_SEND_CAP_COUNT) return NULL; + return send_names[cap_id]; } -const char *datarouter_channel_desc(int channel_id) +const char *datarouter_send_cap_desc(int cap_id) { - if (channel_id < 0 || channel_id >= DATAROUTER_CHANNEL_COUNT) return NULL; - return channel_descs[channel_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]; } diff --git a/datarouter.h b/datarouter.h index 0e449ef..26aa877 100644 --- a/datarouter.h +++ b/datarouter.h @@ -4,30 +4,20 @@ * * ## 概念 * - * - **通道 (Channel)**:编译期声明的报文类型(如 sensor / status / control) - * - **端口 (Port)**:运行期创建的连接点,绑定一个报文类型和多个外设发送 handler - * - **路由**:将报文类型关联到一个或多个物理外设发送函数 - * - **多外设**:一个端口可注册多个 handler,发送时逐一调用全部 handler + * - **发送能力 (Send Capability)**:编译期声明的可发送报文类型 + * - **接收能力 (Recv Capability)**:编译期声明的可接收报文类型 + * - **端口 (Port)**:运行期连接点,独立设置 send_type + recv_type + 外设 handler 列表 * * ## 使用流程 * - * 1. 在 datarouter_config.h 中声明报文类型 - * 2. 调用 datarouter_init() 初始化 - * 3. 通过 datarouter_port_create() 创建端口 - * 4. 调用 datarouter_port_set_type() 指定端口处理的报文类型 - * 5. 调用 datarouter_port_add_handler() 注册一个或多个外设发送函数 - * 6. 调用 datarouter_send_() 触发发送(库逐一调用所有注册的 handler) - * 7. 实现 datarouter_on_recv_() 处理从外设收到的报文 - * 8. datarouter_port_delete() 移除端口 - * - * ## 路由规则 - * - * datarouter_send_(port, data, len) - * → 校验端口类型 - * → for each handler: handler(port, data, len) - * → return 0 - * - * 接收:外设收到数据 → 用户识别报文类型 → 调 datarouter_on_recv_(port, data, len) + * 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_() 处理接收数据 */ #ifndef DATAROUTER_H @@ -36,74 +26,69 @@ #include #include -#include "datarouter_config.h" +/* + * 配置加载策略: + * 1. 优先从 include path 搜索 (用户提供的版本) + * 2. 找不到则回退到库自带的 "datarouter_config.h"(默认配置) + * + * 用户只需将自己的 datarouter_config.h 放到项目目录, + * 编译时添加 -I /path/to/your/config 即可覆盖内置配置。 + */ +#if defined(__has_include) +# if __has_include() +# include +# else +# include "datarouter_config.h" +# endif +#else +# include "datarouter_config.h" +#endif #ifdef __cplusplus extern "C" { #endif -/** - * @enum datarouter_channel - * @brief 报文类型枚举 - * - * 由 DATAROUTER_CHANNELS 宏列表自动生成。 - */ -enum datarouter_channel { -#define DATAROUTER_CHANNEL(id, desc) DATAROUTER_CHANNEL_##id, - DATAROUTER_CHANNELS -#undef DATAROUTER_CHANNEL - DATAROUTER_CHANNEL_COUNT +/* ========== 发送能力枚举 ========== */ + +enum datarouter_send_cap { +#define DATAROUTER_SEND(id, desc) DATAROUTER_SEND_##id, + DATAROUTER_SEND_CAPABILITIES +#undef DATAROUTER_SEND + DATAROUTER_SEND_CAP_COUNT }; -/** - * @brief 外设发送 handler 类型 - * - * @param[in] port_id 源端口 ID - * @param[in] data 数据缓冲区 - * @param[in] len 数据长度 - */ +/* ========== 接收能力枚举 ========== */ + +enum datarouter_recv_cap { +#define DATAROUTER_RECV(id, desc) DATAROUTER_RECV_##id, + DATAROUTER_RECV_CAPABILITIES +#undef DATAROUTER_RECV + DATAROUTER_RECV_CAP_COUNT +}; + +/* ========== 外设发送 handler 类型 ========== */ + typedef void (*datarouter_send_handler_t)(uint32_t port_id, const void *data, size_t len); #ifndef DATAROUTER_MAX_HANDLERS_PER_PORT #define DATAROUTER_MAX_HANDLERS_PER_PORT 4 #endif -/** - * @defgroup send_functions 发送触发函数 - * - * 每个报文类型生成一个发送触发函数。 - * 内部校验端口后,逐一调用端口上注册的所有 handler。 - * @{ - */ +/* ========== 发送触发函数(每个发送能力生成一个) ========== */ -#define DATAROUTER_CHANNEL(id, desc) \ +#define DATAROUTER_SEND(id, desc) \ int datarouter_send_##id(uint32_t port_id, const void *data, size_t len); -DATAROUTER_CHANNELS -#undef DATAROUTER_CHANNEL +DATAROUTER_SEND_CAPABILITIES +#undef DATAROUTER_SEND -/** @} */ +/* ========== 接收回调声明(每个接收能力生成一个,用户实现) ========== */ -/** - * @defgroup recv_callbacks 接收回调声明 - * - * 每个报文类型生成一个接收回调声明。用户实现。 - * @{ - */ - -#define DATAROUTER_CHANNEL(id, desc) \ +#define DATAROUTER_RECV(id, desc) \ void datarouter_on_recv_##id(uint32_t port_id, const void *data, size_t len); -DATAROUTER_CHANNELS -#undef DATAROUTER_CHANNEL +DATAROUTER_RECV_CAPABILITIES +#undef DATAROUTER_RECV -/** @} */ - -/** - * @defgroup port_api 端口管理 API - * - * 每个端口可注册 N 个外设发送 handler(上限 DATAROUTER_MAX_HANDLERS_PER_PORT)。 - * 发送时逐一调用全部 handler,实现一对多广播路由。 - * @{ - */ +/* ========== 端口管理 API ========== */ typedef int32_t datarouter_port_t; #define DATAROUTER_INVALID_PORT ((datarouter_port_t)-1) @@ -114,46 +99,25 @@ void datarouter_reset(void); datarouter_port_t datarouter_port_create(void); void datarouter_port_delete(datarouter_port_t port); -void datarouter_port_set_type(datarouter_port_t port, int channel_id); -int datarouter_port_get_type(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); -/** - * @brief 为端口添加一个外设发送 handler - * @param[in] port 端口 ID - * @param[in] handler 发送函数指针(不可为 NULL) - * @retval 0 成功 - * @retval -1 端口无效 - * @retval -2 handler 列表已满 - * @retval -3 handler 已存在(不重复添加) - */ -int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler); - -/** - * @brief 从端口移除一个外设发送 handler - * @param[in] port 端口 ID - * @param[in] handler 要移除的函数指针 - * @retval 0 成功 - * @retval -1 端口无效 - * @retval -2 handler 未找到 - */ -int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handler_t handler); - -/** @brief 清除端口所有 handler */ +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); void datarouter_port_clear_handlers(datarouter_port_t port); +int datarouter_port_handler_count(datarouter_port_t port); -/** @brief 获取端口当前 handler 数量 @return handler 数量,-1 表示端口无效 */ -int datarouter_port_handler_count(datarouter_port_t port); +/* ========== 能力元数据查询 ========== */ -/** @} */ +int datarouter_send_cap_count(void); +const char *datarouter_send_cap_name(int cap_id); +const char *datarouter_send_cap_desc(int cap_id); -/** - * @defgroup introspection 报文类型元数据查询 - * @{ - */ -int datarouter_channel_count(void); -const char *datarouter_channel_name(int channel_id); -const char *datarouter_channel_desc(int channel_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); #ifdef __cplusplus } diff --git a/datarouter_config.h b/datarouter_config.h index 26b0c3c..8086134 100644 --- a/datarouter_config.h +++ b/datarouter_config.h @@ -1,34 +1,40 @@ /** * @file datarouter_config.h - * @brief 数据报文类型配置 + * @brief 数据路由默认配置 * - * 用户在此文件中定义所有报文类型(通道)。 - * 每个报文类型通过 DATAROUTER_CHANNEL(id, desc) 宏声明, - * 库在编译期据此自动生成: - * - 通道枚举值 DATAROUTER_CHANNEL_ - * - 发送触发函数 datarouter_send_(port, data, len) - * - 接收回调声明 datarouter_on_recv_(port, data, len) + * 这是库内置的默认配置,提供示例能力声明。 + * 用户可自定义此文件以实现自己的报文类型: + * + * 1. 复制当前文件到你的项目目录 + * 2. 按需修改 DATAROUTER_SEND_CAPABILITIES / DATAROUTER_RECV_CAPABILITIES + * 3. 编译时添加 -I /path/to/your/config 覆盖库的默认配置 + * + * 覆盖后库将使用你的版本,无需修改库源代码。 */ #ifndef DATAROUTER_CONFIG_H #define DATAROUTER_CONFIG_H /** - * @def DATAROUTER_CHANNELS - * @brief 报文类型列表宏 + * @def DATAROUTER_SEND_CAPABILITIES + * @brief 发送能力列表 * - * 每行格式:DATAROUTER_CHANNEL(标识符, "描述文字") - * 用户按需增删改。每个标识符对应一种报文类型。 - * - * @code - * #define DATAROUTER_CHANNELS \ - * DATAROUTER_CHANNEL(sensor, "Sensor data") \ - * DATAROUTER_CHANNEL(control, "Control command") - * @endcode + * 每行格式:DATAROUTER_SEND(标识符, "描述") + * 每项生成:发送触发函数 datarouter_send_(port, data, len) */ -#define DATAROUTER_CHANNELS \ - DATAROUTER_CHANNEL(sensor, "Sensor data") \ - DATAROUTER_CHANNEL(status, "Status report") \ - DATAROUTER_CHANNEL(control, "Control command") +#define DATAROUTER_SEND_CAPABILITIES \ + DATAROUTER_SEND(sensor, "Sensor data report") \ + DATAROUTER_SEND(status, "Device status report") + +/** + * @def DATAROUTER_RECV_CAPABILITIES + * @brief 接收能力列表 + * + * 每行格式:DATAROUTER_RECV(标识符, "描述") + * 每项生成:接收回调声明 datarouter_on_recv_(port, data, len) + */ +#define DATAROUTER_RECV_CAPABILITIES \ + DATAROUTER_RECV(rtcm, "RTCM correction data") \ + DATAROUTER_RECV(command, "Remote control command") #endif /* DATAROUTER_CONFIG_H */ \ No newline at end of file diff --git a/datarouter_config_template.h b/datarouter_config_template.h new file mode 100644 index 0000000..d102e4f --- /dev/null +++ b/datarouter_config_template.h @@ -0,0 +1,51 @@ +/** + * @file datarouter_config_template.h + * @brief 数据路由配置模板 —— 复制此文件到项目目录并修改 + * + * 使用方法: + * 1. 将此文件复制到你的项目目录,重命名为 datarouter_config.h + * 2. 修改下面的 DATAROUTER_SEND_CAPABILITIES 和 DATAROUTER_RECV_CAPABILITIES + * 3. 编译时添加 -I /path/to/your/project + * 4. 库将自动使用你的配置,无需修改库源代码 + * + * 格式说明: + * 发送能力:DATAROUTER_SEND(标识符, "描述") + * 接收能力:DATAROUTER_RECV(标识符, "描述") + * + * 标识符只能包含字母、数字、下划线。 + * 描述为字符串字面量。 + * + * 示例配置: + * // 传感器设备:只需发送 + * #define DATAROUTER_SEND_CAPABILITIES \ + * DATAROUTER_SEND(sensor, "Sensor data report") + * #define DATAROUTER_RECV_CAPABILITIES + * + * // 遥控设备:只需接收 + * #define DATAROUTER_SEND_CAPABILITIES + * #define DATAROUTER_RECV_CAPABILITIES \ + * DATAROUTER_RECV(command, "Remote command") + * + * // 网关设备:同时收发 + * #define DATAROUTER_SEND_CAPABILITIES \ + * DATAROUTER_SEND(status, "Status report") + * #define DATAROUTER_RECV_CAPABILITIES \ + * DATAROUTER_RECV(config, "Configuration update") + */ + +#ifndef DATAROUTER_CONFIG_H +#define DATAROUTER_CONFIG_H + +/* ========== 在此声明你的发送能力 ========== */ + +#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") + +#endif /* DATAROUTER_CONFIG_H */ \ No newline at end of file diff --git a/example.c b/example.c index d5b4f46..2760478 100644 --- a/example.c +++ b/example.c @@ -3,18 +3,16 @@ * @brief 数据路由库使用示例 * * 展示: - * 1. 编译期声明报文类型 → 自动生成发送触发函数和接收回调声明 - * 2. 创建端口、设置报文类型 - * 3. 为一个端口注册多个外设 handler(一对多广播路由) - * 4. 运行时增删 handler,切换路由 - * 5. 实现接收回调处理外设收到的数据 + * - 发送能力和接收能力分开声明(sensor 只发送,rtcm 只接收) + * - 端口独立设置 send_type 和 recv_type + * - 一个端口可同时拥有发送和接收能力 */ #include "datarouter.h" #include #include -/* ---- 外设发送 handler 实现 ---- */ +/* ---- 外设发送 handler ---- */ static void uart_send(uint32_t port_id, const void *data, size_t len) { @@ -26,80 +24,70 @@ 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); } -static void file_send(uint32_t port_id, const void *data, size_t len) -{ - printf("[FILE] 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) -{ - printf("[CAN] port %u: %.*s\n", port_id, (int)len, (const char *)data); -} - /* ---- 接收回调实现 ---- */ -void datarouter_on_recv_sensor(uint32_t port_id, const void *data, size_t len) +void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) { - printf("[RX:sensor] 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 datarouter_on_recv_status(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) { - printf("[RX:status] 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) { datarouter_init(); - printf("Channels:\n"); - for (int i = 0; i < datarouter_channel_count(); i++) - printf(" [%d] %s - %s\n", i, - datarouter_channel_name(i), - datarouter_channel_desc(i)); + /* 查询能力 */ + printf("Send capabilities: %d\n", datarouter_send_cap_count()); + 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)); /* - * 创建一个 sensor 端口,注册多个 handler。 - * 一次 send_sensor() 调用同时路由到 UART + TCP + CAN。 + * 端口 A:只有发送能力 —— 上报传感器数据 */ - datarouter_port_t sensor_port = datarouter_port_create(); - datarouter_port_set_type(sensor_port, DATAROUTER_CHANNEL_sensor); - datarouter_port_add_handler(sensor_port, uart_send); - datarouter_port_add_handler(sensor_port, tcp_send); - datarouter_port_add_handler(sensor_port, can_send); + 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); - printf("\n--- send sensor (UART + TCP + CAN) ---\n"); - datarouter_send_sensor(sensor_port, "temperature:25.3", 16); + printf("\n--- send sensor (UART + TCP) ---\n"); + datarouter_send_sensor(tx_port, "temperature:25.3", 16); + /* 该端口没有 recv_type,不能接收任何报文 */ /* - * 运行时移除 TCP handler,只剩 UART + CAN。 + * 端口 B:只有接收能力 —— 接收 RTCM 修正 */ - printf("\n--- remove TCP, keep UART + CAN ---\n"); - datarouter_port_remove_handler(sensor_port, tcp_send); - datarouter_send_sensor(sensor_port, "temperature:26.1", 16); + datarouter_port_t rtcm_port = datarouter_port_create(); + datarouter_port_set_recv_type(rtcm_port, DATAROUTER_RECV_rtcm); + /* 没有 send_type,也没有 handler —— 不能发送 */ + + printf("\n--- simulate receive RTCM ---\n"); + datarouter_on_recv_rtcm(rtcm_port, "rtcm_data_v3.2", 14); /* - * 运行时再添加 FILE handler。 + * 端口 C:同时有收发能力 —— 接收命令 + 上报状态 */ - printf("\n--- add FILE back, now UART + CAN + FILE ---\n"); - datarouter_port_add_handler(sensor_port, file_send); - datarouter_send_sensor(sensor_port, "temperature:27.0", 16); + 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); - /* - * 清除所有 handler,不再输出到任何外设。 - */ - printf("\n--- clear all handlers, nothing sent ---\n"); - datarouter_port_clear_handlers(sensor_port); - int ret = datarouter_send_sensor(sensor_port, "lost", 4); - printf("send_sensor after clear returned %d\n", ret); + printf("\n--- bidirectional port ---\n"); + datarouter_send_status(bi_port, "status:OK", 9); + datarouter_on_recv_command(bi_port, "reboot", 6); - /* ---- 接收 ---- */ - printf("\n--- simulate peripheral receive ---\n"); - datarouter_on_recv_sensor(0, "temperature:24.8", 16); - datarouter_on_recv_status(1, "status:ERROR", 12); - - datarouter_port_delete(sensor_port); + /* 清理 */ + datarouter_port_delete(tx_port); + datarouter_port_delete(rtcm_port); + datarouter_port_delete(bi_port); return 0; } diff --git a/tests/test_datarouter.c b/tests/test_datarouter.c index 43eef52..8f895fa 100644 --- a/tests/test_datarouter.c +++ b/tests/test_datarouter.c @@ -3,12 +3,12 @@ * @brief 数据路由库单元测试 * * 覆盖: - * - 端口管理(创建/删除/复用/边界) - * - 端口类型与多 handler 增删查 - * - 通道元数据查询 - * - 一对多广播发送(多 handler 逐一调用) - * - 运行时增删 handler 改变路由 + * - 端口管理 + * - send_type / recv_type 独立设置 + * - 多 handler 增删 + * - 发送调用(一对多广播) * - 接收回调 + * - 发送/接收能力元数据各自独立 * - 错误处理 */ @@ -66,34 +66,19 @@ cleanup: \ * ================================================================ */ static int _h_cnt; -static int _h_total; -static int _h_ids[16]; -static void _h(uint32_t p, const void *d, size_t l) -{ - (void)d; (void)l; - if (_h_cnt < 16) _h_ids[_h_cnt] = (int)p; - _h_cnt++; - _h_total++; -} - +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_hstate(void) -{ - _h_cnt = _h_total = 0; - memset(_h_ids, 0, sizeof(_h_ids)); -} +static void reset_h(void) { _h_cnt = 0; } /* ================================================================ * 1. 未初始化 * ================================================================ */ TEST_START(error_uninit) - int ret = datarouter_send_sensor(0, "d", 1); - CHECK(ret == -1, "expected -1, got %d", ret); + CHECK(datarouter_send_sensor(0, "d", 1) == -1, "expected -1"); TEST_END() /* ================================================================ @@ -101,296 +86,223 @@ TEST_END() * ================================================================ */ TEST_START(port_create_basic) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(p >= 0, "create returned %d", p); + CHECK(FIXTURE_CREATE(&_fix) >= 0, "create failed"); TEST_END() -TEST_START(port_create_multiple) - datarouter_port_t p1 = FIXTURE_CREATE(&_fix); - datarouter_port_t p2 = FIXTURE_CREATE(&_fix); - datarouter_port_t p3 = FIXTURE_CREATE(&_fix); - CHECK(p1 >= 0 && p2 >= 0 && p3 >= 0, "some invalid"); - CHECK(p1 != p2 && p2 != p3, "IDs should be unique"); -TEST_END() - -TEST_START(port_create_reuse) - datarouter_port_t p1 = FIXTURE_CREATE(&_fix); - datarouter_port_delete(p1); - datarouter_port_t p2 = FIXTURE_CREATE(&_fix); - CHECK(p1 == p2, "expected reuse ID %d, got %d", p1, p2); +TEST_START(port_create_max) + int c = 0; + for (int i = 0; i < 64; i++) { + datarouter_port_t p = datarouter_port_create(); + if (p == DATAROUTER_INVALID_PORT) break; + FIXTURE_PUSH(&_fix, p); c++; + } + CHECK(c >= 64, "got %d", c); + CHECK(datarouter_port_create() == DATAROUTER_INVALID_PORT, NULL); TEST_END() TEST_START(port_delete_invalid) - datarouter_port_delete(-1); - datarouter_port_delete(9999); -TEST_END() - -TEST_START(port_delete_twice) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_delete(p); - datarouter_port_delete(p); - _fix.count--; + datarouter_port_delete(-1); datarouter_port_delete(999); TEST_END() TEST_START(port_default_state) datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_get_type(p) == -1, "type should be -1"); - CHECK(datarouter_port_handler_count(p) == 0, "handler count should be 0"); -TEST_END() - -TEST_START(port_create_max) - int created = 0; - for (int i = 0; i < 64; i++) { - datarouter_port_t p = datarouter_port_create(); - if (p == DATAROUTER_INVALID_PORT) break; - FIXTURE_PUSH(&_fix, p); - created++; - } - CHECK(created >= 64, "expected 64, got %d", created); - CHECK(datarouter_port_create() == DATAROUTER_INVALID_PORT, - "expected INVALID after exhaustion"); + 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"); TEST_END() /* ================================================================ - * 3. 类型 + * 3. send_type / recv_type 独立设置 * ================================================================ */ -TEST_START(type_set_get) +TEST_START(send_type_set_get) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); - CHECK(datarouter_port_get_type(p) == DATAROUTER_CHANNEL_sensor, "type mismatch"); + datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); + CHECK(datarouter_port_get_send_type(p) == DATAROUTER_SEND_sensor, NULL); TEST_END() -TEST_START(type_clear) +TEST_START(recv_type_set_get) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); - datarouter_port_set_type(p, -1); - CHECK(datarouter_port_get_type(p) == -1, "should be -1"); + datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm); + CHECK(datarouter_port_get_recv_type(p) == DATAROUTER_RECV_rtcm, NULL); TEST_END() -TEST_START(type_switch) +TEST_START(types_independent) + /* 一个端口同时设置 send_type 和 recv_type,互不干扰 */ datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_status); - CHECK(datarouter_port_get_type(p) == DATAROUTER_CHANNEL_status, "should be status"); + 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(type_from_invalid) - CHECK(datarouter_port_get_type(-1) == -1, NULL); - CHECK(datarouter_port_get_type(999) == -1, NULL); +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 增删查 + * 4. 多 handler * ================================================================ */ -TEST_START(handler_add_one) +TEST_START(handler_add_remove) datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_add_handler(p, _h) == 0, "add failed"); - CHECK(datarouter_port_handler_count(p) == 1, "count=%d", datarouter_port_handler_count(p)); -TEST_END() - -TEST_START(handler_add_multiple) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_add_handler(p, _h_a) == 0, "add a"); - CHECK(datarouter_port_add_handler(p, _h_b) == 0, "add b"); - CHECK(datarouter_port_add_handler(p, _h_c) == 0, "add c"); - CHECK(datarouter_port_handler_count(p) == 3, "count=%d", datarouter_port_handler_count(p)); -TEST_END() - -TEST_START(handler_add_duplicate) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_add_handler(p, _h) == 0, "first add"); - CHECK(datarouter_port_add_handler(p, _h) == -3, "duplicate should return -3"); -TEST_END() - -TEST_START(handler_add_max) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_add_handler(p, _h_a) == 0, "add a"); - CHECK(datarouter_port_add_handler(p, _h_b) == 0, "add b"); - CHECK(datarouter_port_add_handler(p, _h_c) == 0, "add c"); - CHECK(datarouter_port_add_handler(p, _h_d) == 0, "add d"); - CHECK(datarouter_port_handler_count(p) == DATAROUTER_MAX_HANDLERS_PER_PORT, - "count=%d", datarouter_port_handler_count(p)); - /* 第 5 个应返回 -2(已满) */ - CHECK(datarouter_port_add_handler(p, _h) == -2, - "expected -2 (full), got %d", datarouter_port_add_handler(p, _h)); -TEST_END() - -TEST_START(handler_remove) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_add_handler(p, _h_a); - datarouter_port_add_handler(p, _h_b); - datarouter_port_add_handler(p, _h_c); - CHECK(datarouter_port_remove_handler(p, _h_b) == 0, "remove b"); - CHECK(datarouter_port_handler_count(p) == 2, "count should be 2, got %d", datarouter_port_handler_count(p)); -TEST_END() - -TEST_START(handler_remove_not_found) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - CHECK(datarouter_port_remove_handler(p, _h) == -2, "expected -2"); + 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_handler_count(p) == 1, NULL); TEST_END() TEST_START(handler_clear) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_add_handler(p, _h_a); - datarouter_port_add_handler(p, _h_b); + datarouter_port_add_handler(p, _h); datarouter_port_clear_handlers(p); - CHECK(datarouter_port_handler_count(p) == 0, "count should be 0"); -TEST_END() - -TEST_START(handler_from_invalid) - CHECK(datarouter_port_handler_count(-1) == -1, NULL); - CHECK(datarouter_port_handler_count(999) == -1, NULL); - CHECK(datarouter_port_add_handler(-1, _h) == -1, NULL); - CHECK(datarouter_port_remove_handler(999, _h) == -1, NULL); + CHECK(datarouter_port_handler_count(p) == 0, NULL); TEST_END() /* ================================================================ - * 5. 通道元数据 - * ================================================================ */ - -TEST_START(channel_count) - CHECK(datarouter_channel_count() == 3, "expected 3"); -TEST_END() - -TEST_START(channel_names) - CHECK(strcmp(datarouter_channel_name(0), "sensor") == 0, NULL); - CHECK(strcmp(datarouter_channel_name(1), "status") == 0, NULL); - CHECK(strcmp(datarouter_channel_name(2), "control") == 0, NULL); -TEST_END() - -TEST_START(channel_descs) - CHECK(strcmp(datarouter_channel_desc(0), "Sensor data") == 0, NULL); - CHECK(strcmp(datarouter_channel_desc(1), "Status report") == 0, NULL); - CHECK(strcmp(datarouter_channel_desc(2), "Control command") == 0, NULL); -TEST_END() - -TEST_START(channel_invalid_id) - CHECK(datarouter_channel_name(-1) == NULL, NULL); - CHECK(datarouter_channel_name(99) == NULL, NULL); - CHECK(datarouter_channel_desc(-1) == NULL, NULL); - CHECK(datarouter_channel_desc(99) == NULL, NULL); -TEST_END() - -/* ================================================================ - * 6. 发送 — 一对多广播 + * 5. 发送触发 * ================================================================ */ TEST_START(send_one_handler) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); + datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h); - - reset_hstate(); - CHECK(datarouter_send_sensor(p, "d", 1) == 0, "send failed"); + reset_h(); + CHECK(datarouter_send_sensor(p, "d", 1) == 0, NULL); CHECK(_h_cnt == 1, "handler called %d times", _h_cnt); TEST_END() TEST_START(send_multi_handlers) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); + 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_hstate(); - CHECK(datarouter_send_sensor(p, "d", 1) == 0, "send failed"); - CHECK(_h_cnt == 3, "expected 3 handler calls, got %d", _h_cnt); + 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) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); + datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h); - - reset_hstate(); + reset_h(); CHECK(datarouter_send_status(p, "d", 1) == -3, "expected -3"); - CHECK(_h_cnt == 0, "handler should not fire"); + CHECK(_h_cnt == 0, NULL); TEST_END() TEST_START(send_no_handler) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); - - reset_hstate(); + datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4"); TEST_END() -TEST_START(send_three_channels) - /* 三个通道各一个端口 */ - datarouter_port_t p1 = FIXTURE_CREATE(&_fix); - datarouter_port_t p2 = FIXTURE_CREATE(&_fix); - datarouter_port_t p3 = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p1, DATAROUTER_CHANNEL_sensor); - datarouter_port_set_type(p2, DATAROUTER_CHANNEL_status); - datarouter_port_set_type(p3, DATAROUTER_CHANNEL_control); - datarouter_port_add_handler(p1, _h); - datarouter_port_add_handler(p2, _h); - datarouter_port_add_handler(p3, _h); - - reset_hstate(); - CHECK(datarouter_send_sensor(p1, "a", 1) == 0, NULL); - CHECK(datarouter_send_status(p2, "b", 1) == 0, NULL); - CHECK(datarouter_send_control(p3, "c", 1) == 0, NULL); - CHECK(_h_cnt == 3, "expected 3 total calls, got %d", _h_cnt); +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_END() -/* ================================================================ - * 7. 发送 — 运行时增删 handler - * ================================================================ */ - TEST_START(send_remove_handler) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); + datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor); datarouter_port_add_handler(p, _h_a); datarouter_port_add_handler(p, _h_b); - - reset_hstate(); - datarouter_send_sensor(p, "d", 1); - CHECK(_h_cnt == 2, "2 handlers: count=%d", _h_cnt); - + reset_h(); datarouter_send_sensor(p, "d", 1); CHECK(_h_cnt == 2, NULL); datarouter_port_remove_handler(p, _h_a); - - reset_hstate(); - datarouter_send_sensor(p, "d", 1); - CHECK(_h_cnt == 1, "after remove: count=%d", _h_cnt); + reset_h(); datarouter_send_sensor(p, "d", 1); CHECK(_h_cnt == 1, NULL); TEST_END() TEST_START(send_clear_handlers) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); - datarouter_port_add_handler(p, _h_a); - datarouter_port_add_handler(p, _h_b); + 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"); +TEST_END() - CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4 after clear"); +TEST_START(send_zero_length) + 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, "x", 0) == 0, NULL); + CHECK(_h_cnt == 1, NULL); +TEST_END() + +TEST_START(send_null_data) + 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, NULL, 0) == 0, NULL); + CHECK(_h_cnt == 1, NULL); TEST_END() /* ================================================================ - * 8. 接收回调 + * 6. 接收回调 * ================================================================ */ -static int _rx_s; -static void rx_s(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _rx_s++; } -static int _rx_t; -static void rx_t(uint32_t p, const void *d, size_t l) { (void)p; (void)d; (void)l; _rx_t++; } +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++; } -void datarouter_on_recv_sensor(uint32_t p, const void *d, size_t l) { rx_s(p, d, l); } -void datarouter_on_recv_status(uint32_t p, const void *d, size_t l) { rx_t(p, d, l); } - -TEST_START(recv_callback) - _rx_s = _rx_t = 0; - datarouter_on_recv_sensor(0, "data", 4); - CHECK(_rx_s == 1, "sensor cb: %d", _rx_s); - CHECK(_rx_t == 0, "status cb: %d", _rx_t); - datarouter_on_recv_status(1, "data", 4); - CHECK(_rx_t == 1, "status cb: %d", _rx_t); - CHECK(_rx_s == 1, "sensor cb unchanged: %d", _rx_s); +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_END() /* ================================================================ - * 9. 错误处理 + * 7. 能力元数据(两组独立) + * ================================================================ */ + +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() + +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) @@ -400,42 +312,18 @@ TEST_END() TEST_START(error_deleted_port) datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); + 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, "expected -2"); + CHECK(datarouter_send_sensor(p, "d", 1) == -2, NULL); _fix.count--; TEST_END() -/* ================================================================ - * 10. 零长与 NULL - * ================================================================ */ - -TEST_START(send_zero_length) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); - datarouter_port_add_handler(p, _h); - - reset_hstate(); - CHECK(datarouter_send_sensor(p, "x", 0) == 0, NULL); - CHECK(_h_cnt == 1, "handler called %d", _h_cnt); -TEST_END() - -TEST_START(send_null_data) - datarouter_port_t p = FIXTURE_CREATE(&_fix); - datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor); - datarouter_port_add_handler(p, _h); - - reset_hstate(); - CHECK(datarouter_send_sensor(p, NULL, 0) == 0, NULL); - CHECK(_h_cnt == 1, "handler called %d", _h_cnt); -TEST_END() - /* ================================================================ * 主函数 * ================================================================ */ -#define RUN(name) do { reset_hstate(); test_##name(); } while (0) +#define RUN(name) do { reset_h(); test_##name(); } while (0) int main(void) { @@ -448,46 +336,32 @@ int main(void) printf("\n[port management]\n"); RUN(port_create_basic); - RUN(port_create_multiple); - RUN(port_create_reuse); - RUN(port_delete_invalid); - RUN(port_delete_twice); - RUN(port_default_state); RUN(port_create_max); + RUN(port_delete_invalid); + RUN(port_default_state); printf("\n"); datarouter_reset(); datarouter_init(); - printf("[type set/get]\n"); - RUN(type_set_get); - RUN(type_clear); - RUN(type_switch); - RUN(type_from_invalid); + printf("[send_type / recv_type]\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("[multi-handler add/remove]\n"); - RUN(handler_add_one); - RUN(handler_add_multiple); - RUN(handler_add_duplicate); - RUN(handler_add_max); - RUN(handler_remove); - RUN(handler_remove_not_found); + printf("[handler add/remove]\n"); + RUN(handler_add_remove); RUN(handler_clear); - RUN(handler_from_invalid); printf("\n"); - printf("[channel introspection]\n"); - RUN(channel_count); - RUN(channel_names); - RUN(channel_descs); - RUN(channel_invalid_id); - printf("\n"); - - printf("[send — one-to-many broadcast]\n"); + printf("[send trigger]\n"); RUN(send_one_handler); RUN(send_multi_handlers); RUN(send_type_mismatch); RUN(send_no_handler); - RUN(send_three_channels); + RUN(send_no_send_type); RUN(send_remove_handler); RUN(send_clear_handlers); RUN(send_zero_length); @@ -495,7 +369,12 @@ int main(void) printf("\n"); printf("[receive callback]\n"); - RUN(recv_callback); + RUN(recv_callbacks); + printf("\n"); + + printf("[capability metadata]\n"); + RUN(send_cap_meta); + RUN(recv_cap_meta); printf("\n"); printf("[error handling]\n"); @@ -506,6 +385,5 @@ int main(void) printf("==============================\n"); printf(" total: %d | pass: %d | fail: %d\n\n", _total_run, _total_pass, _total_fail); - return _total_fail > 0 ? 1 : 0; }