# RTU_ALL_AI 模块功能规范（基于 RTU 原工程分析）

> 生成时间: 2026-07-07  
> 原则: 每条功能点均来自 RTU 原工程对应模块的完整分析，不遗漏不编造

---

## 1. myBase.h

### 功能点
| 序号 | 功能 | 说明 |
|------|------|------|
| B01 | 颜色宏 | COLOR_RESET/RED/GREEN/YELLOW/BLUE/MAGENTA/CYAN/WHITE |
| B02 | 文件名缩写宏 | `__SHORT_FILE__` 去除路径前缀 |
| B03 | 基础日志宏 | `MY_LOG(color,level,fmt,...)` / `MY_LOG_I` / `MY_LOG_E` |
| B04 | LOCAL 宏 | `#define LOCAL static` |
| B05 | 字符串类型 | `S_STR[64]` / `L_STR[128]` |
| B06 | uint16 大小端转换 | `MY_GET/SET_SHORT_BIGEND/LITTLEEND` |
| B07 | int32 大小端转换 | `MY_GET/SET_INT_BIGEND/LITTLEEND` |
| B08 | uint64 大小端转换 | `MY_GET/SET_LONG_BIGEND/LITTLEEND` |
| B09 | 类型级存/取宏 | `MY_GET/SET_DATA_WITH_TYPE` |
| B10 | 数据类型常量 | DATA_TYPE_B/S8/U8/.../STR (22种) |

---

## 2. liblist 侵入式链表

### 功能点（Linux 内核标准 list.h 精简版）
| 序号 | 功能 | API |
|------|------|------|
| L01 | 链表初始化 | `LIST_HEAD` / `INIT_LIST_HEAD` |
| L02 | 头插 | `list_add` |
| L03 | 尾插 | `list_add_tail` |
| L04 | 删除 | `list_del` / `list_del_init` |
| L05 | 移动 | `list_move` / `list_move_tail` |
| L06 | 替换 | `list_replace` / `list_replace_init` |
| L07 | 判空 | `list_empty` / `list_is_linked` |
| L08 | 节点→宿主转换 | `list_entry` |
| L09 | 取首/尾元素 | `list_first_entry` / `list_last_entry` / `list_first_entry_or_null` |
| L10 | 遍历(安全) | `list_for_each` / `list_for_each_entry` / `*_safe` |
| L11 | 拼接 | `list_splice_init` |

### 关键约束
- 纯头文件，无运行时依赖
- 所有 API 必须兼容 C 和 C++（变量名不用 `new`/`class`；`typeof`→`__typeof__`）

---

## 3. liblog 异步日志系统

### 功能点（636 行，完整复刻 RTU 原实现）
| 序号 | 功能 | 说明 |
|------|------|------|
| G01 | 异步日志线程 | `log_worker_thread` 独立线程处理日志写入 |
| G02 | 懒初始化 | `pthread_once` 实现首次调用自动初始化 |
| G03 | 消息池预分配 | `calloc(LOG_QUEUE_SIZE)` 预分配消息池，避免频繁 malloc/free |
| G04 | 消息池取还 | `get_message_from_pool` / `return_message_to_pool` |
| G05 | 队列满阻塞 | `pthread_cond_wait` 等待 not_full，不丢弃日志 |
| G06 | 批量消费 | 一次最多取 `LOG_BATCH_SIZE`=100 条，批量写入 |
| G07 | 控制台输出 | 带颜色 ANSI 码输出 |
| G08 | 文件输出 | 带时间戳写入日志文件 |
| G09 | 文件自动创建 | `init_log_directory` 自动创建 logs/ 目录 |
| G10 | 文件按大小轮转 | 超过 10MB 自动轮转，保留 5 个历史文件 |
| G11 | 文件 setlinebuf | 行缓冲模式 |
| G12 | 时间戳格式化 | 精确到毫秒 `YYYY-MM-DD HH:MM:SS.mmm` |
| G13 | 文件信息格式化 | `文件名/函数名:行号` 格式 |
| G14 | LOG_I/LOG_E 宏 | 便捷宏 |
| G15 | 开关控制 | `log_set_file_enabled` / `log_set_console_enabled` |
| G16 | flush | 等待队列清空 |
| G17 | cleanup | 停止线程、关闭文件、释放消息池 |

### 程序运行流程
```
首次 log_prt() 调用
  → pthread_once → log_global_init
    → init_message_pool (预分配 LOG_QUEUE_SIZE 个消息结构)
    → logger.running = 1
    → pthread_create(&log_worker_thread)
    
每次 log_prt()
  → get_message_from_pool()
  → 填充 level/timestamp/file_info/content
  → enqueue_log()
      → 队列满则 pthread_cond_wait(&not_full) 阻塞等待
      → 尾插队列
      → pthread_cond_signal(&not_empty)

log_worker_thread 主循环
  → 批量取消息 (最多 LOG_BATCH_SIZE)
  → write_log_batch_array()
      → 控制台: printf 带颜色
      → 文件: fopen/fprintf, 超过 10MB 触发 rotate_log_files
  → return_message_to_pool() 归还每个消息
```

---

## 4. libfunc 基础工具函数

### 功能点（1783 行，完整复刻 RTU 原实现）

