docs: RTU 模块 + Mongoose API 参考文档
- docs/system/RTU/API-RTU.md: RTU 完整文档(531行) X-Macro 框架/启动流程/数据结构/新增模块指南 - docs/protocol/libmongoose/API-libmongoose.md: mongoose 文档(347行) HTTP/WebSocket/定时器 API + 集成约束 - docs/README.md: 新增 system/ + protocol/libmongoose 索引 - COMPLETION.md: 更新 RTU + mongoose 完成状态
This commit is contained in:
parent
d1569c92bb
commit
db1ee1e979
|
|
@ -41,7 +41,8 @@
|
||||||
| Modbus 主站管理 | `libmodbus_m`(原工程自研模块,导入+适配) | ✅ |
|
| Modbus 主站管理 | `libmodbus_m`(原工程自研模块,导入+适配) | ✅ |
|
||||||
| 通讯通道解码 | `libcom_decode` | ⬜ |
|
| 通讯通道解码 | `libcom_decode` | ⬜ |
|
||||||
| Web 管理服务 | `libweb_server` | ⬜ |
|
| Web 管理服务 | `libweb_server` | ⬜ |
|
||||||
| 主程序入口 | `RTU`(系统模块框架 + app_modules.c + main.c) | ✅ |
|
| Mongoose 网络库 | `libmongoose`(开源库 v7.22,导入+适配) | ✅ |
|
||||||
|
| 主程序入口 | `RTU`(X-Macro 框架 + app_config.json 启停控制 + 独立 config/ 目录) | ✅ |
|
||||||
|
|
||||||
## 阶段 3:系统集成
|
## 阶段 3:系统集成
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,20 @@
|
||||||
| libcmd | [API-libcmd.md](public/libcmd/API-libcmd.md) | C 接口+C++ 内部 | 命令注册/补全 + 内置 linenoise |
|
| libcmd | [API-libcmd.md](public/libcmd/API-libcmd.md) | C 接口+C++ 内部 | 命令注册/补全 + 内置 linenoise |
|
||||||
| libcomm | [API-libcomm.md](public/libcomm/API-libcomm.md) | C 接口+C++ 内部 | TCP/UDP/UART 通讯抽象层 |
|
| libcomm | [API-libcomm.md](public/libcomm/API-libcomm.md) | C 接口+C++ 内部 | TCP/UDP/UART 通讯抽象层 |
|
||||||
|
|
||||||
### 通讯与协议库 (protocol/ — 阶段 2 计划)
|
### 通讯与协议库 (protocol/)
|
||||||
|
|
||||||
|
| 模块 | 文档 | 语言 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| libmodbus | [API-libmodbus.md](protocol/libmodbus/API-libmodbus.md) | C (开源库) | Modbus 协议栈 v3.1.12 |
|
||||||
|
| lib60870 | [API-lib60870.md](protocol/lib60870/API-lib60870.md) | C (自研) | IEC 60870-5-101/103/104 协议栈 |
|
||||||
|
| libmongoose | [API-libmongoose.md](protocol/libmongoose/API-libmongoose.md) | C (MIT 开源) | HTTP/WebSocket 嵌入式网络库 v7.22 |
|
||||||
|
|
||||||
|
### 系统模块 (system/)
|
||||||
|
|
||||||
|
| 模块 | 文档 | 语言 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| RTU | [API-RTU.md](system/RTU/API-RTU.md) | C + C++ | 主程序入口、X-Macro 框架、模块生命周期 |
|
||||||
|
| libmodbus_m | [API-libmodbus_m.md](system/libmodbus_m/API-libmodbus_m.md) | C++ | Modbus 主站管理(通道/点表/轮询/写入) |
|
||||||
|
|
||||||
```
|
```
|
||||||
┌──────────┐
|
┌──────────┐
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,369 @@
|
||||||
|
# API-libmongoose — 嵌入式 HTTP/WebSocket 网络库
|
||||||
|
|
||||||
|
> 版本: v1.0
|
||||||
|
> 日期: 2026-07-08
|
||||||
|
> 来源: Mongoose v7.22 (MIT 许可,https://github.com/cesanta/mongoose)
|
||||||
|
> 状态: ✅ 导入完成,x86 + ARM 双平台编译通过(零警告)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 模块概述
|
||||||
|
|
||||||
|
libmongoose 是 Mongoose v7.22 开源库的封装模块。Mongoose 是一个**单文件、纯 C 实现的嵌入式网络库**,广泛用于 IoT 和嵌入式设备。本模块主要用于 RTU 的 Web 管理服务(`libweb_server`),提供 HTTP REST API 和 WebSocket 双向通信能力。
|
||||||
|
|
||||||
|
| 功能 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| HTTP 服务器 | 解析请求、路由匹配、JSON 响应,用于 REST API |
|
||||||
|
| WebSocket | 双向消息推送(实时遥测数据、告警通知) |
|
||||||
|
| 事件驱动 | 基于 epoll 的异步 IO,单线程管理所有连接 |
|
||||||
|
| 定时器 | 毫秒级软定时器,无需额外线程 |
|
||||||
|
| 跨平台 | Linux / FreeRTOS / Zephyr / ESP32 等,本工程只用 Linux |
|
||||||
|
|
||||||
|
### 架构位置
|
||||||
|
|
||||||
|
```
|
||||||
|
libweb_server (system/) — REST API + WebSocket 管理
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
libmongoose (protocol/) — HTTP/WS 协议栈
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Linux kernel socket + epoll
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/protocol/libmongoose/
|
||||||
|
├── inc/
|
||||||
|
│ ├── myMongoose.h # RTU 适配头(编译配置宏 + #include "mongoose.h")
|
||||||
|
│ └── mongoose.h # Mongoose v7.22 头文件(5748 行,上游文件)
|
||||||
|
└── src/
|
||||||
|
└── mongoose.c # Mongoose v7.22 实现(31786 行,上游文件)
|
||||||
|
|
||||||
|
release/
|
||||||
|
├── inc/
|
||||||
|
│ ├── myMongoose.h # 拷贝自 src,供其他模块包含
|
||||||
|
│ └── mongoose.h # 拷贝自 src
|
||||||
|
└── x86/lib/
|
||||||
|
└── libmongoose.a # x86 编译产物(462KB)
|
||||||
|
└── arm/lib/
|
||||||
|
└── libmongoose.a # ARM 编译产物(471KB)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 编译配置
|
||||||
|
|
||||||
|
在 `myMongoose.h` 中定义以下宏(必须在 `#include "mongoose.h"` 之前):
|
||||||
|
|
||||||
|
| 宏 | 值 | 说明 |
|
||||||
|
|------|-----|------|
|
||||||
|
| `MG_ENABLE_LOG` | 1 | 启用内置日志(printf 到 stderr) |
|
||||||
|
| `MG_ENABLE_FILE` | 0 | 禁用文件系统(RTU 不通过 mongoose 提供静态文件) |
|
||||||
|
| `MG_ENABLE_POSIX_FS` | 0 | 禁用 POSIX 文件 API |
|
||||||
|
| `MG_ENABLE_EPOLL` | 1 | 启用 epoll(Linux 高效 IO 多路复用) |
|
||||||
|
| `MG_ENABLE_POLL` | 1 | 启用 poll(epoll 回退方案) |
|
||||||
|
| `MG_ENABLE_SOCKET` | 1 | 使用 Linux 内核 socket |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 核心数据结构
|
||||||
|
|
||||||
|
### 4.1 mg_mgr — 事件管理器
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct mg_mgr
|
||||||
|
{
|
||||||
|
struct mg_connection *conns; /* 活动连接链表 */
|
||||||
|
struct mg_timer *timers; /* 定时器链表 */
|
||||||
|
// ... 内部字段
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
每个进程只需一个 `mg_mgr` 实例,管理所有连接和定时器。
|
||||||
|
|
||||||
|
### 4.2 mg_connection — 连接
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct mg_connection
|
||||||
|
{
|
||||||
|
struct mg_connection *next; /* 链表指针 */
|
||||||
|
struct mg_mgr *mgr; /* 所属管理器 */
|
||||||
|
struct mg_addr peer; /* 对端地址 */
|
||||||
|
void *fd; /* socket 文件描述符 */
|
||||||
|
unsigned long id; /* 连接 ID */
|
||||||
|
unsigned is_websocket : 1; /* 是否为 WebSocket 连接 */
|
||||||
|
unsigned is_client : 1; /* 是否为客户端连接 */
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 事件类型
|
||||||
|
|
||||||
|
```c
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MG_EV_ERROR, /* 错误 */
|
||||||
|
MG_EV_OPEN, /* 连接建立(仅客户端出站连接) */
|
||||||
|
MG_EV_POLL, /* mg_mgr_poll() 每次迭代触发 */
|
||||||
|
MG_EV_RESOLVE, /* DNS 解析完成 */
|
||||||
|
MG_EV_CONNECT, /* TCP 连接完成 */
|
||||||
|
MG_EV_ACCEPT, /* TCP 新连接接受 */
|
||||||
|
MG_EV_TLS_HS, /* TLS 握手完成 */
|
||||||
|
MG_EV_READ, /* 数据可读 */
|
||||||
|
MG_EV_WRITE, /* 数据可写 */
|
||||||
|
MG_EV_CLOSE, /* 连接关闭 */
|
||||||
|
MG_EV_HTTP_MSG, /* HTTP 请求/响应解析完成 */
|
||||||
|
MG_EV_HTTP_CHUNK, /* HTTP 分块数据 */
|
||||||
|
MG_EV_WS_OPEN, /* WebSocket 握手完成 */
|
||||||
|
MG_EV_WS_MSG, /* WebSocket 消息到达 */
|
||||||
|
MG_EV_WS_CTL, /* WebSocket 控制帧 */
|
||||||
|
MG_EV_MQTT_CMD, /* MQTT 命令 */
|
||||||
|
MG_EV_MQTT_MSG, /* MQTT 消息 */
|
||||||
|
MG_EV_MQTT_OPEN, /* MQTT 连接建立 */
|
||||||
|
MG_EV_SNTP_TIME, /* SNTP 时间 */
|
||||||
|
MG_EV_USER, /* 用户自定义事件起点 */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 接口函数
|
||||||
|
|
||||||
|
### 5.1 生命周期
|
||||||
|
|
||||||
|
```c
|
||||||
|
/** 初始化事件管理器 */
|
||||||
|
void mg_mgr_init(struct mg_mgr *mgr);
|
||||||
|
|
||||||
|
/** 事件循环(每次调用处理一批事件,阻塞不超过 ms 毫秒) */
|
||||||
|
void mg_mgr_poll(struct mg_mgr *mgr, int ms);
|
||||||
|
|
||||||
|
/** 销毁事件管理器(关闭所有连接,释放所有内存) */
|
||||||
|
void mg_mgr_free(struct mg_mgr *mgr);
|
||||||
|
```
|
||||||
|
|
||||||
|
**典型用法**:
|
||||||
|
```c
|
||||||
|
struct mg_mgr mgr;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr);
|
||||||
|
|
||||||
|
/* 注册 HTTP/WS 监听 + 定时器 */
|
||||||
|
// ...
|
||||||
|
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
mg_mgr_poll(&mgr, 50); /* 50ms 超时 */
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 HTTP 服务器
|
||||||
|
|
||||||
|
```c
|
||||||
|
/** 创建 HTTP 监听连接 */
|
||||||
|
struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url,
|
||||||
|
mg_event_handler_t fn, void *fn_data);
|
||||||
|
|
||||||
|
/** 获取 HTTP 请求/响应消息 */
|
||||||
|
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
|
||||||
|
struct mg_http_message *mg_http_parse(const char *buf, size_t len);
|
||||||
|
struct mg_http_message *mg_http_parse2(const char *buf, size_t len);
|
||||||
|
|
||||||
|
/** 响应辅助 */
|
||||||
|
void mg_http_reply(struct mg_connection *c, int status_code,
|
||||||
|
const char *headers, const char *body_fmt, ...);
|
||||||
|
void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
|
const char *root);
|
||||||
|
|
||||||
|
/** 路由匹配 */
|
||||||
|
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
|
||||||
|
bool mg_http_match_uri(const struct mg_http_message *hm, const char *pattern);
|
||||||
|
```
|
||||||
|
|
||||||
|
**REST API 示例**:
|
||||||
|
```c
|
||||||
|
static void fn(struct mg_connection *c, int ev, void *ev_data)
|
||||||
|
{
|
||||||
|
if (ev == MG_EV_HTTP_MSG)
|
||||||
|
{
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *)ev_data;
|
||||||
|
|
||||||
|
if (mg_http_match_uri(hm, "/api/status"))
|
||||||
|
{
|
||||||
|
mg_http_reply(c, 200, "Content-Type: application/json\r\n",
|
||||||
|
"{\"status\":\"ok\"}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mg_http_listen(&mgr, "http://0.0.0.0:8080", fn, NULL);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 WebSocket
|
||||||
|
|
||||||
|
```c
|
||||||
|
/** 升级 HTTP 连接到 WebSocket */
|
||||||
|
void mg_ws_upgrade(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
|
const char *fmt, ...);
|
||||||
|
|
||||||
|
/** 发送 WebSocket 消息 */
|
||||||
|
size_t mg_ws_send(struct mg_connection *c, const void *buf, size_t len, int op);
|
||||||
|
|
||||||
|
/** 格式化发送 WebSocket 消息 */
|
||||||
|
size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...);
|
||||||
|
|
||||||
|
/** 封装非 WS socket 为 WS 帧(用于代理) */
|
||||||
|
size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op,
|
||||||
|
const void *buf, size_t buf_len);
|
||||||
|
|
||||||
|
/** 主动连接 WebSocket 服务器(客户端模式) */
|
||||||
|
struct mg_connection *mg_ws_connect(struct mg_mgr *mgr, const char *url,
|
||||||
|
mg_event_handler_t fn, void *fn_data,
|
||||||
|
const char *fmt, ...);
|
||||||
|
```
|
||||||
|
|
||||||
|
**WebSocket 帧操作码 (op)**:
|
||||||
|
```c
|
||||||
|
#define WEBSOCKET_OP_CONTINUE 0 /* 继续帧 */
|
||||||
|
#define WEBSOCKET_OP_TEXT 1 /* 文本消息 */
|
||||||
|
#define WEBSOCKET_OP_BINARY 2 /* 二进制消息 */
|
||||||
|
#define WEBSOCKET_OP_CLOSE 8 /* 关闭握手 */
|
||||||
|
#define WEBSOCKET_OP_PING 9 /* Ping */
|
||||||
|
#define WEBSOCKET_OP_PONG 10 /* Pong */
|
||||||
|
```
|
||||||
|
|
||||||
|
**WebSocket 服务端示例**:
|
||||||
|
```c
|
||||||
|
static void fn(struct mg_connection *c, int ev, void *ev_data)
|
||||||
|
{
|
||||||
|
if (ev == MG_EV_HTTP_MSG)
|
||||||
|
{
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *)ev_data;
|
||||||
|
|
||||||
|
if (mg_match(hm->uri, mg_str("/ws"), NULL))
|
||||||
|
{
|
||||||
|
mg_ws_upgrade(c, hm, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ev == MG_EV_WS_MSG)
|
||||||
|
{
|
||||||
|
struct mg_ws_message *wm = (struct mg_ws_message *)ev_data;
|
||||||
|
|
||||||
|
/* 回显消息 */
|
||||||
|
mg_ws_send(c, wm->data.buf, wm->data.len, WEBSOCKET_OP_TEXT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.4 定时器
|
||||||
|
|
||||||
|
```c
|
||||||
|
/** 添加一次性定时器 */
|
||||||
|
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
|
||||||
|
unsigned flags, void (*fn)(void *), void *arg);
|
||||||
|
|
||||||
|
/** 定时器标志 */
|
||||||
|
#define MG_TIMER_REPEAT 1 /* 周期性定时器 */
|
||||||
|
#define MG_TIMER_RUN_NOW 2 /* 立即运行一次 */
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.5 通用收发
|
||||||
|
|
||||||
|
```c
|
||||||
|
/** 发送原始数据 */
|
||||||
|
bool mg_send(struct mg_connection *c, const void *buf, size_t len);
|
||||||
|
|
||||||
|
/** 格式化发送 */
|
||||||
|
size_t mg_printf(struct mg_connection *c, const char *fmt, ...);
|
||||||
|
size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap);
|
||||||
|
|
||||||
|
/** 关闭连接 */
|
||||||
|
void mg_close_conn(struct mg_connection *c);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 使用约束与注意事项
|
||||||
|
|
||||||
|
### 6.1 线程安全
|
||||||
|
- `mg_mgr_poll()` 必须在**单线程**中调用
|
||||||
|
- 如需要多线程,通过 `mg_wakeup()` 向事件循环投递消息
|
||||||
|
|
||||||
|
### 6.2 内存管理
|
||||||
|
- 所有通过 `mg_http_listen()` 创建的连接在 `mg_mgr_free()` 时自动关闭释放
|
||||||
|
- `mg_http_message` 中的 `hm->body`、`hm->query` 等字段为协议栈内部缓冲区,**不可手动 free**
|
||||||
|
|
||||||
|
### 6.3 与 RTU 的集成方式
|
||||||
|
- `libweb_server` 作为 RTU 的独立线程模块,在线程内创建自己的 `mg_mgr`
|
||||||
|
- 通过 X-Macro 框架的定时器触发 `mg_mgr_poll()`
|
||||||
|
- HTTP 路由中使用 `dc_get_signal_val()` 获取实时数据,`dc_set_out_signal_val()` 发布前端写入的数据
|
||||||
|
|
||||||
|
### 6.4 日志说明
|
||||||
|
mongoose 内部使用 `printf` 向 stderr 输出日志(`MG_ENABLE_LOG=1`)。在 RTU 装置上 stderr 通常重定向到 `/dev/null` 或 syslog。如需捕获 mongoose 日志,可定义 `MG_ENABLE_CUSTOM_LOG=1` 并提供自定义 `MG_LOG()` 宏。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 依赖关系
|
||||||
|
|
||||||
|
### 7.1 本模块依赖
|
||||||
|
- **无**(mongoose 是纯 C、零依赖的单文件库,直接使用 Linux socket API)
|
||||||
|
|
||||||
|
### 7.2 依赖本模块的模块
|
||||||
|
|
||||||
|
| 模块 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `libweb_server` (system/) | HTTP REST API + WebSocket 通信 |
|
||||||
|
|
||||||
|
### 7.3 模块依赖图
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────┐
|
||||||
|
│ libweb_server │ (RTU 线程模块)
|
||||||
|
│ REST API + WS │
|
||||||
|
└────────┬─────────┘
|
||||||
|
│ myMongoose.h
|
||||||
|
▼
|
||||||
|
┌──────────────────┐
|
||||||
|
│ libmongoose │ (纯 C 协议栈)
|
||||||
|
│ HTTP + WebSocket │
|
||||||
|
└────────┬─────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Linux Socket API + epoll
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 编译与构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 编译 libmongoose.a
|
||||||
|
cd release/src/protocol/libmongoose
|
||||||
|
make -f makefile all # x86
|
||||||
|
make -f makefile CROSS=arm all # ARM
|
||||||
|
|
||||||
|
# 参与全量构建
|
||||||
|
cd release && ./build.sh # x86
|
||||||
|
cd release && ./build.sh arm # ARM
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 许可证
|
||||||
|
|
||||||
|
Mongoose 使用 **MIT 许可证**(商业友好)。完整许可文本见 `mongoose.h` 头部注释。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 参考链接
|
||||||
|
|
||||||
|
- [Mongoose 官方文档](https://mongoose.ws/documentation/)
|
||||||
|
- [Mongoose GitHub](https://github.com/cesanta/mongoose)
|
||||||
|
- [WebSocket RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455)
|
||||||
|
- [HTTP/1.1 RFC 7230](https://datatracker.ietf.org/doc/html/rfc7230)
|
||||||
|
|
@ -0,0 +1,531 @@
|
||||||
|
# API-RTU — RTU 主程序模块
|
||||||
|
|
||||||
|
> 版本: v3.0
|
||||||
|
> 日期: 2026-07-08
|
||||||
|
> 来源: 基于原工程 e173631 重构,升级为 X-Macro 框架
|
||||||
|
> 状态: 已实现,x86 + ARM 双平台编译通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 模块概述
|
||||||
|
|
||||||
|
RTU 模块是 RTU_ALL_AI 项目的**主程序入口和系统管理框架**,负责:
|
||||||
|
|
||||||
|
| 功能 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 模块生命周期管理 | X-Macro 驱动的线程模块注册、初始化(init1→init2→start→stop) |
|
||||||
|
| 配置文件启停控制 | 通过 `config/SYSTEM/app_config.json` 按需启用/禁用模块 |
|
||||||
|
| 三级定时器 | 为每个模块自动创建 10ms / 100ms / 1000ms 三级周期性定时器 |
|
||||||
|
| 事件驱动循环 | 每个模块通过 `stru_task_event_t` 等待定时器事件和自己业务事件 |
|
||||||
|
| 优雅关闭 | `EV_STOP` 信号 + `pthread_join` 确保所有线程安全退出 |
|
||||||
|
| 命令行交互 | 内置 `version` / `uptime` 命令,各模块可注册自定义命令 |
|
||||||
|
|
||||||
|
### 核心设计理念
|
||||||
|
|
||||||
|
**新增模块只需修改 [app_modules.h](../../../src/system/inc/app_modules.h) 中一行宏**,无需改动框架代码。模块的枚举值、`extern` 声明、`g_vec_app` 初始化列表全部由 X-Macro 自动展开生成,消除了传统运行时注册的样板代码。
|
||||||
|
|
||||||
|
### 与原始框架(e173631)对比
|
||||||
|
|
||||||
|
| 方面 | 原始(运行时注册) | 升级后(X-Macro) |
|
||||||
|
|------|---------------|--------------|
|
||||||
|
| 模块注册 | `app_module_register()` 运行时调用 | `APP_MODULE()` 编译时宏,零运行时开销 |
|
||||||
|
| 启停控制 | 无 | `app_config.json` 按需启用 |
|
||||||
|
| 定时器 | 无(模块自行实现) | 自动三级定时器 |
|
||||||
|
| 停止 | `app_modules_stop/join` 独立函数 | `app_sys_stop()` 一体化 |
|
||||||
|
| 新增模块 | 修改 main.c + app_modules.c | 仅修改 app_modules.h 一行 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/system/
|
||||||
|
├── inc/
|
||||||
|
│ ├── app_modules.h # 模块注册表(X-Macro 中心列表)
|
||||||
|
│ └── mySystem.h # 公共定义(stru_app / enum_app / API 声明)
|
||||||
|
└── RTU/
|
||||||
|
└── src/
|
||||||
|
├── main.c # 主程序入口(信号处理 → 初始化 → 主循环 → 关闭)
|
||||||
|
└── app_sys.cpp # 系统管理实现(配置解析 / 模块初始化 / 停止)
|
||||||
|
|
||||||
|
config/
|
||||||
|
└── SYSTEM/
|
||||||
|
└── app_config.json # 运行时模块启停配置(部署到 RK 装置)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
app_config.json → cJSON 解析
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
g_enabled_apps (std::set)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
app_modules.h ──X-Macro──▶ g_vec_app (std::vector<stru_app>)
|
||||||
|
│ │
|
||||||
|
│ ┌─────────┼─────────┐
|
||||||
|
│ ▼ ▼ ▼
|
||||||
|
│ app_init1 app_init2 创建定时器
|
||||||
|
│ │ │ (10/100/1000ms)
|
||||||
|
│ │ │ │
|
||||||
|
│ ▼ ▼ ▼
|
||||||
|
│ ┌────────────────────────────┐
|
||||||
|
└── is_app_enabled( ) ──▶ pthread_create() │
|
||||||
|
│ 独立线程运行 fun_cb │
|
||||||
|
└──────────┬─────────┘
|
||||||
|
│
|
||||||
|
┌──────────▼──────────┐
|
||||||
|
│ task_event_wait() │
|
||||||
|
│ ← EV_TIMER1/2/3 │
|
||||||
|
│ ← EV_STOP │
|
||||||
|
└──────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 启动流程
|
||||||
|
|
||||||
|
```
|
||||||
|
main()
|
||||||
|
│
|
||||||
|
├── signal(SIGINT/SIGTERM) → sig_handler()
|
||||||
|
│
|
||||||
|
├── log_init() / dc_init()
|
||||||
|
│
|
||||||
|
├── app_sys_init()
|
||||||
|
│ ├── [1] app_config_load()
|
||||||
|
│ │ ├── get_system_config_path()
|
||||||
|
│ │ │ ├── WORK_PATH 环境变量(RK 装置)
|
||||||
|
│ │ │ └── func_proc_self_dir()(本地调试)
|
||||||
|
│ │ └── cJSON_Parse(app_config.json)
|
||||||
|
│ │ → g_enabled_apps
|
||||||
|
│ │
|
||||||
|
│ ├── [2] app_init()
|
||||||
|
│ │ ├── init1: 遍历 g_vec_app → 调用各模块 init1()
|
||||||
|
│ │ ├── init2: 遍历 g_vec_app → 调用各模块 init2()
|
||||||
|
│ │ └── start: 遍历 g_vec_app
|
||||||
|
│ │ ├── task_event_create()
|
||||||
|
│ │ ├── app_timer_create() × 3
|
||||||
|
│ │ └── pthread_create() → fun_cb
|
||||||
|
│ │
|
||||||
|
│ └── return 0
|
||||||
|
│
|
||||||
|
├── while(g_running) → 命令行输入循环
|
||||||
|
│
|
||||||
|
└── app_sys_stop() ← Ctrl+C
|
||||||
|
├── EV_STOP → 各模块
|
||||||
|
└── pthread_join → 各线程
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 核心数据结构
|
||||||
|
|
||||||
|
### 4.1 模块枚举(enum_app)
|
||||||
|
|
||||||
|
由 `app_modules.h` X-Macro 生成,无需手动定义:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* app_modules.h */
|
||||||
|
APP_MODULE(MODBUS_M, modbus_m, app_modbus_m_init1, app_modbus_m_init2, app_modbus_m)
|
||||||
|
// 新增模块只需追加一行
|
||||||
|
|
||||||
|
/* mySystem.h 自动展开为 */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ENUM_APP_MODBUS_M, // ← 自动生成
|
||||||
|
// ENUM_APP_XXX, // ← 新增模块的枚举值
|
||||||
|
ENUM_APP_MAX
|
||||||
|
} enum_app;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 模块上下文(stru_app)
|
||||||
|
|
||||||
|
每个线程模块一个实例,由 `app_sys.cpp` 自动创建和管理。
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *name; /**< 模块名称,如 "app_modbus_m" */
|
||||||
|
uint32_t app_id; /**< 模块 ID(枚举值) */
|
||||||
|
app_init_fn app_init1; /**< 阶段 1 初始化(信号注册前) */
|
||||||
|
app_init_fn app_init2; /**< 阶段 2 初始化(信号注册后) */
|
||||||
|
app_func_fn fun_cb; /**< 主循环入口(独立线程) */
|
||||||
|
void *arg; /**< 线程参数(指向自身) */
|
||||||
|
stru_task_event_t p_event; /**< 事件句柄(定时器 + 停止信号) */
|
||||||
|
stru_rtx_data rtx; /**< 收发数据缓冲区 */
|
||||||
|
uint32_t timer_arg[TIMER_NUM]; /**< 定时器参数([app_id:8, idx:8]) */
|
||||||
|
stru_task_timer_t p_timer[TIMER_NUM]; /**< 三级定时器句柄 */
|
||||||
|
uint32_t run_cnt; /**< 运行计数 */
|
||||||
|
} stru_app;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 事件定义
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define TIMER_NUM 3 /* 定时器级数 */
|
||||||
|
#define EV_TIMER1 (0x01 << 0) /* 10ms 定时器事件 */
|
||||||
|
#define EV_TIMER2 (0x01 << 1) /* 100ms 定时器事件 */
|
||||||
|
#define EV_TIMER3 (0x01 << 2) /* 1000ms 定时器事件 */
|
||||||
|
#define EV_STOP (0x80) /* 模块停止信号 */
|
||||||
|
#define MSG_SIZE 2048 /* 收发缓冲区大小 */
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 接口函数
|
||||||
|
|
||||||
|
### 5.1 app_sys_init()
|
||||||
|
|
||||||
|
```c
|
||||||
|
int app_sys_init(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能**: 初始化所有 X-Macro 注册的模块。包括加载 `app_config.json`、执行各模块 init1/init2、创建事件 + 定时器 + 启动线程。
|
||||||
|
|
||||||
|
**参数**: 无
|
||||||
|
|
||||||
|
**返回值**:
|
||||||
|
|
||||||
|
| 值 | 说明 |
|
||||||
|
|----|------|
|
||||||
|
| 0 | 初始化成功 |
|
||||||
|
| -1 | 初始化失败(配置文件解析/模块 init/线程创建) |
|
||||||
|
|
||||||
|
**调用位置**: `main.c` 中 `dc_init()` 之后、主循环之前。
|
||||||
|
|
||||||
|
### 5.2 app_sys_stop()
|
||||||
|
|
||||||
|
```c
|
||||||
|
void app_sys_stop(void);
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能**: 停止所有模块。先向每个模块发送 `EV_STOP` 信号通知其退出主循环,然后 `pthread_join` 等待所有线程安全退出。
|
||||||
|
|
||||||
|
**调用位置**: `main.c` 中收到 SIGINT/SIGTERM 或 `Ctrl+D` 后、`dc_cleanup()` 之前。
|
||||||
|
|
||||||
|
### 5.3 app_get_ptr()
|
||||||
|
|
||||||
|
```c
|
||||||
|
stru_app *app_get_ptr(uint32_t app_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能**: 根据模块 ID 获取 `stru_app` 指针。供模块内部或外部代码访问其他模块数据。
|
||||||
|
|
||||||
|
**参数**:
|
||||||
|
|
||||||
|
| 参数 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| app_id | 模块 ID(`enum_app` 枚举值) |
|
||||||
|
|
||||||
|
**返回值**: 有效模块指针,超出范围返回 `NULL`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 配置文件
|
||||||
|
|
||||||
|
### 6.1 app_config.json
|
||||||
|
|
||||||
|
位置:`config/SYSTEM/app_config.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"apps": [
|
||||||
|
{"name": "app_modbus_m", "enable": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**字段说明**:
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `apps` | Array | 模块配置列表 |
|
||||||
|
| `name` | String | 模块名称,格式为 `app_<配置名>`,与 `app_modules.h` 中 cfg_name 对应 |
|
||||||
|
| `enable` | Boolean | `true`=启用,`false`=禁用 |
|
||||||
|
|
||||||
|
**配置路径优先级**:
|
||||||
|
1. `WORK_PATH` 环境变量 → `$WORK_PATH/config/SYSTEM/`(RK3568 装置运行时由启动脚本设定)
|
||||||
|
2. 可执行文件所在目录 → `<exe_dir>/config/SYSTEM/`(x86 本地调试用 fallback)
|
||||||
|
|
||||||
|
**部署约定**: `config/` 整个目录直接同步到 RK 装置可执行文件的同级目录下。
|
||||||
|
|
||||||
|
### 6.2 配置文件缺失
|
||||||
|
|
||||||
|
若 `app_config.json` 未找到,框架**默认启用所有模块**,不视为错误。日志输出:
|
||||||
|
```
|
||||||
|
app_config.json not found, enabling all modules by default
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 新增模块步骤
|
||||||
|
|
||||||
|
只需三步:
|
||||||
|
|
||||||
|
### Step 1: 在 `app_modules.h` 追加一行
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* src/system/inc/app_modules.h */
|
||||||
|
APP_MODULE(NEW_MODULE, new_module, app_new_module_init1, app_new_module_init2, app_new_module)
|
||||||
|
```
|
||||||
|
|
||||||
|
**宏参数说明**:
|
||||||
|
|
||||||
|
| 参数 | 示例 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `name` | `NEW_MODULE` | 枚举后缀,生成 `ENUM_APP_NEW_MODULE` |
|
||||||
|
| `cfg_name` | `new_module` | 配置名,生成名称 `"app_new_module"` |
|
||||||
|
| `init1` | `app_new_module_init1` | 阶段 1 初始化函数(信号注册前) |
|
||||||
|
| `init2` | `app_new_module_init2` | 阶段 2 初始化函数(信号注册后),可为 `NULL` |
|
||||||
|
| `func` | `app_new_module` | 主循环入口函数(独立线程) |
|
||||||
|
|
||||||
|
### Step 2: 实现模块代码
|
||||||
|
|
||||||
|
在 `src/system/<模块名>/src/` 下实现四个函数:
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* init1: 信号注册前(可解析配置文件) */
|
||||||
|
int app_new_module_init1(void *arg)
|
||||||
|
{
|
||||||
|
stru_app *p_app = (stru_app *)arg;
|
||||||
|
// 1. 注册输出信号到 datacenter
|
||||||
|
// 2. 解析模块配置文件(XML/JSON)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init2: 信号注册后(注册遥控/遥调信号及回调) */
|
||||||
|
int app_new_module_init2(void *arg)
|
||||||
|
{
|
||||||
|
// 注册 yk/ao 信号及其回调函数
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主循环: 事件驱动 */
|
||||||
|
void *app_new_module(void *arg)
|
||||||
|
{
|
||||||
|
stru_app *p_app = (stru_app *)arg;
|
||||||
|
uint32_t event;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
task_event_wait(p_app->p_event, &event, 0);
|
||||||
|
|
||||||
|
if (event & EV_STOP)
|
||||||
|
{
|
||||||
|
break; /* 退出循环 */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event & EV_TIMER1)
|
||||||
|
{
|
||||||
|
/* 10ms 周期任务 */
|
||||||
|
p_app->run_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... 其他事件处理 ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: 更新构建系统
|
||||||
|
|
||||||
|
在 `release/src/system/` 下创建模块子目录和 makefile,并在 `release/src/system/makefile` 中注册:
|
||||||
|
|
||||||
|
```makefile
|
||||||
|
SUBDIRS := ./new_module ./libmodbus_m ./RTU
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: 更新配置文件
|
||||||
|
|
||||||
|
在 `config/SYSTEM/app_config.json` 中添加模块条目:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"name": "app_new_module", "enable": true}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 模块编写规范
|
||||||
|
|
||||||
|
### 8.1 主循环模板
|
||||||
|
|
||||||
|
```c
|
||||||
|
void *app_xxx(void *arg)
|
||||||
|
{
|
||||||
|
stru_app *p_app = (stru_app *)arg;
|
||||||
|
if (!p_app || !p_app->p_event)
|
||||||
|
{
|
||||||
|
LOG_E("app_xxx arg null");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t event;
|
||||||
|
uint32_t cnt_10ms = 0;
|
||||||
|
uint32_t cnt_100ms = 0;
|
||||||
|
uint32_t cnt_1s = 0;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
task_event_wait(p_app->p_event, &event, 0);
|
||||||
|
|
||||||
|
if (event & EV_STOP)
|
||||||
|
{
|
||||||
|
LOG_I("app_xxx stopping, total runs: 10ms=%u 100ms=%u 1s=%u",
|
||||||
|
cnt_10ms, cnt_100ms, cnt_1s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event & EV_TIMER1)
|
||||||
|
{
|
||||||
|
cnt_10ms++;
|
||||||
|
/* 10ms 周期处理 */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event & EV_TIMER2)
|
||||||
|
{
|
||||||
|
cnt_100ms++;
|
||||||
|
/* 100ms 周期处理 */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event & EV_TIMER3)
|
||||||
|
{
|
||||||
|
cnt_1s++;
|
||||||
|
/* 1000ms 周期处理 */
|
||||||
|
p_app->run_cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 事件处理顺序
|
||||||
|
|
||||||
|
事件处理遵循 **先停止后业务** 原则:`EV_STOP` 检查放在最前面,确保收到停止信号后立刻退出。
|
||||||
|
|
||||||
|
### 8.3 配置路径约定
|
||||||
|
|
||||||
|
模块内部读取配置文件时,遵循与框架一致的路径约定(参考 `modbus_m.cpp` 的 `get_base_path()`):
|
||||||
|
|
||||||
|
```c
|
||||||
|
LOCAL std::string get_base_path()
|
||||||
|
{
|
||||||
|
char buf[512] = {0};
|
||||||
|
if (0 == func_get_work_path(buf, sizeof(buf))) /* 优先 WORK_PATH */
|
||||||
|
{
|
||||||
|
return std::string(buf) + "/";
|
||||||
|
}
|
||||||
|
if (0 != func_proc_self_dir(buf, sizeof(buf))) /* fallback 可执行文件目录 */
|
||||||
|
{
|
||||||
|
LOG_E("get_base_path failed");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return std::string(buf) + "/";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 编译与构建
|
||||||
|
|
||||||
|
### 9.1 编译 RTU 可执行文件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# x86 本地编译
|
||||||
|
cd release && ./build.sh
|
||||||
|
|
||||||
|
# ARM 交叉编译(RK3568)
|
||||||
|
cd release && ./build.sh arm
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 产物
|
||||||
|
|
||||||
|
| 平台 | 路径 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| x86 | `release/x86/exe/RTU` | ELF 64-bit x86-64, with debug_info |
|
||||||
|
| ARM | `release/arm/exe/RTU` | ELF 64-bit ARM aarch64, with debug_info |
|
||||||
|
|
||||||
|
### 9.3 链接依赖
|
||||||
|
|
||||||
|
RTU 可执行文件链接以下库:
|
||||||
|
|
||||||
|
```
|
||||||
|
libmodbus_m libmodbus lib60870
|
||||||
|
libdatacenter libxml libcmd libtask
|
||||||
|
liblog libfunc libmy_xxhash libcJSON libmd5 libcomm
|
||||||
|
libpthread libstdc++
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 依赖关系
|
||||||
|
|
||||||
|
### 10.1 依赖的库模块
|
||||||
|
|
||||||
|
| 模块 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `liblog` | 日志输出 |
|
||||||
|
| `libfunc` | 文件操作、环境变量路径、字符串处理 |
|
||||||
|
| `libdatacenter` | 信号数据中心(dc_init/dc_cleanup) |
|
||||||
|
| `libtask` | 事件机制、定时器、延迟 |
|
||||||
|
| `libcmd` | 命令行注册与交互 |
|
||||||
|
| `libcJSON` | app_config.json 解析 |
|
||||||
|
| `libmodbus` | 版本号显示 |
|
||||||
|
| `libmodbus_m` | Modbus 主站线程模块 |
|
||||||
|
|
||||||
|
### 10.2 被依赖
|
||||||
|
|
||||||
|
RTU 是最顶层模块,不被其他模块依赖。
|
||||||
|
|
||||||
|
### 10.3 模块间依赖图
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ RTU │
|
||||||
|
│ (main.c + app_sys.cpp) │
|
||||||
|
│ │
|
||||||
|
│ 信号 → libtask (事件/定时器) │
|
||||||
|
│ 日志 → liblog │
|
||||||
|
│ 配置 → libcJSON + libfunc (文件/路径) │
|
||||||
|
│ 命令 → libcmd (linenoise) │
|
||||||
|
└──────┬──────────────────────────────────────────┘
|
||||||
|
│ app_modules.h X-Macro
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||||
|
│ libmodbus_m │ │ libcom_decode│ │ libweb_server│
|
||||||
|
│ (已实现) │ │ (预留) │ │ (预留) │
|
||||||
|
└──────┬───────┘ └──────────────┘ └──────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────┐
|
||||||
|
│ libdatacenter│
|
||||||
|
│ libmodbus │
|
||||||
|
│ lib60870 │
|
||||||
|
└──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. 命令行参考
|
||||||
|
|
||||||
|
| 命令 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `help` | 显示所有已注册命令(libcmd 内置) |
|
||||||
|
| `version` | 显示固件版本 + libmodbus 版本 |
|
||||||
|
| `uptime` | 显示运行状态 |
|
||||||
|
| `modbus_m info` | 显示 Modbus 主站通道和点表信息 |
|
||||||
|
| `datacenter stat` | 显示数据中心统计(libdatacenter 内置) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. 变更历史
|
||||||
|
|
||||||
|
| 日期 | 版本 | 变更 |
|
||||||
|
|------|------|------|
|
||||||
|
| 2026-07-08 | v3.0 | 配置文件独立化(`config/` 目录)、添加 `app_sys_stop()`、代码风格统一 |
|
||||||
|
| 2026-07-08 | v2.0 | X-Macro 框架重构 + `app_config.json` 启停控制 + 三级定时器 |
|
||||||
|
| 2026-07-08 | v1.0 | 原始运行时注册框架(`app_modules.c` + `main.c`) |
|
||||||
Loading…
Reference in New Issue