/** * @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 #include #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_ */