RTU_ALL_AI/SPEC.md

35 KiB
Raw Permalink Blame History

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/classtypeof__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 数组

数据结构

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 信号存储架构

/* 每种信号类型一个独立的 mapsaddr→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回退 READYctrl.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全覆盖

回路阻断机制(核心设计):

// 回调通知时,跳过发起修改的模块自身
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 hasherO(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绑定(钩子替代)