#### 4.1 校验计算 (6 个函数)
| 序号 | 函数 | 说明 |
|------|------|------|
| F01 | `func_cal_sum_byte` | 8位校验和 |
| F02 | `func_cal_sum_word` | 16位校验和 |
| F03 | `func_cal_crc16` | CRC16(Modbus 多项式) |
| F04 | `func_cal_crc32` | CRC32(标准多项式) |
| F05 | `func_cal_file_crc16` | 文件 CRC16 (4K 缓冲区) |
| F06 | `func_cal_file_crc32` | 文件 CRC32 (4K 缓冲区) |

#### 4.2 字符串转换 (10 个函数)
| 序号 | 函数 | 说明 |
|------|------|------|
| F07 | `func_get_hex_from_ASCLL` | 单字符hex→数值 |
| F08 | `func_get_dec_from_ASCLL` | 单字符dec→数值 |
| F09 | `func_string_hex_to_dec` | hex字符串→int |
| F10 | `func_string_dec_to_dec` | dec字符串→int |
| F11 | `func_string_hex_to_buf` | hex字符串→字节缓冲 |
| F12 | `func_dec_to_string` | int→dec字符串(支持宽度填充) |
| F13 | `func_hex_to_string` | int→hex字符串(支持宽度填充) |
| F14 | `func_float_to_string` | float→字符串(支持宽度) |
| F15 | `func_buf_to_string` | 字节缓冲→hex字符串 |
| F16 | `func_string_to_argv` | 字符串→argc/argv分割 |

#### 4.3 时间处理 (6 个函数)
| 序号 | 函数 | 说明 |
|------|------|------|
| F17 | `func_sys_time_to_string` | 系统时间结构体→字符串 |
| F18 | `func_string_to_sys_time` | 字符串→系统时间结构体 |
| F19 | `func_get_time` | 获取系统时间(支持 sys/cal 两种) |
| F20 | `func_set_time` | 设置系统时间 |
| F21 | `func_time_sys_to_cal` | 系统时间→日历时间 |
| F22 | `func_time_cal_to_sys` | 日历时间→系统时间 |

#### 4.4 文件目录操作 (11 个函数)
| 序号 | 函数 | 说明 |
|------|------|------|
| F23 | `func_dir_exist` | 目录是否存在 |
| F24 | `func_make_dirs` | 创建目录(递归) |
| F25 | `func_del_dirs` | 删除目录(递归) |
| F26 | `func_del_dirs_by_cmd` | 删除目录(系统命令) |
| F27 | `func_file_exist` | 文件是否存在 |
| F28 | `func_get_file_size` | 获取文件大小 |
| F29 | `func_read_file_to_buf` | 读文件到预分配buf |
| F30 | `func_read_file_alloc_buf` | 读文件到动态分配buf |
| F31 | `func_write_buf_to_file` | 写buf到文件 |
| F32 | `func_read_file_tip_info` | 读结构体格式文件信息(含CRC校验) |
| F33 | `func_make_dir`(内部) | 内部单级目录创建 |

#### 4.5 进程管理 (9 个函数)
| 序号 | 函数 | 说明 |
|------|------|------|
| F34 | `func_get_process_self_name` | 获取本进程名 |
| F35 | `func_get_process_name_by_pid` | PID→进程名 |
| F36 | `func_get_process_pid` | 获取本进程PID |
| F37 | `func_get_process_pid_by_name` | 进程名→PID |
| F38 | `func_process_exist_by_name` | 按名判断进程存在 |
| F39 | `func_process_exist_by_pid` | 按PID判断进程存在 |
| F40 | `func_get_process_path_by_pid` | PID→进程路径 |
| F41 | `func_get_process_self_path` | 本进程路径 |
| F42 | `func_get_process_self_dir` | 本进程目录 |

#### 4.6 环境变量路径 (12 个函数)
| 序号 | 函数 | 说明 |
|------|------|------|
| F43 | `func_get_env_value`(内部) | 读环境变量 |
| F44 | `func_get_env_path`(内部) | 读环境变量路径 |
| F45 | `func_get_work_path` | WORK_PATH |
| F46 | `func_get_sys_cfg_path` | SYS_CFG_PATH |
| F47 | `func_get_app_root_path` | APP_ROOT_PATH |
| F48 | `func_get_his_root_path` | HIS_ROOT_PATH |
| F49 | `func_get_log_root_path` | LOG_ROOT_PATH |
| F50 | `func_get_dbc_root_path` | DBC_ROOT_PATH |
| F51 | `func_get_shell_path` | SHELL_PATH |
| F52 | `func_get_update_path` | UPDATE_PATH |
| F53 | `func_get_app_path` | 应用路径(拼接后) |
| F54 | `func_get_app_cfg_path` | 应用配置路径 |
| F55 | `func_get_app_his_path` | 应用历史路径 |
| F56 | `func_get_app_log_path` | 应用日志路径 |

