# 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()` | 每100ms：out变化检测 + 事件弹出 + 异步超时 |
| `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_t（12种） |
| 命令调试 | cmd_dc | 无 | CMD_REGISTER_C（libcmd） |
| 文件数 | 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 信号出厂默认值 |
