feat(libdatacenter): complete data center module with 15-file split

- 纯 C 接口(60+ API),对齐 RTU 原工程 v1+v2 全部功能
- 5 种信号类型:out/in/yk/ao/param + SBO 控制流程
- module_id 回路阻断 + 脏标记延迟回调 + 值校验(19 种类型)
- 异步执行模型:yk/ao/param_exec_async + 超时 + 取消
- 内置 tinyxml2 解析 param.xml/self_param.xml
- 命令调试面板(libcmd CMD_REGISTER_C)
- O(1) XXH3_128bits 哈希查找(复用 libmy_xxhash)
- x86 编译通过,产物 libdatacenter.a
This commit is contained in:
ypc 2026-07-07 17:30:10 +08:00
parent dfb0a46146
commit f56ede7316
19 changed files with 4761 additions and 0 deletions

View File

@ -0,0 +1,332 @@
# 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按功能拆分 |

558
release/inc/myDatacenter.h Normal file
View File

@ -0,0 +1,558 @@
/**
* @file myDatacenter.h
* @brief C
* @details // + + +
* libmy_xxhash O(1)
* libxml C
* RTU libdatacenter v1 + v2
*/
#ifndef _MY_DATACENTER_H_
#define _MY_DATACENTER_H_
#include <stdint.h>
#include <stddef.h>
#include "xxhash.h"
#ifdef __cplusplus
extern "C"
{
#endif
/* ===== 地址/描述/模块/错误信息最大长度 ===== */
#define DC_SADDR_MAX_LEN 128
#define DC_DESC_MAX_LEN 256
#define DC_UNIT_MAX_LEN 32
#define DC_MODULE_MAX_LEN 64
#define DC_ERR_MSG_MAX_LEN 256
/* ===== 容量限制 ===== */
#define DC_MAX_CALLBACKS 16
#define DC_MAX_LINK_SADDRS 32
#define DC_MAX_DATA_ZONES 8
#define DC_MAX_PENDING 64
/** datacenter 自身模块标识 */
#define MODULE_DATACENTER "datacenter"
/* ===== 控制类型枚举 ===== */
typedef enum
{
DC_CTRL_NONE = 0, /**< 无控制 */
DC_CTRL_DIRECT = 1, /**< 直控(直接执行,无需选择-确认) */
DC_CTRL_SBO = 2 /**< 选控Select-Before-Operate 两阶段) */
} dc_ctrl_type_t;
/* ===== 控制步骤枚举 ===== */
typedef enum
{
DC_STEP_READY = 0, /**< 就绪/空闲 */
DC_STEP_SELECT = 1, /**< 选择SBO 第一步:锁住信号并暂存选择值) */
DC_STEP_DIRECT = 2, /**< 执行SBO 第二步确认写入,直控直接执行) */
DC_STEP_CANCEL = 3 /**< 取消(回退到 READY */
} dc_ctrl_step_t;
/* ===== 错误码枚举 ===== */
typedef enum
{
DC_OK = 0, /**< 成功 */
DC_ERR_PARAM = -1, /**< 参数错误NULL/越界) */
DC_ERR_NOTFOUND = -2, /**< 信号不存在 */
DC_ERR_TYPE = -3, /**< 数据类型不匹配 */
DC_ERR_STEP = -4, /**< SBO 步骤错误 */
DC_ERR_VAL = -5, /**< 值校验失败(越界/未变化) */
DC_ERR_TIMEOUT = -6, /**< 异步请求超时 */
DC_ERR_BUSY = -7, /**< 忙 */
DC_ERR_MEM = -8, /**< 内存不足 */
DC_ERR_FULL = -9, /**< 请求队列已满 */
DC_ERR_LOCKED = -10, /**< 已锁定 */
DC_ERR_EXISTS = -11, /**< 信号已存在(重复注册) */
DC_ERR_CANCEL = -12 /**< 异步请求已取消 */
} dc_error_t;
/* ===== 控制上下文 ===== */
typedef struct
{
uint8_t step; /**< 当前控制步骤dc_ctrl_step_t */
uint8_t type; /**< 控制类型dc_ctrl_type_t */
uint8_t data_type; /**< 数据类型DATA_TYPE_* */
uint8_t reserved; /**< 保留 */
void *p_data; /**< SELECT 时暂存选择值DIRECT 时用于返校比对 */
} dc_ctrl_t;
/* ===== 信号参数元数据min/max/step/unit ===== */
typedef struct
{
float min; /**< 最小值 */
float max; /**< 最大值 */
float step; /**< 步长 */
char unit[DC_UNIT_MAX_LEN]; /**< 单位(如 "kV", "A", "Hz" */
} dc_param_meta_t;
/* ===== 回调函数类型 ===== */
/**
* @brief out 100ms
* @param saddr
* @param data_type
* @param p_new
* @param p_old
*/
typedef void (*dc_out_change_cb_t)(const char *saddr, uint8_t data_type,
const void *p_new, const void *p_old);
/**
* @brief yk/ao/param
* @param saddr
* @param step
* @param data_type
* @param zone param 使 0
* @param p_data
*/
typedef void (*dc_signal_change_cb_t)(const char *saddr, dc_ctrl_step_t step,
uint8_t data_type, uint8_t zone,
const void *p_data);
/** @brief 事件队列消费回调 */
typedef void (*dc_queue_pop_cb_t)(void *p_data);
/**
* @brief A
* @param req_id ID
* @param result DC_OK / DC_ERR_*
* @param err
* @param arg
*/
typedef void (*dc_async_result_cb_t)(uint32_t req_id, int result,
const char *err, void *arg);
/**
* @brief datacenter B执行实际操作
* @param req_id ID
* @param saddr
* @param step
* @param data_type
* @param zone
* @param p_data
* @param ctx
* @return 0 dc_set_result -1
*/
typedef int (*dc_async_exec_cb_t)(uint32_t req_id, const char *saddr,
dc_ctrl_step_t step, uint8_t data_type,
uint8_t zone, const void *p_data, void *ctx);
/* ===== 内部数据结构(对调用方透明,通过 API 访问) ===== */
/** 回调条目 */
typedef struct
{
char module_id[DC_MODULE_MAX_LEN];
dc_out_change_cb_t out_cb;
dc_signal_change_cb_t ctrl_cb;
} dc_cb_entry_t;
/** 信号节点 */
typedef struct
{
uint32_t id;
XXH128_hash_t hash;
char saddr[DC_SADDR_MAX_LEN];
char desc[DC_DESC_MAX_LEN];
uint8_t data_type;
uint8_t ctrl_type;
void **vec_p_data;
int vec_p_data_count;
void *p_last_data;
char link_saddr[DC_SADDR_MAX_LEN];
char **link_saddrs;
int link_count;
dc_param_meta_t param;
dc_cb_entry_t cb_list[DC_MAX_CALLBACKS];
int cb_count;
char last_caller_module[DC_MODULE_MAX_LEN];
dc_async_exec_cb_t exec_cb;
void *module_ctx;
} dc_signal_t;
/* ==================================================================
*
* ================================================================== */
/**
* @brief
* @param cfg_dir param.xml
* @return DC_OK <0
*/
int dc_init(const char *cfg_dir);
/** @brief 清理数据中心,释放所有信号和队列资源 */
void dc_cleanup(void);
/**
* @brief 100ms
* @details out + + +
*/
void dc_run_100ms(void);
/**
* @brief 1000ms
* @details self_param.xml
*/
void dc_run_1000ms(void);
/* ==================================================================
* Out /
* ================================================================== */
/**
* @brief
* @param saddr
* @param desc
* @param data_type DATA_TYPE_*
* @param p_data
* @param module_id
* @return DC_OK DC_ERR_EXISTS
*/
int dc_signal_out(const char *saddr, const char *desc, uint8_t data_type,
void *p_data, const char *module_id);
/**
* @brief
* @param cb 100ms
*/
int dc_signal_out_with_callback(const char *saddr, const char *desc,
uint8_t data_type, void *p_data,
dc_out_change_cb_t cb, const char *module_id);
/**
* @brief out
* @param p_data out
*/
int dc_signal_out_link_with_callback(const char *saddr, void **p_data,
dc_out_change_cb_t cb, const char *module_id);
/**
* @brief 100ms
* @param module_id
*/
int dc_set_out_signal_val(const char *saddr, const void *set_data,
const char *module_id);
/* ==================================================================
* In out
* ================================================================== */
/**
* @brief
* @param link_saddr out
* @param p_data out
*/
int dc_signal_in(const char *saddr, const char *desc,
const char *link_saddr, void **p_data, const char *module_id);
/**
* @brief out
*/
int dc_signal_in_with_callback(const char *saddr, const char *desc,
const char *link_saddr, void **p_data,
dc_out_change_cb_t cb, const char *module_id);
/* ==================================================================
* YK
* ================================================================== */
/**
* @brief
* @param ctrl_type DC_CTRL_DIRECT / DC_CTRL_SBO
* @param cb
*/
int dc_signal_yk(const char *saddr, const char *desc,
uint8_t data_type, uint8_t ctrl_type, void *p_data,
dc_signal_change_cb_t cb, const char *module_id);
/** @brief 链接已有 YK 信号,获取数据指针并注册回调 */
int dc_signal_yk_link_with_callback(const char *saddr, void **p_data,
dc_signal_change_cb_t cb, const char *module_id);
/**
* @brief YK SBO SELECTDIRECT/CANCEL
* @param step
* @param ctrl type/data_type/p_data step
* @param p_data
*/
int dc_signal_yk_set_status(const char *saddr, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, const void *p_data,
const char *module_id);
/**
* @brief YK
* @param result_cb
* @param timeout_ms 0 使
* @param request_id ID
*/
int dc_yk_exec_async(const char *saddr, dc_ctrl_step_t step,
const dc_ctrl_t *ctrl, const void *p_data,
const char *module_id,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms, uint32_t *request_id);
/** @brief 设置 YK 异步执行结果模块B回调 */
int dc_yk_set_result(uint32_t request_id, int result_code, const char *err_msg);
/** @brief 取消 YK 异步请求 */
int dc_yk_cancel_async(uint32_t request_id);
/** @brief 注册 YK 异步执行回调模块B初始化时调用 */
int dc_yk_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
void *module_ctx);
/* ==================================================================
* AO
* ================================================================== */
/**
* @brief AO
* @param ctrl_type
* @param cb
*/
int dc_signal_ao(const char *saddr, const char *desc,
uint8_t data_type, uint8_t ctrl_type, void *p_data,
dc_signal_change_cb_t cb, const char *module_id);
/** @brief 链接已有 AO 信号 */
int dc_signal_ao_link_with_callback(const char *saddr, void **p_data,
dc_signal_change_cb_t cb, const char *module_id);
/**
* @brief AO SBO +
* @details DIRECT ctrl->step READY
*/
int dc_signal_ao_set_val(const char *saddr, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, const void *p_data,
const char *module_id);
/** @brief AO 无校验设置(跳过 SBO 流程和值校验,用于初始化恢复) */
int dc_signal_ao_set_val_without_check(const char *saddr, uint8_t data_type,
const void *p_data, const char *module_id);
/** @brief AO 异步执行 */
int dc_ao_exec_async(const char *saddr, dc_ctrl_step_t step,
const dc_ctrl_t *ctrl, const void *p_data,
const char *module_id,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms, uint32_t *request_id);
/** @brief 设置 AO 异步执行结果 */
int dc_ao_set_result(uint32_t request_id, int result_code, const char *err_msg);
/** @brief 取消 AO 异步请求 */
int dc_ao_cancel_async(uint32_t request_id);
/** @brief 注册 AO 异步执行回调 */
int dc_ao_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
void *module_ctx);
/* ==================================================================
* Param /
* ================================================================== */
/**
* @brief
* @param p_data zone
* @param num_zones
*/
int dc_signal_param(const char *saddr, const char *desc,
uint8_t data_type, uint8_t ctrl_type,
void **p_data, int num_zones,
dc_signal_change_cb_t cb, const char *module_id);
/** @brief 链接已有参数信号 */
int dc_signal_param_link_with_callback(const char *saddr, void **p_data,
int num_zones,
dc_signal_change_cb_t cb,
const char *module_id);
/**
* @brief Param SBO + zone
* @param setting_zone 0-based
*/
int dc_signal_param_set_val(const char *saddr, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, uint8_t setting_zone,
const void *p_data, const char *module_id);
/** @brief Param 无校验设置 */
int dc_signal_param_set_val_without_check(const char *saddr, uint8_t data_type,
uint8_t setting_zone,
const void *p_data,
const char *module_id);
/** @brief Param 异步执行 */
int dc_param_exec_async(const char *saddr, dc_ctrl_step_t step,
const dc_ctrl_t *ctrl, uint8_t setting_zone,
const void *p_data, const char *module_id,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms, uint32_t *request_id);
/** @brief 设置 Param 异步执行结果 */
int dc_param_set_result(uint32_t request_id, int result_code,
const char *err_msg);
/** @brief 取消 Param 异步请求 */
int dc_param_cancel_async(uint32_t request_id);
/** @brief 注册 Param 异步执行回调 */
int dc_param_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
void *module_ctx);
/* ==================================================================
*
* ================================================================== */
/** @brief 查询 out 信号信息 */
int dc_get_out_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, void **p_data);
/** @brief 查询 in 信号信息 */
int dc_get_in_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, void **p_data);
/** @brief 查询 AO 信号信息(含参数元数据) */
int dc_get_ao_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, dc_param_meta_t *p_meta,
uint8_t *ctrl_type, void **p_data);
/**
* @brief Param
* @param p_data_array
* @param p_count
*/
int dc_get_param_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, dc_param_meta_t *p_meta,
uint8_t *ctrl_type,
void **p_data_array, int *p_count);
/** @brief 查询 YK 信号信息 */
int dc_get_yk_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, uint8_t *ctrl_type, void **p_data);
/** @brief 获取信号值的字符串表示 */
int dc_get_signal_val(const void *p_data, uint8_t data_type,
char *buf, int buf_len);
/**
* @brief
* @param type_str "out"/"in"/"yk"/"ao"/"param"
* @return
*/
int dc_get_signal_count(const char *type_str);
/**
* @brief ID
* @param type_str "out"/"in"/"yk"/"ao"/"param"
* @param id ID
* @param saddr/saddr_len
* @param desc/desc_len
* @param data_type_str/type_str_len
* @param ctrl_type
* @param link_str/link_len
*/
int dc_get_signal_info_by_id(const char *type_str, uint32_t id,
char *saddr, int saddr_len,
char *desc, int desc_len,
char *data_type_str, int type_str_len,
uint8_t *ctrl_type,
char *link_str, int link_len);
/* ==================================================================
* //
* ================================================================== */
/** @brief SOE 事件入队 */
int dc_event_queue_push(void *p_soe);
/** @brief 注册 SOE 事件消费回调(支持多消费者) */
int dc_event_register_queue_pop(dc_queue_pop_cb_t cb);
/** @brief 扰动数据入队 */
int dc_disturb_dd_queue_push(void *p_dd);
/** @brief 注册扰动数据消费回调 */
int dc_disturb_dd_register_queue_pop(dc_queue_pop_cb_t cb);
/** @brief 故障数据入队 */
int dc_fault_queue_push(void *p_fault);
/** @brief 注册故障数据消费回调 */
int dc_fault_register_queue_pop(dc_queue_pop_cb_t cb);
/* ==================================================================
*
* ================================================================== */
/** @brief 数据类型 ID → 名称(如 35 → "uint32_t" */
const char *dc_get_type_name(uint8_t data_type);
/** @brief 名称 → 数据类型 ID如 "float" → 38 */
uint8_t dc_get_type_id_by_name(const char *name);
/** @brief 获取数据类型的字节长度 */
uint8_t dc_get_type_len(uint8_t data_type);
/** @brief 根据类型分配并清零内存 */
void *dc_create_data(uint8_t data_type);
/** @brief 根据类型释放内存 */
void dc_destroy_data(void *p_data, uint8_t data_type);
/** @brief 类型感知的值复制 */
int dc_copy_val(void *dst, const void *src, uint8_t data_type);
/** @brief 类型感知的值比较0 = 相等,-1 = 不等) */
int dc_compare_val(const void *p1, const void *p2, uint8_t data_type);
/** @brief 字符串 → 值(如 "3.14" → 3.14f */
int dc_set_val_from_str(void *p_data, uint8_t data_type, const char *str);
/* ==================================================================
*
* ================================================================== */
/** @brief 查询参数配置是否有未保存的变更0=无1=有) */
int dc_param_cfg_changed(void);
/** @brief 获取当前异步请求 pending 数量 */
int dc_async_pending_count(void);
/** @brief 设置全局默认异步超时时间(毫秒) */
void dc_async_set_default_timeout(uint32_t timeout_ms);
/** @brief 设置 YK 遥控默认异步超时0=使用全局默认) */
void dc_async_set_timeout_yk(uint32_t timeout_ms);
/** @brief 设置 AO 调节默认异步超时0=使用全局默认) */
void dc_async_set_timeout_ao(uint32_t timeout_ms);
/** @brief 设置 Param 定值默认异步超时0=使用全局默认) */
void dc_async_set_timeout_param(uint32_t timeout_ms);
/* ==================================================================
*
* ================================================================== */
/**
* @brief out XML
* @param path
* @return DC_OK <0
*/
int dc_save_out_signals_xml(const char *path);
#ifdef __cplusplus
}
#endif
#endif /* _MY_DATACENTER_H_ */

