fix+feat(datacenter): 修复5个bug + 补全原工程值校验/strcmp

## Bug 修复 (5项)
1. dc_async_timeout_check(): 持锁回调死锁 → 先收集回调列表,解锁后调用
2. dc_get_type_name(): static string 非线程安全 → thread_local buffer
3. dc_event: 三个事件队列无容量上限 → EVENT_QUEUE_MAX_SIZE=1024, 溢出丢弃最旧
4. dc_signal_out: 回路阻断 last_caller_module 多模块覆盖 → dirty_by_multi 标志
5. dc_change_out_check: 处理完后重置 dirty_by_multi

## 原工程功能补全 (只能加法)
6. dc_check_val_valid: 新增 12 种数值类型范围校验 (min/max), bool 0/1 校验
7. dc_check_ctrl_val_valid: C8/C32/C64/C128/STR 改用 strcmp (原memcmp会误判)
8. dc_internal_type_name: 新增线程安全 buffer 版类型名称查询
This commit is contained in:
ypc 2026-07-10 14:10:29 +08:00
parent fe893c4c75
commit 0133c2950f
9 changed files with 266 additions and 51 deletions

View File

@ -147,6 +147,7 @@ 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);
void dc_internal_type_name(uint8_t dt, char *buf, int buf_len);
uint8_t dc_util_type_id_by_name(const std::string &name);
uint8_t dc_util_type_len(uint8_t data_type);

View File

@ -175,6 +175,7 @@ typedef struct
int cb_count;
char last_caller_module[DC_MODULE_MAX_LEN];
int dirty_by_multi; /**< >0 表示多模块同时修改, 禁用回路阻断 */
dc_async_exec_cb_t exec_cb;
void *module_ctx;

View File

@ -1,14 +1,14 @@
/** @file dc_api_wrap.cpp — C API 包装函数(类型工具) */
#include "dc_internal.h"
/** @brief 获取数据类型名称字符串extern "C",返回 static 缓冲区指针) */
/** @brief 获取数据类型名称字符串 (extern "C", 使用 thread_local 缓冲区避免竞态) */
extern "C" const char *dc_get_type_name(uint8_t dt)
{
static std::string s;
static __thread char s_buf[32] = { 0 };
s = dc_util_type_name(dt);
dc_internal_type_name(dt, s_buf, sizeof(s_buf));
return s.c_str();
return s_buf;
}
/** @brief 通过名称获取数据类型 ID */

View File

@ -237,22 +237,42 @@ int dc_async_cancel(uint32_t request_id)
void dc_async_timeout_check(void)
{
uint64_t now = dc_get_monotonic_ms();
dc_async_request_t *req;
/* 第一阶段: 持锁标记超时, 收集超时回调列表 */
std::vector<dc_async_result_cb_t> timeout_cbs;
std::vector<uint32_t> timeout_ids;
std::vector<int> timeout_codes;
std::vector<char *> timeout_msgs;
std::vector<void *> timeout_args;
{
std::lock_guard<std::mutex> lk(g_async_queue.mtx);
for (dc_async_request_t *req = g_async_queue.head; req; req = req->next)
for (req = g_async_queue.head; NULL != 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)
if (NULL != req->result_cb)
{
req->result_cb(req->request_id, DC_ERR_TIMEOUT, req->err_msg, req->user_arg);
timeout_ids.push_back(req->request_id);
timeout_codes.push_back(DC_ERR_TIMEOUT);
timeout_msgs.push_back(req->err_msg);
timeout_args.push_back(req->user_arg);
timeout_cbs.push_back(req->result_cb);
}
}
}
}
/* 第二阶段: 解锁后调用回调, 避免死锁 (回调可能再次入队) */
for (uint32_t i = 0; i < timeout_cbs.size(); i++)
{
timeout_cbs[i](timeout_ids[i], timeout_codes[i], timeout_msgs[i], timeout_args[i]);
}
}
/**

View File

@ -210,7 +210,8 @@ static void dc_change_out_check(void)
continue;
}
if (0 == strcmp(ps->cb_list[i].module_id, ps->last_caller_module))
/* 回路阻断: 仅当单模块写入且回调属于该模块时跳过 */
if (!ps->dirty_by_multi && 0 == strcmp(ps->cb_list[i].module_id, ps->last_caller_module))
{
continue;
}
@ -219,6 +220,8 @@ static void dc_change_out_check(void)
}
dc_util_val_copy(ps->p_last_data, ps->vec_p_data[0], ps->data_type);
ps->dirty_by_multi = 0;
ps->last_caller_module[0] = '\0';
}
}

View File

@ -2,6 +2,8 @@
#include "dc_internal.h"
#include <queue>
#define EVENT_QUEUE_MAX_SIZE 1024 /**< 每种事件队列最大容量, 超出则丢弃最旧 */
static std::mutex g_soe_mutex;
static std::queue<void *> g_soe_queue;
static std::vector<dc_queue_pop_cb_t> g_soe_cbs;
@ -42,7 +44,7 @@ static void dc_event_pop_inner(std::mutex &mtx, std::queue<void *> &q,
* SOE
* ================================================================ */
/** @brief 向 SOE 事件队列推送数据 */
/** @brief 向 SOE 事件队列推送数据 (超过上限丢弃最旧) */
int dc_event_queue_push(void *p)
{
if (!p)
@ -51,6 +53,16 @@ int dc_event_queue_push(void *p)
}
std::lock_guard<std::mutex> lk(g_soe_mutex);
if (g_soe_queue.size() >= EVENT_QUEUE_MAX_SIZE)
{
LOG_E("dc_event_queue_push: queue full (%zu), dropping oldest",
g_soe_queue.size());
void *old = g_soe_queue.front();
g_soe_queue.pop();
dc_util_destroy_data(old, DATA_TYPE_U8); /* SOE 事件内存释放 */
}
g_soe_queue.push(p);
return DC_OK;
@ -80,7 +92,7 @@ void dc_event_queue_pop(void)
*
* ================================================================ */
/** @brief 向扰动事件队列推送数据 */
/** @brief 向扰动事件队列推送数据 (超过上限丢弃最旧) */
int dc_disturb_dd_queue_push(void *p)
{
if (!p)
@ -89,6 +101,16 @@ int dc_disturb_dd_queue_push(void *p)
}
std::lock_guard<std::mutex> lk(g_dist_mutex);
if (g_dist_queue.size() >= EVENT_QUEUE_MAX_SIZE)
{
LOG_E("dc_disturb_dd_queue_push: queue full (%zu), dropping oldest",
g_dist_queue.size());
void *old = g_dist_queue.front();
g_dist_queue.pop();
dc_util_destroy_data(old, DATA_TYPE_U8);
}
g_dist_queue.push(p);
return DC_OK;
@ -118,7 +140,7 @@ void dc_disturb_dd_queue_pop(void)
*
* ================================================================ */
/** @brief 向故障事件队列推送数据 */
/** @brief 向故障事件队列推送数据 (超过上限丢弃最旧) */
int dc_fault_queue_push(void *p)
{
if (!p)
@ -127,6 +149,16 @@ int dc_fault_queue_push(void *p)
}
std::lock_guard<std::mutex> lk(g_fault_mutex);
if (g_fault_queue.size() >= EVENT_QUEUE_MAX_SIZE)
{
LOG_E("dc_fault_queue_push: queue full (%zu), dropping oldest",
g_fault_queue.size());
void *old = g_fault_queue.front();
g_fault_queue.pop();
dc_util_destroy_data(old, DATA_TYPE_U8);
}
g_fault_queue.push(p);
return DC_OK;

View File

@ -1,9 +1,24 @@
/** @file dc_signal_check.cpp — 控制校验 + 值校验 */
#include "dc_internal.h"
/** @brief 校验直控值是否与选控暂存值一致 */
/** @brief 校验直控值是否与选控暂存值一致 (char 数组用 strcmp) */
int dc_check_ctrl_val_valid(const dc_ctrl_t *ctrl, const void *p_data)
{
/* 字符串类型用 strcmp 而非 memcmp, 避免尾部垃圾字节误判 */
if (ctrl->data_type == DATA_TYPE_C8 ||
ctrl->data_type == DATA_TYPE_C32 ||
ctrl->data_type == DATA_TYPE_C64 ||
ctrl->data_type == DATA_TYPE_C128 ||
ctrl->data_type == DATA_TYPE_STR)
{
if (0 != strcmp((const char *)ctrl->p_data, (const char *)p_data))
{
LOG_E("direct val != selected val\n");
return DC_ERR_VAL;
}
return DC_OK;
}
uint8_t len = dc_util_type_len(ctrl->data_type);
if (!len)
@ -65,7 +80,7 @@ int dc_check_ctrl_valid(const dc_signal_t *ps, dc_ctrl_step_t step,
return DC_ERR_STEP;
}
/** @brief 校验新值与当前值是否不同(变更检测) */
/** @brief 校验新值 (变更检测 + 范围校验 + bool 校验) */
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)
@ -76,12 +91,125 @@ int dc_check_val_valid(const dc_signal_t *ps, uint8_t zone, const void *p_data)
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;
}
/* 范围校验 (基于 param.min / param.max) */
if (ps->data_type == DATA_TYPE_B)
{
uint8_t b = *(const uint8_t *)p_data;
if (b != 0 && b != 1)
{
LOG_E("invalid bool value %d\n", b);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_S8)
{
int8_t val = *(const int8_t *)p_data;
if (val < (int8_t)ps->param.min || val > (int8_t)ps->param.max)
{
LOG_E("invalid s8 %d, range [%d,%d]\n", val, (int)ps->param.min, (int)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_U8)
{
uint8_t val = *(const uint8_t *)p_data;
if (val < ps->param.min || val > ps->param.max)
{
LOG_E("invalid u8 %u, range [%u,%u]\n", val, (unsigned)ps->param.min, (unsigned)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_S16)
{
int16_t val = *(const int16_t *)p_data;
if (val < (int16_t)ps->param.min || val > (int16_t)ps->param.max)
{
LOG_E("invalid s16 %d, range [%d,%d]\n", val, (int)ps->param.min, (int)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_U16)
{
uint16_t val = *(const uint16_t *)p_data;
if (val < ps->param.min || val > ps->param.max)
{
LOG_E("invalid u16 %u, range [%u,%u]\n", val, (unsigned)ps->param.min, (unsigned)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_S32)
{
int32_t val = *(const int32_t *)p_data;
if (val < (int32_t)ps->param.min || val > (int32_t)ps->param.max)
{
LOG_E("invalid s32 %d, range [%d,%d]\n", val, (int)ps->param.min, (int)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_U32)
{
uint32_t val = *(const uint32_t *)p_data;
if (val < (uint32_t)ps->param.min || val > (uint32_t)ps->param.max)
{
LOG_E("invalid u32 %u, range [%u,%u]\n", val, (unsigned)ps->param.min, (unsigned)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_L64)
{
int64_t val = *(const int64_t *)p_data;
if (val < (int64_t)ps->param.min || val > (int64_t)ps->param.max)
{
LOG_E("invalid l64 %lld, range [%lld,%lld]\n", val, (long long)ps->param.min, (long long)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_UL64)
{
uint64_t val = *(const uint64_t *)p_data;
if (val < (uint64_t)ps->param.min || val > (uint64_t)ps->param.max)
{
LOG_E("invalid ul64 %llu, range [%llu,%llu]\n", val, (unsigned long long)ps->param.min, (unsigned long long)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_F32)
{
float val = *(const float *)p_data;
if (val < (float)ps->param.min || val > (float)ps->param.max)
{
LOG_E("invalid f32 %f, range [%f,%f]\n", val, (float)ps->param.min, (float)ps->param.max);
return DC_ERR_VAL;
}
}
else if (ps->data_type == DATA_TYPE_D64)
{
double val = *(const double *)p_data;
if (val < (double)ps->param.min || val > (double)ps->param.max)
{
LOG_E("invalid d64 %f, range [%f,%f]\n", val, (double)ps->param.min, (double)ps->param.max);
return DC_ERR_VAL;
}
}
return DC_OK;
}

View File

@ -111,7 +111,16 @@ int dc_set_out_signal_val(const char *saddr, const void *set_data, const char *m
return DC_ERR_NOTFOUND;
}
/* 记录调用方: 多模块同时修改时设 dirty_by_multi=1, 回路阻断时跳过所有 */
if ('\0' == ps->last_caller_module[0])
{
strncpy(ps->last_caller_module, module_id, DC_MODULE_MAX_LEN - 1);
}
else if (0 != strcmp(ps->last_caller_module, module_id))
{
ps->dirty_by_multi = 1;
}
dc_core_mark_dirty(ps);
dc_util_val_copy(ps->vec_p_data[0], set_data, ps->data_type);

View File

@ -39,6 +39,27 @@ std::string dc_util_type_name(uint8_t dt)
return "unknown";
}
/** @brief 获取数据类型名称到缓冲区 (线程安全, 无堆分配) */
void dc_internal_type_name(uint8_t dt, char *buf, int buf_len)
{
const char *name = "unknown";
for (int i = 0; i < kTypeCount; i++)
{
if (kTypeTab[i].id == dt)
{
name = kTypeTab[i].name;
break;
}
}
if (NULL != buf && buf_len > 0)
{
strncpy(buf, name, buf_len - 1);
buf[buf_len - 1] = '\0';
}
}
/** @brief 通过名称字符串获取数据类型 ID */
uint8_t dc_util_type_id_by_name(const std::string &n)
{