16 KiB
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 中一行宏,无需改动框架代码。模块的枚举值、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 生成,无需手动定义:
/* 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 自动创建和管理。
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 事件定义
#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()
int app_sys_init(void);
功能: 初始化所有 X-Macro 注册的模块。包括加载 app_config.json、执行各模块 init1/init2、创建事件 + 定时器 + 启动线程。
参数: 无
返回值:
| 值 | 说明 |
|---|---|
| 0 | 初始化成功 |
| -1 | 初始化失败(配置文件解析/模块 init/线程创建) |
调用位置: main.c 中 dc_init() 之后、主循环之前。
5.2 app_sys_stop()
void app_sys_stop(void);
功能: 停止所有模块。先向每个模块发送 EV_STOP 信号通知其退出主循环,然后 pthread_join 等待所有线程安全退出。
调用位置: main.c 中收到 SIGINT/SIGTERM 或 Ctrl+D 后、dc_cleanup() 之前。
5.3 app_get_ptr()
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
{
"apps": [
{"name": "app_modbus_m", "enable": true}
]
}
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
apps |
Array | 模块配置列表 |
name |
String | 模块名称,格式为 app_<配置名>,与 app_modules.h 中 cfg_name 对应 |
enable |
Boolean | true=启用,false=禁用 |
配置路径优先级:
WORK_PATH环境变量 →$WORK_PATH/config/SYSTEM/(RK3568 装置运行时由启动脚本设定)- 可执行文件所在目录 →
<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 追加一行
/* 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/ 下实现四个函数:
/* 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 中注册:
SUBDIRS := ./new_module ./libmodbus_m ./RTU
Step 4: 更新配置文件
在 config/SYSTEM/app_config.json 中添加模块条目:
{"name": "app_new_module", "enable": true}
8. 模块编写规范
8.1 主循环模板
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()):
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 可执行文件
# 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) |