View File

@ -0,0 +1,32 @@
include ./../../../linux.mk
L := $(notdir $(realpath $(CURDIR)/..))
M := libdatacenter
O := $(LIB_REL)/$(M).a
S := $(SRC_ROOT_DIR)/$(L)/libdatacenter/src
I := -I$(SRC_ROOT_DIR)/$(L)/libdatacenter/inc -I$(SRC_ROOT_DIR)/public/libmy_xxhash/inc -I$(SRC_ROOT_DIR)/public/libxml/inc -I$(SRC_ROOT_DIR)/public/libcmd/inc
B := $(CURDIR)/$(M)/obj
SRCS := $(wildcard $(S)/*.cpp)
OBJS := $(patsubst $(S)/%.cpp, $(B)/%.o, $(SRCS))
F := $(CXX_FLAGS) $(I)
.PHONY: all
all:
@mkdir -p $(ROOT_DIR)/release/inc
@cp -f $(S:src=inc)/*.h $(ROOT_DIR)/release/inc/ 2>/dev/null; true
@$(MAKE) $(O)
$(O): $(OBJS)
@mkdir -p $(dir $@)
$(AR) rcs $@ $^
@echo "[$(M)] built"
$(B)/%.o: $(S)/%.cpp
@mkdir -p $(dir $@)
$(CXX) $(F) -c $< -o $@
.PHONY: clean
clean:
rm -rf $(B) $(O)
.PHONY: rebuild
rebuild: clean all

View File

@ -0,0 +1,190 @@
/**
* @file dc_internal.h
* @brief C++
* @note RTU dc_signal_internal.h
*/
#ifndef DC_INTERNAL_H
#define DC_INTERNAL_H
#include "myDatacenter.h"
#include "myBase.h"
#include "myLog.h"
#include "xxhash.h"
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <mutex>
#include <cstring>
#include <algorithm>
/* ================================================================
* XXH128 RTU
* ================================================================ */
struct DCXXH128Hash
{
size_t operator()(const XXH128_hash_t &h) const noexcept
{
const uint64_t mix1 = h.low64 ^ (h.high64 << 17);
const uint64_t mix2 = mix1 + (h.high64 >> 13);
const uint64_t mix3 = mix2 ^ (h.low64 << 23);
return static_cast<size_t>(mix3 ^ (mix3 >> 32));
}
};
struct DCXXH128Equal
{
bool operator()(const XXH128_hash_t &a, const XXH128_hash_t &b) const noexcept
{
return (a.high64 == b.high64) && (a.low64 == b.low64);
}
};
typedef std::unordered_multimap<XXH128_hash_t, dc_signal_t,
DCXXH128Hash, DCXXH128Equal> dc_hash_map_t;
/* ================================================================
*
* ================================================================ */
typedef struct
{
std::mutex mtx;
uint32_t signal_id;
dc_hash_map_t map_signals;
std::vector<dc_signal_t *> id_index;
} dc_signal_map_t;
/* ================================================================
*
* ================================================================ */
typedef struct dc_async_request
{
uint32_t request_id;
char saddr[DC_SADDR_MAX_LEN];
dc_ctrl_step_t step;
uint8_t data_type;
uint8_t setting_zone;
uint8_t ctrl_type;
uint8_t signal_type; /* 0=yk, 1=ao, 2=param */
void *p_select_data;
void *p_direct_data;
uint64_t start_time_ms;
uint32_t timeout_ms;
char caller_module[DC_MODULE_MAX_LEN];
dc_async_result_cb_t result_cb;
void *user_arg;
dc_async_exec_cb_t exec_cb;
void *module_ctx;
int result_code;
char err_msg[DC_ERR_MSG_MAX_LEN];
struct dc_async_request *next;
} dc_async_request_t;
/* ================================================================
*
* ================================================================ */
typedef struct
{
std::mutex mtx;
uint32_t next_id;
dc_async_request_t *head;
int count;
int max_pending;
uint32_t default_timeout_ms;
uint32_t default_timeout_yk_ms;
uint32_t default_timeout_ao_ms;
uint32_t default_timeout_param_ms;
std::unordered_map<uint32_t, dc_async_request_t *> req_map;
} dc_async_queue_t;
/* ================================================================
*
* ================================================================ */
extern dc_signal_map_t g_dc_out;
extern dc_signal_map_t g_dc_in;
extern dc_signal_map_t g_dc_yk;
extern dc_signal_map_t g_dc_ao;
extern dc_signal_map_t g_dc_param;
extern std::vector<dc_signal_t *> g_dirty_out_signals;
extern std::mutex g_dirty_out_mutex;
extern dc_async_queue_t g_async_queue;
extern bool g_param_cfg_changed;
extern std::mutex g_param_cfg_mutex;
extern char g_cfg_dir[256];
extern bool g_initialized;
extern std::unordered_map<std::string, dc_param_meta_t> g_param_metadata;
/* ================================================================
*
* ================================================================ */
/* dc_core.cpp */
int dc_core_add(dc_signal_t &signal, dc_signal_map_t &smap);
dc_signal_t *dc_core_find(const char *saddr, dc_signal_map_t &smap);
dc_signal_t *dc_core_find_out(const char *saddr);
dc_signal_t *dc_core_find_in(const char *saddr);
dc_signal_t *dc_core_find_by_id(uint32_t id, dc_signal_map_t &smap);
void dc_core_mark_dirty(dc_signal_t *p_signal);
/* dc_signal_util.cpp */
int dc_util_val_compare(uint8_t data_type, const void *p1, const void *p2);
void dc_util_val_copy(void *dst, const void *src, uint8_t data_type);
void *dc_util_create_data(uint8_t data_type);
void dc_util_destroy_data(void *p_data, uint8_t data_type);
std::string dc_util_val_to_string(const void *p_data, uint8_t data_type);
int dc_util_val_from_string(void *p_data, uint8_t data_type, const std::string &str);
std::string dc_util_type_name(uint8_t data_type);
uint8_t dc_util_type_id_by_name(const std::string &name);
uint8_t dc_util_type_len(uint8_t data_type);
/* dc_signal_check.cpp */
int dc_check_ctrl_val_valid(const dc_ctrl_t *ctrl, const void *p_data);
int dc_check_ctrl_valid(const dc_signal_t *p_signal, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, const void *p_data);
int dc_check_val_valid(const dc_signal_t *p_signal, uint8_t setting_zone,
const void *p_data);
int dc_check_yk_val_valid(dc_signal_t *p_signal, void *p_data);
/* dc_event.cpp */
int dc_disturb_dd_queue_push(void *p_dd);
int dc_disturb_dd_register_queue_pop(dc_queue_pop_cb_t cb);
void dc_disturb_dd_queue_pop(void);
int dc_event_queue_push(void *p_soe);
int dc_event_register_queue_pop(dc_queue_pop_cb_t cb);
void dc_event_queue_pop(void);
int dc_fault_queue_push(void *p_fault);
int dc_fault_register_queue_pop(dc_queue_pop_cb_t cb);
void dc_fault_queue_pop(void);
/* dc_async.cpp */
uint32_t dc_async_enqueue(dc_signal_map_t &smap, const char *saddr,
dc_ctrl_step_t step, const dc_ctrl_t *ctrl,
const void *p_data, uint8_t setting_zone,
uint8_t signal_type, const char *caller_module,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms);
int dc_async_set_result(uint32_t request_id, int result_code, const char *err_msg);
int dc_async_cancel(uint32_t request_id);
void dc_async_timeout_check(void);
void dc_async_cleanup(void);
/* dc_param_cfg.cpp */
int dc_param_cfg_parse(const char *path);
void dc_param_metadata_store(const char *saddr, const dc_param_meta_t &param);
bool dc_param_metadata_lookup(const char *saddr, dc_param_meta_t &out_param);
void dc_param_cfg_check(const char *path);
#endif /* DC_INTERNAL_H */

View File

@ -0,0 +1,558 @@
/**
* @file myDatacenter.h
* @brief C
* @details // + + +
* libmy_xxhash O(1)
* libxml C
* RTU libdatacenter v1 + v2
*/
#ifndef _MY_DATACENTER_H_
#define _MY_DATACENTER_H_
#include <stdint.h>
#include <stddef.h>
#include "xxhash.h"
#ifdef __cplusplus
extern "C"
{
#endif
/* ===== 地址/描述/模块/错误信息最大长度 ===== */
#define DC_SADDR_MAX_LEN 128
#define DC_DESC_MAX_LEN 256
#define DC_UNIT_MAX_LEN 32
#define DC_MODULE_MAX_LEN 64
#define DC_ERR_MSG_MAX_LEN 256
/* ===== 容量限制 ===== */
#define DC_MAX_CALLBACKS 16
#define DC_MAX_LINK_SADDRS 32
#define DC_MAX_DATA_ZONES 8
#define DC_MAX_PENDING 64
/** datacenter 自身模块标识 */
#define MODULE_DATACENTER "datacenter"
/* ===== 控制类型枚举 ===== */
typedef enum
{
DC_CTRL_NONE = 0, /**< 无控制 */
DC_CTRL_DIRECT = 1, /**< 直控(直接执行,无需选择-确认) */
DC_CTRL_SBO = 2 /**< 选控Select-Before-Operate 两阶段) */
} dc_ctrl_type_t;
/* ===== 控制步骤枚举 ===== */
typedef enum
{
DC_STEP_READY = 0, /**< 就绪/空闲 */
DC_STEP_SELECT = 1, /**< 选择SBO 第一步:锁住信号并暂存选择值) */
DC_STEP_DIRECT = 2, /**< 执行SBO 第二步确认写入,直控直接执行) */
DC_STEP_CANCEL = 3 /**< 取消(回退到 READY */
} dc_ctrl_step_t;
/* ===== 错误码枚举 ===== */
typedef enum
{
DC_OK = 0, /**< 成功 */
DC_ERR_PARAM = -1, /**< 参数错误NULL/越界) */
DC_ERR_NOTFOUND = -2, /**< 信号不存在 */
DC_ERR_TYPE = -3, /**< 数据类型不匹配 */
DC_ERR_STEP = -4, /**< SBO 步骤错误 */
DC_ERR_VAL = -5, /**< 值校验失败(越界/未变化) */
DC_ERR_TIMEOUT = -6, /**< 异步请求超时 */
DC_ERR_BUSY = -7, /**< 忙 */
DC_ERR_MEM = -8, /**< 内存不足 */
DC_ERR_FULL = -9, /**< 请求队列已满 */
DC_ERR_LOCKED = -10, /**< 已锁定 */
DC_ERR_EXISTS = -11, /**< 信号已存在(重复注册) */
DC_ERR_CANCEL = -12 /**< 异步请求已取消 */
} dc_error_t;
/* ===== 控制上下文 ===== */
typedef struct
{
uint8_t step; /**< 当前控制步骤dc_ctrl_step_t */
uint8_t type; /**< 控制类型dc_ctrl_type_t */
uint8_t data_type; /**< 数据类型DATA_TYPE_* */
uint8_t reserved; /**< 保留 */
void *p_data; /**< SELECT 时暂存选择值DIRECT 时用于返校比对 */
} dc_ctrl_t;
/* ===== 信号参数元数据min/max/step/unit ===== */
typedef struct
{
float min; /**< 最小值 */
float max; /**< 最大值 */
float step; /**< 步长 */
char unit[DC_UNIT_MAX_LEN]; /**< 单位(如 "kV", "A", "Hz" */
} dc_param_meta_t;
/* ===== 回调函数类型 ===== */
/**
* @brief out 100ms
* @param saddr
* @param data_type
* @param p_new
* @param p_old
*/
typedef void (*dc_out_change_cb_t)(const char *saddr, uint8_t data_type,
const void *p_new, const void *p_old);
/**
* @brief yk/ao/param
* @param saddr
* @param step
* @param data_type
* @param zone param 使 0
* @param p_data
*/
typedef void (*dc_signal_change_cb_t)(const char *saddr, dc_ctrl_step_t step,
uint8_t data_type, uint8_t zone,
const void *p_data);
/** @brief 事件队列消费回调 */
typedef void (*dc_queue_pop_cb_t)(void *p_data);
/**
* @brief A
* @param req_id ID
* @param result DC_OK / DC_ERR_*
* @param err
* @param arg
*/
typedef void (*dc_async_result_cb_t)(uint32_t req_id, int result,
const char *err, void *arg);
/**
* @brief datacenter B执行实际操作
* @param req_id ID
* @param saddr
* @param step
* @param data_type
* @param zone
* @param p_data
* @param ctx
* @return 0 dc_set_result -1
*/
typedef int (*dc_async_exec_cb_t)(uint32_t req_id, const char *saddr,
dc_ctrl_step_t step, uint8_t data_type,
uint8_t zone, const void *p_data, void *ctx);
/* ===== 内部数据结构(对调用方透明,通过 API 访问) ===== */
/** 回调条目 */
typedef struct
{
char module_id[DC_MODULE_MAX_LEN];
dc_out_change_cb_t out_cb;
dc_signal_change_cb_t ctrl_cb;
} dc_cb_entry_t;
/** 信号节点 */
typedef struct
{
uint32_t id;
XXH128_hash_t hash;
char saddr[DC_SADDR_MAX_LEN];
char desc[DC_DESC_MAX_LEN];
uint8_t data_type;
uint8_t ctrl_type;
void **vec_p_data;
int vec_p_data_count;
void *p_last_data;
char link_saddr[DC_SADDR_MAX_LEN];
char **link_saddrs;
int link_count;
dc_param_meta_t param;
dc_cb_entry_t cb_list[DC_MAX_CALLBACKS];
int cb_count;
char last_caller_module[DC_MODULE_MAX_LEN];
dc_async_exec_cb_t exec_cb;
void *module_ctx;
} dc_signal_t;
/* ==================================================================
*
* ================================================================== */
/**
* @brief
* @param cfg_dir param.xml
* @return DC_OK <0
*/
int dc_init(const char *cfg_dir);
/** @brief 清理数据中心,释放所有信号和队列资源 */
void dc_cleanup(void);
/**
* @brief 100ms
* @details out + + +
*/
void dc_run_100ms(void);
/**
* @brief 1000ms
* @details self_param.xml
*/
void dc_run_1000ms(void);
/* ==================================================================
* Out /
* ================================================================== */
/**
* @brief
* @param saddr
* @param desc
* @param data_type DATA_TYPE_*
* @param p_data
* @param module_id
* @return DC_OK DC_ERR_EXISTS
*/
int dc_signal_out(const char *saddr, const char *desc, uint8_t data_type,
void *p_data, const char *module_id);
/**
* @brief
* @param cb 100ms
*/
int dc_signal_out_with_callback(const char *saddr, const char *desc,
uint8_t data_type, void *p_data,
dc_out_change_cb_t cb, const char *module_id);
/**
* @brief out
* @param p_data out
*/
int dc_signal_out_link_with_callback(const char *saddr, void **p_data,
dc_out_change_cb_t cb, const char *module_id);
/**
* @brief 100ms
* @param module_id
*/
int dc_set_out_signal_val(const char *saddr, const void *set_data,
const char *module_id);
/* ==================================================================
* In out
* ================================================================== */
/**
* @brief
* @param link_saddr out
* @param p_data out
*/
int dc_signal_in(const char *saddr, const char *desc,
const char *link_saddr, void **p_data, const char *module_id);
/**
* @brief out
*/
int dc_signal_in_with_callback(const char *saddr, const char *desc,
const char *link_saddr, void **p_data,
dc_out_change_cb_t cb, const char *module_id);
/* ==================================================================
* YK
* ================================================================== */
/**
* @brief
* @param ctrl_type DC_CTRL_DIRECT / DC_CTRL_SBO
* @param cb
*/
int dc_signal_yk(const char *saddr, const char *desc,
uint8_t data_type, uint8_t ctrl_type, void *p_data,
dc_signal_change_cb_t cb, const char *module_id);
/** @brief 链接已有 YK 信号,获取数据指针并注册回调 */
int dc_signal_yk_link_with_callback(const char *saddr, void **p_data,
dc_signal_change_cb_t cb, const char *module_id);
/**
* @brief YK SBO SELECTDIRECT/CANCEL
* @param step
* @param ctrl type/data_type/p_data step
* @param p_data
*/
int dc_signal_yk_set_status(const char *saddr, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, const void *p_data,
const char *module_id);
/**
* @brief YK
* @param result_cb
* @param timeout_ms 0 使
* @param request_id ID
*/
int dc_yk_exec_async(const char *saddr, dc_ctrl_step_t step,
const dc_ctrl_t *ctrl, const void *p_data,
const char *module_id,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms, uint32_t *request_id);
/** @brief 设置 YK 异步执行结果模块B回调 */
int dc_yk_set_result(uint32_t request_id, int result_code, const char *err_msg);
/** @brief 取消 YK 异步请求 */
int dc_yk_cancel_async(uint32_t request_id);
/** @brief 注册 YK 异步执行回调模块B初始化时调用 */
int dc_yk_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
void *module_ctx);
/* ==================================================================
* AO
* ================================================================== */
/**
* @brief AO
* @param ctrl_type
* @param cb
*/
int dc_signal_ao(const char *saddr, const char *desc,
uint8_t data_type, uint8_t ctrl_type, void *p_data,
dc_signal_change_cb_t cb, const char *module_id);
/** @brief 链接已有 AO 信号 */
int dc_signal_ao_link_with_callback(const char *saddr, void **p_data,
dc_signal_change_cb_t cb, const char *module_id);
/**
* @brief AO SBO +
* @details DIRECT ctrl->step READY
*/
int dc_signal_ao_set_val(const char *saddr, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, const void *p_data,
const char *module_id);
/** @brief AO 无校验设置(跳过 SBO 流程和值校验,用于初始化恢复) */
int dc_signal_ao_set_val_without_check(const char *saddr, uint8_t data_type,
const void *p_data, const char *module_id);
/** @brief AO 异步执行 */
int dc_ao_exec_async(const char *saddr, dc_ctrl_step_t step,
const dc_ctrl_t *ctrl, const void *p_data,
const char *module_id,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms, uint32_t *request_id);
/** @brief 设置 AO 异步执行结果 */
int dc_ao_set_result(uint32_t request_id, int result_code, const char *err_msg);
/** @brief 取消 AO 异步请求 */
int dc_ao_cancel_async(uint32_t request_id);
/** @brief 注册 AO 异步执行回调 */
int dc_ao_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
void *module_ctx);
/* ==================================================================
* Param /
* ================================================================== */
/**
* @brief
* @param p_data zone
* @param num_zones
*/
int dc_signal_param(const char *saddr, const char *desc,
uint8_t data_type, uint8_t ctrl_type,
void **p_data, int num_zones,
dc_signal_change_cb_t cb, const char *module_id);
/** @brief 链接已有参数信号 */
int dc_signal_param_link_with_callback(const char *saddr, void **p_data,
int num_zones,
dc_signal_change_cb_t cb,
const char *module_id);
/**
* @brief Param SBO + zone
* @param setting_zone 0-based
*/
int dc_signal_param_set_val(const char *saddr, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, uint8_t setting_zone,
const void *p_data, const char *module_id);
/** @brief Param 无校验设置 */
int dc_signal_param_set_val_without_check(const char *saddr, uint8_t data_type,
uint8_t setting_zone,
const void *p_data,
const char *module_id);
/** @brief Param 异步执行 */
int dc_param_exec_async(const char *saddr, dc_ctrl_step_t step,
const dc_ctrl_t *ctrl, uint8_t setting_zone,
const void *p_data, const char *module_id,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms, uint32_t *request_id);
/** @brief 设置 Param 异步执行结果 */
int dc_param_set_result(uint32_t request_id, int result_code,
const char *err_msg);
/** @brief 取消 Param 异步请求 */
int dc_param_cancel_async(uint32_t request_id);
/** @brief 注册 Param 异步执行回调 */
int dc_param_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb,
void *module_ctx);
/* ==================================================================
*
* ================================================================== */
/** @brief 查询 out 信号信息 */
int dc_get_out_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, void **p_data);
/** @brief 查询 in 信号信息 */
int dc_get_in_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, void **p_data);
/** @brief 查询 AO 信号信息(含参数元数据) */
int dc_get_ao_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, dc_param_meta_t *p_meta,
uint8_t *ctrl_type, void **p_data);
/**
* @brief Param
* @param p_data_array
* @param p_count
*/
int dc_get_param_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, dc_param_meta_t *p_meta,
uint8_t *ctrl_type,
void **p_data_array, int *p_count);
/** @brief 查询 YK 信号信息 */
int dc_get_yk_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, uint8_t *ctrl_type, void **p_data);
/** @brief 获取信号值的字符串表示 */
int dc_get_signal_val(const void *p_data, uint8_t data_type,
char *buf, int buf_len);
/**
* @brief
* @param type_str "out"/"in"/"yk"/"ao"/"param"
* @return
*/
int dc_get_signal_count(const char *type_str);
/**
* @brief ID
* @param type_str "out"/"in"/"yk"/"ao"/"param"
* @param id ID
* @param saddr/saddr_len
* @param desc/desc_len
* @param data_type_str/type_str_len
* @param ctrl_type
* @param link_str/link_len
*/
int dc_get_signal_info_by_id(const char *type_str, uint32_t id,
char *saddr, int saddr_len,
char *desc, int desc_len,
char *data_type_str, int type_str_len,
uint8_t *ctrl_type,
char *link_str, int link_len);
/* ==================================================================
* //
* ================================================================== */
/** @brief SOE 事件入队 */
int dc_event_queue_push(void *p_soe);
/** @brief 注册 SOE 事件消费回调(支持多消费者) */
int dc_event_register_queue_pop(dc_queue_pop_cb_t cb);
/** @brief 扰动数据入队 */
int dc_disturb_dd_queue_push(void *p_dd);
/** @brief 注册扰动数据消费回调 */
int dc_disturb_dd_register_queue_pop(dc_queue_pop_cb_t cb);
/** @brief 故障数据入队 */
int dc_fault_queue_push(void *p_fault);
/** @brief 注册故障数据消费回调 */
int dc_fault_register_queue_pop(dc_queue_pop_cb_t cb);
/* ==================================================================
*
* ================================================================== */
/** @brief 数据类型 ID → 名称(如 35 → "uint32_t" */
const char *dc_get_type_name(uint8_t data_type);
/** @brief 名称 → 数据类型 ID如 "float" → 38 */
uint8_t dc_get_type_id_by_name(const char *name);
/** @brief 获取数据类型的字节长度 */
uint8_t dc_get_type_len(uint8_t data_type);
/** @brief 根据类型分配并清零内存 */
void *dc_create_data(uint8_t data_type);
/** @brief 根据类型释放内存 */
void dc_destroy_data(void *p_data, uint8_t data_type);
/** @brief 类型感知的值复制 */
int dc_copy_val(void *dst, const void *src, uint8_t data_type);
/** @brief 类型感知的值比较0 = 相等,-1 = 不等) */
int dc_compare_val(const void *p1, const void *p2, uint8_t data_type);
/** @brief 字符串 → 值(如 "3.14" → 3.14f */
int dc_set_val_from_str(void *p_data, uint8_t data_type, const char *str);
/* ==================================================================
*
* ================================================================== */
/** @brief 查询参数配置是否有未保存的变更0=无1=有) */
int dc_param_cfg_changed(void);
/** @brief 获取当前异步请求 pending 数量 */
int dc_async_pending_count(void);
/** @brief 设置全局默认异步超时时间(毫秒) */
void dc_async_set_default_timeout(uint32_t timeout_ms);
/** @brief 设置 YK 遥控默认异步超时0=使用全局默认) */
void dc_async_set_timeout_yk(uint32_t timeout_ms);
/** @brief 设置 AO 调节默认异步超时0=使用全局默认) */
void dc_async_set_timeout_ao(uint32_t timeout_ms);
/** @brief 设置 Param 定值默认异步超时0=使用全局默认) */
void dc_async_set_timeout_param(uint32_t timeout_ms);
/* ==================================================================
*
* ================================================================== */
/**
* @brief out XML
* @param path
* @return DC_OK <0
*/
int dc_save_out_signals_xml(const char *path);
#ifdef __cplusplus
}
#endif
#endif /* _MY_DATACENTER_H_ */