#### 4.7 IPC 消息队列 (9 个函数)
| 序号 | 函数 | 说明 |
|------|------|------|
| F57 | `func_create_ipc_msg_queue_by_name` | 按名称创建 |
| F58 | `func_create_ipc_msg_queue_by_pid` | 按PID创建 |
| F59 | `func_get_ipc_msg_queue_by_name` | 按名称获取 |
| F60 | `func_get_ipc_msg_queue_by_pid` | 按PID获取 |
| F61 | `func_ipc_msg_send_buf` | 发送原始buf |
| F62 | `func_ipc_msg_recv_buf` | 接收原始buf |
| F63 | `func_ipc_msg_send_struct` | 发送结构体 |
| F64 | `func_ipc_msg_recv_struct` | 接收结构体 |
| F65 | `func_delete_ipc_msg_queue` | 删除队列 |
| — | `func_process_exist_by_name` | (见4.5) |

---

## 5. 构建系统

### 层次结构 (与 RTU 完全一致)
```
release/
  makefile          ← 顶层入口 (SUBDIRS=./src)
  linux.mk          ← 公共编译选项 (CC/CXX/AR, x86/arm切换)
  build.sh          ← 一键编译脚本
  src/
    makefile        ← 第二层 (SUBDIRS=public protocol system)
    public/
      makefile      ← 第三层 (SUBDIRS=liblist libfunc liblog ...)
      liblist/makefile
      libfunc/makefile
      ...
    protocol/
      makefile
    system/
      makefile
```

### linux.mk 关键变量
- `CC`/`CXX`/`LD`/`AR` — 工具链
- `CROSS=arm` 切换 ARM 交叉编译
- `ROOT_DIR`/`SRC_ROOT_DIR`/`REL_INC`/`LIB_REL`/`EXE_REL` — 路径
- `C_FLAGS`/`CXX_FLAGS` — 编译选项（.c 用 gcc，.cpp 用 g++）

---

## 6. 模块分类：自研 vs 开源/项目库

> **重要**：以下分类决定模块是"自主从零实现"还是"直接使用现有开源库"

### 6.1 自研模块（需要从零实现）

| 模块 | 位置 | 语言 | 说明 |
|------|------|------|------|
| `libmd5` | public/ | 纯C | RFC 1321 MD5 摘要，需自主实现 |
| `libtask` | public/ | C/C++ | 事件/定时器/消息队列综合调度框架 |
| `libdatacenter` | public/ | C++ | 信号数据中心，signal_in/out/ao/yk/param |
| `libcmd` | public/ | C++ | 命令注册/分发/linenoise交互框架 |
| `libcomm` | public/ | C++ | TCP/UDP/UART 通讯抽象层 |

### 6.2 开源/项目库（不需要重写，直接使用）

| 模块 | 位置 | 来源 | 说明 |
|------|------|------|------|
| `libcJSON` | public/ | cJSON | JSON 解析/生成，开源 MIT 许可证 |
| `libmy_xxhash` | public/ | xxHash | 极速哈希，BSD 2-Clause 许可证 |
| `libxml` | public/ | tinyxml2 | XML 解析，Zlib 许可证 |
| `libmy_mosquitto` | public/ | mosquitto | MQTT 客户端 |
| `libmongoose` | protocol/ | mongoose | 嵌入式 HTTP/WebSocket |
| `libmodbus` | protocol/ | libmodbus | Modbus RTU/TCP 协议栈 |
| `lib60870` | protocol/ | lib60870 | IEC 60870-5-101/103/104 协议栈 |

---

## 7. libmd5 自研模块详细功能

### 功能点（来自 RTU 原工程 `md5.c` 分析）
| 序号 | 函数 | 说明 |
|------|------|------|
| M01 | `StartMD5(ctx)` | 初始化上下文：设置魔数 A=0x67452301 等 |
| M02 | `EncodeMD5(ctx, input, len)` | 输入待计算数据，分块处理 64 字节块 |
| M03 | `GetMD5(ctx, result)` | 获取 16 字节 MD5 摘要 |
| M04 | `GetMD5String(ctx, output, mode)` | 格式化 hex 字符串（mode=0 大写, 1 小写） |
| M05 | `MD5Transform`(内部) | 4 轮 × 16 步的 MD5 核心变换 |
| M06 | `Encode`(内部) | uint32 数组 → LE 字节序列 |
| M07 | `Decode`(内部) | LE 字节序列 → uint32 数组 |

### 数据结构
```c
typedef struct {
    uint32_t state[4];        /* A, B, C, D */
    uint32_t count[2];        /* 位计数 (low, high) */
    unsigned char buffer[64]; /* 待处理数据缓冲 */
} MD5_CTX;
```

---

## 8. libtask 自研模块详细功能

### 功能点（来自 RTU 原工程 `myTask.c` + `myTask.h` 分析）

#### 8.1 定时器子系统
| 序号 | 函数 | 说明 |
|------|------|------|
| T01 | `task_timer_create(name, cb, arg, timeout_ms, flags)` | 创建定时器, flags=单次/周期 |
| T02 | `task_timer_start(ptimer)` | 启动定时器 |
| T03 | `task_timer_stop(ptimer)` | 停止定时器 |
| T04 | `task_timer_restart(ptimer, timeout_ms)` | 重启定时器(修改周期) |
| T05 | `task_timer_destroy(ptimer)` | 销毁定时器 |
| T06 | `task_timer_is_active(ptimer)` | 查询是否运行中 |
| T07 | `timer_manager_thread`(内部) | 独立线程轮询所有定时器到期 |

