feat: 新增接收dispatch反向路由
This commit is contained in:
@@ -2,208 +2,57 @@
|
||||
|
||||
## 1. 设计目标
|
||||
|
||||
轻量级 C 语言数据路由库。发送与接收能力分开声明,端口绑定 `send_type` 和外设 handler 列表。调用 `dr_send_<id>(data, len)` 时自动遍历端口表,广播到所有 `send_type` 匹配的端口。
|
||||
轻量级 C 语言数据路由库。发送能力与接收能力分开声明,端口可独立设置 `send_type` 和 `recv_type`。发送时自动广播到所有匹配端口,接收时通过 `dr_port_dispatch()` 查表自动路由到回调。
|
||||
|
||||
### 核心设计
|
||||
|
||||
```
|
||||
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)
|
||||
│ (用户实现回调)
|
||||
▼
|
||||
发送: dr_send_sensor(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
|
||||
接收: dr_port_dispatch(port, data, len)
|
||||
│
|
||||
▼
|
||||
查 recv_table[port.recv_type]
|
||||
│
|
||||
▼
|
||||
dr_on_recv_rtcm(port, data, len) ← 用户实现
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据结构
|
||||
## 2. 数据结构
|
||||
|
||||
### 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`
|
||||
### 2.1 `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;
|
||||
int send_type; // DR_SEND_xxx / -1
|
||||
int recv_type; // DR_RECV_xxx / -1
|
||||
dr_send_handler_t handlers[DR_MAX_HANDLERS_PER_PORT];
|
||||
int count;
|
||||
uint8_t in_use;
|
||||
} port_entry_t;
|
||||
```
|
||||
|
||||
### 5.3 端口表
|
||||
### 2.2 接收回调函数指针表
|
||||
|
||||
```c
|
||||
// 编译期由 X-macro 生成,DR_RECV_rtcm 的枚举值即为数组下标
|
||||
static void (*const recv_table[DR_RECV_CAP_COUNT])(uint32_t, const void*, size_t) = {
|
||||
dr_on_recv_rtcm,
|
||||
dr_on_recv_command,
|
||||
};
|
||||
```
|
||||
|
||||
### 2.3 端口表
|
||||
|
||||
```c
|
||||
#define DR_MAX_PORTS 64
|
||||
@@ -213,224 +62,121 @@ static uint8_t initialized;
|
||||
|
||||
---
|
||||
|
||||
## 6. API 参考
|
||||
## 3. API
|
||||
|
||||
### 6.1 初始化
|
||||
|
||||
```c
|
||||
void dr_init(void); // 幂等初始化
|
||||
void dr_reset(void); // 重置全部状态
|
||||
```
|
||||
|
||||
### 6.2 端口管理
|
||||
### 3.1 端口管理
|
||||
|
||||
```c
|
||||
dr_port_t dr_port_create(void);
|
||||
void dr_port_delete(dr_port_t port);
|
||||
void dr_port_delete(dr_port_t port);
|
||||
|
||||
void dr_port_set_send_type(port, int cap_id);
|
||||
int dr_port_get_send_type(port);
|
||||
void dr_port_set_send_type(port, int cap_id);
|
||||
int dr_port_get_send_type(port);
|
||||
void dr_port_set_recv_type(port, int cap_id);
|
||||
int dr_port_get_recv_type(port);
|
||||
|
||||
int dr_port_add_handler(port, handler);
|
||||
int dr_port_remove_handler(port, handler);
|
||||
void dr_port_clear_handlers(port);
|
||||
int dr_port_handler_count(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);
|
||||
|
||||
void dr_port_dispatch(port, data, len); // ← 自动路由接收
|
||||
```
|
||||
|
||||
### 6.3 发送触发函数(每个 SEND 能力生成一个)
|
||||
### 3.2 发送触发
|
||||
|
||||
```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
|
||||
返回值:0 成功 / -1 未初始化 / -2 无匹配端口
|
||||
|
||||
### 6.4 接收回调(每个 RECV 能力生成一个声明)
|
||||
### 3.3 接收回调
|
||||
|
||||
```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. 核心流程
|
||||
## 4. 核心流程
|
||||
|
||||
### 7.1 发送流程
|
||||
### 4.1 发送
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
actor "应用" as App
|
||||
participant "dr_send_sensor()" as Send
|
||||
participant "端口表 ports[]" as Table
|
||||
participant "handlers[]" as HList
|
||||
participant "外设" as Dev
|
||||
actor App
|
||||
participant dr_send_sensor as Send
|
||||
participant ports[] as Table
|
||||
participant handlers[] as H
|
||||
|
||||
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
|
||||
App -> Send : send_sensor("temp", 9)
|
||||
loop for p in 0..MAX_PORTS-1
|
||||
Send -> Table : in_use && send_type == SENSOR ?
|
||||
alt 匹配
|
||||
loop for h in handlers
|
||||
Send -> H : handlers[h](p, data, len)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alt 至少调了一次 handler
|
||||
alt 至少调了一次
|
||||
Send --> App : return 0
|
||||
else 无匹配端口
|
||||
else 无匹配
|
||||
Send --> App : return -2
|
||||
end
|
||||
@enduml
|
||||
```
|
||||
|
||||
### 7.2 接收流程
|
||||
### 4.2 接收(dispatch)
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
actor "外设" as Dev
|
||||
participant "用户分发代码" as Dispatch
|
||||
participant "on_recv_rtcm()" as Callback
|
||||
actor 外设ISR as Dev
|
||||
participant dr_port_dispatch as Dispatch
|
||||
participant recv_table[] as Table
|
||||
participant dr_on_recv_rtcm as Cb
|
||||
|
||||
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
|
||||
Dev -> Dispatch : dispatch(port, data, len)
|
||||
Dispatch -> Table : 查 recv_table[port.recv_type]
|
||||
Dispatch -> Cb : recv_table[rid](port, data, len)
|
||||
Cb --> Dev : void
|
||||
@enduml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 路由场景
|
||||
## 5. Dispatch 实现
|
||||
|
||||
### 场景 1:sensor 广播到 UART + TCP + CAN
|
||||
`dr_port_dispatch` 是零分支的查表路由:
|
||||
|
||||
```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);
|
||||
void dr_port_dispatch(dr_port_t port, const void *data, size_t len)
|
||||
{
|
||||
if (!initialized) return;
|
||||
if (port < 0 || port >= DR_MAX_PORTS || !ports[port].in_use) return;
|
||||
int rid = ports[port].recv_type;
|
||||
if (rid < 0 || rid >= DR_RECV_CAP_COUNT) return;
|
||||
recv_table[rid]((uint32_t)port, data, len); // 直接函数指针调用
|
||||
}
|
||||
// 外设收到后:dr_on_recv_rtcm(port, data, len);
|
||||
```
|
||||
|
||||
### 场景 3:运行时移除 handler
|
||||
|
||||
```c
|
||||
dr_port_remove_handler(p1, tcp_send);
|
||||
// 下次 send_sensor 不再触发 tcp
|
||||
```
|
||||
`recv_table[]` 是 `const` 数组,编译期由 X-macro 生成。`DR_RECV_rtcm` 的枚举值等于数组下标,查表时间为 O(1),无分支预测开销,适合 ISR。
|
||||
|
||||
---
|
||||
|
||||
## 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. 错误码
|
||||
## 6. 错误码
|
||||
|
||||
| 返回值 | 含义 |
|
||||
|:------:|------|
|
||||
| 0 | 成功,至少有一个 handler 被调用 |
|
||||
| -1 | 库未初始化(未调 init) |
|
||||
| -2 | 无端口匹配该 send_type(或匹配端口均无 handler) |
|
||||
| 0 | 成功 |
|
||||
| -1 | 库未初始化 |
|
||||
| -2 | 无端口匹配该 send_type |
|
||||
|
||||
---
|
||||
|
||||
## 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. 约束
|
||||
## 7. 约束
|
||||
|
||||
| 约束 | 说明 |
|
||||
|------|------|
|
||||
|
||||
Reference in New Issue
Block a user