View File

@ -0,0 +1,79 @@
/** @file dc_api_wrap.cpp — C API 包装函数(类型工具) */
#include "dc_internal.h"
/** @brief 获取数据类型名称字符串extern "C",返回 static 缓冲区指针) */
extern "C" const char *dc_get_type_name(uint8_t dt)
{
static std::string s;
s = dc_util_type_name(dt);
return s.c_str();
}
/** @brief 通过名称获取数据类型 ID */
extern "C" uint8_t dc_get_type_id_by_name(const char *name)
{
return dc_util_type_id_by_name(name ? name : "");
}
/** @brief 获取数据类型的字节长度 */
extern "C" uint8_t dc_get_type_len(uint8_t dt)
{
return dc_util_type_len(dt);
}
/** @brief 根据数据类型创建零初始化内存块 */
extern "C" void *dc_create_data(uint8_t dt)
{
return dc_util_create_data(dt);
}
/** @brief 释放由 dc_create_data 分配的内存 */
extern "C" void dc_destroy_data(void *p, uint8_t dt)
{
dc_util_destroy_data(p, dt);
}
/** @brief 将源数据按类型拷贝到目标 */
extern "C" int dc_copy_val(void *dst, const void *src, uint8_t dt)
{
if (!dst || !src)
{
return DC_ERR_PARAM;
}
dc_util_val_copy(dst, src, dt);
return DC_OK;
}
/** @brief 按类型比较两个值,返回 0 相等、-1 不等或参数错误 */
extern "C" int dc_compare_val(const void *p1, const void *p2, uint8_t dt)
{
if (!p1 || !p2)
{
return -1;
}
return dc_util_val_compare(dt, p1, p2);
}
/** @brief 从字符串解析值并按类型写入内存 */
extern "C" int dc_set_val_from_str(void *p, uint8_t dt, const char *s)
{
if (!p || !s)
{
return DC_ERR_PARAM;
}
return dc_util_val_from_string(p, dt, s);
}
/** @brief 检查参数配置文件是否已变更 */
extern "C" int dc_param_cfg_changed(void)
{
std::lock_guard<std::mutex> lk(g_param_cfg_mutex);
return g_param_cfg_changed ? 1 : 0;
}