#### 8.2 事件子系统
| 序号 | 函数 | 说明 |
|------|------|------|
| T08 | `task_event_create(name)` | 创建事件对象 |
| T09 | `task_event_destroy(pevent)` | 销毁事件对象 |
| T10 | `task_event_send(pevent, event)` | 发送事件位 |
| T11 | `task_event_recv(pevent, set, opt, timeout_ms, recved)` | 等待事件, opt=AND/OR |
| T12 | `task_event_clear(pevent, event)` | 清除事件位 |
| T13 | `task_event_query(pevent)` | 查询当前事件位 |

#### 8.3 消息队列子系统
| 序号 | 函数 | 说明 |
|------|------|------|
| T14 | `task_msg_queue_create(name, msg_size, msg_num)` | 创建定长消息队列 |
| T15 | `task_msg_queue_destroy(pqueue)` | 销毁消息队列 |
| T16 | `task_msg_queue_send(pqueue, msg, size)` | 发送消息(阻塞) |
| T17 | `task_msg_queue_send_timeout(pqueue, msg, size, timeout_ms)` | 发送消息(超时) |
| T18 | `task_msg_queue_recv(pqueue, msg, size, timeout_ms)` | 接收消息(超时) |
| T19 | `task_msg_queue_try_recv(pqueue, msg, size)` | 尝试接收(非阻塞) |
| T20 | `task_msg_queue_get_count(pqueue)` | 获取队列当前消息数 |
| T21 | `task_msg_queue_space(pqueue)` | 获取队列剩余空间 |

#### 8.4 程序运行流程
```
定时器: timer_manager_thread 每 10ms 轮询所有活跃定时器 → 到期则回调
事件:   内部位图 + pthread_cond, send 置位+唤醒, recv 等待+清零
消息队列: 环形缓冲区 + pthread_mutex + pthread_cond
```

---

## 9. libcmd 自研模块详细功能

### 功能点（来自 RTU 原工程 `myCmd.h` + `my_cmd.cpp` 分析）
| 序号 | 函数/宏 | 说明 |
|------|---------|------|
| C01 | `cmd_manager_add_command(name, func, desc, complete)` | 注册命令 |
| C02 | `cmd_manager_get_commands(out_count)` | 获取所有已注册命令列表 |
| C03 | `cmd_help(argc, argv)` | 打印帮助信息 |
| C04 | `cmd_find(name)` | 按名称查找命令 |
| C05 | `cmd_complete(buf, completions, ncomp)` | Tab 自动补全回调 |
| C06 | `cmd_sub_complete(buf, completions, ncomp, subs, sub_count)` | 子命令补全 |
| C07 | `linenoise(prompt)` | 行编辑+历史交互式输入 |
| C08 | `linenoiseFree(ptr)` | 释放 linenoise 返回的内存 |
| C09 | `linenoiseHistoryAdd(line)` | 添加历史记录 |
| C10 | `linenoiseHistoryFree()` | 释放历史 |
| C11 | `CMD_REGISTER(name, func, desc)` | 宏：注册无补全的命令 |
| C12 | `CMD_REGISTER_C(name, func, desc, complete)` | 宏：注册带补全的命令 |

### 数据结构
```c
typedef struct {
    const char *name;       /* 命令名 */
    void (*func)(int argc, char *argv[]);  /* 回调 */
    const char *desc;       /* 描述 */
    void (*complete)(...);  /* 补全回调 */
} stru_cmd;
```

### 程序运行流程
```
命令注册 → cmd_manager_add_command → 存入全局命令哈希表
交互式Shell → linenoise(提示符) → 用户输入 → 查找命令 → 回调执行
Tab补全 → cmd_complete/cmd_sub_complete → linenoiseSetCompletionCallback
```

---

## 10. libcomm 自研模块详细功能

### 功能点（来自 RTU 原工程 myComm.h + comm/*.cpp 分析）
| 序号 | 函数 | 说明 |
|------|------|------|
| N01 | `comm_create(type, debug, para)` | 创建通讯实例(TCP/UDP/UART) |
| N02 | `comm_connect(id)` | 建立连接 |
| N03 | `comm_disconnect(id)` | 断开连接 |
| N04 | `comm_send(id, fd, data, len)` | 发送数据 |
| N05 | `comm_recv_register(id, cb)` | 注册接收回调 |
| N06 | `comm_state_register(id, cb)` | 注册状态变化回调 |
| N07 | `comm_destroy(id)` | 销毁实例 |

### 通讯类型枚举
```c
COMM_TYPE_TCP_SERVER, COMM_TYPE_TCP_CLIENT,
COMM_TYPE_UDP_SERVER, COMM_TYPE_UDP_CLIENT,
COMM_TYPE_UART
```

### 参数结构体
```c
stru_tcp_para { local_ip, local_port, remote_ip, remote_port }
stru_udp_para { local_ip, local_port, remote_ip, remote_port }
stru_uart_para { device, baudrate, data_bits, stop_bits, parity }
```

