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:
parent
dfb0a46146
commit
f56ede7316
|
|
@ -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()` | 每100ms:out变化检测 + 事件弹出 + 异步超时 |
|
||||
| `dc_run_1000ms()` | 每1000ms:参数变更检测 → 写 self_param.xml |
|
||||
|
||||
### 3.2 Out 信号(遥测/遥信)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_signal_out(saddr, desc, data_type, p_data, module_id)` | 注册输出信号 |
|
||||
| `dc_signal_out_with_callback(..., cb, module_id)` | 注册 + 延迟变化回调 |
|
||||
| `dc_signal_out_link_with_callback(saddr, pp_data, cb, module_id)` | 链接已有信号 |
|
||||
| `dc_set_out_signal_val(saddr, set_data, module_id)` | 设值(脏标记 + 延迟回调) |
|
||||
|
||||
### 3.3 In 信号(输入信号,链接 out)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_signal_in(saddr, desc, link_saddr, pp_data, module_id)` | 注册并关联 out |
|
||||
| `dc_signal_in_with_callback(..., cb, module_id)` | 注册 + 回调(挂在 out 上) |
|
||||
|
||||
### 3.4 YK 信号(遥控)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_signal_yk(...)` | 注册遥控信号 |
|
||||
| `dc_signal_yk_link_with_callback(...)` | 链接已有 |
|
||||
| `dc_signal_yk_set_status(saddr, step, ctrl, p_data, module_id)` | 同步控制 |
|
||||
| `dc_yk_exec_async(saddr, step, ctrl, p_data, module_id, result_cb, arg, timeout, req_id)` | 异步控制 |
|
||||
| `dc_yk_set_result(req_id, result, err)` | 异步结果通知 |
|
||||
| `dc_yk_cancel_async(req_id)` | 取消异步 |
|
||||
| `dc_yk_register_exec_cb(saddr, exec_cb, ctx)` | 注册执行回调 |
|
||||
|
||||
### 3.5 AO 信号(模拟输出)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_signal_ao(...)` | 注册 AO 信号 |
|
||||
| `dc_signal_ao_link_with_callback(...)` | 链接已有 |
|
||||
| `dc_signal_ao_set_val(saddr, step, ctrl, p_data, module_id)` | 同步控制(SBO + 值校验) |
|
||||
| `dc_signal_ao_set_val_without_check(...)` | 无校验设值 |
|
||||
| `dc_ao_exec_async(...)` | 异步控制 |
|
||||
| `dc_ao_set_result(...)` | 异步结果通知 |
|
||||
| `dc_ao_cancel_async(...)` | 取消异步 |
|
||||
| `dc_ao_register_exec_cb(...)` | 注册执行回调 |
|
||||
|
||||
### 3.6 Param 信号(定值,多区)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_signal_param(saddr, desc, data_type, ctrl_type, pp_data, num_zones, cb, module_id)` | 注册参数 |
|
||||
| `dc_signal_param_link_with_callback(...)` | 链接已有 |
|
||||
| `dc_signal_param_set_val(saddr, step, ctrl, zone, p_data, module_id)` | 同步设值 |
|
||||
| `dc_signal_param_set_val_without_check(...)` | 无校验设值 |
|
||||
| `dc_param_exec_async(...)` | 异步控制 |
|
||||
| `dc_param_set_result(...)` | 异步结果通知 |
|
||||
| `dc_param_cancel_async(...)` | 取消异步 |
|
||||
| `dc_param_register_exec_cb(...)` | 注册执行回调 |
|
||||
|
||||
### 3.7 信号查询
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_get_out_signal_info(saddr, desc, desc_len, data_type, pp_data)` | 查询 out |
|
||||
| `dc_get_in_signal_info(...)` | 查询 in |
|
||||
| `dc_get_ao_signal_info(saddr, desc, desc_len, data_type, p_meta, ctrl_type, pp_data)` | 查询 AO(含元数据) |
|
||||
| `dc_get_param_signal_info(saddr, desc, desc_len, data_type, p_meta, ctrl_type, p_data_array, p_count)` | 查询 Param(多区) |
|
||||
| `dc_get_yk_signal_info(...)` | 查询 YK |
|
||||
| `dc_get_signal_val(p_data, data_type, buf, buf_len)` | 值 → 字符串 |
|
||||
| `dc_get_signal_count(type_str)` | 按类型统计 |
|
||||
| `dc_get_signal_info_by_id(type_str, id, ...)` | 按 ID 查询 |
|
||||
|
||||
### 3.8 事件队列
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_event_queue_push(p_soe)` | SOE 入队 |
|
||||
| `dc_event_register_queue_pop(cb)` | 注册 SOE 消费回调 |
|
||||
| `dc_disturb_dd_queue_push(p_dd)` | 扰动数据入队 |
|
||||
| `dc_disturb_dd_register_queue_pop(cb)` | 注册扰动消费回调 |
|
||||
| `dc_fault_queue_push(p_fault)` | 故障数据入队 |
|
||||
| `dc_fault_register_queue_pop(cb)` | 注册故障消费回调 |
|
||||
|
||||
### 3.9 类型工具
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_get_type_name(data_type)` | ID → 名称(35 → "uint32_t") |
|
||||
| `dc_get_type_id_by_name(name)` | 名称 → ID("float" → 38) |
|
||||
| `dc_get_type_len(data_type)` | 获取字节长度 |
|
||||
| `dc_create_data(data_type)` / `dc_destroy_data(p_data, data_type)` | 类型感知的分配/释放 |
|
||||
| `dc_copy_val(dst, src, data_type)` / `dc_compare_val(p1, p2, data_type)` | 类型感知的复制/比较 |
|
||||
| `dc_set_val_from_str(p_data, data_type, str)` | 字符串 → 值 |
|
||||
|
||||
### 3.10 配置与异步管理
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| `dc_param_cfg_changed()` | 查询是否有未保存变更 |
|
||||
| `dc_async_pending_count()` | 异步请求 pending 数 |
|
||||
| `dc_async_set_default_timeout(ms)` | 全局默认超时 |
|
||||
| `dc_async_set_timeout_yk/ao/param(ms)` | 各类型默认超时 |
|
||||
| `dc_save_out_signals_xml(path)` | 导出 out 信号到 XML |
|
||||
|
||||
---
|
||||
|
||||
## 4. 使用示例
|
||||
|
||||
```c
|
||||
#include "myDatacenter.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static void on_out_changed(const char *saddr, uint8_t data_type,
|
||||
const void *p_new, const void *p_old)
|
||||
{
|
||||
printf("out %s: %d → %d\n", saddr, *(int *)p_old, *(int *)p_new);
|
||||
}
|
||||
|
||||
static void on_yk_changed(const char *saddr, dc_ctrl_step_t step,
|
||||
uint8_t data_type, uint8_t zone,
|
||||
const void *p_data)
|
||||
{
|
||||
printf("yk %s step=%d val=%d\n", saddr, step, *(uint8_t *)p_data);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* 初始化,自动加载 param.xml */
|
||||
dc_init("/etc/rtu/config/");
|
||||
|
||||
/* 注册 out 信号 */
|
||||
int yc_val = 0;
|
||||
dc_signal_out("RPC1:YC:1:val", "母线电压", DATA_TYPE_S32,
|
||||
&yc_val, "protocol_104");
|
||||
|
||||
/* 另一个模块链接 out 信号并注册回调 */
|
||||
void *p_linked = NULL;
|
||||
dc_signal_out_link_with_callback("RPC1:YC:1:val", &p_linked,
|
||||
on_out_changed, "web_server");
|
||||
|
||||
/* 注册 yk 信号(直控模式) */
|
||||
uint8_t yk_val = 0;
|
||||
dc_signal_yk("RPC1:YK:1:val", "开关合闸", DATA_TYPE_U8,
|
||||
DC_CTRL_DIRECT, &yk_val, on_yk_changed, "protocol_104");
|
||||
|
||||
/* 修改 out 信号值(回路阻断:protocol_104 自身的回调不会被触发) */
|
||||
yc_val = 220;
|
||||
dc_set_out_signal_val("RPC1:YC:1:val", &yc_val, "protocol_104");
|
||||
|
||||
/* 100ms 周期:on_out_changed 被调用 */
|
||||
dc_run_100ms();
|
||||
|
||||
/* YK 直控执行(同步回调:on_yk_changed 立即触发) */
|
||||
dc_ctrl_t ctrl = {0};
|
||||
ctrl.type = DC_CTRL_DIRECT;
|
||||
ctrl.data_type = DATA_TYPE_U8;
|
||||
uint8_t new_val = 1;
|
||||
dc_signal_yk_set_status("RPC1:YK:1:val", DC_STEP_DIRECT,
|
||||
&ctrl, &new_val, "web_server");
|
||||
|
||||
dc_cleanup();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 依赖关系
|
||||
|
||||
| 依赖 | 模块 | 用途 |
|
||||
|------|------|------|
|
||||
| ⇩ | `myBase.h` | 基础类型定义(DATA_TYPE_* 宏) |
|
||||
| ⇩ | `myLog.h` | 日志输出 |
|
||||
| ⇩ | `libmy_xxhash` | XXH3_128bits 哈希查找(O(1)) |
|
||||
| ⇩ | `libxml` | 纯 C 接口 XML 解析(param.xml/self_param.xml) |
|
||||
| ⇩ | `libcmd` | 命令注册(`datacenter` 调试面板) |
|
||||
|
||||
---
|
||||
|
||||
## 6. 源文件组成(15 文件,对齐 RTU 原工程)
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `inc/myDatacenter.h` | 纯 C 对外接口(60+ API) |
|
||||
| `src/dc_internal.h` | C++ 内部声明(哈希/信号表/异步队列) |
|
||||
| `src/dc_core.cpp` | 全局变量 + XXH128 哈希 + dc_init/run |
|
||||
| `src/dc_signal_out.cpp` | Out 信号注册/链接/设值 |
|
||||
| `src/dc_signal_in.cpp` | In 信号注册(关联 out) |
|
||||
| `src/dc_signal_yk.cpp` | YK 注册 + 同步/异步控制 |
|
||||
| `src/dc_signal_ao.cpp` | AO 注册 + 同步/异步控制 + SBO |
|
||||
| `src/dc_signal_param.cpp` | Param 多区定值 + 同步/异步控制 |
|
||||
| `src/dc_signal_query.cpp` | 全部 get_xxx_info 函数 |
|
||||
| `src/dc_signal_util.cpp` | 19 种类型工具函数 |
|
||||
| `src/dc_signal_check.cpp` | SBO 校验 + 值校验 + YK 校验 |
|
||||
| `src/dc_signal_show.cpp` | 命令注册 + XML 导出 |
|
||||
| `src/dc_param_cfg.cpp` | param.xml 解析 + self_param.xml 保存 |
|
||||
| `src/dc_event.cpp` | SOE/扰动/故障事件队列 |
|
||||
| `src/dc_async.cpp` | 异步请求队列(入队/超时/取消/清理) |
|
||||
| `src/dc_api_wrap.cpp` | C API 包装(get_type_name 等) |
|
||||
|
||||
---
|
||||
|
||||
## 7. 新旧工程差异
|
||||
|
||||
| 维度 | RTU v1 | RTU v2 | 新工程 |
|
||||
|------|--------|--------|--------|
|
||||
| 接口语言 | C++ (std::string) | 纯 C (const char*) | 纯 C + Allman 风格 |
|
||||
| 哈希 | XXH128 multimap | 同 v1 | unordered_map + XXH128 hasher |
|
||||
| 参数文件 | tinyxml2 嵌入 | 同 v1 | libxml C 接口 |
|
||||
| 异步执行 | 无 | _exec_async 系列 | _exec_async 系列 |
|
||||
| 回路阻断 | ✓ | ✓ | ✓ |
|
||||
| SBO 流程 | ✓ | ✓ | ✓ |
|
||||
| 多区定值 | ✓ | ✓ | ✓ |
|
||||
| 错误码 | -1/0 | enum dc_error_t | enum dc_error_t(12种) |
|
||||
| 命令调试 | cmd_dc | 无 | CMD_REGISTER_C(libcmd) |
|
||||
| 文件数 | 13 .cpp | 8 .cpp | 15 .cpp(按功能拆分) |
|
||||
|
|
@ -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 流程:SELECT→DIRECT/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_ */
|
||||
|
|
@ -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
|
||||
|
|
@ -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 ¶m);
|
||||
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 */
|
||||
|
|
@ -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 流程:SELECT→DIRECT/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_ */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 ¶m)
|
||||
{
|
||||
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 saddr→value 映射表
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 信号到 XML(PLC 配置导出)
|
||||
* @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" */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue