Files
DataRouter/DESIGN.md
T

494 lines
13 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 语言数据路由库。发送与接收能力分开声明,每个端口独立设置发送类型和接收类型,支持多外设一对多广播路由。
### 核心设计
```
DATAROUTER_SEND_CAPABILITIES DATAROUTER_RECV_CAPABILITIES
DATAROUTER_SEND(sensor, ...) DATAROUTER_RECV(rtcm, ...)
DATAROUTER_SEND(status, ...) DATAROUTER_RECV(command, ...)
│ │
▼ ▼
send_sensor(port, data) on_recv_rtcm(port, data)
send_status(port, data) on_recv_command(port, data)
│ (用户实现回调)
▼
端口校验 → for each handler(port, data)
│
┌────────┼────────┐
▼ ▼ ▼
UART TCP CAN
```
**关键区别**:sensor 只声明发送,不生成接收回调;rtcm 只声明接收,不生成发送函数。端口可独立设置 send_type 和 recv_type,互不干扰。
### 设计原则
| 原则 | 说明 |
|------|------|
| 收发分离 | 发送能力和接收能力在配置中分开声明,按需组合 |
| 编译期确定能力 | 所有能力在 `datarouter_config.h` 中声明,库自动生成代码 |
| 运行期绑定外设 | 端口创建后,通过 `add_handler` 注册物理外设发送函数 |
| 一对多广播 | 一个端口可注册多个 handler,发送时逐一调用 |
| 不关心物理外设 | 库不依赖任何外设 API,只调用用户提供的 handler |
| 零动态内存 | 纯静态数组,无 malloc/free |
---
## 2. 整体架构
### 2.1 模块依赖
```plantuml
@startuml
left to right direction
rectangle "用户代码" as User {
component "应用逻辑" as App
component "datarouter_config.h\n声明发送+接收能力" as Cfg
component "外设 handler\nUART / TCP / CAN" as Handlers
component "接收回调\n处理收到的报文" as Callbacks
}
database "DataRouter 库" as Lib {
component "datarouter.h\n公共 API + X-macro 展开" as Hdr
component "datarouter.c\n端口表 + handler 遍历" as Impl
}
App --> Hdr
Cfg --> Hdr
Impl --> Cfg
Handlers --> Impl : 注册 handler
Callbacks --> Impl : 实现 on_recv_<id>
@enduml
```
### 2.2 文件结构
| 文件 | 职责 |
|------|------|
| `datarouter_config.h` | 分别声明 `SEND_CAPABILITIES` 和 `RECV_CAPABILITIES` |
| `datarouter.h` | 公共 API + X-macro 展开(两组枚举 + 声明) |
| `datarouter.c` | 端口表 + handler 列表遍历 + 弱符号回调 |
| `example.c` | 收发分离示例 |
| `tests/test_datarouter.c` | 27 个单元测试 |
---
## 3. 收发分离设计
### 3.1 配置声明
```c
// 发送能力 —— 只生成发送触发函数
#define DATAROUTER_SEND_CAPABILITIES \
DATAROUTER_SEND(sensor, "Sensor data report") \
DATAROUTER_SEND(status, "Device status report")
// 接收能力 —— 只生成接收回调声明
#define DATAROUTER_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM correction data") \
DATAROUTER_RECV(command, "Remote control command")
```
### 3.2 展开产物
```
DATAROUTER_SEND_CAPABILITIES 展开:
enum: DATAROUTER_SEND_sensor = 0,
DATAROUTER_SEND_status = 1,
DATAROUTER_SEND_CAP_COUNT = 2
发送函数: int datarouter_send_sensor(port, data, len);
int datarouter_send_status(port, data, len);
元数据: send_names[] = {"sensor", "status"}
send_descs[] = {"Sensor data report", "Device status report"}
DATAROUTER_RECV_CAPABILITIES 展开:
enum: DATAROUTER_RECV_rtcm = 0,
DATAROUTER_RECV_command = 1,
DATAROUTER_RECV_CAP_COUNT = 2
接收回调: void datarouter_on_recv_rtcm(port, data, len);
void datarouter_on_recv_command(port, data, len);
弱符号默认: 每个回调提供空的 weak 实现,用户覆盖
元数据: recv_names[] = {"rtcm", "command"}
recv_descs[] = {"RTCM correction data", "Remote control command"}
```
### 3.3 端口组合示例
```
端口 A: send_type = sensor, recv_type = -1 → 只发送
端口 B: send_type = -1, recv_type = rtcm → 只接收
端口 C: send_type = status, recv_type = command → 同时收发
端口 D: send_type = -1, recv_type = -1 → 无功能
```
---
## 4. X-macro 代码生成
### 4.1 展开流程
```plantuml
@startuml
== datarouter.h:4 次展开 ==
-> datarouter_config.h: 获得两组宏列表
datarouter.h -> "第 1 遍":
#define DATAROUTER_SEND(id,desc) DATAROUTER_SEND_##id,
DATAROUTER_SEND_CAPABILITIES → 发送 enum
datarouter.h -> "第 2 遍":
#define DATAROUTER_RECV(id,desc) DATAROUTER_RECV_##id,
DATAROUTER_RECV_CAPABILITIES → 接收 enum
datarouter.h -> "第 3 遍":
#define DATAROUTER_SEND(id,desc) int datarouter_send_##id(...);
DATAROUTER_SEND_CAPABILITIES → 发送函数声明
datarouter.h -> "第 4 遍":
#define DATAROUTER_RECV(id,desc) void datarouter_on_recv_##id(...);
DATAROUTER_RECV_CAPABILITIES → 接收回调声明
== datarouter.c:4 次展开 ==
datarouter.c -> "第 5 遍":
弱符号回调默认实现(DATAROUTER_RECV 展开)
datarouter.c -> "第 6 遍":
发送函数实现 body(DATAROUTER_SEND 展开)
校验 → 遍历 handler 列表
datarouter.c -> "第 7 ~ 8 遍":
send_names[] / send_descs[]
recv_names[] / recv_descs[]
@enduml
```
---
## 5. 数据结构
### 5.1 核心数据类型
```plantuml
@startuml
skinparam classAttributeIconSize 0
enum "datarouter_send_cap" as S {
SENSOR
STATUS
SEND_CAP_COUNT
}
enum "datarouter_recv_cap" as R {
RTCM
COMMAND
RECV_CAP_COUNT
}
class "port_entry_t" as Entry {
- int send_type
- int recv_type
- datarouter_send_handler_t handlers[]
- int count
- uint8_t in_use
}
class "datarouter_send_handler_t" as Handler {
+ void (*)(uint32_t port_id, const void *data, size_t len)
}
class "发送函数" as Send {
+ int send_sensor(port, data, len)
+ int send_status(port, data, len)
}
class "接收回调" as Recv {
+ void on_recv_rtcm(port, data, len) ← 用户实现
+ void on_recv_command(port, data, len)
}
Entry --> S : send_type ∈ S enum / -1
Entry --> R : recv_type ∈ R enum / -1
Entry --> Handler : handlers[] 数组
Send --> Entry : 校验 send_type + 遍历 handlers
Recv --> Entry : 用户根据业务调对应回调
@enduml
```
### 5.2 `port_entry_t`
```c
#define DATAROUTER_MAX_HANDLERS_PER_PORT 4 // 每端口最多 4 个外设
typedef struct {
int send_type; // DATAROUTER_SEND_xxx / -1
int recv_type; // DATAROUTER_RECV_xxx / -1
datarouter_send_handler_t handlers[DATAROUTER_MAX_HANDLERS_PER_PORT];
int count; // 当前 handler 数量
uint8_t in_use;
} port_entry_t;
```
### 5.3 端口表
```c
#define DATAROUTER_MAX_PORTS 64
static port_entry_t ports[DATAROUTER_MAX_PORTS];
static uint8_t initialized;
```
---
## 6. API 参考
### 6.1 初始化
```c
void datarouter_init(void); // 幂等初始化
void datarouter_reset(void); // 重置全部状态
```
### 6.2 端口管理
```c
datarouter_port_t datarouter_port_create(void); // 创建
void datarouter_port_delete(datarouter_port_t port); // 删除
void datarouter_port_set_send_type(port, int cap_id); // 设置发送类型
int datarouter_port_get_send_type(port); // -1 表示未设置
void datarouter_port_set_recv_type(port, int cap_id); // 设置接收类型
int datarouter_port_get_recv_type(port); // -1 表示未设置
int datarouter_port_add_handler(port, handler); // 注册外设 handler
int datarouter_port_remove_handler(port, handler); // 移除
void datarouter_port_clear_handlers(port); // 清除全部
int datarouter_port_handler_count(port); // 数量
```
### 6.3 发送触发函数(每个 SEND 能力生成一个)
```c
int datarouter_send_sensor(port, data, len);
int datarouter_send_status(port, data, len);
```
返回值:0 成功 / -1 未初始化 / -2 端口无效 / -3 send_type 不匹配 / -4 无 handler
### 6.4 接收回调(每个 RECV 能力生成一个声明)
```c
void datarouter_on_recv_rtcm(uint32_t port_id, const void *data, size_t len);
void datarouter_on_recv_command(uint32_t port_id, const void *data, size_t len);
```
库提供弱符号默认空实现,用户定义同名函数覆盖。
### 6.5 能力元数据(两组独立)
```c
// 发送能力
int datarouter_send_cap_count(void);
const char *datarouter_send_cap_name(int cap_id);
const char *datarouter_send_cap_desc(int cap_id);
// 接收能力
int datarouter_recv_cap_count(void);
const char *datarouter_recv_cap_name(int cap_id);
const char *datarouter_recv_cap_desc(int cap_id);
```
---
## 7. 核心流程
### 7.1 发送流程
```plantuml
@startuml
actor "应用" as App
participant "datarouter_send_sensor()" as Send
participant "端口表" as Table
participant "handlers[]" as HList
participant "外设" as Dev
App -> Send : send_sensor(port=2, data, len)
group 校验
Send -> Send : initialized?
alt 否 → return -1
end
Send -> Table : ports[2].in_use?
alt 否 → return -2
end
Send -> Table : ports[2].send_type == SENSOR?
alt 否 → return -3
end
Send -> Table : ports[2].count > 0?
alt 否 → return -4
end
end
loop for each handler in ports[2].handlers[]
Send -> HList : handlers[i](port, data, len)
HList -> Dev : 实际外设写入
end
Send --> App : return 0
@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=-1 recv=-1" as Created
state "已配置" as Config
[*] --> Idle : reset / 启动
Idle --> Created : port_create()
Created --> Config : set_send_type / set_recv_type / add_handler
Config --> Config : 独立切换 send_type / recv_type\n增删 handler
Config --> Created : set_send_type(-1) / set_recv_type(-1)\nclear_handlers
Created --> Idle : port_delete()
Config --> Idle : port_delete()
Idle --> Created : 复用 ID
@enduml
```
---
## 8. 路由场景
### 场景 1:传感器只发送(sensor → UART + TCP)
```c
datarouter_port_t p = datarouter_port_create();
datarouter_port_set_send_type(p, DATAROUTER_SEND_sensor);
datarouter_port_add_handler(p, uart_send);
datarouter_port_add_handler(p, tcp_send);
datarouter_send_sensor(p, "temp:25.3", 9);
// → uart_send(p, "temp:25.3", 9)
// → tcp_send(p, "temp:25.3", 9)
```
### 场景 2:RTCM 只接收
```c
datarouter_port_t p = datarouter_port_create();
datarouter_port_set_recv_type(p, DATAROUTER_RECV_rtcm);
// 无 send_type,不注册 handler
// 外设收到数据后由用户调回调:
datarouter_on_recv_rtcm(p, data, len);
```
### 场景 3:双向端口(status 发送 + command 接收)
```c
datarouter_port_t p = datarouter_port_create();
datarouter_port_set_send_type(p, DATAROUTER_SEND_status);
datarouter_port_set_recv_type(p, DATAROUTER_RECV_command);
datarouter_port_add_handler(p, uart_send);
// 发送
datarouter_send_status(p, "status:OK", 9);
// 接收
datarouter_on_recv_command(p, "reboot", 6);
```
---
## 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 | 端口无效(越界/未创建/已删除) |
| -3 | 端口 send_type 与调用的发送函数不匹配 |
| -4 | 端口 handler 列表为空(count == 0) |
---
## 11. 配置扩展
### 增加能力
```c
#define DATAROUTER_SEND_CAPABILITIES \
DATAROUTER_SEND(sensor, "Sensor data") \
DATAROUTER_SEND(debug, "Debug log") /* 新增发送能力 */
#define DATAROUTER_RECV_CAPABILITIES \
DATAROUTER_RECV(rtcm, "RTCM data") \
DATAROUTER_RECV(ota, "OTA firmware") /* 新增接收能力 */
```
### 自定义限制
```bash
cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8"
```
---
## 12. 约束
| 约束 | 说明 |
|------|------|
| 能力声明 | 编译期固定,修改需重新编译 |
| 最大端口数 | 默认 64,通过 `-D` 覆盖 |
| 每端口 handler 上限 | 默认 4,通过 `-D` 覆盖 |
| 线程安全 | 不支持(无锁),多线程需外部加锁 |
| 弱符号 | GCC/Clang 原生支持 |
| 内存分配 | 纯静态,无 malloc/free |