### 程序运行流程
```
comm_create → 按类型创建 TCP/UDP/UART 实例 → 返回 id
comm_connect → TCP 三次握手 / UDP bind / UART open
comm_send → 投递到对应 fd
接收线程 → 读 fd → 调用 comm_recv_cb 回调
状态变化 → 调用 comm_state_cb 回调(CONNECTED/DISCONNECTED)
```

---

## 11. libdatacenter 自研模块详细功能

> 基于 RTU 原工程 libdatacenter v1 与 libdatacenter_2 完整源码分析。
> 新工程采用纯 C 接口（`extern "C"`） + C++ 内部实现，参数文件读写通过钩子解耦。

### 11.0 模块架构与设计思想

```
┌──────────────────────────────────────────────────────────────┐
│                    应用层（协议栈/Web/主程序）                │
│  调用 dc_xxx_register / dc_xxx_set_val / dc_run_100ms 等     │
├──────────────────────────────────────────────────────────────┤
│                  libdatacenter（纯 C 接口层）                 │
│  ┌──────────┬──────────┬──────────┬──────────┬────────────┐ │
│  │ out 信号 │ in 信号  │ yk 信号  │ ao 信号  │ param 信号 │ │
│  │ (遥测/   │ (关联    │ (遥控)   │ (模拟    │ (定值/     │ │
│  │  遥信)   │  输出)   │          │  输出)   │  参数)     │ │
│  └──────────┴──────────┴──────────┴──────────┴────────────┘ │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │ 内部引擎：hash查找 / 值校验 / 脏标记 / 变化检测 / 回路阻断│ │
│  └──────────────────────────────────────────────────────────┘ │
│  ┌──────────────┬──────────────┬────────────────────────────┐ │
│  │ 事件队列     │ 扰动队列     │ 故障队列                    │ │
│  │ (SOE)        │ (Disturb)    │ (Fault)                     │ │
│  └──────────────┴──────────────┴────────────────────────────┘ │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │        参数配置钩子（解析/保存/变更通知）                  │ │
│  │        异步执行钩子（发起/结果/超时/取消）                  │ │
│  └──────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│              实现层（C++: unordered_map + XXH3_128bits 哈希 + mutex）│
└──────────────────────────────────────────────────────────────┘
```

**核心约束**：
- 信号唯一标识用 `saddr`（字符串，如 `"RPC:1:YC:1:val"`）
- 每个信号注册时绑定 `module_id`（模块标识字符串），用于回路阻断
- out 信号值变更不是同步回调，而是**脏标记 + 100ms 延迟批量检测**（避免频繁回调）
- yk/ao/param 控制值变更才是**同步回调**（控制场景需要即时响应）
- 参数定值支持多区（如运行区/检修区），每区独立存储

### 11.1 数据结构

#### 11.1.1 控制枚举
```c
enum SIGNAL_CTRL_TYPE
{
    NONE          = 0,   /* 无控制 */
    DIRECT_NORMAL = 1,   /* 直控（直接执行，无需选择-确认） */
    SBO_NORMAL    = 2,   /* 选控（选择-确认两阶段，SBO: Select Before Operate） */
};

enum SIGNAL_CTRL_STEP
{
    READY  = 0,   /* 就绪/空闲 */
    SELECT = 1,   /* 选择（SBO 第一步：锁住信号，暂存选择值） */
    DIRECT = 2,   /* 执行（SBO 第二步：确认后写入，或直控直接执行） */
    CANCEL = 3,   /* 取消（回退到 READY） */
};
```

#### 11.1.2 控制上下文
```c
typedef struct
{
    uint8_t step      : 2;   /* 当前控制步骤 SIGNAL_CTRL_STEP */
    uint8_t type      : 2;   /* 控制类型 SIGNAL_CTRL_TYPE */
    uint8_t backup    : 4;   /* 备用 */
    uint8_t data_type;       /* 数据类型 */
    void   *p_data;          /* 暂存数据指针（SELECT时保存选择值，DIRECT时用于校验） */
} stru_signal_ctrl;
```

#### 11.1.3 信号参数元数据
```c
typedef struct
{
    float min;      /* 最小值 */
    float max;      /* 最大值 */
    float step;     /* 步长 */
    char  unit[32]; /* 单位（如 "kV", "A", "Hz"） */
} stru_signal_param;
```

#### 11.1.4 内部信号节点
```c
typedef struct stru_signal
{
    uint32_t id;                        /* 全局唯一ID（递增分配，用于 id_index 数组索引） */
    XXH128_hash_t hash;                 /* 128位哈希（saddr 的 XXH3_128bits） */
    char saddr[128];                    /* 短地址 */
    char desc[256];                     /* 描述 */
    uint8_t data_type;                  /* 数据类型 */
    
    /* 数据指针列表（out/in/yk/ao 为单元素，param 为多区数组） */
    void **vec_p_data;                  /* 动态数组：当前数据指针 */
    int    vec_p_data_count;            /* 数据指针个数 */
    void  *p_last_data;                 /* 上一次数据快照（out信号变更检测用） */
    void **vec_p_default_data;          /* 默认值指针（param 恢复出厂用） */
    
    /* 链接关系 */
    char  link_saddr[128];              /* in 信号：链接的 out saddr */
    char  **link_saddrs;                /* out 被哪些 in 链接 */
    int   link_count;
    
    uint8_t ctrl_type;                  /* 控制类型 SIGNAL_CTRL_TYPE */
    stru_signal_param param;            /* 参数元数据（min/max/step/unit） */
    
    /* 回调列表（每个 cb 绑定 module_id 用于回路阻断） */
    struct
    {
        char              *module_id;
        out_signal_change_cb out_cb;    /* out 信号专用 */  
        signal_change_cb  ctrl_cb;      /* yk/ao/param 专用 */
    } cb_list[16];
    int cb_count;
    
    char last_caller_module[64];        /* 最后修改本信号的模块ID（回路阻断） */
} stru_signal;
```

