Files
DataRouter/DESIGN.md
T

13 KiB
Raw Blame History

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 模块依赖

@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 配置声明

// 发送能力 —— 只生成发送触发函数
#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 展开流程

@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 核心数据类型

@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

#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 端口表

#define DATAROUTER_MAX_PORTS 64
static port_entry_t ports[DATAROUTER_MAX_PORTS];
static uint8_t      initialized;

6. API 参考

6.1 初始化

void datarouter_init(void);    // 幂等初始化
void datarouter_reset(void);   // 重置全部状态

6.2 端口管理

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 能力生成一个)

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 能力生成一个声明)

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 能力元数据(两组独立)

// 发送能力
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 发送流程

@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 接收流程

@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 端口生命周期

@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)

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 只接收

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 接收)

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. 弱符号机制

@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. 配置扩展

增加能力

#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")   /* 新增接收能力 */

自定义限制

cmake -DCMAKE_C_FLAGS="-DDATAROUTER_MAX_PORTS=128 -DDATAROUTER_MAX_HANDLERS_PER_PORT=8"

12. 约束

约束 说明
能力声明 编译期固定,修改需重新编译
最大端口数 默认 64,通过 -D 覆盖
每端口 handler 上限 默认 4,通过 -D 覆盖
线程安全 不支持(无锁),多线程需外部加锁
弱符号 GCC/Clang 原生支持
内存分配 纯静态,无 malloc/free