Files
DataRouter/DESIGN.md
T
2026-07-26 01:40:05 +08:00

513 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |