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(¬_full) 阻塞等待
→ 尾插队列
→ pthread_cond_signal(¬_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 数组 |
数据结构
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) |
宏:注册带补全的命令 |
数据结构
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) |
销毁实例 |
通讯类型枚举
COMM_TYPE_TCP_SERVER, COMM_TYPE_TCP_CLIENT,
COMM_TYPE_UDP_SERVER, COMM_TYPE_UDP_CLIENT,
COMM_TYPE_UART
参数结构体
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 控制枚举
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 控制上下文
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 信号参数元数据
typedef struct
{
float min; /* 最小值 */
float max; /* 最大值 */
float step; /* 步长 */
char unit[32]; /* 单位(如 "kV", "A", "Hz") */
} stru_signal_param;
11.1.4 内部信号节点
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 信号存储架构
/* 每种信号类型一个独立的 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 为例):
- 检查 zone 是否越界
- 检查新旧值是否变化(无变化→拒绝)
- 根据 data_type 检查值是否在 param.min/max范围内
- 所有类型(bool/int/float/double/IP/MAC/str)全覆盖
回路阻断机制(核心设计):
// 回调通知时,跳过发起修改的模块自身
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。
/**
* @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绑定(钩子替代) |