Files
DataRouter/DESIGN.md
T

443 lines
10 KiB
Markdown
Raw Permalink 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 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_<id>(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。
### 核心设计
```
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 匹配?
│
▼
for each handler(port, data) ─── UART / TCP / CAN
```
**关键**:send 无 port_id 参数,自动路由到所有匹配端口。
### 设计原则
| 原则 | 说明 |
|------|------|
| 收发分离 | 发送/接收能力在配置中分开声明,按需组合 |
| 编译期确定能力 | 所有能力在 `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
```
---
## 5. 数据结构
### 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
#define DR_MAX_HANDLERS_PER_PORT 4
typedef struct {
int send_type;
dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT];
int count;
uint8_t in_use;
} port_entry_t;
```
### 5.3 端口表
```c
#define DR_MAX_PORTS 64
static port_entry_t ports[DR_MAX_PORTS];
static uint8_t initialized;
```
---
## 6. API 参考
### 6.1 初始化
```c
void dr_init(void); // 幂等初始化
void dr_reset(void); // 重置全部状态
```
### 6.2 端口管理
```c
dr_port_t dr_port_create(void);
void dr_port_delete(dr_port_t port);
void dr_port_set_send_type(port, int cap_id);
int dr_port_get_send_type(port);
int dr_port_add_handler(port, handler);
int dr_port_remove_handler(port, handler);
void dr_port_clear_handlers(port);
int dr_port_handler_count(port);
```
### 6.3 发送触发函数(每个 SEND 能力生成一个)
```c
int dr_send_sensor(const void *data, size_t len);
int dr_send_status(const void *data, size_t len);
```
返回值:0 成功 / -1 未初始化 / -2 无端口匹配该 send_type
### 6.4 接收回调(每个 RECV 能力生成一个声明)
```c
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);
```
库提供弱符号默认空实现。
### 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);
```
---
## 7. 核心流程
### 7.1 发送流程
```plantuml
@startuml
actor "应用" as App
participant "dr_send_sensor()" as Send
participant "端口表 ports[]" as Table
participant "handlers[]" as HList
participant "外设" as Dev
App -> Send : send_sensor("temp:25.3", 9)
group 初始化检查
Send -> Send : initialized?
alt 否 → return -1
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
alt 至少调了一次 handler
Send --> App : return 0
else 无匹配端口
Send --> App : return -2
end
@enduml
```
### 7.2 接收流程
```plantuml
@startuml
actor "外设" as Dev
participant "用户分发代码" as Dispatch
participant "on_recv_rtcm()" as Callback
Dev -> Dispatch : 收到数据,识别为 rtcm
Dispatch -> Callback : on_recv_rtcm(port_id, data, len)
Callback --> Dispatch : 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
```
---
## 8. 路由场景
### 场景 1:sensor 广播到 UART + TCP + CAN
```c
dr_port_t p1 = dr_port_create();
dr_port_set_send_type(p1, DR_SEND_sensor);
dr_port_add_handler(p1, uart_send);
dr_port_add_handler(p1, tcp_send);
dr_port_t p2 = dr_port_create();
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)
```
### 场景 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
```
---
## 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 | 成功,至少有一个 handler 被调用 |
| -1 | 库未初始化(未调 init) |
| -2 | 无端口匹配该 send_type(或匹配端口均无 handler) |
---
## 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. 约束
| 约束 | 说明 |
|------|------|
| 能力声明 | 编译期固定,修改需重新编译 |
| 最大端口数 | 默认 64,通过 `-D` 覆盖 |
| 每端口 handler 上限 | 默认 4,通过 `-D` 覆盖 |
| 线程安全 | 不支持(无锁),多线程需外部加锁 |
| 弱符号 | GCC/Clang 原生支持 |
| 内存分配 | 纯静态,无 malloc/free |