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