diff --git a/COMPLETION.md b/COMPLETION.md index 8d20a66..7a35a12 100644 --- a/COMPLETION.md +++ b/COMPLETION.md @@ -41,7 +41,8 @@ | Modbus 主站管理 | `libmodbus_m`(原工程自研模块,导入+适配) | ✅ | | 通讯通道解码 | `libcom_decode` | ⬜ | | Web 管理服务 | `libweb_server` | ⬜ | -| 主程序入口 | `RTU`(系统模块框架 + app_modules.c + main.c) | ✅ | +| Mongoose 网络库 | `libmongoose`(开源库 v7.22,导入+适配) | ✅ | +| 主程序入口 | `RTU`(X-Macro 框架 + app_config.json 启停控制 + 独立 config/ 目录) | ✅ | ## 阶段 3:系统集成 diff --git a/docs/README.md b/docs/README.md index a2c15ce..a238626 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,7 +24,20 @@ | 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 通讯抽象层 | -### 通讯与协议库 (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 主站管理(通道/点表/轮询/写入) | ``` ┌──────────┐ diff --git a/docs/protocol/libmongoose/API-libmongoose.md b/docs/protocol/libmongoose/API-libmongoose.md new file mode 100644 index 0000000..07dab86 --- /dev/null +++ b/docs/protocol/libmongoose/API-libmongoose.md @@ -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) diff --git a/docs/system/RTU/API-RTU.md b/docs/system/RTU/API-RTU.md new file mode 100644 index 0000000..0b17f9b --- /dev/null +++ b/docs/system/RTU/API-RTU.md @@ -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) + │ │ + │ ┌─────────┼─────────┐ + │ ▼ ▼ ▼ + │ 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. 可执行文件所在目录 → `/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`) |