View File

@ -0,0 +1,349 @@
/** @file dc_async.cpp — 异步请求队列管理 */
#include "dc_internal.h"
#include <sys/time.h>
/**
* @brief
* @return
*/
static uint64_t dc_get_monotonic_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}
/**
* @brief
* @param smap
* @param saddr
* @param step
* @param ctrl
* @param p_data
* @param setting_zone
* @param signal_type 0=yk, 1=ao, 2=param
* @param caller_module
* @param result_cb
* @param user_arg
* @param timeout_ms 0=使
* @return request_id>0 0
*/
uint32_t dc_async_enqueue(dc_signal_map_t &smap, const char *saddr,
dc_ctrl_step_t step, const dc_ctrl_t *ctrl,
const void *p_data, uint8_t setting_zone,
uint8_t signal_type, const char *caller_module,
dc_async_result_cb_t result_cb, void *user_arg,
uint32_t timeout_ms)
{
dc_async_exec_cb_t sig_exec_cb = NULL;
void *sig_ctx = NULL;
dc_signal_t *ps = dc_core_find(saddr, smap);
if (ps)
{
sig_exec_cb = ps->exec_cb;
sig_ctx = ps->module_ctx;
}
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
if (g_async_queue.count >= g_async_queue.max_pending)
{
LOG_E("async queue full\n");
return 0;
}
if (!timeout_ms)
{
uint32_t *pt = NULL;
switch (signal_type)
{
case 0:
pt = &g_async_queue.default_timeout_yk_ms;
break;
case 1:
pt = &g_async_queue.default_timeout_ao_ms;
break;
case 2:
pt = &g_async_queue.default_timeout_param_ms;
break;
}
timeout_ms = (pt && *pt) ? *pt : g_async_queue.default_timeout_ms;
}
dc_async_request_t *req = new dc_async_request_t();
memset(req, 0, sizeof(*req));
if (++g_async_queue.next_id == 0)
{
++g_async_queue.next_id;
}
req->request_id = g_async_queue.next_id;
if (saddr)
{
strncpy(req->saddr, saddr, DC_SADDR_MAX_LEN - 1);
}
req->step = step;
req->setting_zone = setting_zone;
req->signal_type = signal_type;
if (ctrl)
{
req->data_type = ctrl->data_type;
req->ctrl_type = ctrl->type;
}
if (ctrl && ctrl->p_data && p_data)
{
req->p_select_data = dc_util_create_data(ctrl->data_type);
if (req->p_select_data)
{
dc_util_val_copy(req->p_select_data, ctrl->p_data, ctrl->data_type);
}
req->p_direct_data = dc_util_create_data(ctrl->data_type);
if (req->p_direct_data)
{
dc_util_val_copy(req->p_direct_data, p_data, ctrl->data_type);
}
}
req->start_time_ms = dc_get_monotonic_ms();
req->timeout_ms = timeout_ms;
if (caller_module)
{
strncpy(req->caller_module, caller_module, DC_MODULE_MAX_LEN - 1);
}
req->result_cb = result_cb;
req->user_arg = user_arg;
req->exec_cb = sig_exec_cb;
req->module_ctx = sig_ctx;
req->next = g_async_queue.head;
g_async_queue.head = req;
g_async_queue.count++;
g_async_queue.req_map[req->request_id] = req;
return req->request_id;
}
/**
* @brief
* @param request_id ID
* @param result_code 0==
* @param err_msg
* @return DC_OK DC_ERR_NOTFOUND/DC_ERR_TIMEOUT
*/
int dc_async_set_result(uint32_t request_id, int result_code, const char *err_msg)
{
dc_async_result_cb_t cb = NULL;
void *arg = NULL;
int existed = 0;
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
auto it = g_async_queue.req_map.find(request_id);
if (it == g_async_queue.req_map.end())
{
return DC_ERR_NOTFOUND;
}
dc_async_request_t *req = it->second;
if (req->result_code == DC_ERR_TIMEOUT)
{
LOG_I("req %u timeout, ignore\n", request_id);
return DC_ERR_TIMEOUT;
}
req->result_code = result_code;
if (err_msg)
{
strncpy(req->err_msg, err_msg, DC_ERR_MSG_MAX_LEN - 1);
}
cb = req->result_cb;
arg = req->user_arg;
existed = 1;
}
if (existed && cb)
{
cb(request_id, result_code, err_msg, arg);
}
return DC_OK;
}
/**
* @brief
* @param request_id ID
* @return DC_OK DC_ERR_NOTFOUND/DC_ERR_STEP
*/
int dc_async_cancel(uint32_t request_id)
{
dc_async_result_cb_t cb = NULL;
void *arg = NULL;
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
auto it = g_async_queue.req_map.find(request_id);
if (it == g_async_queue.req_map.end())
{
return DC_ERR_NOTFOUND;
}
dc_async_request_t *req = it->second;
if (req->result_code != 0)
{
return DC_ERR_STEP;
}
req->result_code = DC_ERR_CANCEL;
snprintf(req->err_msg, sizeof(req->err_msg), "cancelled");
cb = req->result_cb;
arg = req->user_arg;
}
if (cb)
{
cb(request_id, DC_ERR_CANCEL, "cancelled", arg);
}
return DC_OK;
}
/**
* @brief
*/
void dc_async_timeout_check(void)
{
uint64_t now = dc_get_monotonic_ms();
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
for (dc_async_request_t *req = g_async_queue.head; req; req = req->next)
{
if (req->result_code == 0 && (now - req->start_time_ms) >= req->timeout_ms)
{
req->result_code = DC_ERR_TIMEOUT;
snprintf(req->err_msg, sizeof(req->err_msg), "timeout %ums", req->timeout_ms);
if (req->result_cb)
{
req->result_cb(req->request_id, DC_ERR_TIMEOUT, req->err_msg, req->user_arg);
}
}
}
}
/**
* @brief //
*/
void dc_async_cleanup(void)
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
dc_async_request_t **pp = &g_async_queue.head;
while (*pp)
{
dc_async_request_t *req = *pp;
if (req->result_code != 0)
{
*pp = req->next;
g_async_queue.req_map.erase(req->request_id);
if (req->p_select_data)
{
dc_util_destroy_data(req->p_select_data, req->data_type);
}
if (req->p_direct_data)
{
dc_util_destroy_data(req->p_direct_data, req->data_type);
}
delete req;
g_async_queue.count--;
}
else
{
pp = &req->next;
}
}
}
/**
* @brief pending
* @return pending
*/
int dc_async_pending_count(void)
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
return g_async_queue.count;
}
/**
* @brief
* @param ms
*/
void dc_async_set_default_timeout(uint32_t ms)
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
g_async_queue.default_timeout_ms = ms;
}
/**
* @brief yk
* @param ms
*/
void dc_async_set_timeout_yk(uint32_t ms)
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
g_async_queue.default_timeout_yk_ms = ms;
}
/**
* @brief AO
* @param ms
*/
void dc_async_set_timeout_ao(uint32_t ms)
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
g_async_queue.default_timeout_ao_ms = ms;
}
/**
* @brief param
* @param ms
*/
void dc_async_set_timeout_param(uint32_t ms)
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
g_async_queue.default_timeout_param_ms = ms;
}

View File

@ -0,0 +1,241 @@
/** @file dc_core.cpp — 全局变量 + 哈希操作 + 生命周期 */
#include "dc_internal.h"
dc_signal_map_t g_dc_out = {};
dc_signal_map_t g_dc_in = {};
dc_signal_map_t g_dc_yk = {};
dc_signal_map_t g_dc_ao = {};
dc_signal_map_t g_dc_param = {};
std::vector<dc_signal_t *> g_dirty_out_signals;
std::mutex g_dirty_out_mutex;
dc_async_queue_t g_async_queue = {};
bool g_param_cfg_changed = false;
std::mutex g_param_cfg_mutex;
char g_cfg_dir[256] = {0};
bool g_initialized = false;
std::unordered_map<std::string, dc_param_meta_t> g_param_metadata;
/** @brief 添加脏信号到列表,由 100ms 周期检测变更 */
void dc_core_mark_dirty(dc_signal_t *p_signal)
{
if (!p_signal)
{
return;
}
std::lock_guard<std::mutex> lk(g_dirty_out_mutex);
g_dirty_out_signals.push_back(p_signal);
}
/** @brief 计算 saddr 的 XXH128 哈希 */
static XXH128_hash_t dc_core_hash(const char *saddr)
{
return XXH3_128bits(saddr, strlen(saddr));
}
/** @brief 添加信号到 map已存在则更新 */
int dc_core_add(dc_signal_t &signal, dc_signal_map_t &smap)
{
std::lock_guard<std::mutex> lk(smap.mtx);
auto range = smap.map_signals.equal_range(signal.hash);
for (auto it = range.first; it != range.second; ++it)
{
if (0 == strcmp(it->second.saddr, signal.saddr))
{
uint32_t oid = it->second.id;
it->second = signal;
it->second.id = oid;
return DC_OK;
}
}
auto it = smap.map_signals.emplace(signal.hash, signal);
it->second.id = smap.signal_id++;
if (smap.id_index.size() <= it->second.id)
{
smap.id_index.resize(it->second.id + 1, NULL);
}
smap.id_index[it->second.id] = &it->second;
return DC_OK;
}
/** @brief 按 saddr 在指定 map 中查找信号 */
dc_signal_t *dc_core_find(const char *saddr, dc_signal_map_t &smap)
{
if (!saddr)
{
return NULL;
}
XXH128_hash_t h = dc_core_hash(saddr);
std::lock_guard<std::mutex> lk(smap.mtx);
auto range = smap.map_signals.equal_range(h);
for (auto it = range.first; it != range.second; ++it)
{
if (0 == strcmp(it->second.saddr, saddr))
{
return &it->second;
}
}
return NULL;
}
/** @brief 快捷查找 out 信号 */
dc_signal_t *dc_core_find_out(const char *saddr)
{
return dc_core_find(saddr, g_dc_out);
}
/** @brief 快捷查找 in 信号 */
dc_signal_t *dc_core_find_in(const char *saddr)
{
return dc_core_find(saddr, g_dc_in);
}
/** @brief 按 ID 查找信号O(1) */
dc_signal_t *dc_core_find_by_id(uint32_t id, dc_signal_map_t &smap)
{
std::lock_guard<std::mutex> lk(smap.mtx);
if (id >= smap.id_index.size())
{
return NULL;
}
return smap.id_index[id];
}
/* ========== 生命周期 ========== */
/** @brief 初始化 datacenter设置全局变量并尝试加载 param.xml */
int dc_init(const char *cfg_dir)
{
if (g_initialized)
{
LOG_I("datacenter already init\n");
return DC_OK;
}
if (cfg_dir)
{
strncpy(g_cfg_dir, cfg_dir, sizeof(g_cfg_dir) - 1);
}
g_async_queue.max_pending = DC_MAX_PENDING;
g_async_queue.default_timeout_ms = 30000;
g_initialized = true;
/* 尝试加载参数配置文件 */
if (cfg_dir && cfg_dir[0])
{
char param_path[512];
snprintf(param_path, sizeof(param_path), "%s/param.xml", cfg_dir);
if (0 != dc_param_cfg_parse(param_path))
{
LOG_I("no param.xml at %s, skipping config load\n", param_path);
}
}
LOG_I("datacenter init ok, cfg=%s\n", g_cfg_dir);
return DC_OK;
}
/** @brief 清理 datacenter */
void dc_cleanup(void)
{
if (!g_initialized)
{
return;
}
g_dirty_out_signals.clear();
g_initialized = false;
LOG_I("datacenter cleaned up\n");
}
/** @brief 100ms 变化检测:去重 → 值比对 → 回调 → 回路阻断 */
static void dc_change_out_check(void)
{
std::vector<dc_signal_t *> dirty;
{
std::lock_guard<std::mutex> lk(g_dirty_out_mutex);
dirty.swap(g_dirty_out_signals);
}
if (dirty.empty())
{
return;
}
std::sort(dirty.begin(), dirty.end());
auto last = std::unique(dirty.begin(), dirty.end());
for (auto it = dirty.begin(); it != last; ++it)
{
dc_signal_t *ps = *it;
if (!ps->vec_p_data || !ps->vec_p_data[0] || !ps->p_last_data)
{
continue;
}
if (0 == dc_util_val_compare(ps->data_type, ps->vec_p_data[0], ps->p_last_data))
{
continue;
}
for (int i = 0; i < ps->cb_count; i++)
{
if (!ps->cb_list[i].out_cb)
{
continue;
}
if (0 == strcmp(ps->cb_list[i].module_id, ps->last_caller_module))
{
continue;
}
ps->cb_list[i].out_cb(ps->saddr, ps->data_type, ps->vec_p_data[0], ps->p_last_data);
}
dc_util_val_copy(ps->p_last_data, ps->vec_p_data[0], ps->data_type);
}
}
/** @brief 100ms 周期:变化检测 + 事件出队 + 异步超时检查 */
void dc_run_100ms(void)
{
dc_change_out_check();
dc_disturb_dd_queue_pop();
dc_event_queue_pop();
dc_fault_queue_pop();
dc_async_timeout_check();
dc_async_cleanup();
}
/** @brief 1000ms 周期:参数配置文件变更检测 */
void dc_run_1000ms(void)
{
dc_param_cfg_check(g_cfg_dir);
}

View File