#### 11.1.5 信号存储架构
```c
/* 每种信号类型一个独立的 map（saddr→signal*）+ id数组 */
typedef struct
{
    pthread_mutex_t mtx;
    uint32_t        signal_id;    /* 自增ID分配器 */
    void           *map_signals;  /* C++ unordered_multimap<XXH128, signal*> */
    stru_signal   **id_index;    /* id→指针 数组（支持按ID遍历） */
    int             id_index_cap;
} stru_signal_map;

typedef struct
{
    stru_signal_map signal_out;
    stru_signal_map signal_in;
    stru_signal_map signal_yk;
    stru_signal_map signal_ao;
    stru_signal_map signal_param;
} stru_datacenter;
```

### 11.2 模块生命周期与周期性函数

| 序号 | 函数 | 说明 |
|------|------|------|
| D00a | `dc_init(path)` | 初始化：解析参数配置文件(param.xml→self_param.xml)，注册配置中的信号 |
| D00b | `dc_run_100ms()` | 每100ms：① out信号变化检测+批量触发回调 ② 事件/扰动/故障队列弹出消费 |
| D00c | `dc_run_1000ms()` | 每1000ms：检测参数变更标志，有变更则写 self_param.xml |

### 11.3 信号注册（5 种类型，均带 module_id）

**关键设计**：所有注册函数必须传入 `module_id` 字符串，用于回路阻断——当模块 A 修改信号值时，A 自身的回调不会被触发，只通知其他模块。

| 序号 | 函数 | 说明 |
|------|------|------|
| D01 | `dc_signal_out(saddr, desc, data_type, p_data, module_id)` | 注册输出信号（遥测/遥信） |
| D02 | `dc_signal_out_with_callback(saddr, desc, data_type, p_data, cb, module_id)` | 注册输出信号+变化回调（延迟触发，非同步） |
| D03 | `dc_signal_out_link_with_callback(saddr, pp_data, cb, module_id)` | 链接已有 out 信号，获取数据指针并注册回调 |
| D04 | `dc_signal_in(saddr, desc, link_saddr, pp_data, module_id)` | 注册输入信号，自动关联输出信号的数据指针 |
| D05 | `dc_signal_in_with_callback(saddr, desc, link_saddr, pp_data, cb, module_id)` | 输入信号+回调（回调挂在被链接的out上） |
| D06 | `dc_signal_ao(saddr, desc, data_type, ctrl_type, p_data, cb, module_id)` | 注册模拟输出信号 |
| D07 | `dc_signal_ao_link_with_callback(saddr, pp_data, cb, module_id)` | 链接已有 ao 信号 |
| D08 | `dc_signal_param(saddr, desc, data_type, ctrl_type, pp_data, num, cb, module_id)` | 注册定值参数信号，num为定值区数 |
| D09 | `dc_signal_param_link_with_callback(saddr, pp_data, num, cb, module_id)` | 链接已有 param 信号 |
| D10 | `dc_signal_yk(saddr, desc, data_type, ctrl_type, p_data, cb, module_id)` | 注册遥控信号 |
| D11 | `dc_signal_yk_link_with_callback(saddr, pp_data, cb, module_id)` | 链接已有 yk 信号 |

**注册流程**（以 out 为例）：
```
dc_signal_out(saddr, desc, data_type, p_data, module_id)
  → 检查 saddr 是否已存在（重复注册报错）
  → 计算 XXH3_128bits(saddr)
  → 分配 p_last_data（保存旧值快照，用于变更检测）
  → 插入 g_datacenter.signal_out
  → dc_set_param_cfg_change(true)  /* ao/param 注册需标记配置变更 */
```

**in 信号关联逻辑**：
```
dc_signal_in(saddr, desc, link_saddr, pp_data)
  → 查找 link_saddr 对应的 out 信号
  → (*pp_data) = out信号的 vec_p_data[0]（共享同一数据指针）
  → 在 out 信号的 link_saddrs 中追加 in 的 saddr
  → signal.data_type = out.data_type（数据类型跟随 out）
```

### 11.4 信号控制（含 SBO 流程 + 值校验 + 回路阻断）

| 序号 | 函数 | 说明 |
|------|------|------|
| D12 | `dc_signal_ao_set_val(saddr, step, ctrl, p_data, module_id)` | 设置 AO 值（带 SBO 流程控制+值校验） |
| D13 | `dc_signal_ao_set_val_without_check(saddr, data_type, p_data, module_id)` | 设置 AO 值（跳过校验，用于初始化恢复） |
| D14 | `dc_signal_param_set_val(saddr, step, ctrl, zone, p_data, module_id)` | 设置定值（指定区号，带校验） |
| D15 | `dc_signal_param_set_val_without_check(saddr, data_type, zone, p_data, module_id)` | 设置定值（跳过校验） |
| D16 | `dc_signal_yk_set_status(saddr, step, ctrl, p_data, module_id)` | 设置遥控状态 |

**SBO 控制流程**：
```
─── READY ──→ SELECT（锁住信号，暂存选择值到 ctrl.p_data）
        │         │
        │         ├──→ CANCEL（回退 READY，ctrl.step = READY）
        │         │
        │         └──→ DIRECT（校验值==选择值 → 写入 vec_p_data → ctrl.step=READY）
        │
        └──→ DIRECT（直控模式：直接写入，无需 SELECT）
```

**值校验链**（以 `dc_check_val_valid` 为例）：
1. 检查 zone 是否越界
2. 检查新旧值是否变化（无变化→拒绝）
3. 根据 data_type 检查值是否在 param.min/max范围内
4. 所有类型（bool/int/float/double/IP/MAC/str）全覆盖

**回路阻断机制**（核心设计）：
```cpp
// 回调通知时，跳过发起修改的模块自身
for(auto &entry : p_signal->cb_list)
{
    if (entry.first == module_id) continue;  // 回路阻断：不发给自己
    entry.second(saddr, step, data_type, zone, p_data);
}
```

**out 信号变化的延迟回调**（与 ao/yk/param 不同）：
```
dc_set_out_signal_val(saddr, set_data, module_id)
  → p_signal->last_caller_module = module_id   /* 记录调用方 */
  → dc_mark_signal_dirty(p_signal)             /* 标记脏，不立即回调 */
  → 写入新值到 vec_p_data[0]
  
dc_run_100ms()
  → dc_signal_out_change_check()
    → 遍历 g_dirty_out_signals
    → 比较 vec_p_data[0] vs p_last_data
    → 有变化→通知cb_list（跳过 last_caller_module）
    → 更新 p_last_data
```

### 11.5 信号查询

| 序号 | 函数 | 说明 |
|------|------|------|
| D17 | `dc_get_out_signal_info(saddr, desc, desc_len, data_type, pp_data)` | 查询 out 信号 |
| D18 | `dc_get_in_signal_info(saddr, desc, desc_len, data_type, pp_data)` | 查询 in 信号 |
| D19 | `dc_get_ao_signal_info(saddr, desc, desc_len, data_type, p_param, ctrl_type, pp_data)` | 查询 ao 信号（含参数元数据） |
| D20 | `dc_get_param_signal_info(saddr, desc, desc_len, data_type, p_param, ctrl_type, pp_vec_data, p_count)` | 查询 param 信号（多区数据指针列表） |
| D21 | `dc_get_yk_signal_info(saddr, desc, desc_len, data_type, ctrl_type, pp_data)` | 查询 yk 信号 |
| D22 | `dc_get_signal_val(p_data, data_type, buf, buf_len)` | 信号值→字符串（纯 C 接口） |
| D23 | `dc_set_out_signal_val(saddr, set_data, module_id)` | 设置 out 信号值（脏标记+延迟回调） |
| D24 | `dc_get_signal_count(type_str)` | 按类型统计信号数（"out"/"in"/"yk"/"ao"/"param"） |
| D25 | `dc_get_signal_info_by_id(type_str, id, saddr, saddr_len, desc, desc_len, data_type_str, type_str_len, ctrl_type, link_str, link_len)` | 按ID查询信号基本信息（供前端遍历） |

### 11.6 事件/扰动/故障队列

| 序号 | 类型 | 入队函数 | 注册消费回调 | 数据内容 |
|------|------|----------|-------------|----------|
| D26 | SOE 事件 | `dc_event_queue_push(p_soe)` | `dc_event_register_queue_pop(cb)` | saddr + status + 时间戳(sec,ms) |
| D27 | 扰动数据 | `dc_disturb_dd_queue_push(p_dd)` | `dc_disturb_dd_register_queue_pop(cb)` | saddr + fVal + 时间戳(sec,ms) |
| D28 | 故障录波 | `dc_fault_queue_push(p_fault)` | `dc_fault_register_queue_pop(cb)` | vec_soe + vec_disturb_yc |

**队列消费机制**：
- 支持多个消费者（`vector<dc_queue_pop_cb>`）
- `dc_run_100ms()` 中批量弹出消费，加锁 swap 减少锁持有时间
- push 时自动补时间戳（sec==0 && ms==0 时自动获取当前时间）

### 11.7 参数配置文件解析与保存（解耦钩子设计）

**设计原则**：参数文件读写通过钩子函数注册，datacenter 不直接依赖 tinyxml2。

```c
/**
 * @brief 参数配置文件提供者（由应用层注册）
 * @param path  输出：返回配置文件的路径
 * @param len   路径缓冲区大小
 * @param buf   输出：返回配置文件的内容缓冲区
 * @param size  输出：返回内容大小
 * @return 0 成功，-1 没找到/不支持
 */
typedef int (*dc_param_cfg_read_cb)(char *path, int len, char **buf, int *size);

/**
 * @brief 参数配置保存回调（由应用层注册）
 * @param path  文件路径
 * @param buf   要保存的内容
 * @param size  内容大小
 * @return 0 成功，-1 失败
 */
typedef int (*dc_param_cfg_save_cb)(const char *path, const char *buf, int size);
```

| 序号 | 函数 | 说明 |
|------|------|------|
| D29 | `dc_param_cfg_register_read_cb(cb)` | 注册参数配置读取回调 |
| D30 | `dc_param_cfg_register_save_cb(cb)` | 注册参数配置保存回调 |
| D31 | `dc_param_cfg_changed()` | 查询配置是否有未保存变更（返回 0/1） |

**配置文件语义**（由钩子实现方保证，datacenter 不解析格式）：
- `param.xml`：参数模板（元数据：saddr, desc, type, min, max, step, unit, default, value）
- `self_param.xml`：用户运行时值（仅 saddr + value，持久化动态修改）
- 初始化时：先读 param.xml 的元数据注册信号 → 再读 self_param.xml 覆盖运行时值
- 配置变更时：dc_param_cfg_check() 检测到变更标志 → 调 save_cb 写入 self_param.xml

### 11.8 类型系统工具函数

| 序号 | 函数 | 说明 |
|------|------|------|
| D32 | `dc_get_data_type_str_by_id(data_type)` | DATA_TYPE_U8→"uint8_t" |
| D33 | `dc_get_data_type_id_by_str("float")` | "float"→DATA_TYPE_F32 |
| D34 | `dc_get_data_type_len(data_type)` | 获取字节长度 |
| D35 | `dc_create_data_ptr_by_type(data_type)` | 根据类型 new 并清零 |
| D36 | `dc_delete_signal_data(p_data, data_type)` | 根据类型 delete |
| D37 | `dc_set_signal_val_from_str(p_data, data_type, str)` | 字符串→值（"3.14"→3.14f） |
| D38 | `dc_set_signal_val(p_data, data_type, set_data)` | 内存拷贝赋值 |
| D39 | `dc_data_compare(data_type, p_data1, p_data2)` | 类型感知的值比较 |

### 11.9 调试与导出

| 序号 | 函数 | 说明 |
|------|------|------|
| D40 | `dc_save_out_signals_xml(path)` | 导出所有已注册 out 信号到 XML（供前端 PLC 配置读取） |

### 11.10 源文件对照表

| RTU原工程文件 | 新工程文件 | 说明 |
|---------------|-----------|------|
| `inc/dc_signal.h` | `inc/myDatacenter.h` | 合并为单头文件（纯C接口） |
| `inc/dc_event.h` | ↑ | 合并 |
| `inc/dc_param.h` | ↑ | 合并 |
| `src/dc_signal.cpp` | `src/myDatacenter.cpp` | 信号注册主体 |
| `src/dc_signal_core.cpp` | ↑ | map操作+脏标记 |
| `src/dc_signal_ctrl.cpp` | ↑ | 值校验+控制流程 |
| `src/dc_signal_query.cpp` | ↑ | 信号查询+out变化检测 |
| `src/dc_signal_compare.cpp` | ↑ | 值比较+元数据应用 |
| `src/dc_signal_find.cpp` | ↑ | hash查找 |
| `src/dc_signal_util.cpp` | ↑ | 类型工具函数 |
| `src/dc_signal_show.cpp` | ↑ | 信号展示+cmd命令（可选） |
| `src/datacenter.cpp` | ↑ | 初始化+生命周期 |
| `src/dc_param.cpp` | `src/dc_param_hook.cpp` | 参数配置文件钩子注册 |
| `src/dc_cfg_check.cpp` | ↑ | self_param.xml变更检测+保存 |
| `src/dc_event.cpp` | `src/dc_event.cpp` | 事件队列 |
| `src/dc_signal_internal.h` | `src/dc_signal_internal.h` | 内部共享声明 |

### 11.11 新旧工程差异对比

| 维度 | RTU 原工程 v1 | RTU 原工程 v2 | 新工程 libdatacenter |
|------|-------------|-------------|-------------------|
| 接口语言 | C++ (std::string) | 纯C (const char*) | 纯C (extern "C") |
| 哈希查找 | XXH128 multimap | XXH128 multimap | unordered_map + XXH3_128bits hasher（O(1)，复用 libmy_xxhash CBB） |
| 参数文件 | tinyxml2 直接解析 | 同上 | 回调钩子解耦 |
| 异步执行 | 无 | 有（_exec_async） | 预留接口+钩子 |
| 回路阻断 | ✓ | ✓ | ✓ |
| module_id | std::string | const char* | const char* |
| 控制校验 | ✓ | ✓ | ✓ |
| SBO流程 | ✓ | ✓ | ✓ |
| 多区定值 | ✓ | ✓ | ✓ |
| 变更检测 | 脏标记+100ms | 同v1 | 同v1 |
| 调试命令 | cmd_dc | 无 | 预留 |
| 错误码 | -1/0 | enum dc_error_t | enum dc_error_t |
| 未实现 | — | — | 异步执行模型(预留)、tinyxml2绑定(钩子替代) |
