Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccde2f73e9 | ||
|
|
c5fea61784 | ||
|
|
5d1ef621db | ||
|
|
b717c5f163 | ||
|
|
4e61ef5655 | ||
|
|
5fd55496bf | ||
|
|
26862bf05f | ||
|
|
04770e5df2 | ||
|
|
5a92e2ccfa | ||
|
|
badb4ebf61 | ||
|
|
5aba22d8e9 | ||
|
|
8c670003b8 | ||
|
|
dee130197c | ||
|
|
9258ced2d0 | ||
|
|
b3578b0b53 | ||
|
|
6144b93e10 | ||
|
|
714281dbf1 | ||
|
|
9bce4b12cf | ||
|
|
f6684bebc9 | ||
|
|
924c33fedf | ||
|
|
323b6bd464 | ||
|
|
29c49057b4 | ||
|
|
03039bd493 | ||
|
|
0e0047b946 | ||
|
|
0779e9171f | ||
|
|
18bc4eb08f | ||
|
|
a9f0f995de | ||
|
|
f280a4d383 |
+1
-3
@@ -12,9 +12,6 @@ option(DR_COMPILE_CHECK "Build compile-compatibility tests for each Cortex-M va
|
|||||||
set(DR_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/datarouter.c CACHE INTERNAL "")
|
set(DR_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/datarouter.c CACHE INTERNAL "")
|
||||||
set(DR_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
|
set(DR_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
|
||||||
|
|
||||||
# 预编译包使用者可用 find_package(DataRouter CONFIG) 从此路径加载
|
|
||||||
set(DataRouter_CMAKE_CONFIG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/cmake CACHE INTERNAL "")
|
|
||||||
|
|
||||||
# ── 编译兼容性矩阵 (仅在 host 编译器和 ARM GCC 均可用时生效) ──
|
# ── 编译兼容性矩阵 (仅在 host 编译器和 ARM GCC 均可用时生效) ──
|
||||||
|
|
||||||
if(DR_COMPILE_CHECK)
|
if(DR_COMPILE_CHECK)
|
||||||
@@ -61,6 +58,7 @@ endif()
|
|||||||
add_library(DataRouter STATIC datarouter.c)
|
add_library(DataRouter STATIC datarouter.c)
|
||||||
target_include_directories(DataRouter PUBLIC
|
target_include_directories(DataRouter PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
)
|
)
|
||||||
|
|
||||||
if(BUILD_EXAMPLES OR NOT DEFINED BUILD_EXAMPLES)
|
if(BUILD_EXAMPLES OR NOT DEFINED BUILD_EXAMPLES)
|
||||||
|
|||||||
@@ -2,57 +2,208 @@
|
|||||||
|
|
||||||
## 1. 设计目标
|
## 1. 设计目标
|
||||||
|
|
||||||
轻量级 C 语言数据路由库。发送能力与接收能力分开声明,端口可独立设置 `send_type` 和 `recv_type`。发送时自动广播到所有匹配端口,接收时通过 `dr_port_dispatch()` 查表自动路由到回调。
|
轻量级 C 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_<id>(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。
|
||||||
|
|
||||||
### 核心设计
|
### 核心设计
|
||||||
|
|
||||||
```
|
```
|
||||||
发送: dr_send_sensor(data, len)
|
DR_SEND_CAPABILITIES DR_RECV_CAPABILITIES
|
||||||
│
|
DR_SEND(sensor, ...) DR_RECV(rtcm, ...)
|
||||||
▼
|
DR_SEND(status, ...) DR_RECV(command, ...)
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
send_sensor(data, len) on_recv_rtcm(port, data, len)
|
||||||
|
│ (用户实现回调)
|
||||||
|
▼
|
||||||
遍历端口表 → send_type 匹配?
|
遍历端口表 → send_type 匹配?
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
for each handler(port, data) ─── UART / TCP / CAN
|
for each handler(port, data) ─── UART / TCP / CAN
|
||||||
|
```
|
||||||
|
|
||||||
接收: dr_port_dispatch(port, data, len)
|
**关键**:send 无 port_id 参数,自动路由到所有匹配端口。
|
||||||
│
|
|
||||||
▼
|
### 设计原则
|
||||||
查 recv_table[port.recv_type]
|
|
||||||
│
|
| 原则 | 说明 |
|
||||||
▼
|
|------|------|
|
||||||
dr_on_recv_rtcm(port, data, len) ← 用户实现
|
| 收发分离 | 发送/接收能力在配置中分开声明,按需组合 |
|
||||||
|
| 编译期确定能力 | 所有能力在 `dr_config.h` 中声明,库自动生成代码 |
|
||||||
|
| 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 |
|
||||||
|
| 自动广播 | `send_<id>(data, len)` 遍历所有端口,自动匹配 send_type |
|
||||||
|
| 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler |
|
||||||
|
| 零动态内存 | 纯静态数组,无 malloc/free |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 整体架构
|
||||||
|
|
||||||
|
### 2.1 模块依赖
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
rectangle "用户代码" as User {
|
||||||
|
component "应用逻辑\n调 send_<id>(data, len)" as App
|
||||||
|
component "dr_config.h\n声明能力" as Cfg
|
||||||
|
component "外设 handler\nUART / TCP / CAN" as Handlers
|
||||||
|
component "接收回调\non_recv_<id>()" as Callbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
database "DataRouter 库" as Lib {
|
||||||
|
component "datarouter.h\n公共 API + X-macro" as Hdr
|
||||||
|
component "datarouter.c\n端口表+遍历路由" as Impl
|
||||||
|
}
|
||||||
|
|
||||||
|
App --> Hdr
|
||||||
|
Cfg --> Hdr
|
||||||
|
Impl --> Cfg
|
||||||
|
Handlers --> Impl : add_handler
|
||||||
|
Callbacks --> Impl : 实现 on_recv_<id>
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 文件结构
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `dr_config.h` | 声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` |
|
||||||
|
| `datarouter.h` | 公共 API + X-macro 展开 |
|
||||||
|
| `datarouter.c` | 端口表 + send 遍历 + 弱符号回调 |
|
||||||
|
| `example.c` | 使用示例 |
|
||||||
|
| `tests/test_datarouter.c` | 22 个单元测试 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 收发分离设计
|
||||||
|
|
||||||
|
### 3.1 配置声明
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define DR_SEND_CAPABILITIES \
|
||||||
|
DR_SEND(sensor, "Sensor data report") \
|
||||||
|
DR_SEND(status, "Device status report")
|
||||||
|
|
||||||
|
#define DR_RECV_CAPABILITIES \
|
||||||
|
DR_RECV(rtcm, "RTCM correction data") \
|
||||||
|
DR_RECV(command, "Remote control command")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 展开产物
|
||||||
|
|
||||||
|
```
|
||||||
|
DR_SEND_CAPABILITIES 展开:
|
||||||
|
enum: sensor = 0, status = 1, SEND_CAP_COUNT = 2
|
||||||
|
发送函数: int dr_send_sensor(const void *data, size_t len);
|
||||||
|
int dr_send_status(const void *data, size_t len);
|
||||||
|
元数据: send_names[] = {"sensor", "status"}
|
||||||
|
send_descs[] = {"Sensor data report", "Device status report"}
|
||||||
|
|
||||||
|
DR_RECV_CAPABILITIES 展开:
|
||||||
|
enum: rtcm = 0, command = 1, RECV_CAP_COUNT = 2
|
||||||
|
接收回调: void dr_on_recv_rtcm(uint32_t port, const void*, size_t);
|
||||||
|
void dr_on_recv_command(uint32_t port, const void*, size_t);
|
||||||
|
弱符号默认: 空实现,用户覆盖
|
||||||
|
元数据: recv_names[] = {"rtcm", "command"}
|
||||||
|
recv_descs[] = {"RTCM correction data", "Remote control command"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 端口示例
|
||||||
|
|
||||||
|
```
|
||||||
|
端口 A: send_type = sensor, handlers=[uart_send, tcp_send]
|
||||||
|
端口 B: send_type = sensor, handlers=[can_send]
|
||||||
|
端口 C: send_type = status, handlers=[uart_send]
|
||||||
|
```
|
||||||
|
|
||||||
|
调用 `send_sensor("temp", 4)` → A 和 B 同时触发。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. X-macro 代码生成
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
== datarouter.h ==
|
||||||
|
|
||||||
|
-> dr_config.h: 获得两组宏列表
|
||||||
|
|
||||||
|
datarouter.h -> "1": #define DR_SEND(id,desc) DR_SEND_##id,
|
||||||
|
note right: 发送 enum
|
||||||
|
|
||||||
|
datarouter.h -> "2": #define DR_RECV(id,desc) DR_RECV_##id,
|
||||||
|
note right: 接收 enum
|
||||||
|
|
||||||
|
datarouter.h -> "3": #define DR_SEND(id,desc) int send_##id(data, len);
|
||||||
|
note right: 发送函数声明
|
||||||
|
|
||||||
|
datarouter.h -> "4": #define DR_RECV(id,desc) void on_recv_##id(...);
|
||||||
|
note right: 接收回调声明
|
||||||
|
|
||||||
|
== datarouter.c ==
|
||||||
|
|
||||||
|
datarouter.c -> "5": 弱符号回调默认实现
|
||||||
|
datarouter.c -> "6": 发送函数实现(遍历端口表)
|
||||||
|
datarouter.c -> "7~8": send_names[] / send_descs[] / recv_names[] / recv_descs[]
|
||||||
|
@enduml
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. 数据结构
|
## 5. 数据结构
|
||||||
|
|
||||||
### 2.1 `port_entry_t`
|
### 5.1 核心数据类型
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
skinparam classAttributeIconSize 0
|
||||||
|
|
||||||
|
enum "dr_send_cap" as S { SENSOR, STATUS, SEND_CAP_COUNT }
|
||||||
|
enum "dr_recv_cap" as R { RTCM, COMMAND, RECV_CAP_COUNT }
|
||||||
|
|
||||||
|
class "port_entry_t" as Entry {
|
||||||
|
- int send_type
|
||||||
|
- dr_send_handler_t handlers[]
|
||||||
|
- int count
|
||||||
|
- uint8_t in_use
|
||||||
|
}
|
||||||
|
|
||||||
|
class "dr_send_handler_t" as Handler {
|
||||||
|
+ void (*)(uint32_t port_id, const void *data, size_t len)
|
||||||
|
}
|
||||||
|
|
||||||
|
class "发送函数" as Send {
|
||||||
|
+ int send_sensor(data, len)
|
||||||
|
+ int send_status(data, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
class "接收回调" as Recv {
|
||||||
|
+ void on_recv_rtcm(port, data, len) ← 用户实现
|
||||||
|
+ void on_recv_command(port, data, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry --> S : send_type
|
||||||
|
Entry --> Handler : handlers[]
|
||||||
|
Send --> Entry : 遍历端口表 → 匹配 send_type → 遍历 handlers
|
||||||
|
Recv --> Entry : 用户根据业务调对应回调
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 `port_entry_t`
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define DR_MAX_HANDLERS_PER_PORT 4
|
#define DR_MAX_HANDLERS_PER_PORT 4
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int send_type; // DR_SEND_xxx / -1
|
int send_type;
|
||||||
int recv_type; // DR_RECV_xxx / -1
|
dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT];
|
||||||
dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT];
|
int count;
|
||||||
int count;
|
uint8_t in_use;
|
||||||
uint8_t in_use;
|
|
||||||
} port_entry_t;
|
} port_entry_t;
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.2 接收回调函数指针表
|
### 5.3 端口表
|
||||||
|
|
||||||
```c
|
|
||||||
// 编译期由 X-macro 生成,DR_RECV_rtcm 的枚举值即为数组下标
|
|
||||||
static void (*const recv_table[DR_RECV_CAP_COUNT])(uint32_t, const void*, size_t) = {
|
|
||||||
dr_on_recv_rtcm,
|
|
||||||
dr_on_recv_command,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.3 端口表
|
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define DR_MAX_PORTS 64
|
#define DR_MAX_PORTS 64
|
||||||
@@ -62,121 +213,224 @@ static uint8_t initialized;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. API
|
## 6. API 参考
|
||||||
|
|
||||||
### 3.1 端口管理
|
### 6.1 初始化
|
||||||
|
|
||||||
|
```c
|
||||||
|
void dr_init(void); // 幂等初始化
|
||||||
|
void dr_reset(void); // 重置全部状态
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 端口管理
|
||||||
|
|
||||||
```c
|
```c
|
||||||
dr_port_t dr_port_create(void);
|
dr_port_t dr_port_create(void);
|
||||||
void dr_port_delete(dr_port_t port);
|
void dr_port_delete(dr_port_t port);
|
||||||
|
|
||||||
void dr_port_set_send_type(port, int cap_id);
|
void dr_port_set_send_type(port, int cap_id);
|
||||||
int dr_port_get_send_type(port);
|
int dr_port_get_send_type(port);
|
||||||
void dr_port_set_recv_type(port, int cap_id);
|
|
||||||
int dr_port_get_recv_type(port);
|
|
||||||
|
|
||||||
int dr_port_add_handler(port, handler);
|
int dr_port_add_handler(port, handler);
|
||||||
int dr_port_remove_handler(port, handler);
|
int dr_port_remove_handler(port, handler);
|
||||||
void dr_port_clear_handlers(port);
|
void dr_port_clear_handlers(port);
|
||||||
int dr_port_handler_count(port);
|
int dr_port_handler_count(port);
|
||||||
|
|
||||||
void dr_port_dispatch(port, data, len); // ← 自动路由接收
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.2 发送触发
|
### 6.3 发送触发函数(每个 SEND 能力生成一个)
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int dr_send_sensor(const void *data, size_t len);
|
int dr_send_sensor(const void *data, size_t len);
|
||||||
int dr_send_status(const void *data, size_t len);
|
int dr_send_status(const void *data, size_t len);
|
||||||
```
|
```
|
||||||
|
|
||||||
返回值:0 成功 / -1 未初始化 / -2 无匹配端口
|
返回值:0 成功 / -1 未初始化 / -2 无端口匹配该 send_type
|
||||||
|
|
||||||
### 3.3 接收回调
|
### 6.4 接收回调(每个 RECV 能力生成一个声明)
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len);
|
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len);
|
||||||
void dr_on_recv_command(uint32_t port_id, const void *data, size_t len);
|
void dr_on_recv_command(uint32_t port_id, const void *data, size_t len);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
库提供弱符号默认空实现。
|
||||||
|
|
||||||
|
### 6.5 能力元数据
|
||||||
|
|
||||||
|
```c
|
||||||
|
int dr_send_cap_count(void);
|
||||||
|
const char *dr_send_cap_name(int cap_id);
|
||||||
|
const char *dr_send_cap_desc(int cap_id);
|
||||||
|
|
||||||
|
int dr_recv_cap_count(void);
|
||||||
|
const char *dr_recv_cap_name(int cap_id);
|
||||||
|
const char *dr_recv_cap_desc(int cap_id);
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 核心流程
|
## 7. 核心流程
|
||||||
|
|
||||||
### 4.1 发送
|
### 7.1 发送流程
|
||||||
|
|
||||||
```plantuml
|
```plantuml
|
||||||
@startuml
|
@startuml
|
||||||
actor App
|
actor "应用" as App
|
||||||
participant dr_send_sensor as Send
|
participant "dr_send_sensor()" as Send
|
||||||
participant ports[] as Table
|
participant "端口表 ports[]" as Table
|
||||||
participant handlers[] as H
|
participant "handlers[]" as HList
|
||||||
|
participant "外设" as Dev
|
||||||
|
|
||||||
App -> Send : send_sensor("temp", 9)
|
App -> Send : send_sensor("temp:25.3", 9)
|
||||||
loop for p in 0..MAX_PORTS-1
|
|
||||||
Send -> Table : in_use && send_type == SENSOR ?
|
group 初始化检查
|
||||||
alt 匹配
|
Send -> Send : initialized?
|
||||||
loop for h in handlers
|
alt 否 → return -1
|
||||||
Send -> H : handlers[h](p, data, len)
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
group 遍历端口表
|
||||||
|
loop for p in 0..MAX_PORTS-1
|
||||||
|
Send -> Table : ports[p].in_use && ports[p].send_type == SENSOR ?
|
||||||
|
alt 匹配
|
||||||
|
loop for h in 0..count-1
|
||||||
|
Send -> HList : handlers[h](p, data, len)
|
||||||
|
HList -> Dev : 实际外设写入
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alt 至少调了一次
|
|
||||||
|
alt 至少调了一次 handler
|
||||||
Send --> App : return 0
|
Send --> App : return 0
|
||||||
else 无匹配
|
else 无匹配端口
|
||||||
Send --> App : return -2
|
Send --> App : return -2
|
||||||
end
|
end
|
||||||
@enduml
|
@enduml
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 接收(dispatch)
|
### 7.2 接收流程
|
||||||
|
|
||||||
```plantuml
|
```plantuml
|
||||||
@startuml
|
@startuml
|
||||||
actor 外设ISR as Dev
|
actor "外设" as Dev
|
||||||
participant dr_port_dispatch as Dispatch
|
participant "用户分发代码" as Dispatch
|
||||||
participant recv_table[] as Table
|
participant "on_recv_rtcm()" as Callback
|
||||||
participant dr_on_recv_rtcm as Cb
|
|
||||||
|
|
||||||
Dev -> Dispatch : dispatch(port, data, len)
|
Dev -> Dispatch : 收到数据,识别为 rtcm
|
||||||
Dispatch -> Table : 查 recv_table[port.recv_type]
|
Dispatch -> Callback : on_recv_rtcm(port_id, data, len)
|
||||||
Dispatch -> Cb : recv_table[rid](port, data, len)
|
Callback --> Dispatch : void
|
||||||
Cb --> Dev : void
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 端口生命周期
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
state "空闲" as Idle
|
||||||
|
state "已创建\nsend_type=-1\ncount=0" as Created
|
||||||
|
state "已配置" as Config
|
||||||
|
|
||||||
|
[*] --> Idle : reset / 启动
|
||||||
|
Idle --> Created : port_create()
|
||||||
|
Created --> Config : set_send_type / add_handler
|
||||||
|
Config --> Config : 切换 send_type\n增删 handler
|
||||||
|
Config --> Created : set_send_type(-1) / clear_handlers
|
||||||
|
Created --> Idle : port_delete()
|
||||||
|
Config --> Idle : port_delete()
|
||||||
|
Idle --> Created : 复用 ID
|
||||||
@enduml
|
@enduml
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Dispatch 实现
|
## 8. 路由场景
|
||||||
|
|
||||||
`dr_port_dispatch` 是零分支的查表路由:
|
### 场景 1:sensor 广播到 UART + TCP + CAN
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void dr_port_dispatch(dr_port_t port, const void *data, size_t len)
|
dr_port_t p1 = dr_port_create();
|
||||||
{
|
dr_port_set_send_type(p1, DR_SEND_sensor);
|
||||||
if (!initialized) return;
|
dr_port_add_handler(p1, uart_send);
|
||||||
if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return;
|
dr_port_add_handler(p1, tcp_send);
|
||||||
int rid = ports[port].recv_type;
|
|
||||||
if (rid < 0 || rid >= DR_RECV_CAP_COUNT) return;
|
dr_port_t p2 = dr_port_create();
|
||||||
recv_table[rid]((uint32_t)port, data, len); // 直接函数指针调用
|
dr_port_set_send_type(p2, DR_SEND_sensor);
|
||||||
}
|
dr_port_add_handler(p2, can_send);
|
||||||
|
|
||||||
|
dr_send_sensor("temp:25.3", 9);
|
||||||
|
// → uart_send(p1, "temp:25.3", 9)
|
||||||
|
// → tcp_send(p1, "temp:25.3", 9)
|
||||||
|
// → can_send(p2, "temp:25.3", 9)
|
||||||
```
|
```
|
||||||
|
|
||||||
`recv_table[]` 是 `const` 数组,编译期由 X-macro 生成。`DR_RECV_rtcm` 的枚举值等于数组下标,查表时间为 O(1),无分支预测开销,适合 ISR。
|
### 场景 2:仅接收
|
||||||
|
|
||||||
|
```c
|
||||||
|
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
|
||||||
|
apply_rtcm_correction(data, len);
|
||||||
|
}
|
||||||
|
// 外设收到后:dr_on_recv_rtcm(port, data, len);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景 3:运行时移除 handler
|
||||||
|
|
||||||
|
```c
|
||||||
|
dr_port_remove_handler(p1, tcp_send);
|
||||||
|
// 下次 send_sensor 不再触发 tcp
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 错误码
|
## 9. 弱符号机制
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
package "libDataRouter.a" {
|
||||||
|
component "弱符号\nvoid on_recv_rtcm(...){}" as Weak
|
||||||
|
}
|
||||||
|
package "用户代码" {
|
||||||
|
component "强符号\nvoid on_recv_rtcm(...){...}" as Strong
|
||||||
|
}
|
||||||
|
package "最终链接产物" {
|
||||||
|
component "使用用户版本" as Result
|
||||||
|
}
|
||||||
|
Strong --> Result
|
||||||
|
Weak ..> Result : 后备
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 错误码
|
||||||
|
|
||||||
| 返回值 | 含义 |
|
| 返回值 | 含义 |
|
||||||
|:------:|------|
|
|:------:|------|
|
||||||
| 0 | 成功 |
|
| 0 | 成功,至少有一个 handler 被调用 |
|
||||||
| -1 | 库未初始化 |
|
| -1 | 库未初始化(未调 init) |
|
||||||
| -2 | 无端口匹配该 send_type |
|
| -2 | 无端口匹配该 send_type(或匹配端口均无 handler) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. 约束
|
## 11. 配置扩展
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define DR_SEND_CAPABILITIES \
|
||||||
|
DR_SEND(sensor, "Sensor data") \
|
||||||
|
DR_SEND(debug, "Debug log") /* 新增 */
|
||||||
|
|
||||||
|
#define DR_RECV_CAPABILITIES \
|
||||||
|
DR_RECV(rtcm, "RTCM data") \
|
||||||
|
DR_RECV(ota, "OTA firmware") /* 新增 */
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -DCMAKE_C_FLAGS="-DDR_MAX_PORTS=128 -DDR_MAX_HANDLERS_PER_PORT=8"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 约束
|
||||||
|
|
||||||
| 约束 | 说明 |
|
| 约束 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
# DataRouter
|
# DataRouter
|
||||||
|
|
||||||
轻量级 C 语言数据路由库。发送能力和接收能力分开声明,端口绑定 `send_type` + `recv_type` + handler 列表。发送时自动广播到所有匹配端口,接收时 `dr_port_dispatch()` 自动路由到对应回调。
|
轻量级 C 语言数据路由库。**发送能力和接收能力分开声明**,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_<id>(data, len)` 时自动广播到所有匹配端口。
|
||||||
|
|
||||||
## 概念
|
## 概念
|
||||||
|
|
||||||
| 术语 | 说明 |
|
| 术语 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **发送能力** | 编译期声明的可发送报文类型,生成 `dr_send_<id>(data, len)` |
|
| **发送能力 (Send Capability)** | 编译期声明的可发送报文类型,生成 `dr_send_<id>(data, len)` |
|
||||||
| **接收能力** | 编译期声明的可接收报文类型,生成 `dr_on_recv_<id>(port, data, len)` |
|
| **接收能力 (Recv Capability)** | 编译期声明的可接收报文类型,生成 `dr_on_recv_<id>(port, data, len)` |
|
||||||
| **端口** | 运行期连接点,包含 `send_type` + `recv_type` + handler 列表 |
|
| **端口 (Port)** | 运行期创建的连接点,包含 `send_type` + handler 列表 |
|
||||||
| **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) |
|
| **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) |
|
||||||
| **Dispatch** | `dr_port_dispatch(port, data, len)` 根据端口的 `recv_type` 自动路由到回调 |
|
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
### 1. 声明能力
|
### 1. 声明能力
|
||||||
|
|
||||||
创建自己的 `datarouter_config.h`(可参考 `datarouter_config_template.h`):
|
创建自己的 `dr_config.h`(可参考 `dr_config_template.h`):
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define DR_SEND_CAPABILITIES \
|
#define DR_SEND_CAPABILITIES \
|
||||||
@@ -28,15 +27,24 @@
|
|||||||
DR_RECV(command, "Remote control command")
|
DR_RECV(command, "Remote control command")
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 实现 handler 和回调
|
> 库自带默认配置。将你的 `dr_config.h` 放到项目目录,
|
||||||
|
> 编译时添加 `-I /path/to/your/config` 即可覆盖。无需修改库源码。
|
||||||
|
>
|
||||||
|
> ```cmake
|
||||||
|
> include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
> add_subdirectory(path/to/DataRouter)
|
||||||
|
> target_link_libraries(my_app DataRouter)
|
||||||
|
> ```
|
||||||
|
|
||||||
|
### 2. 实现外设发送 handler
|
||||||
|
|
||||||
```c
|
```c
|
||||||
static void uart_write(uint32_t port_id, const void *data, size_t len) {
|
static void uart_send(uint32_t port_id, const void *data, size_t len) {
|
||||||
HAL_UART_Transmit(&huart1, data, len, 1000);
|
HAL_UART_Transmit(&huart1, data, len, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
|
static void tcp_send(uint32_t port_id, const void *data, size_t len) {
|
||||||
apply_rtcm_correction(data, len);
|
send(tcp_socket, data, len, 0);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -45,17 +53,25 @@ void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
|
|||||||
```c
|
```c
|
||||||
dr_init();
|
dr_init();
|
||||||
|
|
||||||
dr_port_t uart = dr_port_create();
|
/* —— 传感器上报:sensor 报文走 UART + TCP —— */
|
||||||
dr_port_set_send_type(uart, DR_SEND_sensor);
|
dr_port_t tx = dr_port_create();
|
||||||
dr_port_set_recv_type(uart, DR_RECV_rtcm);
|
dr_port_set_send_type(tx, DR_SEND_sensor);
|
||||||
dr_port_add_handler(uart, uart_write);
|
dr_port_add_handler(tx, uart_send);
|
||||||
dr_port_add_handler(uart, tcp_write);
|
dr_port_add_handler(tx, tcp_send);
|
||||||
|
|
||||||
/* 发送:自动广播到所有 send_type=sensor 的端口 */
|
/* 不指定端口,库自动广播到所有 send_type=sensor 的端口 */
|
||||||
dr_send_sensor("temperature:25.3", 16);
|
dr_send_sensor("temperature:25.3", 16); // → UART + TCP
|
||||||
|
|
||||||
/* 接收:外设中断中一行 dispatch,自动调 dr_on_recv_rtcm() */
|
/* —— 接收:实现回调,外设数据到达后自行调用 —— */
|
||||||
dr_port_dispatch(uart, data, len);
|
dr_on_recv_rtcm(rx_port, data, len);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 实现接收回调
|
||||||
|
|
||||||
|
```c
|
||||||
|
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len) {
|
||||||
|
apply_rtcm_correction(data, len);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## API 一览
|
## API 一览
|
||||||
@@ -63,45 +79,45 @@ dr_port_dispatch(uart, data, len);
|
|||||||
### 初始化
|
### 初始化
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void dr_init(void);
|
void dr_init(void); // 幂等初始化
|
||||||
void dr_reset(void);
|
void dr_reset(void); // 重置全部状态
|
||||||
```
|
```
|
||||||
|
|
||||||
### 端口管理
|
### 端口管理
|
||||||
|
|
||||||
```c
|
```c
|
||||||
dr_port_t dr_port_create(void);
|
dr_port_t dr_port_create(void); // 创建端口
|
||||||
void dr_port_delete(dr_port_t port);
|
void dr_port_delete(dr_port_t port); // 删除
|
||||||
|
|
||||||
void dr_port_set_send_type(port, int cap_id);
|
void dr_port_set_send_type(port, int cap_id); // 设置发送类型
|
||||||
int dr_port_get_send_type(port);
|
int dr_port_get_send_type(port); // -1 = 未设置
|
||||||
void dr_port_set_recv_type(port, int cap_id);
|
|
||||||
int dr_port_get_recv_type(port);
|
|
||||||
|
|
||||||
int dr_port_add_handler(port, handler);
|
int dr_port_add_handler(port, handler); // 注册外设 handler
|
||||||
int dr_port_remove_handler(port, handler);
|
int dr_port_remove_handler(port, handler); // 移除
|
||||||
void dr_port_clear_handlers(port);
|
void dr_port_clear_handlers(port); // 清除全部
|
||||||
int dr_port_handler_count(port);
|
int dr_port_handler_count(port); // 当前数量
|
||||||
|
|
||||||
void dr_port_dispatch(port, data, len); /* 自动路由接收 */
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 发送触发
|
### 发送触发(每个 SEND 能力生成一个)
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int dr_send_sensor(const void *data, size_t len); // 0 成功
|
int dr_send_sensor(const void *data, size_t len); // 0 成功
|
||||||
int dr_send_status(const void *data, size_t len); // -1 未初始化
|
int dr_send_status(const void *data, size_t len); // -1 未初始化
|
||||||
// -2 无匹配端口
|
// -2 无匹配端口
|
||||||
```
|
```
|
||||||
|
|
||||||
### 接收回调
|
不指定端口,库遍历所有端口,`send_type` 匹配的逐一调用其全部 handler。
|
||||||
|
|
||||||
|
### 接收回调(每个 RECV 能力生成一个声明)
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len);
|
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len);
|
||||||
void dr_on_recv_command(uint32_t port_id, const void *data, size_t len);
|
void dr_on_recv_command(uint32_t port_id, const void *data, size_t len);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 能力元数据
|
库提供弱符号默认空实现,用户定义同名函数覆盖。
|
||||||
|
|
||||||
|
### 能力元数据(两组独立)
|
||||||
|
|
||||||
```c
|
```c
|
||||||
int dr_send_cap_count(void);
|
int dr_send_cap_count(void);
|
||||||
@@ -116,23 +132,114 @@ const char *dr_recv_cap_desc(int cap_id);
|
|||||||
## 路由模型
|
## 路由模型
|
||||||
|
|
||||||
```
|
```
|
||||||
发送:dr_send_sensor("temp:25.3", 9)
|
dr_send_sensor("temperature:25.3", 16)
|
||||||
│
|
│
|
||||||
├─→ port A (send_type=sensor) handlers = [uart_write, tcp_write]
|
├─→ port 0 (send_type=sensor) handlers[0]=uart_send → UART
|
||||||
└─→ port B (send_type=sensor) handlers = [can_write]
|
│ handlers[1]=tcp_send → TCP
|
||||||
|
└─→ port 1 (send_type=sensor) handlers[0]=can_send → CAN
|
||||||
接收:dr_port_dispatch(uart1, data, len)
|
|
||||||
│
|
|
||||||
└─→ uart1.recv_type = DR_RECV_rtcm
|
|
||||||
└─→ dr_on_recv_rtcm(uart1, data, len) ← 查表调用
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- 调用 `send_<id>(data, len)` 时,库遍历所有端口,自动匹配 `send_type`
|
||||||
|
- 每个端口可注册多个 handler(一对多广播)
|
||||||
|
- 同一 handler 可注册到多个端口(多路复用)
|
||||||
|
- 运行时通过 `add_handler` / `remove_handler` / `port_delete` 动态改变路由
|
||||||
|
- 无任何端口匹配该 `send_type` 时返回 -2
|
||||||
|
|
||||||
|
## 编译
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -S . -B build
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
可选配置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -DCMAKE_C_FLAGS="-DDR_MAX_PORTS=128 -DDR_MAX_HANDLERS_PER_PORT=8"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 嵌入式 / Cortex-M 构建
|
||||||
|
|
||||||
|
库支持 `arm-none-eabi-gcc` 交叉编译,提供 **7 个 CMake preset** 覆盖常见 Cortex-M 目标:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 列出所有 preset
|
||||||
|
cmake --list-presets
|
||||||
|
|
||||||
|
# Cortex-M3(软浮点)
|
||||||
|
cmake --preset cm3
|
||||||
|
cmake --build --preset cm3
|
||||||
|
|
||||||
|
# Cortex-M4F(硬件浮点,单精度)
|
||||||
|
cmake --preset cm4-hard
|
||||||
|
cmake --build --preset cm4-hard
|
||||||
|
|
||||||
|
# Cortex-M7F(硬件浮点,双精度)
|
||||||
|
cmake --preset cm7-hard-dp
|
||||||
|
cmake --build --preset cm7-hard-dp
|
||||||
|
```
|
||||||
|
|
||||||
|
> 使用 preset 编译需要 `arm-none-eabi-gcc` 在 PATH 中。
|
||||||
|
> 各 preset 均启用 `DR_SOURCE_ONLY` 模式——只提供源文件路径,
|
||||||
|
> 你需要将 `datarouter.c` 加入自己的 target 以继承项目编译标志:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
# 你的 CMakeLists.txt
|
||||||
|
set(DR_SOURCE_ONLY ON)
|
||||||
|
FetchContent_MakeAvailable(datarouter)
|
||||||
|
target_sources(my_app PRIVATE ${datarouter_SOURCE_DIR}/datarouter.c)
|
||||||
|
target_include_directories(my_app PRIVATE ${datarouter_SOURCE_DIR})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 编译兼容性矩阵
|
||||||
|
|
||||||
|
启用 `DR_COMPILE_CHECK` 可自动检测 `arm-none-eabi-gcc` 并对 CM3/CM4/CM7
|
||||||
|
各变体执行编译验证:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -B build-check -DDR_COMPILE_CHECK=ON
|
||||||
|
cmake --build build-check # 生成 check_cm3, check_cm4-hard, … 等 target
|
||||||
|
# 或单独检查某个变体:
|
||||||
|
make check_cm7-hard-dp
|
||||||
|
```
|
||||||
|
|
||||||
|
### 内置 CMake Preset 列表
|
||||||
|
|
||||||
|
| Preset | 目标 | FPU | ABI |
|
||||||
|
|--------|------|-----|-----|
|
||||||
|
| `cm3` | Cortex-M3 | 无 | soft |
|
||||||
|
| `cm4-soft` | Cortex-M4 | 无 | soft |
|
||||||
|
| `cm4-hard` | Cortex-M4F | FPv4-SP-D16 | hard |
|
||||||
|
| `cm7-soft` | Cortex-M7 | 无 | soft |
|
||||||
|
| `cm7-hard-sp` | Cortex-M7F | FPv5-SP-D16 | hard |
|
||||||
|
| `cm7-hard-dp` | Cortex-M7F | FPv5-D16 | hard |
|
||||||
|
|
||||||
## 测试
|
## 测试
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake --build build
|
cmake --build build
|
||||||
ctest --test-dir build
|
ctest --test-dir build
|
||||||
|
# 或直接运行
|
||||||
./build/test_datarouter
|
./build/test_datarouter
|
||||||
```
|
```
|
||||||
|
|
||||||
当前 24 个测试用例,覆盖端口管理、send_type/recv_type、handler、自动广播、dispatch、能力元数据。
|
当前 22 个测试用例,覆盖端口管理、send_type 与 handler 设置、自动广播路由、接收回调、能力元数据。
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
dr_config.h 发送 + 接收能力配置(用户可覆盖)
|
||||||
|
datarouter.h 公共 API + X-macro 展开
|
||||||
|
datarouter.c 实现(端口表 + send 遍历)
|
||||||
|
dr_config_template.h 外部配置模板
|
||||||
|
example.c 使用示例
|
||||||
|
tests/test_datarouter.c 单元测试(22 用例)
|
||||||
|
DESIGN.md 详细设计文档(含 PlantUML 图)
|
||||||
|
TEST_CASES.md 测试用例文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- C11 编译器
|
||||||
|
- CMake ≥ 3.10
|
||||||
|
- 无第三方库依赖
|
||||||
|
|||||||
+30
-32
@@ -26,65 +26,63 @@ ctest --test-dir build
|
|||||||
|
|
||||||
| # | 测试名 | 描述 | 步骤 | 预期 |
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|---|--------|------|------|------|
|
|---|--------|------|------|------|
|
||||||
| 1 | `error_uninit` | 未 init 时发送 | `dr_send_sensor("d", 1)` | 返回 -1 |
|
| 1 | `error_uninit` | 未 init 时发送 | `send_sensor("d", 1)` | 返回 -1 |
|
||||||
|
|
||||||
### 2. 端口管理(4 个)
|
### 2. 端口管理(4 个)
|
||||||
|
|
||||||
| # | 测试名 | 描述 | 步骤 | 预期 |
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|---|--------|------|------|------|
|
|---|--------|------|------|------|
|
||||||
| 2 | `port_create_basic` | 基本创建 | `dr_port_create()` | 返回 ≥ 0 |
|
| 2 | `port_create_basic` | 基本创建 | `port_create()` | 返回 ≥ 0 |
|
||||||
| 3 | `port_create_max` | 满容量 64 | 循环到满 | 第 65 个 INVALID |
|
| 3 | `port_create_max` | 满容量 64 | 循环创建到满再尝试 | 64 个成功,第 65 个 INVALID |
|
||||||
| 4 | `port_delete_invalid` | 删除无效 ID | `delete(-1)` + `delete(999)` | 不崩溃 |
|
| 4 | `port_delete_invalid` | 删除无效 ID | `delete(-1)` + `delete(999)` | 不崩溃 |
|
||||||
| 5 | `port_default_state` | 默认状态 | 查 send_type / recv_type / count | 均为 -1 / 0 |
|
| 5 | `port_default_state` | 默认状态 | 新建端口,查 send_type / handler count | `send_type=-1`, `count=0` |
|
||||||
|
|
||||||
### 3. send_type / recv_type / handler(4 个)
|
### 3. send_type 与 handler(3 个)
|
||||||
|
|
||||||
| # | 测试名 | 描述 | 步骤 | 预期 |
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|---|--------|------|------|------|
|
|---|--------|------|------|------|
|
||||||
| 6 | `send_type_set_clear` | 设置并清除 send_type | set(sensor) → get → set(-1) → get | sensor → -1 |
|
| 6 | `send_type_set_get` | 设置并清除 send_type | `set_send_type(sensor)` → `get` → `set(-1)` → `get` | sensor → -1 |
|
||||||
| 7 | `recv_type_set_clear` | 设置并清除 recv_type | set(rtcm) → get → set(-1) → get | rtcm → -1 |
|
| 7 | `handler_add_remove` | 添加后移除 handler | add(h) count=1 → remove(h) count=0 | count 正确 |
|
||||||
| 8 | `types_independent` | 收发类型独立设置 | 同时 set_send(sensor) + set_recv(command) | 各自返回正确值 |
|
| 8 | `handler_clear` | 清除全部 handler | add → clear | count == 0 |
|
||||||
| 9 | `handler_add_remove` | 添加后移除 handler | add(h) → count=1 → remove(h) → count=0 | 正确 |
|
|
||||||
|
|
||||||
### 4. 发送 — 自动广播(7 个)
|
### 4. 发送 — 自动广播(11 个)
|
||||||
|
|
||||||
| # | 测试名 | 描述 | 步骤 | 预期 |
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|---|--------|------|------|------|
|
|---|--------|------|------|------|
|
||||||
| 10 | `send_no_match_port` | 无匹配端口 | 不创建端口直接 send | 返回 -2 |
|
| 9 | `send_no_match_port` | 无匹配端口 | 不创建任何端口,直接 `send_sensor` | 返回 -2 |
|
||||||
| 11 | `send_one_port_one_handler` | 单端口单 handler | send_type + add → send | 0,handler 1 次 |
|
| 10 | `send_one_port_one_handler` | 单端口单 handler | 创建端口中 send_type + add(h) → send | 返回 0;handler 1 次 |
|
||||||
| 12 | `send_multi_ports` | 两端口同类型 | p1(sensor) + p2(sensor) → send | handler 2 次 |
|
| 11 | `send_one_port_multi_handler` | 单端口双 handler | add(h) + add(h2) → send | handler 2 次 |
|
||||||
| 13 | `send_type_filter` | 类型过滤 | p1(sensor) + p2(status) → send_sensor | 只触发 p1 |
|
| 12 | `send_multi_ports_same_type` | 两端口同类型 | p1(sensor,h) + p2(sensor,h) → send | handler 2 次,各端口一次 |
|
||||||
| 14 | `send_after_delete` | 删除端口后 send | 创建→设好→删除→send | 返回 -2 |
|
| 13 | `send_multi_port_multi_handler` | 多端口多 handler | p1(h,h2) + p2(h3) → send | handler 3 次 |
|
||||||
| 15 | `send_zero_length` | 零长度数据 | send("x", 0) | 0,handler 被调 |
|
| 14 | `send_type_filter` | 类型过滤 | p1(sensor,h) + p2(status,h) → `send_sensor` | 只触发 p1 |
|
||||||
| 16 | `send_null_data` | NULL 数据 | send(NULL, 0) | 0,handler 被调 |
|
| 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. 接收回调 + dispatch(6 个)
|
### 5. 接收回调(1 个)
|
||||||
|
|
||||||
| # | 测试名 | 描述 | 步骤 | 预期 |
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|---|--------|------|------|------|
|
|---|--------|------|------|------|
|
||||||
| 17 | `recv_callback_direct` | 直接调用回调 | `dr_on_recv_rtcm()` + `dr_on_recv_command()` | 各自触发 |
|
| 20 | `recv_callback` | 用户实现接收回调 | `on_recv_rtcm(0, "d", 1)` | 回调被触发 |
|
||||||
| 18 | `dispatch_valid` | dispatch 自动路由 rtcm | set_recv(rtcm) → dispatch(port, data) | dr_on_recv_rtcm 触发 |
|
|
||||||
| 19 | `dispatch_command` | dispatch 自动路由 command | set_recv(command) → dispatch(port, data) | dr_on_recv_command 触发 |
|
|
||||||
| 20 | `dispatch_unset` | recv_type 未设置 | recv_type=-1 → dispatch | 无回调触发 |
|
|
||||||
| 21 | `dispatch_invalid_port` | 无效端口 dispatch | dispatch(999/-1, ...) | 无回调触发 |
|
|
||||||
| 22 | `dispatch_deleted_port` | 已删端口 dispatch | 创建→设置→删除→dispatch | 无回调触发 |
|
|
||||||
|
|
||||||
### 6. 能力元数据(2 个)
|
### 6. 能力元数据(2 个)
|
||||||
|
|
||||||
| # | 测试名 | 描述 | 步骤 | 预期 |
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|---|--------|------|------|------|
|
|---|--------|------|------|------|
|
||||||
| 23 | `send_cap_meta` | 发送能力元数据 | count / name / 边界 | 正确 |
|
| 21 | `send_cap_meta` | 发送能力元数据 | count / name / desc / 边界 | SEND 两组正确 |
|
||||||
| 24 | `recv_cap_meta` | 接收能力元数据 | count / name / 边界 | 正确 |
|
| 22 | `recv_cap_meta` | 接收能力元数据 | count / name / desc / 边界 | RECV 两组正确 |
|
||||||
|
|
||||||
## 覆盖矩阵
|
## 覆盖矩阵
|
||||||
|
|
||||||
| 功能模块 | 用例数 | 主要验证点 |
|
| 功能模块 | 用例数 | 主要验证点 |
|
||||||
|----------|:------:|------------|
|
|----------|:------:|------------|
|
||||||
| 未初始化 | 1 | send → -1 |
|
| 未初始化 | 1 | send → -1 |
|
||||||
| 端口管理 | 4 | 创建/满容量/删除/默认状态 |
|
| 端口管理 | 4 | 创建/满容量/删除异常/默认状态 |
|
||||||
| 收发类型 & handler | 4 | send_type/recv_type 独立设置、handler 增删 |
|
| send_type & handler | 3 | 设置/清除/添加/移除/清空 |
|
||||||
| 发送自动广播 | 7 | 单播/多播/过滤/删除后/零长/NULL |
|
| 发送自动广播 | 11 | 无匹配/单播/多 handler 单端口/多端口同类型/多端口多 handler/类型过滤/移除后/删除后/零长/NULL/多能力独立 |
|
||||||
| 接收 dispatch | 6 | 直接回调/有效路由/无效路由/未设置/无效端口/已删除 |
|
| 接收回调 | 1 | 用户实现覆盖弱符号 |
|
||||||
| 能力元数据 | 2 | 两组独立查询 |
|
| 能力元数据 | 2 | SEND 与 RECV 两组独立查询 |
|
||||||
|
|
||||||
**总计:24 个测试用例**
|
**总计:22 个测试用例**
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
####### Any changes to this file will be overwritten by the next CMake run ####
|
####### Any changes to this file will be overwritten by the next CMake run ####
|
||||||
####### The input file was DataRouterConfig.cmake.in ########
|
####### The input file was DataRouterConfig.cmake.in ########
|
||||||
|
|
||||||
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
|
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE)
|
||||||
|
|
||||||
macro(set_and_check _var _file)
|
macro(set_and_check _var _file)
|
||||||
set(${_var} "${_file}")
|
set(${_var} "${_file}")
|
||||||
@@ -24,23 +24,12 @@ endmacro()
|
|||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
|
|
||||||
get_filename_component(DataRouter_INCLUDE_DIR
|
set_and_check(DataRouter_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/../../../include" ABSOLUTE)
|
set_and_check(DataRouter_LIBRARY_DIR "${PACKAGE_PREFIX_DIR}/lib")
|
||||||
get_filename_component(DataRouter_LIBRARY_DIR
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/../../../lib" ABSOLUTE)
|
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/DataRouterTargets.cmake" OPTIONAL)
|
include("${CMAKE_CURRENT_LIST_DIR}/DataRouterTargets.cmake" OPTIONAL)
|
||||||
|
|
||||||
# 无论 targets 文件是否加载,都覆盖 include 路径为正确值
|
if(NOT TARGET DataRouter::DataRouter)
|
||||||
# (targets 文件中的 _IMPORT_PREFIX 在某些平台/工具链下可能计算异常)
|
|
||||||
if(TARGET DataRouter)
|
|
||||||
set_target_properties(DataRouter PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${DataRouter_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
if(NOT TARGET DataRouter::DataRouter)
|
|
||||||
add_library(DataRouter::DataRouter ALIAS DataRouter)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
add_library(DataRouter::DataRouter STATIC IMPORTED)
|
add_library(DataRouter::DataRouter STATIC IMPORTED)
|
||||||
set_target_properties(DataRouter::DataRouter PROPERTIES
|
set_target_properties(DataRouter::DataRouter PROPERTIES
|
||||||
IMPORTED_LOCATION "${DataRouter_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}DataRouter${CMAKE_STATIC_LIBRARY_SUFFIX}"
|
IMPORTED_LOCATION "${DataRouter_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}DataRouter${CMAKE_STATIC_LIBRARY_SUFFIX}"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,22 +1,11 @@
|
|||||||
@PACKAGE_INIT@
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
get_filename_component(DataRouter_INCLUDE_DIR
|
set(DataRouter_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/../../../include" ABSOLUTE)
|
set(DataRouter_LIBRARY_DIR "@PACKAGE_LIB_INSTALL_DIR@")
|
||||||
get_filename_component(DataRouter_LIBRARY_DIR
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/../../../lib" ABSOLUTE)
|
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/DataRouterTargets.cmake" OPTIONAL)
|
include("${CMAKE_CURRENT_LIST_DIR}/DataRouterTargets.cmake" OPTIONAL)
|
||||||
|
|
||||||
# 无论 targets 文件是否加载,都覆盖 include 路径为正确值
|
if(NOT TARGET DataRouter::DataRouter)
|
||||||
# (targets 文件中的 _IMPORT_PREFIX 在某些平台/工具链下可能计算异常)
|
|
||||||
if(TARGET DataRouter)
|
|
||||||
set_target_properties(DataRouter PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${DataRouter_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
if(NOT TARGET DataRouter::DataRouter)
|
|
||||||
add_library(DataRouter::DataRouter ALIAS DataRouter)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
add_library(DataRouter::DataRouter STATIC IMPORTED)
|
add_library(DataRouter::DataRouter STATIC IMPORTED)
|
||||||
set_target_properties(DataRouter::DataRouter PROPERTIES
|
set_target_properties(DataRouter::DataRouter PROPERTIES
|
||||||
IMPORTED_LOCATION "${DataRouter_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}DataRouter${CMAKE_STATIC_LIBRARY_SUFFIX}"
|
IMPORTED_LOCATION "${DataRouter_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}DataRouter${CMAKE_STATIC_LIBRARY_SUFFIX}"
|
||||||
|
|||||||
+1
-34
@@ -17,8 +17,7 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int send_type;
|
int send_type;
|
||||||
int recv_type;
|
dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT];
|
||||||
dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT];
|
|
||||||
int count;
|
int count;
|
||||||
uint8_t in_use;
|
uint8_t in_use;
|
||||||
} port_entry_t;
|
} port_entry_t;
|
||||||
@@ -52,14 +51,6 @@ static const char *recv_descs[] = {
|
|||||||
#undef DR_RECV
|
#undef DR_RECV
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---- 接收回调函数指针表(dispatch 查表用) ---- */
|
|
||||||
|
|
||||||
static void (*const recv_table[DR_RECV_CAP_COUNT])(uint32_t, const void*, size_t) = {
|
|
||||||
#define DR_RECV(id, desc) dr_on_recv_##id,
|
|
||||||
DR_RECV_CAPABILITIES
|
|
||||||
#undef DR_RECV
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ---- 弱符号接收回调 ---- */
|
/* ---- 弱符号接收回调 ---- */
|
||||||
|
|
||||||
#define DR_RECV(id, desc) \
|
#define DR_RECV(id, desc) \
|
||||||
@@ -111,7 +102,6 @@ dr_port_t dr_port_create(void)
|
|||||||
if (!ports[i].in_use) {
|
if (!ports[i].in_use) {
|
||||||
ports[i].in_use = 1;
|
ports[i].in_use = 1;
|
||||||
ports[i].send_type = -1;
|
ports[i].send_type = -1;
|
||||||
ports[i].recv_type = -1;
|
|
||||||
ports[i].count = 0;
|
ports[i].count = 0;
|
||||||
return (dr_port_t)i;
|
return (dr_port_t)i;
|
||||||
}
|
}
|
||||||
@@ -140,29 +130,6 @@ int dr_port_get_send_type(dr_port_t port)
|
|||||||
return ports[port].send_type;
|
return ports[port].send_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dr_port_set_recv_type(dr_port_t port, int cap_id)
|
|
||||||
{
|
|
||||||
if (!initialized) return;
|
|
||||||
if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return;
|
|
||||||
ports[port].recv_type = cap_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dr_port_get_recv_type(dr_port_t port)
|
|
||||||
{
|
|
||||||
if (!initialized) return -1;
|
|
||||||
if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return -1;
|
|
||||||
return ports[port].recv_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dr_port_dispatch(dr_port_t port, const void *data, size_t len)
|
|
||||||
{
|
|
||||||
if (!initialized) return;
|
|
||||||
if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return;
|
|
||||||
int rid = ports[port].recv_type;
|
|
||||||
if (rid < 0 || rid >= DR_RECV_CAP_COUNT) return;
|
|
||||||
recv_table[rid]((uint32_t)port, data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler)
|
int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler)
|
||||||
{
|
{
|
||||||
if (!initialized) return -1;
|
if (!initialized) return -1;
|
||||||
|
|||||||
+5
-22
@@ -6,17 +6,14 @@
|
|||||||
*
|
*
|
||||||
* - **发送能力**:编译期声明的可发送报文类型,生成 dr_send_<id>(data, len)
|
* - **发送能力**:编译期声明的可发送报文类型,生成 dr_send_<id>(data, len)
|
||||||
* - **接收能力**:编译期声明的可接收报文类型,生成 dr_on_recv_<id>(port, data, len)
|
* - **接收能力**:编译期声明的可接收报文类型,生成 dr_on_recv_<id>(port, data, len)
|
||||||
* - **端口**:运行期连接点,包含 send_type + recv_type + handler 列表
|
* - **端口**:运行期创建的连接点,包含 send_type + handler 列表
|
||||||
*
|
*
|
||||||
* ## 发送路由
|
* ## 路由
|
||||||
*
|
*
|
||||||
* dr_send_<id>(data, len) 遍历所有端口,找到 send_type 匹配的端口,
|
* 调用 dr_send_<id>(data, len) 时,库遍历所有端口,
|
||||||
* 逐一调用其全部 handler,无需指定从哪个端口发送。
|
* 找到 send_type 匹配的端口,逐一调用其全部 handler。无需指定从哪个端口发送。
|
||||||
*
|
*
|
||||||
* ## 接收路由
|
* 接收侧由用户自行调用 dr_on_recv_<id>(port, data, len) 处理外设收到的数据。
|
||||||
*
|
|
||||||
* 外设(UART / SPI / CAN)收到数据后,调用 dr_port_dispatch(port, data, len),
|
|
||||||
* 库根据端口的 recv_type 自动查表调用对应的 dr_on_recv_<id>(),无需手动 if/else。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DR_H
|
#ifndef DR_H
|
||||||
@@ -92,26 +89,12 @@ void dr_port_delete(dr_port_t port);
|
|||||||
|
|
||||||
void dr_port_set_send_type(dr_port_t port, int cap_id);
|
void dr_port_set_send_type(dr_port_t port, int cap_id);
|
||||||
int dr_port_get_send_type(dr_port_t port);
|
int dr_port_get_send_type(dr_port_t port);
|
||||||
void dr_port_set_recv_type(dr_port_t port, int cap_id);
|
|
||||||
int dr_port_get_recv_type(dr_port_t port);
|
|
||||||
|
|
||||||
int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler);
|
int dr_port_add_handler(dr_port_t port, dr_send_handler_t handler);
|
||||||
int dr_port_remove_handler(dr_port_t port, dr_send_handler_t handler);
|
int dr_port_remove_handler(dr_port_t port, dr_send_handler_t handler);
|
||||||
void dr_port_clear_handlers(dr_port_t port);
|
void dr_port_clear_handlers(dr_port_t port);
|
||||||
int dr_port_handler_count(dr_port_t port);
|
int dr_port_handler_count(dr_port_t port);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 按端口绑定的 recv_type 自动路由到对应的 dr_on_recv_<id>() 回调
|
|
||||||
*
|
|
||||||
* 外设中断中收到数据后调用此函数,无需自己写 if/else 判断报文类型。
|
|
||||||
* 库通过 X-macro 生成函数指针表,直接查表调用,零分支预测开销。
|
|
||||||
*
|
|
||||||
* @param[in] port 接收端口 ID
|
|
||||||
* @param[in] data 数据缓冲区
|
|
||||||
* @param[in] len 数据长度
|
|
||||||
*/
|
|
||||||
void dr_port_dispatch(dr_port_t port, const void *data, size_t len);
|
|
||||||
|
|
||||||
/* ========== 能力元数据查询 ========== */
|
/* ========== 能力元数据查询 ========== */
|
||||||
|
|
||||||
int dr_send_cap_count(void);
|
int dr_send_cap_count(void);
|
||||||
|
|||||||
@@ -1,76 +1,73 @@
|
|||||||
/**
|
/**
|
||||||
* @file example.c
|
* @file example.c
|
||||||
*
|
*
|
||||||
* 展示:
|
* 调用 dr_send_sensor(data, len) 无需指定端口,
|
||||||
* 1. send_type + 多 handler 发送广播
|
* 库自动广播到所有 send_type=sensor 的端口。
|
||||||
* 2. recv_type + dr_port_dispatch 自动路由接收
|
|
||||||
* 3. 外设中断中只需一行 dr_port_dispatch()
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "datarouter.h"
|
#include "datarouter.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
/* ---- 发送 handler ---- */
|
static void uart_send(uint32_t port_id, const void *data, size_t len)
|
||||||
|
|
||||||
static void uart_write(uint32_t port_id, const void *data, size_t len)
|
|
||||||
{
|
{
|
||||||
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_write(uint32_t port_id, const void *data, size_t len)
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 接收回调 ---- */
|
static void can_send(uint32_t port_id, const void *data, size_t len)
|
||||||
|
|
||||||
void dr_on_recv_rtcm(uint32_t port_id, const void *data, size_t len)
|
|
||||||
{
|
{
|
||||||
printf("[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 dr_on_recv_command(uint32_t port_id, const void *data, size_t len)
|
void dr_on_recv_command(uint32_t port_id, const void *data, size_t len)
|
||||||
{
|
{
|
||||||
printf("[RX:cmd port %u] %.*s\n", port_id, (int)len, (const char *)data);
|
printf("[RX:cmd port %u] %.*s\n", port_id, (int)len, (const char *)data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- main ---- */
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
dr_init();
|
dr_init();
|
||||||
|
|
||||||
|
printf("Capabilities:\n");
|
||||||
|
for (int i = 0; i < dr_send_cap_count(); i++)
|
||||||
|
printf(" SEND [%d] %s - %s\n", i,
|
||||||
|
dr_send_cap_name(i), dr_send_cap_desc(i));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 创建端口,配置发送和接收能力。
|
* 创建两个端口,都设 send_type=sensor,各绑不同外设。
|
||||||
* 一个端口可以同时有 send_type 和 recv_type。
|
* 调用 dr_send_sensor() 时两个端口同时触发。
|
||||||
*/
|
*/
|
||||||
dr_port_t uart1 = dr_port_create();
|
dr_port_t p1 = dr_port_create();
|
||||||
dr_port_set_send_type(uart1, DR_SEND_sensor);
|
dr_port_set_send_type(p1, DR_SEND_sensor);
|
||||||
dr_port_set_recv_type(uart1, DR_RECV_rtcm);
|
dr_port_add_handler(p1, uart_send);
|
||||||
dr_port_add_handler(uart1, uart_write);
|
dr_port_add_handler(p1, tcp_send);
|
||||||
dr_port_add_handler(uart1, tcp_write);
|
|
||||||
|
|
||||||
dr_port_t uart2 = dr_port_create();
|
dr_port_t p2 = dr_port_create();
|
||||||
dr_port_set_send_type(uart2, DR_SEND_status);
|
dr_port_set_send_type(p2, DR_SEND_sensor);
|
||||||
dr_port_set_recv_type(uart2, DR_RECV_command);
|
dr_port_add_handler(p2, can_send);
|
||||||
dr_port_add_handler(uart2, uart_write);
|
|
||||||
|
|
||||||
/* ---- 发送 ---- */
|
/* 不指定端口,自动广播到所有 sensor 端口 */
|
||||||
printf("--- send sensor (auto-route to uart1) ---\n");
|
printf("\n--- send_sensor (auto-route to ALL sensor ports) ---\n");
|
||||||
dr_send_sensor("temperature:25.3", 16);
|
dr_send_sensor("temperature:25.3", 16);
|
||||||
|
|
||||||
printf("\n--- send status (auto-route to uart2) ---\n");
|
/* 运行时移除一个 handler,下次发送不再触发它 */
|
||||||
dr_send_status("status:OK", 9);
|
printf("\n--- remove TCP handler from p1, resend ---\n");
|
||||||
|
dr_port_remove_handler(p1, tcp_send);
|
||||||
|
dr_send_sensor("temperature:26.1", 16);
|
||||||
|
|
||||||
/* ---- 接收(模拟外设中断) ---- */
|
/* 接收侧:用户自行调用回调 */
|
||||||
printf("\n--- UART1 received data → dispatch to rtcm ---\n");
|
printf("\n--- recv command ---\n");
|
||||||
dr_port_dispatch(uart1, "rtcm_data_v3.2", 14);
|
dr_on_recv_command(0, "reboot", 6);
|
||||||
|
|
||||||
printf("\n--- UART2 received data → dispatch to command ---\n");
|
/* 删除一个端口,触发时不再走它 */
|
||||||
dr_port_dispatch(uart2, "reboot", 6);
|
printf("\n--- delete p2 (CAN), resend ---\n");
|
||||||
|
dr_port_delete(p2);
|
||||||
|
dr_send_sensor("temperature:27.0", 16);
|
||||||
|
|
||||||
/* 清理 */
|
dr_port_delete(p1);
|
||||||
dr_port_delete(uart1);
|
|
||||||
dr_port_delete(uart2);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+97
-101
@@ -52,20 +52,16 @@ cleanup: \
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* 测试用 handler / 回调
|
* 测试用 handler
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
static int _h_cnt;
|
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 int _h_port[16];
|
||||||
static void reset_h(void) { _h_cnt = 0; }
|
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 int _rx_rtcm;
|
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 int _rx_cmd;
|
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; }
|
||||||
void dr_on_recv_rtcm(uint32_t p, const void *d, size_t l)
|
static void reset_h(void) { _h_cnt = 0; _h_pos = 0; }
|
||||||
{ (void)p; (void)d; (void)l; _rx_rtcm++; }
|
|
||||||
void dr_on_recv_command(uint32_t p, const void *d, size_t l)
|
|
||||||
{ (void)p; (void)d; (void)l; _rx_cmd++; }
|
|
||||||
static void reset_rx(void) { _rx_rtcm = _rx_cmd = 0; }
|
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* 1. 未初始化
|
* 1. 未初始化
|
||||||
@@ -101,15 +97,14 @@ TEST_END()
|
|||||||
TEST_START(port_default_state)
|
TEST_START(port_default_state)
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
dr_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
CHECK(dr_port_get_send_type(p) == -1, NULL);
|
CHECK(dr_port_get_send_type(p) == -1, NULL);
|
||||||
CHECK(dr_port_get_recv_type(p) == -1, NULL);
|
|
||||||
CHECK(dr_port_handler_count(p) == 0, NULL);
|
CHECK(dr_port_handler_count(p) == 0, NULL);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* 3. send_type / recv_type 与 handler
|
* 3. send_type 与 handler
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
TEST_START(send_type_set_clear)
|
TEST_START(send_type_set_get)
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
dr_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
dr_port_set_send_type(p, DR_SEND_sensor);
|
dr_port_set_send_type(p, DR_SEND_sensor);
|
||||||
CHECK(dr_port_get_send_type(p) == DR_SEND_sensor, NULL);
|
CHECK(dr_port_get_send_type(p) == DR_SEND_sensor, NULL);
|
||||||
@@ -117,22 +112,6 @@ TEST_START(send_type_set_clear)
|
|||||||
CHECK(dr_port_get_send_type(p) == -1, NULL);
|
CHECK(dr_port_get_send_type(p) == -1, NULL);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
TEST_START(recv_type_set_clear)
|
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
|
||||||
dr_port_set_recv_type(p, DR_RECV_rtcm);
|
|
||||||
CHECK(dr_port_get_recv_type(p) == DR_RECV_rtcm, NULL);
|
|
||||||
dr_port_set_recv_type(p, -1);
|
|
||||||
CHECK(dr_port_get_recv_type(p) == -1, NULL);
|
|
||||||
TEST_END()
|
|
||||||
|
|
||||||
TEST_START(types_independent)
|
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
|
||||||
dr_port_set_send_type(p, DR_SEND_sensor);
|
|
||||||
dr_port_set_recv_type(p, DR_RECV_command);
|
|
||||||
CHECK(dr_port_get_send_type(p) == DR_SEND_sensor, NULL);
|
|
||||||
CHECK(dr_port_get_recv_type(p) == DR_RECV_command, NULL);
|
|
||||||
TEST_END()
|
|
||||||
|
|
||||||
TEST_START(handler_add_remove)
|
TEST_START(handler_add_remove)
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
dr_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
CHECK(dr_port_add_handler(p, _h) == 0, NULL);
|
CHECK(dr_port_add_handler(p, _h) == 0, NULL);
|
||||||
@@ -141,11 +120,19 @@ TEST_START(handler_add_remove)
|
|||||||
CHECK(dr_port_handler_count(p) == 0, NULL);
|
CHECK(dr_port_handler_count(p) == 0, NULL);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(handler_clear)
|
||||||
|
dr_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
|
dr_port_add_handler(p, _h);
|
||||||
|
dr_port_clear_handlers(p);
|
||||||
|
CHECK(dr_port_handler_count(p) == 0, NULL);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* 4. 发送 — 自动广播
|
* 4. 发送 — 自动广播
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
TEST_START(send_no_match_port)
|
TEST_START(send_no_match_port)
|
||||||
|
/* 没有端口配置该 send_type */
|
||||||
CHECK(dr_send_sensor("d", 1) == -2, "expected -2");
|
CHECK(dr_send_sensor("d", 1) == -2, "expected -2");
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
@@ -156,9 +143,21 @@ TEST_START(send_one_port_one_handler)
|
|||||||
reset_h();
|
reset_h();
|
||||||
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
||||||
CHECK(_h_cnt == 1, "cnt=%d", _h_cnt);
|
CHECK(_h_cnt == 1, "cnt=%d", _h_cnt);
|
||||||
|
CHECK(_h_port[0] == (int)p, "port=%d", _h_port[0]);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
TEST_START(send_multi_ports)
|
TEST_START(send_one_port_multi_handler)
|
||||||
|
dr_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
|
dr_port_set_send_type(p, DR_SEND_sensor);
|
||||||
|
dr_port_add_handler(p, _h);
|
||||||
|
dr_port_add_handler(p, _h2);
|
||||||
|
reset_h();
|
||||||
|
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
||||||
|
CHECK(_h_cnt == 2, "cnt=%d", _h_cnt);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(send_multi_ports_same_type)
|
||||||
|
/* 两个端口同类型,各一个 handler */
|
||||||
dr_port_t p1 = FIXTURE_CREATE(&_fix);
|
dr_port_t p1 = FIXTURE_CREATE(&_fix);
|
||||||
dr_port_t p2 = FIXTURE_CREATE(&_fix);
|
dr_port_t p2 = FIXTURE_CREATE(&_fix);
|
||||||
dr_port_set_send_type(p1, DR_SEND_sensor);
|
dr_port_set_send_type(p1, DR_SEND_sensor);
|
||||||
@@ -168,9 +167,26 @@ TEST_START(send_multi_ports)
|
|||||||
reset_h();
|
reset_h();
|
||||||
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
||||||
CHECK(_h_cnt == 2, "cnt=%d", _h_cnt);
|
CHECK(_h_cnt == 2, "cnt=%d", _h_cnt);
|
||||||
|
/* p1 和 p2 各触发一次 */
|
||||||
|
CHECK((_h_port[0]==p1 && _h_port[1]==p2) ||
|
||||||
|
(_h_port[0]==p2 && _h_port[1]==p1), "ports %d,%d", _h_port[0], _h_port[1]);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(send_multi_port_multi_handler)
|
||||||
|
dr_port_t p1 = FIXTURE_CREATE(&_fix);
|
||||||
|
dr_port_t p2 = FIXTURE_CREATE(&_fix);
|
||||||
|
dr_port_set_send_type(p1, DR_SEND_sensor);
|
||||||
|
dr_port_set_send_type(p2, DR_SEND_sensor);
|
||||||
|
dr_port_add_handler(p1, _h);
|
||||||
|
dr_port_add_handler(p1, _h2); /* p1 两个 */
|
||||||
|
dr_port_add_handler(p2, _h3); /* p2 一个 */
|
||||||
|
reset_h();
|
||||||
|
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
||||||
|
CHECK(_h_cnt == 3, "cnt=%d", _h_cnt);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
TEST_START(send_type_filter)
|
TEST_START(send_type_filter)
|
||||||
|
/* 两个端口不同类型,只触发匹配的 */
|
||||||
dr_port_t p1 = FIXTURE_CREATE(&_fix);
|
dr_port_t p1 = FIXTURE_CREATE(&_fix);
|
||||||
dr_port_t p2 = FIXTURE_CREATE(&_fix);
|
dr_port_t p2 = FIXTURE_CREATE(&_fix);
|
||||||
dr_port_set_send_type(p1, DR_SEND_sensor);
|
dr_port_set_send_type(p1, DR_SEND_sensor);
|
||||||
@@ -180,9 +196,19 @@ TEST_START(send_type_filter)
|
|||||||
reset_h();
|
reset_h();
|
||||||
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
||||||
CHECK(_h_cnt == 1, "cnt=%d", _h_cnt);
|
CHECK(_h_cnt == 1, "cnt=%d", _h_cnt);
|
||||||
|
CHECK(_h_port[0] == (int)p1, "only p1 should fire");
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
TEST_START(send_after_delete)
|
TEST_START(send_after_remove_handler)
|
||||||
|
dr_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
|
dr_port_set_send_type(p, DR_SEND_sensor);
|
||||||
|
dr_port_add_handler(p, _h);
|
||||||
|
reset_h(); dr_send_sensor("d", 1); CHECK(_h_cnt == 1, NULL);
|
||||||
|
dr_port_remove_handler(p, _h);
|
||||||
|
CHECK(dr_send_sensor("d", 1) == -2, "expected -2 after remove");
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(send_after_delete_port)
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
dr_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
dr_port_set_send_type(p, DR_SEND_sensor);
|
dr_port_set_send_type(p, DR_SEND_sensor);
|
||||||
dr_port_add_handler(p, _h);
|
dr_port_add_handler(p, _h);
|
||||||
@@ -209,65 +235,33 @@ TEST_START(send_null_data)
|
|||||||
CHECK(_h_cnt == 1, NULL);
|
CHECK(_h_cnt == 1, NULL);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(send_multi_cap)
|
||||||
|
/* 发送 status 不影响 sensor 端口 */
|
||||||
|
dr_port_t p1 = FIXTURE_CREATE(&_fix);
|
||||||
|
dr_port_t p2 = FIXTURE_CREATE(&_fix);
|
||||||
|
dr_port_set_send_type(p1, DR_SEND_sensor);
|
||||||
|
dr_port_set_send_type(p2, DR_SEND_status);
|
||||||
|
dr_port_add_handler(p1, _h);
|
||||||
|
dr_port_add_handler(p2, _h);
|
||||||
|
reset_h();
|
||||||
|
CHECK(dr_send_sensor("d", 1) == 0, NULL);
|
||||||
|
CHECK(_h_cnt == 1, "sensor: cnt=%d", _h_cnt);
|
||||||
|
CHECK(dr_send_status("d", 1) == 0, NULL);
|
||||||
|
CHECK(_h_cnt == 2, "status: cnt=%d", _h_cnt);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* 5. 接收回调 + dispatch
|
* 5. 接收回调
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
TEST_START(recv_callback_direct)
|
static int _rx_r;
|
||||||
/* 直接调用回调(旧方式) */
|
void dr_on_recv_rtcm(uint32_t p, const void *d, size_t l)
|
||||||
reset_rx();
|
{ (void)p; (void)d; (void)l; _rx_r++; }
|
||||||
|
|
||||||
|
TEST_START(recv_callback)
|
||||||
|
_rx_r = 0;
|
||||||
dr_on_recv_rtcm(0, "d", 1);
|
dr_on_recv_rtcm(0, "d", 1);
|
||||||
CHECK(_rx_rtcm == 1, "rtcm=%d", _rx_rtcm);
|
CHECK(_rx_r == 1, "cnt=%d", _rx_r);
|
||||||
dr_on_recv_command(1, "d", 1);
|
|
||||||
CHECK(_rx_cmd == 1, "cmd=%d", _rx_cmd);
|
|
||||||
TEST_END()
|
|
||||||
|
|
||||||
TEST_START(dispatch_valid)
|
|
||||||
/* dr_port_dispatch 自动路由到对应回调 */
|
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
|
||||||
dr_port_set_recv_type(p, DR_RECV_rtcm);
|
|
||||||
|
|
||||||
reset_rx();
|
|
||||||
dr_port_dispatch(p, "data", 4);
|
|
||||||
CHECK(_rx_rtcm == 1, "rtcm should fire: %d", _rx_rtcm);
|
|
||||||
CHECK(_rx_cmd == 0, "cmd should not fire: %d", _rx_cmd);
|
|
||||||
TEST_END()
|
|
||||||
|
|
||||||
TEST_START(dispatch_command)
|
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
|
||||||
dr_port_set_recv_type(p, DR_RECV_command);
|
|
||||||
|
|
||||||
reset_rx();
|
|
||||||
dr_port_dispatch(p, "data", 4);
|
|
||||||
CHECK(_rx_cmd == 1, "cmd should fire: %d", _rx_cmd);
|
|
||||||
CHECK(_rx_rtcm == 0, "rtcm should not fire: %d", _rx_rtcm);
|
|
||||||
TEST_END()
|
|
||||||
|
|
||||||
TEST_START(dispatch_unset)
|
|
||||||
/* recv_type 为 -1 时 dispatch 不应调用任何回调 */
|
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
|
||||||
reset_rx();
|
|
||||||
dr_port_dispatch(p, "data", 4);
|
|
||||||
CHECK(_rx_rtcm == 0, NULL);
|
|
||||||
CHECK(_rx_cmd == 0, NULL);
|
|
||||||
TEST_END()
|
|
||||||
|
|
||||||
TEST_START(dispatch_invalid_port)
|
|
||||||
reset_rx();
|
|
||||||
dr_port_dispatch(999, "data", 4);
|
|
||||||
dr_port_dispatch(-1, "data", 4);
|
|
||||||
CHECK(_rx_rtcm == 0, NULL);
|
|
||||||
CHECK(_rx_cmd == 0, NULL);
|
|
||||||
TEST_END()
|
|
||||||
|
|
||||||
TEST_START(dispatch_deleted_port)
|
|
||||||
dr_port_t p = FIXTURE_CREATE(&_fix);
|
|
||||||
dr_port_set_recv_type(p, DR_RECV_rtcm);
|
|
||||||
dr_port_delete(p);
|
|
||||||
reset_rx();
|
|
||||||
dr_port_dispatch(p, "data", 4);
|
|
||||||
CHECK(_rx_rtcm == 0, "deleted should not fire");
|
|
||||||
_fix.count--;
|
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
@@ -277,20 +271,24 @@ TEST_END()
|
|||||||
TEST_START(send_cap_meta)
|
TEST_START(send_cap_meta)
|
||||||
CHECK(dr_send_cap_count() == 2, NULL);
|
CHECK(dr_send_cap_count() == 2, NULL);
|
||||||
CHECK(strcmp(dr_send_cap_name(0), "sensor") == 0, NULL);
|
CHECK(strcmp(dr_send_cap_name(0), "sensor") == 0, NULL);
|
||||||
|
CHECK(strcmp(dr_send_cap_desc(0), "Sensor data report") == 0, NULL);
|
||||||
CHECK(dr_send_cap_name(-1) == NULL, NULL);
|
CHECK(dr_send_cap_name(-1) == NULL, NULL);
|
||||||
|
CHECK(dr_send_cap_name(99) == NULL, NULL);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
TEST_START(recv_cap_meta)
|
TEST_START(recv_cap_meta)
|
||||||
CHECK(dr_recv_cap_count() == 2, NULL);
|
CHECK(dr_recv_cap_count() == 2, NULL);
|
||||||
CHECK(strcmp(dr_recv_cap_name(0), "rtcm") == 0, NULL);
|
CHECK(strcmp(dr_recv_cap_name(0), "rtcm") == 0, NULL);
|
||||||
|
CHECK(strcmp(dr_recv_cap_desc(0), "RTCM correction data") == 0, NULL);
|
||||||
CHECK(dr_recv_cap_name(-1) == NULL, NULL);
|
CHECK(dr_recv_cap_name(-1) == NULL, NULL);
|
||||||
|
CHECK(dr_recv_cap_desc(99) == NULL, NULL);
|
||||||
TEST_END()
|
TEST_END()
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* 主函数
|
* 主函数
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
#define RUN(name) do { reset_h(); reset_rx(); test_##name(); } while (0)
|
#define RUN(name) do { reset_h(); _rx_r = 0; test_##name(); } while (0)
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
@@ -309,30 +307,28 @@ int main(void)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
dr_reset(); dr_init();
|
dr_reset(); dr_init();
|
||||||
printf("[send_type / recv_type / handler]\n");
|
printf("[send_type & handler]\n");
|
||||||
RUN(send_type_set_clear);
|
RUN(send_type_set_get);
|
||||||
RUN(recv_type_set_clear);
|
|
||||||
RUN(types_independent);
|
|
||||||
RUN(handler_add_remove);
|
RUN(handler_add_remove);
|
||||||
|
RUN(handler_clear);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
printf("[send — auto broadcast]\n");
|
printf("[send — auto broadcast]\n");
|
||||||
RUN(send_no_match_port);
|
RUN(send_no_match_port);
|
||||||
RUN(send_one_port_one_handler);
|
RUN(send_one_port_one_handler);
|
||||||
RUN(send_multi_ports);
|
RUN(send_one_port_multi_handler);
|
||||||
|
RUN(send_multi_ports_same_type);
|
||||||
|
RUN(send_multi_port_multi_handler);
|
||||||
RUN(send_type_filter);
|
RUN(send_type_filter);
|
||||||
RUN(send_after_delete);
|
RUN(send_after_remove_handler);
|
||||||
|
RUN(send_after_delete_port);
|
||||||
RUN(send_zero_length);
|
RUN(send_zero_length);
|
||||||
RUN(send_null_data);
|
RUN(send_null_data);
|
||||||
|
RUN(send_multi_cap);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
printf("[receive — dispatch]\n");
|
printf("[receive callback]\n");
|
||||||
RUN(recv_callback_direct);
|
RUN(recv_callback);
|
||||||
RUN(dispatch_valid);
|
|
||||||
RUN(dispatch_command);
|
|
||||||
RUN(dispatch_unset);
|
|
||||||
RUN(dispatch_invalid_port);
|
|
||||||
RUN(dispatch_deleted_port);
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
printf("[capability metadata]\n");
|
printf("[capability metadata]\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user