@ -0,0 +1,153 @@
/** @file dc_event.cpp — 事件队列SOE/扰动/故障) */
#include "dc_internal.h"
#include <queue>
static std::mutex g_soe_mutex;
static std::queue<void *> g_soe_queue;
static std::vector<dc_queue_pop_cb_t> g_soe_cbs;
static std::mutex g_dist_mutex;
static std::queue<void *> g_dist_queue;
static std::vector<dc_queue_pop_cb_t> g_dist_cbs;
static std::mutex g_fault_mutex;
static std::queue<void *> g_fault_queue;
static std::vector<dc_queue_pop_cb_t> g_fault_cbs;
/** @brief 事件队列弹出内部实现swap 交换后遍历回调 */
static void dc_event_pop_inner(std::mutex &mtx, std::queue<void *> &q,
std::vector<dc_queue_pop_cb_t> &cbs)
{
std::queue<void *> lq;
std::vector<dc_queue_pop_cb_t> lcbs;
{
std::lock_guard<std::mutex> lk(mtx);
lq.swap(q);
lcbs = cbs;
}
while (!lq.empty())
{
void *p = lq.front();
lq.pop();
for (auto &cb : lcbs)
{
cb(p);
}
}
}
/* ================================================================
* SOE
* ================================================================ */
/** @brief 向 SOE 事件队列推送数据 */
int dc_event_queue_push(void *p)
{
if (!p)
{
return DC_ERR_PARAM;
}
std::lock_guard<std::mutex> lk(g_soe_mutex);
g_soe_queue.push(p);
return DC_OK;
}
/** @brief 注册 SOE 事件队列弹出回调 */
int dc_event_register_queue_pop(dc_queue_pop_cb_t cb)
{
if (!cb)
{
return DC_ERR_PARAM;
}
std::lock_guard<std::mutex> lk(g_soe_mutex);
g_soe_cbs.push_back(cb);
return DC_OK;
}
/** @brief 触发 SOE 事件队列弹出 */
void dc_event_queue_pop(void)
{
dc_event_pop_inner(g_soe_mutex, g_soe_queue, g_soe_cbs);
}
/* ================================================================
*
* ================================================================ */
/** @brief 向扰动事件队列推送数据 */
int dc_disturb_dd_queue_push(void *p)
{
if (!p)
{
return DC_ERR_PARAM;
}
std::lock_guard<std::mutex> lk(g_dist_mutex);
g_dist_queue.push(p);
return DC_OK;
}
/** @brief 注册扰动事件队列弹出回调 */
int dc_disturb_dd_register_queue_pop(dc_queue_pop_cb_t cb)
{
if (!cb)
{
return DC_ERR_PARAM;
}
std::lock_guard<std::mutex> lk(g_dist_mutex);
g_dist_cbs.push_back(cb);
return DC_OK;
}
/** @brief 触发扰动事件队列弹出 */
void dc_disturb_dd_queue_pop(void)
{
dc_event_pop_inner(g_dist_mutex, g_dist_queue, g_dist_cbs);
}
/* ================================================================
*
* ================================================================ */
/** @brief 向故障事件队列推送数据 */
int dc_fault_queue_push(void *p)
{
if (!p)
{
return DC_ERR_PARAM;
}
std::lock_guard<std::mutex> lk(g_fault_mutex);
g_fault_queue.push(p);
return DC_OK;
}
/** @brief 注册故障事件队列弹出回调 */
int dc_fault_register_queue_pop(dc_queue_pop_cb_t cb)
{
if (!cb)
{
return DC_ERR_PARAM;
}
std::lock_guard<std::mutex> lk(g_fault_mutex);
g_fault_cbs.push_back(cb);
return DC_OK;
}
/** @brief 触发故障事件队列弹出 */
void dc_fault_queue_pop(void)
{
dc_event_pop_inner(g_fault_mutex, g_fault_queue, g_fault_cbs);
}

View File

@ -0,0 +1,423 @@
/** @file dc_param_cfg.cpp — 参数配置解析与保存(直接调用 tinyxml2 C++ API */
#include "dc_internal.h"
#include "tinyxml2.h"
#include <unordered_map>
/**
* @brief
* @param saddr
* @param param min/max/step/unit
*/
void dc_param_metadata_store(const char *saddr, const dc_param_meta_t &param)
{
g_param_metadata[saddr] = param;
}
/**
* @brief
* @param saddr
* @param out_param
* @return true false
*/
bool dc_param_metadata_lookup(const char *saddr, dc_param_meta_t &out_param)
{
auto it = g_param_metadata.find(saddr);
if (it != g_param_metadata.end())
{
out_param = it->second;
return true;
}
return false;
}
/**
* @brief param.xml Ao/Param min/max/step/unit
* @param path param.xml
* @return 0 -1
*/
static int dc_param_parse_metadata(const char *path)
{
tinyxml2::XMLDocument doc;
if (doc.LoadFile(path) != tinyxml2::XML_SUCCESS)
{
LOG_E("load %s failed\n", path);
return -1;
}
tinyxml2::XMLElement *root = doc.RootElement();
if (!root)
{
return -1;
}
const char *sections[] = {"Ao", "Param"};
for (int si = 0; si < 2; si++)
{
tinyxml2::XMLElement *elem = root->FirstChildElement(sections[si]);
for (tinyxml2::XMLElement *sig = (elem ? elem->FirstChildElement("Signal") : NULL);
sig;
sig = sig->NextSiblingElement("Signal"))
{
const char *saddr = sig->Attribute("saddr");
if (!saddr)
{
continue;
}
dc_param_meta_t m;
m.min = sig->FloatAttribute("min", 0.0f);
m.max = sig->FloatAttribute("max", 0.0f);
m.step = sig->FloatAttribute("step", 0.0f);
const char *u = sig->Attribute("unit");
strncpy(m.unit, u ? u : "", DC_UNIT_MAX_LEN - 1);
g_param_metadata[saddr] = m;
}
}
return 0;
}
/**
* @brief self_param.xml
* @param path self_param.xml
* @return saddrvalue
*/
static std::unordered_map<std::string, std::string> dc_self_param_load(const char *path)
{
std::unordered_map<std::string, std::string> vals;
tinyxml2::XMLDocument doc;
if (doc.LoadFile(path) != tinyxml2::XML_SUCCESS)
{
return vals;
}
tinyxml2::XMLElement *root = doc.RootElement();
if (!root)
{
return vals;
}
const char *sections[] = {"Ao", "Param"};
for (int si = 0; si < 2; si++)
{
tinyxml2::XMLElement *elem = root->FirstChildElement(sections[si]);
for (tinyxml2::XMLElement *sig = (elem ? elem->FirstChildElement("Signal") : NULL);
sig;
sig = sig->NextSiblingElement("Signal"))
{
const char *sa = sig->Attribute("saddr");
if (!sa)
{
continue;
}
if (si == 0)
{
const char *v = sig->Attribute("value");
if (v)
{
vals[sa] = v;
}
}
else
{
for (tinyxml2::XMLElement *item = sig->FirstChildElement("Item");
item;
item = item->NextSiblingElement("Item"))
{
int idx = item->IntAttribute("index", -1);
const char *v = item->Attribute("value");
if (idx >= 1 && v)
{
vals[std::string(sa) + "_" + std::to_string(idx)] = v;
}
}
}
}
}
return vals;
}
/**
* @brief param.xml + self_param.xml Ao Param
* @param path param.xml
* @return 0 -1
*/
int dc_param_cfg_parse(const char *path)
{
if (0 != dc_param_parse_metadata(path))
{
LOG_E("parse metadata failed\n");
return -1;
}
std::string self_path = path;
size_t pos = self_path.rfind("param.xml");
if (pos != std::string::npos)
{
self_path.replace(pos, 9, "self_param.xml");
}
auto self_vals = dc_self_param_load(self_path.c_str());
LOG_I("loaded %zu saved values from self_param.xml\n", self_vals.size());
/* 注册 Ao 和 Param 信号 */
tinyxml2::XMLDocument doc;
if (doc.LoadFile(path) != tinyxml2::XML_SUCCESS)
{
return -1;
}
tinyxml2::XMLElement *root = doc.RootElement();
if (!root)
{
return -1;
}
/* Ao 段 */
tinyxml2::XMLElement *ao_elem = root->FirstChildElement("Ao");
if (ao_elem)
{
for (tinyxml2::XMLElement *sig = ao_elem->FirstChildElement("Signal");
sig;
sig = sig->NextSiblingElement("Signal"))
{
const char *sa = sig->Attribute("saddr");
const char *de = sig->Attribute("desc");
const char *ty = sig->Attribute("type");
if (!sa || !de || !ty)
{
continue;
}
uint8_t dt = dc_util_type_id_by_name(ty);
if (!dt)
{
continue;
}
auto it = self_vals.find(sa);
std::string rv = (it != self_vals.end()) ? it->second : "";
if (rv.empty())
{
const char *va = sig->Attribute("value");
if (va)
{
rv = va;
}
}
void *pd = dc_util_create_data(dt);
if (pd && !rv.empty())
{
dc_util_val_from_string(pd, dt, rv);
}
if (pd)
{
dc_signal_ao(sa, de, dt, DC_CTRL_SBO, pd, NULL, MODULE_DATACENTER);
}
}
}
/* Param 段 */
tinyxml2::XMLElement *param_elem = root->FirstChildElement("Param");
if (param_elem)
{
for (tinyxml2::XMLElement *sig = param_elem->FirstChildElement("Signal");
sig;
sig = sig->NextSiblingElement("Signal"))
{
const char *sa = sig->Attribute("saddr");
const char *de = sig->Attribute("desc");
const char *ty = sig->Attribute("type");
if (!sa || !de || !ty)
{
continue;
}
uint8_t dt = dc_util_type_id_by_name(ty);
if (!dt)
{
continue;
}
int num = sig->IntAttribute("num", -1);
if (num < 1)
{
continue;
}
std::vector<void *> vec;
for (tinyxml2::XMLElement *item = sig->FirstChildElement("Item");
item;
item = item->NextSiblingElement("Item"))
{
int idx = item->IntAttribute("index", -1);
std::string key = std::string(sa) + "_" + std::to_string(idx);
auto it = self_vals.find(key);
std::string rv = (it != self_vals.end()) ? it->second : "";
const char *va = item->Attribute("value");
if (rv.empty() && va)
{
rv = va;
}
void *pd = dc_util_create_data(dt);
if (pd && !rv.empty())
{
dc_util_val_from_string(pd, dt, rv);
}
if (pd)
{
vec.push_back(pd);
}
}
if (!vec.empty())
{
dc_signal_param(sa, de, dt, DC_CTRL_SBO, vec.data(), num, NULL,
MODULE_DATACENTER);
}
}
}
g_param_cfg_changed = true;
return 0;
}
/**
* @brief self_param.xml
* @param path self_param.xml
*/
void dc_param_cfg_check(const char *path)
{
if (!g_param_cfg_changed || !path || !path[0])
{
return;
}
tinyxml2::XMLDocument doc;
doc.InsertEndChild(doc.NewDeclaration());
tinyxml2::XMLElement *root = doc.NewElement("Root");
doc.InsertEndChild(root);
/* Ao 段 */
tinyxml2::XMLElement *ao_elem = doc.NewElement("Ao");
for (uint32_t i = 0; i < g_dc_ao.signal_id; i++)
{
dc_signal_t *ps = dc_core_find_by_id(i, g_dc_ao);
if (!ps || !ps->vec_p_data || !ps->vec_p_data[0])
{
continue;
}
std::string v = dc_util_val_to_string(ps->vec_p_data[0], ps->data_type);
tinyxml2::XMLElement *sig = doc.NewElement("Signal");
sig->SetAttribute("saddr", ps->saddr);
sig->SetAttribute("value", v.c_str());
ao_elem->InsertEndChild(sig);
}
root->InsertEndChild(ao_elem);
/* Param 段 */
tinyxml2::XMLElement *param_elem = doc.NewElement("Param");
for (uint32_t i = 0; i < g_dc_param.signal_id; i++)
{
dc_signal_t *ps = dc_core_find_by_id(i, g_dc_param);
if (!ps || !ps->vec_p_data)
{
continue;
}
tinyxml2::XMLElement *sig = doc.NewElement("Signal");
sig->SetAttribute("saddr", ps->saddr);
for (int z = 0; z < ps->vec_p_data_count; z++)
{
if (!ps->vec_p_data[z])
{
continue;
}
std::string v = dc_util_val_to_string(ps->vec_p_data[z], ps->data_type);
tinyxml2::XMLElement *item = doc.NewElement("Item");
item->SetAttribute("index", z + 1);
item->SetAttribute("value", v.c_str());
sig->InsertEndChild(item);
}
param_elem->InsertEndChild(sig);
}
root->InsertEndChild(param_elem);
std::string self_path = path;
size_t pos = self_path.rfind("param.xml");
if (pos != std::string::npos)
{
self_path.replace(pos, 9, "self_param.xml");
}
if (doc.SaveFile(self_path.c_str()) == tinyxml2::XML_SUCCESS)
{
LOG_I("param saved to %s\n", self_path.c_str());
}
else
{
LOG_E("failed to save param to %s\n", self_path.c_str());
}
g_param_cfg_changed = false;
}

View File

@ -0,0 +1,247 @@
/** @file dc_signal_ao.cpp — AO 信号注册/控制/异步执行 */
#include "dc_internal.h"
/**
* @brief AO
*/
extern "C" int dc_signal_ao(const char *saddr, const char *desc, uint8_t data_type, uint8_t ctrl_type, void *p_data, dc_signal_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *existing = dc_core_find(saddr, g_dc_ao);
if (existing)
{
dc_util_val_copy(p_data, existing->vec_p_data[0], existing->data_type);
}
dc_signal_t sig = {};
sig.hash = XXH3_128bits(saddr, strlen(saddr));
strncpy(sig.saddr, saddr, DC_SADDR_MAX_LEN - 1);
if (desc)
{
strncpy(sig.desc, desc, DC_DESC_MAX_LEN - 1);
}
sig.data_type = data_type;
sig.ctrl_type = ctrl_type;
sig.vec_p_data = (void **)malloc(sizeof(void *));
sig.vec_p_data[0] = p_data;
sig.vec_p_data_count = 1;
if (existing)
{
for (int i = 0; i < existing->cb_count && sig.cb_count < DC_MAX_CALLBACKS; i++)
{
sig.cb_list[sig.cb_count] = existing->cb_list[i];
sig.cb_count++;
}
}
if (cb && sig.cb_count < DC_MAX_CALLBACKS)
{
strncpy(sig.cb_list[sig.cb_count].module_id, module_id, DC_MODULE_MAX_LEN - 1);
sig.cb_list[sig.cb_count].ctrl_cb = cb;
sig.cb_count++;
}
g_param_cfg_changed = true;
return dc_core_add(sig, g_dc_ao);
}
/**
* @brief AO *p_data vec_p_data[0] cb
*/
extern "C" int dc_signal_ao_link_with_callback(const char *saddr, void **p_data, dc_signal_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_ao);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (!ps->vec_p_data || !ps->vec_p_data[0])
{
return DC_ERR_PARAM;
}
*p_data = ps->vec_p_data[0];
if (cb && ps->cb_count < DC_MAX_CALLBACKS)
{
strncpy(ps->cb_list[ps->cb_count].module_id, module_id, DC_MODULE_MAX_LEN - 1);
ps->cb_list[ps->cb_count].ctrl_cb = cb;
ps->cb_count++;
}
return DC_OK;
}
/**
* @brief AO ctrl step DIRECT ctrl.step = READY module_id changed
*/
extern "C" int dc_signal_ao_set_val(const char *saddr, dc_ctrl_step_t step, dc_ctrl_t *ctrl, const void *p_data, const char *module_id)
{
if (!g_initialized || !saddr || !ctrl || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_ao);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
int ret = dc_check_ctrl_valid(ps, step, ctrl, p_data);
if (ret != DC_OK)
{
return ret;
}
if (step == DC_STEP_SELECT)
{
dc_util_val_copy(ctrl->p_data, p_data, ps->data_type);
ctrl->step = DC_STEP_SELECT;
}
else if (step == DC_STEP_DIRECT)
{
dc_util_val_copy(ps->vec_p_data[0], p_data, ps->data_type);
ctrl->step = DC_STEP_READY;
}
else if (step == DC_STEP_CANCEL)
{
ctrl->step = DC_STEP_READY;
}
for (int i = 0; i < ps->cb_count; i++)
{
if (!ps->cb_list[i].ctrl_cb)
{
continue;
}
if (0 == strcmp(ps->cb_list[i].module_id, module_id))
{
continue;
}
ps->cb_list[i].ctrl_cb(ps->saddr, step, ps->data_type, 0, ps->vec_p_data[0]);
}
g_param_cfg_changed = true;
return DC_OK;
}
/**
* @brief ctrl AO module_id changed
*/
extern "C" int dc_signal_ao_set_val_without_check(const char *saddr, uint8_t data_type, const void *p_data, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_ao);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
dc_util_val_copy(ps->vec_p_data[0], p_data, data_type);
for (int i = 0; i < ps->cb_count; i++)
{
if (!ps->cb_list[i].ctrl_cb)
{
continue;
}
if (0 == strcmp(ps->cb_list[i].module_id, module_id))
{
continue;
}
ps->cb_list[i].ctrl_cb(ps->saddr, DC_STEP_DIRECT, ps->data_type, 0, ps->vec_p_data[0]);
}
g_param_cfg_changed = true;
return DC_OK;
}
/**
* @brief AO
*/
extern "C" int dc_ao_exec_async(const char *saddr, dc_ctrl_step_t step, const dc_ctrl_t *ctrl, const void *p_data, const char *module_id, dc_async_result_cb_t result_cb, void *user_arg, uint32_t timeout_ms, uint32_t *request_id)
{
if (!g_initialized || !saddr || !ctrl || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
uint32_t req_id = dc_async_enqueue(g_dc_ao, saddr, step, ctrl, p_data, 0, 1, module_id, result_cb, user_arg, timeout_ms);
if (request_id)
{
*request_id = req_id;
}
return (req_id == 0) ? DC_ERR_FULL : DC_OK;
}
/**
* @brief B AO
*/
extern "C" int dc_ao_set_result(uint32_t request_id, int result_code, const char *err_msg)
{
return dc_async_set_result(request_id, result_code, err_msg);
}
/**
* @brief AO
*/
extern "C" int dc_ao_cancel_async(uint32_t request_id)
{
return dc_async_cancel(request_id);
}
/**
* @brief AO B
*/
extern "C" int dc_ao_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb, void *module_ctx)
{
if (!g_initialized || !saddr)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_ao);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
ps->exec_cb = exec_cb;
ps->module_ctx = module_ctx;
return DC_OK;
}

View File

@ -0,0 +1,105 @@
/** @file dc_signal_check.cpp — 控制校验 + 值校验 */
#include "dc_internal.h"
/** @brief 校验直控值是否与选控暂存值一致 */
int dc_check_ctrl_val_valid(const dc_ctrl_t *ctrl, const void *p_data)
{
uint8_t len = dc_util_type_len(ctrl->data_type);
if (!len)
{
return DC_ERR_TYPE;
}
if (0 != memcmp(ctrl->p_data, p_data, len))
{
LOG_E("direct val != selected val\n");
return DC_ERR_VAL;
}
return DC_OK;
}
/** @brief SBO 选控-直控-撤销完整流程校验 */
int dc_check_ctrl_valid(const dc_signal_t *ps, dc_ctrl_step_t step,
dc_ctrl_t *ctrl, const void *p_data)
{
if (ps->data_type != ctrl->data_type)
{
LOG_E("type mismatch %d!=%d\n", ps->data_type, ctrl->data_type);
return DC_ERR_TYPE;
}
if (ctrl->type == DC_CTRL_DIRECT)
{
if (step == DC_STEP_DIRECT)
{
return DC_OK;
}
return DC_ERR_STEP;
}
if (ctrl->type == DC_CTRL_SBO)
{
if (ctrl->step == DC_STEP_READY && step == DC_STEP_SELECT)
{
return DC_OK;
}
if (ctrl->step == DC_STEP_SELECT)
{
if (step == DC_STEP_DIRECT)
{
return dc_check_ctrl_val_valid(ctrl, p_data);
}
if (step == DC_STEP_CANCEL)
{
return DC_OK;
}
}
}
LOG_E("invalid ctrl type=%d step=%d\n", ctrl->type, step);
return DC_ERR_STEP;
}
/** @brief 校验新值与当前值是否不同(变更检测) */
int dc_check_val_valid(const dc_signal_t *ps, uint8_t zone, const void *p_data)
{
if (zone >= (uint8_t)ps->vec_p_data_count)
{
LOG_E("zone %d out of range\n", zone);
return DC_ERR_PARAM;
}
const void *cur = ps->vec_p_data[zone];
if (0 == dc_util_val_compare(ps->data_type, cur, p_data))
{
LOG_E("val not changed\n");
return DC_ERR_VAL;
}
return DC_OK;
}
/** @brief 校验遥控值有效性(仅允许 0/1 */
int dc_check_yk_val_valid(dc_signal_t *ps, void *p_data)
{
if (ps->data_type == DATA_TYPE_B || ps->data_type == DATA_TYPE_U8)
{
uint8_t v = *(uint8_t *)p_data;
if (v != 0 && v != 1)
{
LOG_E("invalid yk val %d\n", v);
return DC_ERR_VAL;
}
return DC_OK;
}
return DC_ERR_TYPE;
}

View File

@ -0,0 +1,79 @@
/** @file dc_signal_in.cpp — In 信号注册 */
#include "dc_internal.h"
/** @brief In 信号注册内部实现:关联 out 信号、建立链接、追加回调 */
static int dc_in_register_impl(const char *saddr, const char *desc, const char *link_saddr, void **p_data, dc_out_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !link_saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
if (!link_saddr[0])
{
return DC_ERR_PARAM;
}
if (dc_core_find_in(saddr))
{
LOG_E("in %s exists\n", saddr);
return DC_ERR_EXISTS;
}
dc_signal_t *ps_out = dc_core_find_out(link_saddr);
if (!ps_out)
{
LOG_E("link out %s not found\n", link_saddr);
return DC_ERR_NOTFOUND;
}
*p_data = ps_out->vec_p_data[0];
dc_signal_t sig = {};
sig.hash = XXH3_128bits(saddr, strlen(saddr));
strncpy(sig.saddr, saddr, DC_SADDR_MAX_LEN - 1);
if (desc)
{
strncpy(sig.desc, desc, DC_DESC_MAX_LEN - 1);
}
sig.data_type = ps_out->data_type;
strncpy(sig.link_saddr, link_saddr, DC_SADDR_MAX_LEN - 1);
sig.vec_p_data = (void **)malloc(sizeof(void *));
sig.vec_p_data[0] = *p_data;
sig.vec_p_data_count = 1;
/* out 记录 in 的链接 */
char **nl = (char **)realloc(ps_out->link_saddrs, (ps_out->link_count + 1) * sizeof(char *));
nl[ps_out->link_count] = strdup(saddr);
ps_out->link_saddrs = nl;
ps_out->link_count++;
if (cb && ps_out->cb_count < DC_MAX_CALLBACKS)
{
strncpy(ps_out->cb_list[ps_out->cb_count].module_id, module_id, DC_MODULE_MAX_LEN - 1);
ps_out->cb_list[ps_out->cb_count].out_cb = cb;
ps_out->cb_count++;
}
return dc_core_add(sig, g_dc_in);
}
/** @brief 注册 in 信号(链接到 out无回调 */
int dc_signal_in(const char *saddr, const char *desc, const char *link_saddr, void **p_data, const char *module_id)
{
return dc_in_register_impl(saddr, desc, link_saddr, p_data, NULL, module_id);
}
/** @brief 注册 in 信号(链接到 out带变更回调 */
int dc_signal_in_with_callback(const char *saddr, const char *desc, const char *link_saddr, void **p_data, dc_out_change_cb_t cb, const char *module_id)
{
return dc_in_register_impl(saddr, desc, link_saddr, p_data, cb, module_id);
}

View File

@ -0,0 +1,120 @@
/** @file dc_signal_out.cpp — Out 信号注册/链接/设值 */
#include "dc_internal.h"
/** @brief Out 信号注册内部实现:创建信号、分配内存、注册回调 */
static int dc_out_register_impl(const char *saddr, const char *desc, uint8_t data_type, void *p_data, dc_out_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
if (dc_core_find_out(saddr))
{
LOG_E("out %s exists\n", saddr);
return DC_ERR_EXISTS;
}
dc_signal_t sig = {};
sig.hash = XXH3_128bits(saddr, strlen(saddr));
strncpy(sig.saddr, saddr, DC_SADDR_MAX_LEN - 1);
if (desc)
{
strncpy(sig.desc, desc, DC_DESC_MAX_LEN - 1);
}
sig.data_type = data_type;
sig.vec_p_data = (void **)malloc(sizeof(void *));
sig.vec_p_data[0] = p_data;
sig.vec_p_data_count = 1;
sig.p_last_data = dc_util_create_data(data_type);
if (!sig.p_last_data)
{
free(sig.vec_p_data);
return DC_ERR_MEM;
}
dc_util_val_copy(sig.p_last_data, p_data, data_type);
if (cb)
{
strncpy(sig.cb_list[0].module_id, module_id, DC_MODULE_MAX_LEN - 1);
sig.cb_list[0].out_cb = cb;
sig.cb_count = 1;
}
return dc_core_add(sig, g_dc_out);
}
/** @brief 注册 out 信号(无回调) */
int dc_signal_out(const char *saddr, const char *desc, uint8_t data_type, void *p_data, const char *module_id)
{
return dc_out_register_impl(saddr, desc, data_type, p_data, NULL, module_id);
}
/** @brief 注册 out 信号(带变更回调) */
int dc_signal_out_with_callback(const char *saddr, const char *desc, uint8_t data_type, void *p_data, dc_out_change_cb_t cb, const char *module_id)
{
return dc_out_register_impl(saddr, desc, data_type, p_data, cb, module_id);
}
/** @brief 链接已有 out 信号并追加回调 */
int dc_signal_out_link_with_callback(const char *saddr, void **p_data, dc_out_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find_out(saddr);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (!ps->vec_p_data || !ps->vec_p_data[0])
{
return DC_ERR_PARAM;
}
*p_data = ps->vec_p_data[0];
if (cb && ps->cb_count < DC_MAX_CALLBACKS)
{
strncpy(ps->cb_list[ps->cb_count].module_id, module_id, DC_MODULE_MAX_LEN - 1);
ps->cb_list[ps->cb_count].out_cb = cb;
ps->cb_count++;
}
return DC_OK;
}
/** @brief 设置 out 信号值(标记脏 + 回路阻断) */
int dc_set_out_signal_val(const char *saddr, const void *set_data, const char *module_id)
{
if (!g_initialized || !saddr || !set_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find_out(saddr);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
strncpy(ps->last_caller_module, module_id, DC_MODULE_MAX_LEN - 1);
dc_core_mark_dirty(ps);
dc_util_val_copy(ps->vec_p_data[0], set_data, ps->data_type);
return DC_OK;
}

View File

@ -0,0 +1,290 @@
/** @file dc_signal_param.cpp — Param 信号注册/控制/异步执行 */
#include "dc_internal.h"
/**
* @brief Param zone metadata_lookup changed
*/
extern "C" int dc_signal_param(const char *saddr, const char *desc, uint8_t data_type, uint8_t ctrl_type, void **p_data, int num_zones, dc_signal_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
if (num_zones <= 0 || num_zones > DC_MAX_DATA_ZONES)
{
return DC_ERR_PARAM;
}
dc_signal_t *existing = dc_core_find(saddr, g_dc_param);
if (existing && existing->data_type != data_type)
{
LOG_E("param %s data_type mismatch %d!=%d\n", saddr, existing->data_type, data_type);
return DC_ERR_TYPE;
}
if (existing)
{
for (int i = 0; i < num_zones && i < existing->vec_p_data_count; i++)
{
dc_util_val_copy(p_data[i], existing->vec_p_data[i], existing->data_type);
}
}
dc_signal_t sig = {};
sig.hash = XXH3_128bits(saddr, strlen(saddr));
strncpy(sig.saddr, saddr, DC_SADDR_MAX_LEN - 1);
if (desc)
{
strncpy(sig.desc, desc, DC_DESC_MAX_LEN - 1);
}
sig.data_type = data_type;
sig.ctrl_type = ctrl_type;
sig.vec_p_data = (void **)malloc(sizeof(void *) * num_zones);
for (int i = 0; i < num_zones; i++)
{
sig.vec_p_data[i] = p_data[i];
}
sig.vec_p_data_count = num_zones;
if (!existing)
{
dc_param_meta_t meta;
if (dc_param_metadata_lookup(saddr, meta))
{
sig.param = meta;
}
}
else
{
sig.param = existing->param;
for (int i = 0; i < existing->cb_count && sig.cb_count < DC_MAX_CALLBACKS; i++)
{
sig.cb_list[sig.cb_count] = existing->cb_list[i];
sig.cb_count++;
}
}
if (cb && sig.cb_count < DC_MAX_CALLBACKS)
{
strncpy(sig.cb_list[sig.cb_count].module_id, module_id, DC_MODULE_MAX_LEN - 1);
sig.cb_list[sig.cb_count].ctrl_cb = cb;
sig.cb_count++;
}
g_param_cfg_changed = true;
return dc_core_add(sig, g_dc_param);
}
/**
* @brief Param zone p_data vec_p_data cb
*/
extern "C" int dc_signal_param_link_with_callback(const char *saddr, void **p_data, int num_zones, dc_signal_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_param);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (num_zones > ps->vec_p_data_count)
{
return DC_ERR_PARAM;
}
for (int i = 0; i < num_zones; i++)
{
p_data[i] = ps->vec_p_data[i];
}
if (cb && ps->cb_count < DC_MAX_CALLBACKS)
{
strncpy(ps->cb_list[ps->cb_count].module_id, module_id, DC_MODULE_MAX_LEN - 1);
ps->cb_list[ps->cb_count].ctrl_cb = cb;
ps->cb_count++;
}
return DC_OK;
}
/**
* @brief Param ctrl + val step module_id changed
*/
extern "C" int dc_signal_param_set_val(const char *saddr, dc_ctrl_step_t step, dc_ctrl_t *ctrl, uint8_t setting_zone, const void *p_data, const char *module_id)
{
if (!g_initialized || !saddr || !ctrl || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_param);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
int ret = dc_check_ctrl_valid(ps, step, ctrl, p_data);
if (ret != DC_OK)
{
return ret;
}
ret = dc_check_val_valid(ps, setting_zone, p_data);
if (ret != DC_OK)
{
return ret;
}
if (step == DC_STEP_SELECT)
{
dc_util_val_copy(ctrl->p_data, p_data, ps->data_type);
}
else if (step == DC_STEP_DIRECT)
{
dc_util_val_copy(ps->vec_p_data[setting_zone], p_data, ps->data_type);
}
/* DC_STEP_CANCEL: no data operation */
ctrl->step = step;
for (int i = 0; i < ps->cb_count; i++)
{
if (!ps->cb_list[i].ctrl_cb)
{
continue;
}
if (0 == strcmp(ps->cb_list[i].module_id, module_id))
{
continue;
}
ps->cb_list[i].ctrl_cb(ps->saddr, step, ps->data_type, setting_zone, ps->vec_p_data[setting_zone]);
}
g_param_cfg_changed = true;
return DC_OK;
}
/**
* @brief ctrl Param zone module_id changed
*/
extern "C" int dc_signal_param_set_val_without_check(const char *saddr, uint8_t data_type, uint8_t setting_zone, const void *p_data, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_param);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (setting_zone >= (uint8_t)ps->vec_p_data_count)
{
return DC_ERR_PARAM;
}
dc_util_val_copy(ps->vec_p_data[setting_zone], p_data, data_type);
for (int i = 0; i < ps->cb_count; i++)
{
if (!ps->cb_list[i].ctrl_cb)
{
continue;
}
if (0 == strcmp(ps->cb_list[i].module_id, module_id))
{
continue;
}
ps->cb_list[i].ctrl_cb(ps->saddr, DC_STEP_DIRECT, ps->data_type, setting_zone, ps->vec_p_data[setting_zone]);
}
g_param_cfg_changed = true;
return DC_OK;
}
/**
* @brief Param
*/
extern "C" int dc_param_exec_async(const char *saddr, dc_ctrl_step_t step, const dc_ctrl_t *ctrl, uint8_t setting_zone, const void *p_data, const char *module_id, dc_async_result_cb_t result_cb, void *user_arg, uint32_t timeout_ms, uint32_t *request_id)
{
if (!g_initialized || !saddr || !ctrl || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
uint32_t req_id = dc_async_enqueue(g_dc_param, saddr, step, ctrl, p_data, setting_zone, 2, module_id, result_cb, user_arg, timeout_ms);
if (request_id)
{
*request_id = req_id;
}
return (req_id == 0) ? DC_ERR_FULL : DC_OK;
}
/**
* @brief B Param
*/
extern "C" int dc_param_set_result(uint32_t request_id, int result_code, const char *err_msg)
{
return dc_async_set_result(request_id, result_code, err_msg);
}
/**
* @brief Param
*/
extern "C" int dc_param_cancel_async(uint32_t request_id)
{
return dc_async_cancel(request_id);
}
/**
* @brief Param B
*/
extern "C" int dc_param_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb, void *module_ctx)
{
if (!g_initialized || !saddr)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_param);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
ps->exec_cb = exec_cb;
ps->module_ctx = module_ctx;
return DC_OK;
}

