RTU_ALL_AI/docs/public/libdatacenter/API-libdatacenter.md

360 lines
13 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-libdatacenter — 数据中心模块
> 版本: v3.0
> 日期: 2026-07-07
> 状态: 已实现(对齐 RTU 原工程 v1+v2 全部功能)
---
## 1. 模块概述
libdatacenter 是 RTU 通讯装置的核心数据中间层,负责:
| 功能 | 说明 |
|------|------|
| 信号注册与管理 | 5 种信号类型out/in/yk/ao/param统一存储和 XXH3 哈希索引 |
| 回路阻断 | 通过 `module_id` 实现"修改信号的模块不收到自己的回调" |
| 延迟变化检测 | out 信号采用脏标记 + 100ms 批量检测,避免频繁回调 |
| SBO 控制流程 | 支持直控Direct和选控Select-Before-Operate两种模式 |
| 参数元数据 | min/max/step/unit 值校验 |
| 配置文件持久化 | 通过 libxml C 接口读写 param.xml/self_param.xml |
| 事件队列 | SOE 事件、扰动数据、故障录波三类队列(支持多消费者) |
| 异步执行 | yk/ao/param 异步控制 + 超时检测 + 取消 |
| 命令调试 | 通过 libcmd 注册 `datacenter` 命令,交互式查看信号 |
### 架构依赖
```
libmy_xxhash → libxml → libcmd
↘ ↙
libdatacenter
```
---
## 2. 数据结构
### 2.1 控制类型
```c
typedef enum
{
DC_CTRL_NONE = 0, /**< 无控制 */
DC_CTRL_DIRECT = 1, /**< 直控(直接执行) */
DC_CTRL_SBO = 2 /**< 选控Select-Before-Operate */
} dc_ctrl_type_t;
```
### 2.2 控制步骤
```c
typedef enum
{
DC_STEP_READY = 0, /**< 就绪 */
DC_STEP_SELECT = 1, /**< 选择 */
DC_STEP_DIRECT = 2, /**< 执行 */
DC_STEP_CANCEL = 3 /**< 取消 */
} dc_ctrl_step_t;
```
### 2.3 SBO 状态转换
```
READY ──→ SELECT锁住信号暂存值到 ctrl.p_data
│ │
│ ├──→ DIRECT返校比对 → 写入 → ctrl.step = READY
│ │
│ └──→ CANCEL回退ctrl.step = READY
└──→ DIRECT直控直接写入
```
### 2.4 控制上下文
```c
typedef struct
{
uint8_t step; /**< 当前步骤 */
uint8_t type; /**< 控制类型 */
uint8_t data_type; /**< 数据类型 */
uint8_t reserved; /**< 保留 */
void *p_data; /**< SELECT时暂存选择值DIRECT时返校比对 */
} dc_ctrl_t;
```
### 2.5 参数元数据
```c
typedef struct
{
float min; /**< 最小值 */
float max; /**< 最大值 */
float step; /**< 步长 */
char unit[32]; /**< 单位 */
} dc_param_meta_t;
```
### 2.6 错误码
| 值 | 名称 | 说明 |
|----|------|------|
| 0 | DC_OK | 成功 |
| -1 | DC_ERR_PARAM | 参数错误 |
| -2 | DC_ERR_NOTFOUND | 信号不存在 |
| -3 | DC_ERR_TYPE | 数据类型不匹配 |
| -4 | DC_ERR_STEP | SBO 步骤错误 |
| -5 | DC_ERR_VAL | 值校验失败 |
| -6 | DC_ERR_TIMEOUT | 异步超时 |
| -7 | DC_ERR_BUSY | 忙 |
| -8 | DC_ERR_MEM | 内存不足 |
| -9 | DC_ERR_FULL | 队列已满 |
| -10 | DC_ERR_LOCKED | 已锁定 |
| -11 | DC_ERR_EXISTS | 重复注册 |
| -12 | DC_ERR_CANCEL | 已取消 |
---
## 3. 接口函数
### 3.1 模块生命周期
| 函数 | 说明 |
|------|------|
| `dc_init(cfg_dir)` | 初始化,自动加载 param.xml |
| `dc_cleanup()` | 清理所有资源 |
| `dc_run_100ms()` | 每100msout变化检测 + 事件弹出 + 异步超时 |
| `dc_run_1000ms()` | 每1000ms参数变更检测 → 写 self_param.xml |
### 3.2 Out 信号(遥测/遥信)
| 函数 | 说明 |
|------|------|
| `dc_signal_out(saddr, desc, data_type, p_data, module_id)` | 注册输出信号 |
| `dc_signal_out_with_callback(..., cb, module_id)` | 注册 + 延迟变化回调 |
| `dc_signal_out_link_with_callback(saddr, pp_data, cb, module_id)` | 链接已有信号 |
| `dc_set_out_signal_val(saddr, set_data, module_id)` | 设值(脏标记 + 延迟回调) |
### 3.3 In 信号(输入信号,链接 out
| 函数 | 说明 |
|------|------|
| `dc_signal_in(saddr, desc, link_saddr, pp_data, module_id)` | 注册并关联 out |
| `dc_signal_in_with_callback(..., cb, module_id)` | 注册 + 回调(挂在 out 上) |
### 3.4 YK 信号(遥控)
| 函数 | 说明 |
|------|------|
| `dc_signal_yk(...)` | 注册遥控信号 |
| `dc_signal_yk_link_with_callback(...)` | 链接已有 |
| `dc_signal_yk_set_status(saddr, step, ctrl, p_data, module_id)` | 同步控制 |
| `dc_yk_exec_async(saddr, step, ctrl, p_data, module_id, result_cb, arg, timeout, req_id)` | 异步控制 |
| `dc_yk_set_result(req_id, result, err)` | 异步结果通知 |
| `dc_yk_cancel_async(req_id)` | 取消异步 |
| `dc_yk_register_exec_cb(saddr, exec_cb, ctx)` | 注册执行回调 |
### 3.5 AO 信号(模拟输出)
| 函数 | 说明 |
|------|------|
| `dc_signal_ao(...)` | 注册 AO 信号 |
| `dc_signal_ao_link_with_callback(...)` | 链接已有 |
| `dc_signal_ao_set_val(saddr, step, ctrl, p_data, module_id)` | 同步控制SBO + 值校验) |
| `dc_signal_ao_set_val_without_check(...)` | 无校验设值 |
| `dc_ao_exec_async(...)` | 异步控制 |
| `dc_ao_set_result(...)` | 异步结果通知 |
| `dc_ao_cancel_async(...)` | 取消异步 |
| `dc_ao_register_exec_cb(...)` | 注册执行回调 |
### 3.6 Param 信号(定值,多区)
| 函数 | 说明 |
|------|------|
| `dc_signal_param(saddr, desc, data_type, ctrl_type, pp_data, num_zones, cb, module_id)` | 注册参数 |
| `dc_signal_param_link_with_callback(...)` | 链接已有 |
| `dc_signal_param_set_val(saddr, step, ctrl, zone, p_data, module_id)` | 同步设值 |
| `dc_signal_param_set_val_without_check(...)` | 无校验设值 |
| `dc_param_exec_async(...)` | 异步控制 |
| `dc_param_set_result(...)` | 异步结果通知 |
| `dc_param_cancel_async(...)` | 取消异步 |
| `dc_param_register_exec_cb(...)` | 注册执行回调 |
### 3.7 信号查询
| 函数 | 说明 |
|------|------|
| `dc_get_out_signal_info(saddr, desc, desc_len, data_type, pp_data)` | 查询 out |
| `dc_get_in_signal_info(...)` | 查询 in |
| `dc_get_ao_signal_info(saddr, desc, desc_len, data_type, p_meta, ctrl_type, pp_data)` | 查询 AO含元数据 |
| `dc_get_param_signal_info(saddr, desc, desc_len, data_type, p_meta, ctrl_type, p_data_array, p_count)` | 查询 Param多区 |
| `dc_get_yk_signal_info(...)` | 查询 YK |
| `dc_get_signal_val(p_data, data_type, buf, buf_len)` | 值 → 字符串 |
| `dc_get_signal_count(type_str)` | 按类型统计 |
| `dc_get_signal_info_by_id(type_str, id, ...)` | 按 ID 查询 |
### 3.8 事件队列
| 函数 | 说明 |
|------|------|
| `dc_event_queue_push(p_soe)` | SOE 入队 |
| `dc_event_register_queue_pop(cb)` | 注册 SOE 消费回调 |
| `dc_disturb_dd_queue_push(p_dd)` | 扰动数据入队 |
| `dc_disturb_dd_register_queue_pop(cb)` | 注册扰动消费回调 |
| `dc_fault_queue_push(p_fault)` | 故障数据入队 |
| `dc_fault_register_queue_pop(cb)` | 注册故障消费回调 |
### 3.9 类型工具
| 函数 | 说明 |
|------|------|
| `dc_get_type_name(data_type)` | ID → 名称35 → "uint32_t" |
| `dc_get_type_id_by_name(name)` | 名称 → ID"float" → 38 |
| `dc_get_type_len(data_type)` | 获取字节长度 |
| `dc_create_data(data_type)` / `dc_destroy_data(p_data, data_type)` | 类型感知的分配/释放 |
| `dc_copy_val(dst, src, data_type)` / `dc_compare_val(p1, p2, data_type)` | 类型感知的复制/比较 |
| `dc_set_val_from_str(p_data, data_type, str)` | 字符串 → 值 |
### 3.10 配置与异步管理
| 函数 | 说明 |
|------|------|
| `dc_param_cfg_changed()` | 查询是否有未保存变更 |
| `dc_async_pending_count()` | 异步请求 pending 数 |
| `dc_async_set_default_timeout(ms)` | 全局默认超时 |
| `dc_async_set_timeout_yk/ao/param(ms)` | 各类型默认超时 |
| `dc_save_out_signals_xml(path)` | 导出 out 信号到 XML |
---
## 4. 使用示例
```c
#include "myDatacenter.h"
#include <stdio.h>
static void on_out_changed(const char *saddr, uint8_t data_type,
const void *p_new, const void *p_old)
{
printf("out %s: %d → %d\n", saddr, *(int *)p_old, *(int *)p_new);
}
static void on_yk_changed(const char *saddr, dc_ctrl_step_t step,
uint8_t data_type, uint8_t zone,
const void *p_data)
{
printf("yk %s step=%d val=%d\n", saddr, step, *(uint8_t *)p_data);
}
int main(void)
{
/* 初始化,自动加载 param.xml */
dc_init("/etc/rtu/config/");
/* 注册 out 信号 */
int yc_val = 0;
dc_signal_out("RPC1:YC:1:val", "母线电压", DATA_TYPE_S32,
&yc_val, "protocol_104");
/* 另一个模块链接 out 信号并注册回调 */
void *p_linked = NULL;
dc_signal_out_link_with_callback("RPC1:YC:1:val", &p_linked,
on_out_changed, "web_server");
/* 注册 yk 信号(直控模式) */
uint8_t yk_val = 0;
dc_signal_yk("RPC1:YK:1:val", "开关合闸", DATA_TYPE_U8,
DC_CTRL_DIRECT, &yk_val, on_yk_changed, "protocol_104");
/* 修改 out 信号值回路阻断protocol_104 自身的回调不会被触发) */
yc_val = 220;
dc_set_out_signal_val("RPC1:YC:1:val", &yc_val, "protocol_104");
/* 100ms 周期on_out_changed 被调用 */
dc_run_100ms();
/* YK 直控执行同步回调on_yk_changed 立即触发) */
dc_ctrl_t ctrl = {0};
ctrl.type = DC_CTRL_DIRECT;
ctrl.data_type = DATA_TYPE_U8;
uint8_t new_val = 1;
dc_signal_yk_set_status("RPC1:YK:1:val", DC_STEP_DIRECT,
&ctrl, &new_val, "web_server");
dc_cleanup();
return 0;
}
```
---
## 5. 依赖关系
| 依赖 | 模块 | 用途 |
|------|------|------|
| ⇩ | `myBase.h` | 基础类型定义DATA_TYPE_* 宏) |
| ⇩ | `myLog.h` | 日志输出 |
| ⇩ | `libmy_xxhash` | XXH3_128bits 哈希查找O(1) |
| ⇩ | `libxml` | 纯 C 接口 XML 解析param.xml/self_param.xml |
| ⇩ | `libcmd` | 命令注册(`datacenter` 调试面板) |
---
## 6. 源文件组成15 文件,对齐 RTU 原工程)
| 文件 | 职责 |
|------|------|
| `inc/myDatacenter.h` | 纯 C 对外接口60+ API |
| `src/dc_internal.h` | C++ 内部声明(哈希/信号表/异步队列) |
| `src/dc_core.cpp` | 全局变量 + XXH128 哈希 + dc_init/run |
| `src/dc_signal_out.cpp` | Out 信号注册/链接/设值 |
| `src/dc_signal_in.cpp` | In 信号注册(关联 out |
| `src/dc_signal_yk.cpp` | YK 注册 + 同步/异步控制 |
| `src/dc_signal_ao.cpp` | AO 注册 + 同步/异步控制 + SBO |
| `src/dc_signal_param.cpp` | Param 多区定值 + 同步/异步控制 |
| `src/dc_signal_query.cpp` | 全部 get_xxx_info 函数 |
| `src/dc_signal_util.cpp` | 19 种类型工具函数 |
| `src/dc_signal_check.cpp` | SBO 校验 + 值校验 + YK 校验 |
| `src/dc_signal_show.cpp` | 命令注册 + XML 导出 |
| `src/dc_param_cfg.cpp` | param.xml 解析 + self_param.xml 保存 |
| `src/dc_event.cpp` | SOE/扰动/故障事件队列 |
| `src/dc_async.cpp` | 异步请求队列(入队/超时/取消/清理) |
| `src/dc_api_wrap.cpp` | C API 包装get_type_name 等) |
---
## 7. 新旧工程差异
| 维度 | RTU v1 | RTU v2 | 新工程 |
|------|--------|--------|--------|
| 接口语言 | C++ (std::string) | 纯 C (const char*) | 纯 C + Allman 风格 |
| 哈希 | XXH128 multimap | 同 v1 | unordered_map + XXH128 hasher |
| 参数文件 | tinyxml2 嵌入 | 同 v1 | libxml C 接口 |
| 异步执行 | 无 | _exec_async 系列 | _exec_async 系列 |
| 回路阻断 | ✓ | ✓ | ✓ |
| SBO 流程 | ✓ | ✓ | ✓ |
| 多区定值 | ✓ | ✓ | ✓ |
| 错误码 | -1/0 | enum dc_error_t | enum dc_error_t12种 |
| 命令调试 | cmd_dc | 无 | CMD_REGISTER_Clibcmd |
| 文件数 | 13 .cpp | 8 .cpp | 15 .cpp按功能拆分 |
### 7.1 版本历史
| 日期 | 版本 | 变更 |
|------|------|------|
| 2026-07-07 | v3.0 | 初始实现,对齐 RTU 原工程 v1+v2 |
| 2026-07-10 | v3.1 | Bug 修复 + 原工程功能补全(详见下表) |
### v3.1 修复明细2026-07-10
| # | 文件 | 变更 | 说明 |
|:--|------|------|------|
| 1 | `dc_async.cpp` | 🔧 死锁修复 | `timeout_check()` 持锁调回调 → 先收集列表,解锁后批量调用 |
| 2 | `dc_api_wrap.cpp` | 🔧 线程安全 | `dc_get_type_name()` `static string``thread_local char[32]` |
| 3 | `dc_event.cpp` | 🔧 队列防溢 | SOE/扰动/故障队列无上限 → `EVENT_QUEUE_MAX_SIZE=1024` |
| 4 | `dc_signal_out.cpp` | 🔧 回路阻断竞态 | `last_caller_module` 多模块覆盖 → 新增 `dirty_by_multi` 标志 |
| 5 | `dc_signal_check.cpp` | 值范围校验 | `dc_check_val_valid` 扩展为 12 种数值类型 min/max 范围 + bool 0/1 校验 |
| 6 | `dc_signal_check.cpp` | strcmp 替代 | `dc_check_ctrl_val_valid` 对 C8/C32/C64/C128/STR 改用 `strcmp` |
| 7 | `dc_signal_util.cpp` | 线程安全 API | 新增 `dc_internal_type_name()` buffer 版类型名称查询 |
### 已知未完成
| 功能 | 优先级 | 说明 |
|------|:----:|------|
| 事件队列自动补时间戳 | 🟡 | push 时 `sec==0 && ms==0` 自动获取当前时间 |
| `dc_param_cfg_register_read_cb/save_cb` 钩子 | 🟡 | SPEC D29-D30当前直接内置 tinyxml2 |
| `vec_p_default_data` | 🟢 | param 信号出厂默认值 |