Merge pull request 'feature/route' (#1) from feature/route into main
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
Generated
+1
@@ -4,4 +4,5 @@
|
|||||||
<option name="pythonIntegrationState" value="YES" />
|
<option name="pythonIntegrationState" value="YES" />
|
||||||
</component>
|
</component>
|
||||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
<component name="WestSettings"><![CDATA[{}]]></component>
|
||||||
</project>
|
</project>
|
||||||
+15
-2
@@ -1,6 +1,19 @@
|
|||||||
cmake_minimum_required(VERSION 4.3)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(DataRouter C)
|
project(DataRouter C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
add_library(DataRouter STATIC library.c)
|
add_library(DataRouter STATIC datarouter.c)
|
||||||
|
target_include_directories(DataRouter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
if(BUILD_EXAMPLES OR NOT DEFINED BUILD_EXAMPLES)
|
||||||
|
add_executable(example example.c)
|
||||||
|
target_link_libraries(example DataRouter)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_TESTING OR NOT DEFINED BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
add_executable(test_datarouter tests/test_datarouter.c)
|
||||||
|
target_link_libraries(test_datarouter DataRouter)
|
||||||
|
add_test(NAME datarouter COMMAND test_datarouter)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,512 @@
|
|||||||
|
# DataRouter 详细设计文档
|
||||||
|
|
||||||
|
## 1. 设计目标
|
||||||
|
|
||||||
|
轻量级 C 语言数据路由库。核心思想:
|
||||||
|
|
||||||
|
- **报文类型(Channel)**:编译期通过宏声明的数据类型(如 `sensor` / `status` / `control`)
|
||||||
|
- **端口(Port)**:运行期创建的连接点,绑定一个报文类型 + 一个物理外设发送 handler
|
||||||
|
- **路由(Routing)**:将逻辑报文类型连接到实际的物理外设发送函数
|
||||||
|
|
||||||
|
```
|
||||||
|
应用层 send_sensor(port, data)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
库校验端口
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
调用注册的 handler(data) ← 用户实现:UART / TCP / CAN / 文件发送
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
物理外设
|
||||||
|
```
|
||||||
|
|
||||||
|
### 设计原则
|
||||||
|
|
||||||
|
| 原则 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 编译期确定类型 | 所有报文类型在 `datarouter_config.h` 中声明,库自动生成代码 |
|
||||||
|
| 运行期绑定外设 | 端口创建后,用户注册具体的发送 handler(函数指针) |
|
||||||
|
| 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler |
|
||||||
|
| 零动态内存 | 纯静态数组,无 malloc/free |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 整体架构
|
||||||
|
|
||||||
|
### 2.1 模块依赖
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
left to right direction
|
||||||
|
|
||||||
|
rectangle "用户代码" as User {
|
||||||
|
component "应用逻辑\n调用 datarouter_send_<type>()" 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
|
||||||
|
}
|
||||||
|
|
||||||
|
App --> Hdr
|
||||||
|
Cfg --> Hdr
|
||||||
|
Impl --> Cfg
|
||||||
|
Handlers --> Impl : 注册为 send_handler
|
||||||
|
Callbacks --> Impl : 实现 on_recv_<type>
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 文件结构
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `datarouter_config.h` | 用户在此声明所有报文类型 |
|
||||||
|
| `datarouter.h` | 公共 API + X-macro 展开(enum + 发送函数声明 + 回调声明) |
|
||||||
|
| `datarouter.c` | 发送函数实现 + handler 调用 + 端口管理 |
|
||||||
|
| `example.c` | 使用示例 |
|
||||||
|
| `tests/test_datarouter.c` | 31 个单元测试 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 核心概念
|
||||||
|
|
||||||
|
### 3.1 报文类型(Channel)
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 端口与 handler
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
skinparam objectBorderColor black
|
||||||
|
|
||||||
|
object "端口 A" as PA {
|
||||||
|
type = sensor
|
||||||
|
handler = uart_send
|
||||||
|
}
|
||||||
|
|
||||||
|
object "端口 B" as PB {
|
||||||
|
type = sensor
|
||||||
|
handler = tcp_send
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. X-macro 代码生成
|
||||||
|
|
||||||
|
### 4.1 展开流程
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
|
||||||
|
== datarouter.h: 3 次展开 ==
|
||||||
|
|
||||||
|
-> datarouter_config.h: #include 获得 DATAROUTER_CHANNELS
|
||||||
|
|
||||||
|
datarouter.h -> "第 1 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_CHANNEL_##id,
|
||||||
|
note right: 生成 enum 通道枚举
|
||||||
|
|
||||||
|
datarouter.h -> "第 2 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(...);
|
||||||
|
note right: 生成发送触发函数声明
|
||||||
|
|
||||||
|
datarouter.h -> "第 3 遍": #define DATAROUTER_CHANNEL(id,desc) void datarouter_on_recv_##id(...);
|
||||||
|
note right: 生成接收回调声明
|
||||||
|
|
||||||
|
== datarouter.c: 3 次展开 ==
|
||||||
|
|
||||||
|
datarouter.c -> "第 4 遍": #define DATAROUTER_CHANNEL(id,desc) DATAROUTER_WEAK void ...
|
||||||
|
note right: 弱符号回调默认空实现
|
||||||
|
|
||||||
|
datarouter.c -> "第 5 遍": #define DATAROUTER_CHANNEL(id,desc) int datarouter_send_##id(…) {…}
|
||||||
|
note right: 发送函数实现 → 调用handler
|
||||||
|
|
||||||
|
datarouter.c -> "第 6 遍": #define DATAROUTER_CHANNEL(id,desc) #id, → channel_names[]
|
||||||
|
datarouter.c -> "第 7 遍": #define DATAROUTER_CHANNEL(id,desc) desc, → channel_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. 数据结构
|
||||||
|
|
||||||
|
### 5.1 核心数据类型
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry --> Chan : type ∈ enum
|
||||||
|
Entry --> Handler : handler 函数指针
|
||||||
|
Table --> Entry : ports[]
|
||||||
|
Send --> Table : 校验端口 + 调用 handler
|
||||||
|
Recv --> Send : 被用户从外设收数据时调用
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 `port_entry_t`
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
int type; // 报文类型(DATAROUTER_CHANNEL_xxx / -1)
|
||||||
|
datarouter_send_handler_t handler; // 外设发送函数(NULL 表示未注册)
|
||||||
|
uint8_t in_use; // 1=已分配,0=空闲
|
||||||
|
} port_entry_t;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 端口表
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define DATAROUTER_MAX_PORTS 64 // 可由 -D 覆盖
|
||||||
|
static port_entry_t ports[64]; // 静态数组,无动态内存
|
||||||
|
static uint8_t initialized; // init/reset 标志
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. API 参考
|
||||||
|
|
||||||
|
### 6.1 初始化
|
||||||
|
|
||||||
|
```c
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 发送触发函数(每通道 1 个,X-macro 生成)
|
||||||
|
|
||||||
|
```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);
|
||||||
|
```
|
||||||
|
|
||||||
|
返回值:
|
||||||
|
| 值 | 含义 |
|
||||||
|
|:--:|------|
|
||||||
|
| 0 | 成功,handler 已被调用 |
|
||||||
|
| -1 | 库未初始化 |
|
||||||
|
| -2 | 端口无效或已删除 |
|
||||||
|
| -3 | 端口类型与触发的报文类型不匹配 |
|
||||||
|
| -4 | 端口未注册外设发送 handler |
|
||||||
|
|
||||||
|
### 6.4 接收回调声明(每通道 1 个,用户实现)
|
||||||
|
|
||||||
|
```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);
|
||||||
|
```
|
||||||
|
|
||||||
|
库提供弱符号默认空实现,用户定义同名函数即可覆盖。
|
||||||
|
|
||||||
|
### 6.5 元数据查询
|
||||||
|
|
||||||
|
```c
|
||||||
|
int datarouter_channel_count(void);
|
||||||
|
const char *datarouter_channel_name(int channel_id);
|
||||||
|
const char *datarouter_channel_desc(int channel_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 核心流程
|
||||||
|
|
||||||
|
### 7.1 发送流程
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
actor "应用" as App
|
||||||
|
participant "datarouter_send_sensor()" as Send
|
||||||
|
participant "端口表" as Table
|
||||||
|
participant "handler" as Handler
|
||||||
|
participant "外设" as Dev
|
||||||
|
|
||||||
|
App -> Send : send_sensor(port=2, data, len)
|
||||||
|
|
||||||
|
group 校验
|
||||||
|
Send -> Send : initialized?
|
||||||
|
alt 未初始化
|
||||||
|
Send --> App : return -1
|
||||||
|
end
|
||||||
|
|
||||||
|
Send -> Table : ports[2].in_use?
|
||||||
|
alt 端口无效
|
||||||
|
Send --> App : return -2
|
||||||
|
end
|
||||||
|
|
||||||
|
Send -> Table : ports[2].type == sensor?
|
||||||
|
alt 类型不匹配
|
||||||
|
Send --> App : return -3
|
||||||
|
end
|
||||||
|
|
||||||
|
Send -> Table : ports[2].handler != NULL?
|
||||||
|
alt 无 handler
|
||||||
|
Send --> App : return -4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Send -> Handler : handler(2, data, len)
|
||||||
|
Handler -> Dev : UART_SendData(data, len)
|
||||||
|
Handler --> Send : void
|
||||||
|
Send --> App : return 0
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 接收流程
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
actor "外设" as Dev
|
||||||
|
participant "用户分发代码" as Dispatch
|
||||||
|
participant "datarouter_on_recv_sensor()" as Callback
|
||||||
|
participant "用户处理逻辑" as Process
|
||||||
|
|
||||||
|
Dev -> Dispatch : 收到数据,识别为 sensor 类型
|
||||||
|
Dispatch -> Callback : on_recv_sensor(port_id, data, len)
|
||||||
|
Callback -> Process : 解析数据、更新状态等
|
||||||
|
Process --> Callback : void
|
||||||
|
Callback --> Dispatch : void
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 端口生命周期
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
[*] --> Idle : datarouter_reset()
|
||||||
|
|
||||||
|
Idle --> Created : datarouter_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 --> Idle : datarouter_port_delete()
|
||||||
|
Config --> Idle : datarouter_port_delete()
|
||||||
|
Idle --> Created : 复用 ID
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 路由示例
|
||||||
|
|
||||||
|
### 场景:传感器数据同时路由到 UART 和 TCP
|
||||||
|
|
||||||
|
```
|
||||||
|
应用
|
||||||
|
│
|
||||||
|
├─ send_sensor(sensor_uart_port, data, len)
|
||||||
|
│ │
|
||||||
|
│ ▼
|
||||||
|
│ handler = uart_send → UART 外设
|
||||||
|
│
|
||||||
|
└─ send_sensor(sensor_tcp_port, data, len)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
handler = tcp_send → TCP 外设
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景:运行时切换路由
|
||||||
|
|
||||||
|
```
|
||||||
|
端口 sensor_tcp 原 handler = tcp_send
|
||||||
|
|
||||||
|
datarouter_port_set_handler(sensor_tcp, file_send);
|
||||||
|
|
||||||
|
端口 sensor_tcp 现 handler = file_send → 改为写入文件
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 弱符号机制
|
||||||
|
|
||||||
|
```plantuml
|
||||||
|
@startuml
|
||||||
|
package "libDataRouter.a" {
|
||||||
|
component "弱符号\nvoid on_recv_sensor(...){}" as Weak
|
||||||
|
}
|
||||||
|
package "用户代码" {
|
||||||
|
component "强符号\nvoid on_recv_sensor(...){...}" as Strong
|
||||||
|
}
|
||||||
|
package "最终链接产物" {
|
||||||
|
component "使用用户版本" as Result
|
||||||
|
}
|
||||||
|
Strong --> Result
|
||||||
|
Weak ..> Result : 后备
|
||||||
|
@enduml
|
||||||
|
```
|
||||||
|
|
||||||
|
- 库为每个回调提供 `__attribute__((weak))` 空实现
|
||||||
|
- 用户定义同名函数自动覆盖
|
||||||
|
- 未定义的回调在调用时静默空转
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 错误码体系
|
||||||
|
|
||||||
|
| 返回值 | 含义 |
|
||||||
|
|:------:|------|
|
||||||
|
| 0 | 成功 |
|
||||||
|
| -1 | 库未初始化 |
|
||||||
|
| -2 | 端口无效(越界/未创建/已删除) |
|
||||||
|
| -3 | 端口类型不匹配 |
|
||||||
|
| -4 | 端口未注册发送 handler |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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") /* 新增 */
|
||||||
|
```
|
||||||
|
|
||||||
|
重新编译后自动生成:枚举、发送函数、回调声明、元数据数组。
|
||||||
|
|
||||||
|
### 修改最大端口数
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128" ...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 约束
|
||||||
|
|
||||||
|
| 约束 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 报文类型 | 编译期固定,修改需重新编译 |
|
||||||
|
| 最大端口数 | 默认 64,可由 `-D` 覆盖 |
|
||||||
|
| 线程安全 | 不支持(无锁),多线程需外部加锁 |
|
||||||
|
| 弱符号 | GCC/Clang 原生支持 |
|
||||||
|
| 内存分配 | 纯静态,无 malloc/free |
|
||||||
@@ -1,2 +1,168 @@
|
|||||||
# DataRouter
|
# DataRouter
|
||||||
|
|
||||||
|
轻量级 C 语言数据路由库。编译期声明报文类型,运行期将端口绑定到物理外设发送函数,实现一对多广播路由。
|
||||||
|
|
||||||
|
## 概念
|
||||||
|
|
||||||
|
| 术语 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **通道 (Channel)** | 编译期声明的报文类型(如 `sensor` / `status` / `control`) |
|
||||||
|
| **端口 (Port)** | 运行期创建的连接点,绑定一个报文类型和 N 个外设发送 handler |
|
||||||
|
| **路由** | 将报文类型连接到物理外设发送函数的过程 |
|
||||||
|
| **Handler** | 用户实现的物理外设发送函数,一个端口可注册多个(默认上限 4) |
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 声明报文类型
|
||||||
|
|
||||||
|
编辑 `datarouter_config.h`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define DATAROUTER_CHANNELS \
|
||||||
|
DATAROUTER_CHANNEL(sensor, "Sensor data") \
|
||||||
|
DATAROUTER_CHANNEL(status, "Status report") \
|
||||||
|
DATAROUTER_CHANNEL(control, "Control command")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcp_send(uint32_t port_id, const void *data, size_t len) {
|
||||||
|
send(tcp_socket, data, len, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 创建端口并注册 handler
|
||||||
|
|
||||||
|
```c
|
||||||
|
datarouter_init();
|
||||||
|
|
||||||
|
datarouter_port_t p = datarouter_port_create();
|
||||||
|
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor);
|
||||||
|
|
||||||
|
/* 一个端口同时路由到两个外设 */
|
||||||
|
datarouter_port_add_handler(p, uart_send);
|
||||||
|
datarouter_port_add_handler(p, tcp_send);
|
||||||
|
|
||||||
|
/* 发送 → 同时走 UART 和 TCP */
|
||||||
|
datarouter_send_sensor(p, "temperature:25.3", 16);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 一览
|
||||||
|
|
||||||
|
### 初始化
|
||||||
|
|
||||||
|
```c
|
||||||
|
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 数量
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发送触发(每通道一个,X-macro 自动生成)
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 接收回调(每通道一个,用户实现)
|
||||||
|
|
||||||
|
```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);
|
||||||
|
```
|
||||||
|
|
||||||
|
库提供弱符号默认空实现,用户定义同名函数即可覆盖。
|
||||||
|
|
||||||
|
### 通道元数据
|
||||||
|
|
||||||
|
```c
|
||||||
|
int datarouter_channel_count(void);
|
||||||
|
const char *datarouter_channel_name(int channel_id);
|
||||||
|
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(一对多广播)
|
||||||
|
- 同一 handler 可注册到多个端口(多路复用)
|
||||||
|
- 运行时通过 `add_handler` / `remove_handler` 动态改变路由
|
||||||
|
- 清空 handler 后发送返回 -4
|
||||||
|
|
||||||
|
## 编译
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -S . -B build
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
可选配置项:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake --build build
|
||||||
|
ctest --test-dir build
|
||||||
|
# 或直接运行
|
||||||
|
./build/test_datarouter
|
||||||
|
```
|
||||||
|
|
||||||
|
当前 36 个测试用例,覆盖端口管理、类型、handler 增删、一对多广播、接收回调、错误边界。
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
datarouter_config.h 报文类型配置(用户编辑)
|
||||||
|
datarouter.h 公共 API + X-macro 展开
|
||||||
|
datarouter.c 实现(端口表 + handler 调用)
|
||||||
|
example.c 使用示例
|
||||||
|
tests/test_datarouter.c 单元测试
|
||||||
|
DESIGN.md 详细设计文档
|
||||||
|
TEST_CASES.md 测试用例文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- C11 编译器
|
||||||
|
- CMake ≥ 3.10
|
||||||
|
- 无第三方库依赖
|
||||||
|
|||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
# DataRouter 单元测试用例文档
|
||||||
|
|
||||||
|
## 测试环境
|
||||||
|
|
||||||
|
| 项目 | 内容 |
|
||||||
|
|------|------|
|
||||||
|
| 语言标准 | C11 |
|
||||||
|
| 编译器 | GCC 11.4.0 |
|
||||||
|
| 测试框架 | 自定义轻量框架(宏 + goto cleanup) |
|
||||||
|
| 构建系统 | CMake ≥ 3.10 |
|
||||||
|
| 报文类型 | `sensor` / `status` / `control` 三个通道 |
|
||||||
|
|
||||||
|
## 运行方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cmake -S . -B build
|
||||||
|
cmake --build build
|
||||||
|
ctest --test-dir build
|
||||||
|
# 或直接运行:
|
||||||
|
./build/test_datarouter
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试用例列表
|
||||||
|
|
||||||
|
### 1. 未初始化(1 个)
|
||||||
|
|
||||||
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|
|---|--------|------|------|------|
|
||||||
|
| 1 | `error_uninit` | 未初始化时不能发送 | ① 不调 init 直接 `send_sensor(0, ...)` | 返回 -1 |
|
||||||
|
|
||||||
|
### 2. 端口管理(7 个)
|
||||||
|
|
||||||
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|
|---|--------|------|------|------|
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
### 3. 类型与 handler 设置获取(7 个)
|
||||||
|
|
||||||
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|
|---|--------|------|------|------|
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
### 4. 通道元数据查询(4 个)
|
||||||
|
|
||||||
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|
|---|--------|------|------|------|
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
### 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 被调用 |
|
||||||
|
|
||||||
|
### 6. 接收回调(1 个)
|
||||||
|
|
||||||
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|
|---|--------|------|------|------|
|
||||||
|
| 30 | `recv_callback_user_impl` | 用户实现接收回调 | ① `on_recv_sensor(port, data, len)` ② `on_recv_status(port, data, len)` | sensor 回调触发,status 不触发;再触发 status 回调,sensor 不变 |
|
||||||
|
|
||||||
|
### 7. 错误处理(2 个)
|
||||||
|
|
||||||
|
| # | 测试名 | 描述 | 步骤 | 预期 |
|
||||||
|
|---|--------|------|------|------|
|
||||||
|
| 30 | `error_invalid_port` | 无效端口 ID | ① `send_sensor(999, ...)` ② `send_sensor(-1, ...)` | 均返回 -2 |
|
||||||
|
| 31 | `error_deleted_port` | 已删除端口 | ① 创建设好类型 + handler ② 删除 ③ `send_sensor(原ID, ...)` | 返回 -2 |
|
||||||
|
|
||||||
|
## 覆盖矩阵
|
||||||
|
|
||||||
|
| 功能模块 | 用例数 | 主要验证点 |
|
||||||
|
|----------|:------:|------------|
|
||||||
|
| 未初始化 | 1 | 未 init 调用 send → -1 |
|
||||||
|
| 端口生命周期 | 7 | 创建/多创建/ID复用/删除异常/满容量 |
|
||||||
|
| 类型与 handler | 7 | set/get/clear/switch/无效端口 |
|
||||||
|
| 通道元数据 | 4 | count/names/descs/boundary |
|
||||||
|
| 发送触发 | 9 | 三通道/类型不匹配/无handler/多端口/切换handler/零长/NULL |
|
||||||
|
| 接收回调 | 1 | 用户实现覆盖弱符号 |
|
||||||
|
| 错误处理 | 2 | 无效端口/已删除端口 |
|
||||||
|
|
||||||
|
**总计:31 个测试用例**
|
||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
+188
@@ -0,0 +1,188 @@
|
|||||||
|
/**
|
||||||
|
* @file datarouter.c
|
||||||
|
* @brief 数据路由库 —— 实现
|
||||||
|
*
|
||||||
|
* 端口表:固定大小数组,每个端口存储报文类型 + handler 列表。
|
||||||
|
* 发送触发函数逐一调用端口上注册的全部 handler,实现一对多广播。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "datarouter.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define DATAROUTER_WEAK __attribute__((weak))
|
||||||
|
#else
|
||||||
|
#define DATAROUTER_WEAK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DATAROUTER_MAX_PORTS
|
||||||
|
#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;
|
||||||
|
datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT];
|
||||||
|
int count;
|
||||||
|
uint8_t in_use;
|
||||||
|
} port_entry_t;
|
||||||
|
|
||||||
|
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 *channel_descs[] = {
|
||||||
|
#define DATAROUTER_CHANNEL(id, desc) desc,
|
||||||
|
DATAROUTER_CHANNELS
|
||||||
|
#undef DATAROUTER_CHANNEL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- 弱符号接收回调默认实现 ---- */
|
||||||
|
|
||||||
|
#define DATAROUTER_CHANNEL(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
|
||||||
|
|
||||||
|
/* ---- 发送触发函数实现 ---- */
|
||||||
|
|
||||||
|
#define DATAROUTER_CHANNEL(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].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
|
||||||
|
|
||||||
|
/* ---- 端口管理 API ---- */
|
||||||
|
|
||||||
|
void datarouter_init(void)
|
||||||
|
{
|
||||||
|
if (initialized) return;
|
||||||
|
memset(ports, 0, sizeof(ports));
|
||||||
|
initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void datarouter_reset(void)
|
||||||
|
{
|
||||||
|
initialized = 0;
|
||||||
|
memset(ports, 0, sizeof(ports));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
return (datarouter_port_t)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DATAROUTER_INVALID_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void datarouter_port_delete(datarouter_port_t port)
|
||||||
|
{
|
||||||
|
if (!initialized) return;
|
||||||
|
if (port < 0 || port >= DATAROUTER_MAX_PORTS) return;
|
||||||
|
ports[port].in_use = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void datarouter_port_set_type(datarouter_port_t port, int channel_id)
|
||||||
|
{
|
||||||
|
if (!initialized) return;
|
||||||
|
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return;
|
||||||
|
ports[port].type = channel_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int datarouter_port_get_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int datarouter_port_add_handler(datarouter_port_t port, datarouter_send_handler_t handler)
|
||||||
|
{
|
||||||
|
if (!initialized) return -1;
|
||||||
|
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return -1;
|
||||||
|
if (ports[port].count >= DATAROUTER_MAX_HANDLERS_PER_PORT) return -2;
|
||||||
|
for (int i = 0; i < ports[port].count; i++)
|
||||||
|
if (ports[port].handlers[i] == handler) return -3;
|
||||||
|
ports[port].handlers[ports[port].count++] = handler;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int datarouter_port_remove_handler(datarouter_port_t port, datarouter_send_handler_t handler)
|
||||||
|
{
|
||||||
|
if (!initialized) return -1;
|
||||||
|
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return -1;
|
||||||
|
for (int i = 0; i < ports[port].count; i++) {
|
||||||
|
if (ports[port].handlers[i] == handler) {
|
||||||
|
ports[port].handlers[i] = ports[port].handlers[--ports[port].count];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void datarouter_port_clear_handlers(datarouter_port_t port)
|
||||||
|
{
|
||||||
|
if (!initialized) return;
|
||||||
|
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return;
|
||||||
|
ports[port].count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int datarouter_port_handler_count(datarouter_port_t port)
|
||||||
|
{
|
||||||
|
if (!initialized) return -1;
|
||||||
|
if (port < 0 || port >= DATAROUTER_MAX_PORTS || !ports[port].in_use) return -1;
|
||||||
|
return ports[port].count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 通道元数据查询 ---- */
|
||||||
|
|
||||||
|
int datarouter_channel_count(void)
|
||||||
|
{
|
||||||
|
return DATAROUTER_CHANNEL_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *datarouter_channel_name(int channel_id)
|
||||||
|
{
|
||||||
|
if (channel_id < 0 || channel_id >= DATAROUTER_CHANNEL_COUNT) return NULL;
|
||||||
|
return channel_names[channel_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *datarouter_channel_desc(int channel_id)
|
||||||
|
{
|
||||||
|
if (channel_id < 0 || channel_id >= DATAROUTER_CHANNEL_COUNT) return NULL;
|
||||||
|
return channel_descs[channel_id];
|
||||||
|
}
|
||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* @file datarouter.h
|
||||||
|
* @brief 数据路由库 —— 公共接口
|
||||||
|
*
|
||||||
|
* ## 概念
|
||||||
|
*
|
||||||
|
* - **通道 (Channel)**:编译期声明的报文类型(如 sensor / status / control)
|
||||||
|
* - **端口 (Port)**:运行期创建的连接点,绑定一个报文类型和多个外设发送 handler
|
||||||
|
* - **路由**:将报文类型关联到一个或多个物理外设发送函数
|
||||||
|
* - **多外设**:一个端口可注册多个 handler,发送时逐一调用全部 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_<id>() 触发发送(库逐一调用所有注册的 handler)
|
||||||
|
* 7. 实现 datarouter_on_recv_<id>() 处理从外设收到的报文
|
||||||
|
* 8. datarouter_port_delete() 移除端口
|
||||||
|
*
|
||||||
|
* ## 路由规则
|
||||||
|
*
|
||||||
|
* datarouter_send_<id>(port, data, len)
|
||||||
|
* → 校验端口类型
|
||||||
|
* → for each handler: handler(port, data, len)
|
||||||
|
* → return 0
|
||||||
|
*
|
||||||
|
* 接收:外设收到数据 → 用户识别报文类型 → 调 datarouter_on_recv_<id>(port, data, len)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DATAROUTER_H
|
||||||
|
#define DATAROUTER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "datarouter_config.h"
|
||||||
|
|
||||||
|
#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
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 外设发送 handler 类型
|
||||||
|
*
|
||||||
|
* @param[in] port_id 源端口 ID
|
||||||
|
* @param[in] data 数据缓冲区
|
||||||
|
* @param[in] len 数据长度
|
||||||
|
*/
|
||||||
|
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) \
|
||||||
|
int datarouter_send_##id(uint32_t port_id, const void *data, size_t len);
|
||||||
|
DATAROUTER_CHANNELS
|
||||||
|
#undef DATAROUTER_CHANNEL
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup recv_callbacks 接收回调声明
|
||||||
|
*
|
||||||
|
* 每个报文类型生成一个接收回调声明。用户实现。
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DATAROUTER_CHANNEL(id, desc) \
|
||||||
|
void datarouter_on_recv_##id(uint32_t port_id, const void *data, size_t len);
|
||||||
|
DATAROUTER_CHANNELS
|
||||||
|
#undef DATAROUTER_CHANNEL
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup port_api 端口管理 API
|
||||||
|
*
|
||||||
|
* 每个端口可注册 N 个外设发送 handler(上限 DATAROUTER_MAX_HANDLERS_PER_PORT)。
|
||||||
|
* 发送时逐一调用全部 handler,实现一对多广播路由。
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef int32_t datarouter_port_t;
|
||||||
|
#define DATAROUTER_INVALID_PORT ((datarouter_port_t)-1)
|
||||||
|
|
||||||
|
void datarouter_init(void);
|
||||||
|
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 */
|
||||||
|
void datarouter_port_clear_handlers(datarouter_port_t port);
|
||||||
|
|
||||||
|
/** @brief 获取端口当前 handler 数量 @return handler 数量,-1 表示端口无效 */
|
||||||
|
int datarouter_port_handler_count(datarouter_port_t port);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup introspection 报文类型元数据查询
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
int datarouter_channel_count(void);
|
||||||
|
const char *datarouter_channel_name(int channel_id);
|
||||||
|
const char *datarouter_channel_desc(int channel_id);
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DATAROUTER_H */
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @file datarouter_config.h
|
||||||
|
* @brief 数据报文类型配置
|
||||||
|
*
|
||||||
|
* 用户在此文件中定义所有报文类型(通道)。
|
||||||
|
* 每个报文类型通过 DATAROUTER_CHANNEL(id, desc) 宏声明,
|
||||||
|
* 库在编译期据此自动生成:
|
||||||
|
* - 通道枚举值 DATAROUTER_CHANNEL_<id>
|
||||||
|
* - 发送触发函数 datarouter_send_<id>(port, data, len)
|
||||||
|
* - 接收回调声明 datarouter_on_recv_<id>(port, data, len)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DATAROUTER_CONFIG_H
|
||||||
|
#define DATAROUTER_CONFIG_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def DATAROUTER_CHANNELS
|
||||||
|
* @brief 报文类型列表宏
|
||||||
|
*
|
||||||
|
* 每行格式:DATAROUTER_CHANNEL(标识符, "描述文字")
|
||||||
|
* 用户按需增删改。每个标识符对应一种报文类型。
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* #define DATAROUTER_CHANNELS \
|
||||||
|
* DATAROUTER_CHANNEL(sensor, "Sensor data") \
|
||||||
|
* DATAROUTER_CHANNEL(control, "Control command")
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define DATAROUTER_CHANNELS \
|
||||||
|
DATAROUTER_CHANNEL(sensor, "Sensor data") \
|
||||||
|
DATAROUTER_CHANNEL(status, "Status report") \
|
||||||
|
DATAROUTER_CHANNEL(control, "Control command")
|
||||||
|
|
||||||
|
#endif /* DATAROUTER_CONFIG_H */
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* @file example.c
|
||||||
|
* @brief 数据路由库使用示例
|
||||||
|
*
|
||||||
|
* 展示:
|
||||||
|
* 1. 编译期声明报文类型 → 自动生成发送触发函数和接收回调声明
|
||||||
|
* 2. 创建端口、设置报文类型
|
||||||
|
* 3. 为一个端口注册多个外设 handler(一对多广播路由)
|
||||||
|
* 4. 运行时增删 handler,切换路由
|
||||||
|
* 5. 实现接收回调处理外设收到的数据
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "datarouter.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ---- 外设发送 handler 实现 ---- */
|
||||||
|
|
||||||
|
static void uart_send(uint32_t port_id, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
printf("[UART] port %u: %.*s\n", port_id, (int)len, (const char *)data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
printf("[RX:sensor] 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)
|
||||||
|
{
|
||||||
|
printf("[RX:status] 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));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 创建一个 sensor 端口,注册多个 handler。
|
||||||
|
* 一次 send_sensor() 调用同时路由到 UART + TCP + CAN。
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
printf("\n--- send sensor (UART + TCP + CAN) ---\n");
|
||||||
|
datarouter_send_sensor(sensor_port, "temperature:25.3", 16);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 运行时移除 TCP handler,只剩 UART + CAN。
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 运行时再添加 FILE handler。
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 清除所有 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--- 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);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#include "library.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
void hello(void) {
|
|
||||||
printf("Hello, World!\n");
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef DATAROUTER_LIBRARY_H
|
|
||||||
#define DATAROUTER_LIBRARY_H
|
|
||||||
|
|
||||||
void hello(void);
|
|
||||||
|
|
||||||
#endif // DATAROUTER_LIBRARY_H
|
|
||||||
@@ -0,0 +1,511 @@
|
|||||||
|
/**
|
||||||
|
* @file test_datarouter.c
|
||||||
|
* @brief 数据路由库单元测试
|
||||||
|
*
|
||||||
|
* 覆盖:
|
||||||
|
* - 端口管理(创建/删除/复用/边界)
|
||||||
|
* - 端口类型与多 handler 增删查
|
||||||
|
* - 通道元数据查询
|
||||||
|
* - 一对多广播发送(多 handler 逐一调用)
|
||||||
|
* - 运行时增删 handler 改变路由
|
||||||
|
* - 接收回调
|
||||||
|
* - 错误处理
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "datarouter.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 测试框架
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static int _total_run, _total_pass, _total_fail;
|
||||||
|
static int _err;
|
||||||
|
static const char *_cur_name;
|
||||||
|
|
||||||
|
#define FIXTURE_MAX 16
|
||||||
|
typedef struct {
|
||||||
|
datarouter_port_t ports[FIXTURE_MAX];
|
||||||
|
int count;
|
||||||
|
} fixture_t;
|
||||||
|
|
||||||
|
#define FIXTURE_INIT(f) memset((f), 0, sizeof(*(f)))
|
||||||
|
#define FIXTURE_PUSH(f, p) do { if ((f)->count < FIXTURE_MAX) (f)->ports[(f)->count++] = (p); } while (0)
|
||||||
|
#define FIXTURE_CLEAN(f) do { for (int _i = 0; _i < (f)->count; _i++) datarouter_port_delete((f)->ports[_i]); } while (0)
|
||||||
|
#define FIXTURE_CREATE(f) ({ datarouter_port_t _p = datarouter_port_create(); FIXTURE_PUSH(f, _p); _p; })
|
||||||
|
|
||||||
|
#define TEST_START(name) \
|
||||||
|
static void test_##name(void) \
|
||||||
|
{ \
|
||||||
|
fixture_t _fix; \
|
||||||
|
FIXTURE_INIT(&_fix); \
|
||||||
|
_cur_name = #name; \
|
||||||
|
_err = 0;
|
||||||
|
|
||||||
|
#define TEST_END() \
|
||||||
|
cleanup: \
|
||||||
|
FIXTURE_CLEAN(&_fix); \
|
||||||
|
if (_err) { _total_fail++; printf(" FAIL %s\n", _cur_name); } \
|
||||||
|
else { _total_pass++; printf(" PASS %s\n", _cur_name); } \
|
||||||
|
_total_run++; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHECK(cond, ...) do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
printf(" %s [line %d]: ", _cur_name, __LINE__); \
|
||||||
|
printf(__VA_ARGS__); \
|
||||||
|
printf("\n"); \
|
||||||
|
_err = 1; \
|
||||||
|
goto cleanup; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 测试用 handler
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
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_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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 1. 未初始化
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
TEST_START(error_uninit)
|
||||||
|
int ret = datarouter_send_sensor(0, "d", 1);
|
||||||
|
CHECK(ret == -1, "expected -1, got %d", ret);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 2. 端口管理
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
TEST_START(port_create_basic)
|
||||||
|
datarouter_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
|
CHECK(p >= 0, "create returned %d", p);
|
||||||
|
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_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--;
|
||||||
|
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");
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 3. 类型
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
TEST_START(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");
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(type_clear)
|
||||||
|
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");
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(type_switch)
|
||||||
|
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");
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(type_from_invalid)
|
||||||
|
CHECK(datarouter_port_get_type(-1) == -1, NULL);
|
||||||
|
CHECK(datarouter_port_get_type(999) == -1, NULL);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 4. 多 handler 增删查
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
TEST_START(handler_add_one)
|
||||||
|
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");
|
||||||
|
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_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);
|
||||||
|
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. 发送 — 一对多广播
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
TEST_START(send_one_handler)
|
||||||
|
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, "d", 1) == 0, "send failed");
|
||||||
|
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_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);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(send_type_mismatch)
|
||||||
|
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_status(p, "d", 1) == -3, "expected -3");
|
||||||
|
CHECK(_h_cnt == 0, "handler should not fire");
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(send_no_handler)
|
||||||
|
datarouter_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
|
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor);
|
||||||
|
|
||||||
|
reset_hstate();
|
||||||
|
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_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_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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
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_clear_handlers(p);
|
||||||
|
|
||||||
|
CHECK(datarouter_send_sensor(p, "d", 1) == -4, "expected -4 after clear");
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 8. 接收回调
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
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++; }
|
||||||
|
|
||||||
|
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_END()
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 9. 错误处理
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
TEST_START(error_invalid_port)
|
||||||
|
CHECK(datarouter_send_sensor(999, "d", 1) == -2, NULL);
|
||||||
|
CHECK(datarouter_send_sensor(-1, "d", 1) == -2, NULL);
|
||||||
|
TEST_END()
|
||||||
|
|
||||||
|
TEST_START(error_deleted_port)
|
||||||
|
datarouter_port_t p = FIXTURE_CREATE(&_fix);
|
||||||
|
datarouter_port_set_type(p, DATAROUTER_CHANNEL_sensor);
|
||||||
|
datarouter_port_add_handler(p, _h);
|
||||||
|
datarouter_port_delete(p);
|
||||||
|
CHECK(datarouter_send_sensor(p, "d", 1) == -2, "expected -2");
|
||||||
|
_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)
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("==== DataRouter Unit Tests ====\n\n");
|
||||||
|
|
||||||
|
printf("[uninitialized]\n");
|
||||||
|
RUN(error_uninit);
|
||||||
|
|
||||||
|
datarouter_init();
|
||||||
|
|
||||||
|
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);
|
||||||
|
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("\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);
|
||||||
|
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");
|
||||||
|
RUN(send_one_handler);
|
||||||
|
RUN(send_multi_handlers);
|
||||||
|
RUN(send_type_mismatch);
|
||||||
|
RUN(send_no_handler);
|
||||||
|
RUN(send_three_channels);
|
||||||
|
RUN(send_remove_handler);
|
||||||
|
RUN(send_clear_handlers);
|
||||||
|
RUN(send_zero_length);
|
||||||
|
RUN(send_null_data);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("[receive callback]\n");
|
||||||
|
RUN(recv_callback);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("[error handling]\n");
|
||||||
|
RUN(error_invalid_port);
|
||||||
|
RUN(error_deleted_port);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("==============================\n");
|
||||||
|
printf(" total: %d | pass: %d | fail: %d\n\n",
|
||||||
|
_total_run, _total_pass, _total_fail);
|
||||||
|
|
||||||
|
return _total_fail > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user