View File

@ -0,0 +1,336 @@
/** @file dc_signal_query.cpp — 信号查询 */
#include "dc_internal.h"
/** @brief 通用信号信息查询内部函数 */
static int dc_query_info(dc_signal_map_t *pmap, const char *saddr,
char *desc, int desc_len, uint8_t *data_type, void **p_data)
{
if (!g_initialized || !saddr)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, *pmap);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (desc && desc_len > 0)
{
strncpy(desc, ps->desc, desc_len - 1);
desc[desc_len - 1] = '\0';
}
if (data_type)
{
*data_type = ps->data_type;
}
if (p_data && ps->vec_p_data && ps->vec_p_data_count > 0)
{
*p_data = ps->vec_p_data[0];
}
return DC_OK;
}
/** @brief 查询 out 信号信息 */
int dc_get_out_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, void **p_data)
{
return dc_query_info(&g_dc_out, saddr, desc, desc_len, data_type, p_data);
}
/** @brief 查询 in 信号信息 */
int dc_get_in_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, void **p_data)
{
return dc_query_info(&g_dc_in, saddr, desc, desc_len, data_type, p_data);
}
/** @brief 查询 AO 信号信息(含参数元数据和控型) */
int dc_get_ao_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, dc_param_meta_t *p_meta, uint8_t *ctrl_type,
void **p_data)
{
if (!g_initialized || !saddr)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_ao);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (desc && desc_len > 0)
{
strncpy(desc, ps->desc, desc_len - 1);
desc[desc_len - 1] = '\0';
}
if (data_type)
{
*data_type = ps->data_type;
}
if (p_meta)
{
*p_meta = ps->param;
}
if (ctrl_type)
{
*ctrl_type = ps->ctrl_type;
}
if (p_data && ps->vec_p_data && ps->vec_p_data_count > 0)
{
*p_data = ps->vec_p_data[0];
}
return DC_OK;
}
/** @brief 查询定值信号信息(含多定值区数据指针数组) */
int dc_get_param_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, dc_param_meta_t *p_meta, uint8_t *ctrl_type,
void **p_data_array, int *p_count)
{
if (!g_initialized || !saddr)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_param);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (desc && desc_len > 0)
{
strncpy(desc, ps->desc, desc_len - 1);
desc[desc_len - 1] = '\0';
}
if (data_type)
{
*data_type = ps->data_type;
}
if (p_meta)
{
*p_meta = ps->param;
}
if (ctrl_type)
{
*ctrl_type = ps->ctrl_type;
}
if (p_data_array && p_count)
{
*p_count = ps->vec_p_data_count;
for (int i = 0; i < ps->vec_p_data_count; i++)
{
p_data_array[i] = ps->vec_p_data[i];
}
}
return DC_OK;
}
/** @brief 查询遥控信号信息 */
int dc_get_yk_signal_info(const char *saddr, char *desc, int desc_len,
uint8_t *data_type, uint8_t *ctrl_type, void **p_data)
{
if (!g_initialized || !saddr)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_yk);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (desc && desc_len > 0)
{
strncpy(desc, ps->desc, desc_len - 1);
desc[desc_len - 1] = '\0';
}
if (data_type)
{
*data_type = ps->data_type;
}
if (ctrl_type)
{
*ctrl_type = ps->ctrl_type;
}
if (p_data && ps->vec_p_data && ps->vec_p_data_count > 0)
{
*p_data = ps->vec_p_data[0];
}
return DC_OK;
}
/** @brief 将信号值按类型转为字符串写入 buf */
int dc_get_signal_val(const void *p_data, uint8_t data_type,
char *buf, int buf_len)
{
if (!p_data || !buf || buf_len <= 0)
{
return DC_ERR_PARAM;
}
std::string s = dc_util_val_to_string(p_data, data_type);
if (s.empty())
{
return DC_ERR_TYPE;
}
strncpy(buf, s.c_str(), buf_len - 1);
buf[buf_len - 1] = '\0';
return DC_OK;
}
/** @brief 按信号类型字符串获取已注册信号数量 */
int dc_get_signal_count(const char *type_str)
{
dc_signal_map_t *pmap = NULL;
if (type_str)
{
if (0 == strcmp(type_str, "out"))
{
pmap = &g_dc_out;
}
else if (0 == strcmp(type_str, "in"))
{
pmap = &g_dc_in;
}
else if (0 == strcmp(type_str, "yk"))
{
pmap = &g_dc_yk;
}
else if (0 == strcmp(type_str, "ao"))
{
pmap = &g_dc_ao;
}
else if (0 == strcmp(type_str, "param"))
{
pmap = &g_dc_param;
}
}
if (!pmap)
{
return 0;
}
return (int)pmap->signal_id;
}
/** @brief 按信号类型和 ID 获取信号详细信息 */
int dc_get_signal_info_by_id(const char *type_str, uint32_t id,
char *saddr, int saddr_len, char *desc, int desc_len,
char *data_type_str, int type_str_len, uint8_t *ctrl_type,
char *link_str, int link_len)
{
if (!type_str)
{
return DC_ERR_PARAM;
}
dc_signal_map_t *pmap = NULL;
if (0 == strcmp(type_str, "out"))
{
pmap = &g_dc_out;
}
else if (0 == strcmp(type_str, "in"))
{
pmap = &g_dc_in;
}
else if (0 == strcmp(type_str, "yk"))
{
pmap = &g_dc_yk;
}
else if (0 == strcmp(type_str, "ao"))
{
pmap = &g_dc_ao;
}
else if (0 == strcmp(type_str, "param"))
{
pmap = &g_dc_param;
}
if (!pmap)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find_by_id(id, *pmap);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (saddr && saddr_len > 0)
{
strncpy(saddr, ps->saddr, saddr_len - 1);
saddr[saddr_len - 1] = '\0';
}
if (desc && desc_len > 0)
{
strncpy(desc, ps->desc, desc_len - 1);
desc[desc_len - 1] = '\0';
}
if (data_type_str && type_str_len > 0)
{
std::string n = dc_util_type_name(ps->data_type);
strncpy(data_type_str, n.c_str(), type_str_len - 1);
data_type_str[type_str_len - 1] = '\0';
}
if (ctrl_type)
{
*ctrl_type = ps->ctrl_type;
}
if (link_str && link_len > 0)
{
link_str[0] = '\0';
for (int i = 0; i < ps->link_count; i++)
{
if (i > 0)
{
strncat(link_str, ",", link_len - strlen(link_str) - 1);
}
strncat(link_str, ps->link_saddrs[i],
link_len - strlen(link_str) - 1);
}
}
return DC_OK;
}

View File

@ -0,0 +1,205 @@
/** @file dc_signal_show.cpp — 信号展示/导出tinyxml2 C++ API + libcmd C 接口) */
#include "dc_internal.h"
#include "myCmd.h"
#include "tinyxml2.h"
#include <cstdio>
#include <iostream>
extern "C" {
/**
* @brief
* @param smap
*/
static void dc_show_signals(dc_signal_map_t &smap)
{
const int COL_ID = 8;
const int COL_SADDR = 36;
const int COL_DESC = 48;
const int COL_TYPE = 10;
const int COL_VAL = 24;
const int COL_LINK = 48;
auto pad_right = [](const std::string &s, int w) -> std::string
{
int pad = w - (int)s.length();
if (pad > 0)
{
return s + std::string((size_t)pad, ' ');
}
return s + ' ';
};
std::cout << "\t"
<< pad_right("id", COL_ID)
<< pad_right("saddr", COL_SADDR)
<< pad_right("desc", COL_DESC)
<< pad_right("type", COL_TYPE)
<< pad_right("val", COL_VAL)
<< pad_right("link_saddrs", COL_LINK)
<< std::endl;
int total = COL_ID + COL_SADDR + COL_DESC + COL_TYPE + COL_VAL + COL_LINK;
std::cout << "\t" << std::string((size_t)total, '-') << std::endl;
for (uint32_t i = 0; i < smap.signal_id; i++)
{
dc_signal_t *ps = dc_core_find_by_id(i, smap);
if (!ps)
{
continue;
}
std::string link_str;
for (int j = 0; j < ps->link_count; j++)
{
if (j > 0)
{
link_str += ", ";
}
link_str += ps->link_saddrs[j];
}
std::string val_str = dc_util_val_to_string(ps->vec_p_data[0],
ps->data_type);
std::cout << "\t"
<< pad_right(std::to_string(ps->id), COL_ID)
<< pad_right(ps->saddr, COL_SADDR)
<< pad_right(ps->desc, COL_DESC)
<< pad_right(dc_util_type_name(ps->data_type), COL_TYPE)
<< pad_right(val_str, COL_VAL)
<< pad_right(link_str, COL_LINK)
<< std::endl;
}
}
/**
* @brief datacenter out/in/yk/ao/param/all
*/
static void cmd_dc_func(int argc, char *argv[])
{
const char *help = "datacenter out|in|yk|ao|param|all\n";
if (argc < 2)
{
printf("%s", help);
return;
}
if (0 == strcmp(argv[1], "out"))
{
printf("out signals:\n");
dc_show_signals(g_dc_out);
}
else if (0 == strcmp(argv[1], "in"))
{
printf("in signals:\n");
dc_show_signals(g_dc_in);
}
else if (0 == strcmp(argv[1], "yk"))
{
printf("yk signals:\n");
dc_show_signals(g_dc_yk);
}
else if (0 == strcmp(argv[1], "ao"))
{
printf("ao signals:\n");
dc_show_signals(g_dc_ao);
}
else if (0 == strcmp(argv[1], "param"))
{
printf("param signals:\n");
dc_show_signals(g_dc_param);
}
else if (0 == strcmp(argv[1], "all"))
{
printf("out signals:\n");
dc_show_signals(g_dc_out);
printf("in signals:\n");
dc_show_signals(g_dc_in);
printf("yk signals:\n");
dc_show_signals(g_dc_yk);
printf("ao signals:\n");
dc_show_signals(g_dc_ao);
printf("param signals:\n");
dc_show_signals(g_dc_param);
}
}
/**
* @brief datacenter Tab
*/
static void cmd_dc_complete_func(const char *buf, char ***completions, int *ncomp)
{
static const char *subs[] = {"out", "in", "yk", "ao", "param", "all"};
cmd_sub_complete(buf, completions, ncomp, subs,
(int)(sizeof(subs) / sizeof(subs[0])));
}
CMD_REGISTER_C("datacenter", cmd_dc_func, "datacenter信号查看", cmd_dc_complete_func);
/**
* @brief out XMLPLC
* @param path
* @return DC_OK <0
*/
int dc_save_out_signals_xml(const char *path)
{
if (!g_initialized || !path)
{
return DC_ERR_PARAM;
}
tinyxml2::XMLDocument doc;
doc.InsertEndChild(doc.NewDeclaration());
tinyxml2::XMLElement *root = doc.NewElement("Root");
doc.InsertEndChild(root);
tinyxml2::XMLElement *out_elem = doc.NewElement("Out");
out_elem->SetAttribute("count", (int)g_dc_out.signal_id);
for (uint32_t i = 0; i < g_dc_out.signal_id; i++)
{
dc_signal_t *ps = dc_core_find_by_id(i, g_dc_out);
if (!ps)
{
continue;
}
tinyxml2::XMLElement *sig = doc.NewElement("Signal");
sig->SetAttribute("no", (int)i);
sig->SetAttribute("saddr", ps->saddr);
sig->SetAttribute("desc", ps->desc);
out_elem->InsertEndChild(sig);
}
root->InsertEndChild(out_elem);
if (doc.SaveFile(path) != tinyxml2::XML_SUCCESS)
{
LOG_E("failed to save out signals to %s\n", path);
return DC_ERR_PARAM;
}
LOG_I("out signals saved to %s, count=%u\n", path, g_dc_out.signal_id);
return DC_OK;
}
} /* extern "C" */

