# 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`) |