RTU_ALL_AI/docs/system/RTU/API-RTU.md

532 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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