View File

@ -0,0 +1,272 @@
/** @file dc_signal_util.cpp — 类型工具函数 */
#include "dc_internal.h"
struct { uint8_t id; const char *name; uint8_t len; } static const kTypeTab[] = {
{DATA_TYPE_B, "bool", 1},
{DATA_TYPE_S8, "int8_t", 1},
{DATA_TYPE_U8, "uint8_t", 1},
{DATA_TYPE_S16, "int16_t", 2},
{DATA_TYPE_U16, "uint16_t", 2},
{DATA_TYPE_S32, "int32_t", 4},
{DATA_TYPE_U32, "uint32_t", 4},
{DATA_TYPE_L64, "int64_t", 8},
{DATA_TYPE_UL64,"uint64_t", 8},
{DATA_TYPE_F32, "float", 4},
{DATA_TYPE_D64, "double", 8},
{DATA_TYPE_IP, "ip", 4},
{DATA_TYPE_MAC, "mac", 8},
{DATA_TYPE_C8, "char8", 8},
{DATA_TYPE_C32, "char32", 32},
{DATA_TYPE_C64, "char64", 64},
{DATA_TYPE_C128,"char128", 128},
{DATA_TYPE_C1, "char1", 1},
{DATA_TYPE_STR, "str", 128},
};
static const int kTypeCount = sizeof(kTypeTab) / sizeof(kTypeTab[0]);
/** @brief 获取数据类型名称字符串 */
std::string dc_util_type_name(uint8_t dt)
{
for (int i = 0; i < kTypeCount; i++)
{
if (kTypeTab[i].id == dt)
{
return kTypeTab[i].name;
}
}
return "unknown";
}
/** @brief 通过名称字符串获取数据类型 ID */
uint8_t dc_util_type_id_by_name(const std::string &n)
{
for (int i = 0; i < kTypeCount; i++)
{
if (n == kTypeTab[i].name)
{
return kTypeTab[i].id;
}
}
return 0;
}
/** @brief 获取数据类型的字节长度 */
uint8_t dc_util_type_len(uint8_t dt)
{
for (int i = 0; i < kTypeCount; i++)
{
if (kTypeTab[i].id == dt)
{
return kTypeTab[i].len;
}
}
return 0;
}
/** @brief 根据数据类型创建零初始化内存块 */
void *dc_util_create_data(uint8_t dt)
{
uint8_t len = dc_util_type_len(dt);
if (!len)
{
return NULL;
}
return calloc(1, len);
}
/** @brief 释放由 dc_util_create_data 分配的内存 */
void dc_util_destroy_data(void *p, uint8_t dt)
{
(void)dt;
free(p);
}
/** @brief 将源数据按类型拷贝到目标 */
void dc_util_val_copy(void *dst, const void *src, uint8_t dt)
{
uint8_t len = dc_util_type_len(dt);
if (len && dst && src)
{
memcpy(dst, src, len);
}
}
/** @brief 按类型比较两个值是否相等,返回 0 相等,-1 不等或错误 */
int dc_util_val_compare(uint8_t dt, const void *p1, const void *p2)
{
uint8_t len = dc_util_type_len(dt);
if (!len || !p1 || !p2)
{
return -1;
}
return memcmp(p1, p2, len) == 0 ? 0 : -1;
}
/** @brief 将值按类型转换为字符串表示 */
std::string dc_util_val_to_string(const void *p, uint8_t dt)
{
if (!p)
{
return "";
}
char buf[256] = {0};
switch (dt)
{
case DATA_TYPE_B:
snprintf(buf, sizeof(buf), "%s", *(uint8_t *)p ? "true" : "false");
break;
case DATA_TYPE_S8:
snprintf(buf, sizeof(buf), "%d", *(int8_t *)p);
break;
case DATA_TYPE_U8:
snprintf(buf, sizeof(buf), "%u", *(uint8_t *)p);
break;
case DATA_TYPE_S16:
snprintf(buf, sizeof(buf), "%d", *(int16_t *)p);
break;
case DATA_TYPE_U16:
snprintf(buf, sizeof(buf), "%u", *(uint16_t *)p);
break;
case DATA_TYPE_S32:
snprintf(buf, sizeof(buf), "%d", *(int32_t *)p);
break;
case DATA_TYPE_U32:
snprintf(buf, sizeof(buf), "%u", *(uint32_t *)p);
break;
case DATA_TYPE_L64:
snprintf(buf, sizeof(buf), "%lld", (long long)*(int64_t *)p);
break;
case DATA_TYPE_UL64:
snprintf(buf, sizeof(buf), "%llu", (unsigned long long)*(uint64_t *)p);
break;
case DATA_TYPE_F32:
snprintf(buf, sizeof(buf), "%.6g", (double)*(float *)p);
break;
case DATA_TYPE_D64:
snprintf(buf, sizeof(buf), "%.12g", *(double *)p);
break;
case DATA_TYPE_IP:
snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
((uint8_t *)p)[0], ((uint8_t *)p)[1],
((uint8_t *)p)[2], ((uint8_t *)p)[3]);
break;
case DATA_TYPE_MAC:
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
((uint8_t *)p)[0], ((uint8_t *)p)[1],
((uint8_t *)p)[2], ((uint8_t *)p)[3],
((uint8_t *)p)[4], ((uint8_t *)p)[5]);
break;
case DATA_TYPE_C8:
case DATA_TYPE_C32:
case DATA_TYPE_C64:
case DATA_TYPE_C128:
case DATA_TYPE_STR:
snprintf(buf, sizeof(buf), "%s", (char *)p);
break;
case DATA_TYPE_C1:
snprintf(buf, sizeof(buf), "%c", *(char *)p);
break;
default:
return "";
}
return std::string(buf);
}
/** @brief 从字符串解析值并按类型写入内存 */
int dc_util_val_from_string(void *p, uint8_t dt, const std::string &s)
{
if (!p || s.empty())
{
return -1;
}
switch (dt)
{
case DATA_TYPE_B:
*(uint8_t *)p = (s == "true" || s == "1") ? 1 : 0;
break;
case DATA_TYPE_S8:
*(int8_t *)p = (int8_t)atoi(s.c_str());
break;
case DATA_TYPE_U8:
*(uint8_t *)p = (uint8_t)atoi(s.c_str());
break;
case DATA_TYPE_S16:
*(int16_t *)p = (int16_t)atoi(s.c_str());
break;
case DATA_TYPE_U16:
*(uint16_t *)p = (uint16_t)atoi(s.c_str());
break;
case DATA_TYPE_S32:
*(int32_t *)p = (int32_t)atoi(s.c_str());
break;
case DATA_TYPE_U32:
*(uint32_t *)p = (uint32_t)strtoul(s.c_str(), NULL, 10);
break;
case DATA_TYPE_L64:
*(int64_t *)p = (int64_t)strtoll(s.c_str(), NULL, 10);
break;
case DATA_TYPE_UL64:
*(uint64_t *)p = (uint64_t)strtoull(s.c_str(), NULL, 10);
break;
case DATA_TYPE_F32:
*(float *)p = (float)atof(s.c_str());
break;
case DATA_TYPE_D64:
*(double *)p = atof(s.c_str());
break;
case DATA_TYPE_IP:
{
int a, b, c, d;
if (4 == sscanf(s.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d))
{
((uint8_t *)p)[0] = (uint8_t)a;
((uint8_t *)p)[1] = (uint8_t)b;
((uint8_t *)p)[2] = (uint8_t)c;
((uint8_t *)p)[3] = (uint8_t)d;
}
break;
}
case DATA_TYPE_MAC:
{
int m[6];
if (6 == sscanf(s.c_str(), "%02X:%02X:%02X:%02X:%02X:%02X",
&m[0], &m[1], &m[2], &m[3], &m[4], &m[5]))
{
for (int i = 0; i < 6; i++)
{
((uint8_t *)p)[i] = (uint8_t)m[i];
}
}
break;
}
case DATA_TYPE_C8:
case DATA_TYPE_C32:
case DATA_TYPE_C64:
case DATA_TYPE_C128:
case DATA_TYPE_STR:
strncpy((char *)p, s.c_str(), dc_util_type_len(dt) - 1);
break;
case DATA_TYPE_C1:
*(char *)p = s[0];
break;
default:
return -1;
}
return 0;
}

View File

@ -0,0 +1,192 @@
/** @file dc_signal_yk.cpp — YK 信号注册/控制/异步执行 */
#include "dc_internal.h"
/**
* @brief YK XXH128 hash g_dc_yk cb ctrl_cb
*/
extern "C" int dc_signal_yk(const char *saddr, const char *desc, uint8_t data_type, uint8_t ctrl_type, void *p_data, dc_signal_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
if (dc_core_find(saddr, g_dc_yk))
{
LOG_E("yk %s exists\n", saddr);
return DC_ERR_EXISTS;
}
dc_signal_t sig = {};
sig.hash = XXH3_128bits(saddr, strlen(saddr));
strncpy(sig.saddr, saddr, DC_SADDR_MAX_LEN - 1);
if (desc)
{
strncpy(sig.desc, desc, DC_DESC_MAX_LEN - 1);
}
sig.data_type = data_type;
sig.ctrl_type = ctrl_type;
sig.vec_p_data = (void **)malloc(sizeof(void *));
sig.vec_p_data[0] = p_data;
sig.vec_p_data_count = 1;
if (cb)
{
strncpy(sig.cb_list[0].module_id, module_id, DC_MODULE_MAX_LEN - 1);
sig.cb_list[0].ctrl_cb = cb;
sig.cb_count = 1;
}
return dc_core_add(sig, g_dc_yk);
}
/**
* @brief YK *p_data vec_p_data[0] cb
*/
extern "C" int dc_signal_yk_link_with_callback(const char *saddr, void **p_data, dc_signal_change_cb_t cb, const char *module_id)
{
if (!g_initialized || !saddr || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_yk);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
if (!ps->vec_p_data || !ps->vec_p_data[0])
{
return DC_ERR_PARAM;
}
*p_data = ps->vec_p_data[0];
if (cb && ps->cb_count < DC_MAX_CALLBACKS)
{
strncpy(ps->cb_list[ps->cb_count].module_id, module_id, DC_MODULE_MAX_LEN - 1);
ps->cb_list[ps->cb_count].ctrl_cb = cb;
ps->cb_count++;
}
return DC_OK;
}
/**
* @brief ctrl step SELECT / DIRECT module_id
*/
extern "C" int dc_signal_yk_set_status(const char *saddr, dc_ctrl_step_t step, dc_ctrl_t *ctrl, const void *p_data, const char *module_id)
{
if (!g_initialized || !saddr || !ctrl || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_yk);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
int ret = dc_check_ctrl_valid(ps, step, ctrl, p_data);
if (ret != DC_OK)
{
return ret;
}
if (step == DC_STEP_SELECT)
{
dc_util_val_copy(ctrl->p_data, p_data, ps->data_type);
}
else if (step == DC_STEP_DIRECT)
{
dc_util_val_copy(ps->vec_p_data[0], p_data, ps->data_type);
}
/* DC_STEP_CANCEL: no data operation */
ctrl->step = step;
for (int i = 0; i < ps->cb_count; i++)
{
if (!ps->cb_list[i].ctrl_cb)
{
continue;
}
if (0 == strcmp(ps->cb_list[i].module_id, module_id))
{
continue;
}
ps->cb_list[i].ctrl_cb(ps->saddr, step, ps->data_type, 0, ps->vec_p_data[0]);
}
return DC_OK;
}
/**
* @brief
*/
extern "C" int dc_yk_exec_async(const char *saddr, dc_ctrl_step_t step, const dc_ctrl_t *ctrl, const void *p_data, const char *module_id, dc_async_result_cb_t result_cb, void *user_arg, uint32_t timeout_ms, uint32_t *request_id)
{
if (!g_initialized || !saddr || !ctrl || !p_data || !module_id)
{
return DC_ERR_PARAM;
}
uint32_t req_id = dc_async_enqueue(g_dc_yk, saddr, step, ctrl, p_data, 0, 0, module_id, result_cb, user_arg, timeout_ms);
if (request_id)
{
*request_id = req_id;
}
return (req_id == 0) ? DC_ERR_FULL : DC_OK;
}
/**
* @brief B
*/
extern "C" int dc_yk_set_result(uint32_t request_id, int result_code, const char *err_msg)
{
return dc_async_set_result(request_id, result_code, err_msg);
}
/**
* @brief
*/
extern "C" int dc_yk_cancel_async(uint32_t request_id)
{
return dc_async_cancel(request_id);
}
/**
* @brief B
*/
extern "C" int dc_yk_register_exec_cb(const char *saddr, dc_async_exec_cb_t exec_cb, void *module_ctx)
{
if (!g_initialized || !saddr)
{
return DC_ERR_PARAM;
}
dc_signal_t *ps = dc_core_find(saddr, g_dc_yk);
if (!ps)
{
return DC_ERR_NOTFOUND;
}
ps->exec_cb = exec_cb;
ps->module_ctx = module_ctx;
return DC_OK;
}