From 0133c2950f5ed1933ec806323b86a80f261e1732 Mon Sep 17 00:00:00 2001 From: ypc <15051963820@163.com> Date: Fri, 10 Jul 2026 14:10:29 +0800 Subject: [PATCH] =?UTF-8?q?fix+feat(datacenter):=20=E4=BF=AE=E5=A4=8D5?= =?UTF-8?q?=E4=B8=AAbug=20+=20=E8=A1=A5=E5=85=A8=E5=8E=9F=E5=B7=A5?= =?UTF-8?q?=E7=A8=8B=E5=80=BC=E6=A0=A1=E9=AA=8C/strcmp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 版类型名称查询 --- src/public/libdatacenter/inc/dc_internal.h | 1 + src/public/libdatacenter/inc/myDatacenter.h | 1 + src/public/libdatacenter/src/dc_api_wrap.cpp | 8 +- src/public/libdatacenter/src/dc_async.cpp | 48 ++++-- src/public/libdatacenter/src/dc_core.cpp | 5 +- src/public/libdatacenter/src/dc_event.cpp | 66 ++++++-- .../libdatacenter/src/dc_signal_check.cpp | 156 ++++++++++++++++-- .../libdatacenter/src/dc_signal_out.cpp | 11 +- .../libdatacenter/src/dc_signal_util.cpp | 21 +++ 9 files changed, 266 insertions(+), 51 deletions(-) diff --git a/src/public/libdatacenter/inc/dc_internal.h b/src/public/libdatacenter/inc/dc_internal.h index 560910f..e6c21b2 100644 --- a/src/public/libdatacenter/inc/dc_internal.h +++ b/src/public/libdatacenter/inc/dc_internal.h @@ -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); diff --git a/src/public/libdatacenter/inc/myDatacenter.h b/src/public/libdatacenter/inc/myDatacenter.h index 51b01f7..671607d 100644 --- a/src/public/libdatacenter/inc/myDatacenter.h +++ b/src/public/libdatacenter/inc/myDatacenter.h @@ -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; diff --git a/src/public/libdatacenter/src/dc_api_wrap.cpp b/src/public/libdatacenter/src/dc_api_wrap.cpp index 5966f59..2803597 100644 --- a/src/public/libdatacenter/src/dc_api_wrap.cpp +++ b/src/public/libdatacenter/src/dc_api_wrap.cpp @@ -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 */ diff --git a/src/public/libdatacenter/src/dc_async.cpp b/src/public/libdatacenter/src/dc_async.cpp index 973b112..167097f 100644 --- a/src/public/libdatacenter/src/dc_async.cpp +++ b/src/public/libdatacenter/src/dc_async.cpp @@ -236,23 +236,43 @@ int dc_async_cancel(uint32_t request_id) */ void dc_async_timeout_check(void) { - uint64_t now = dc_get_monotonic_ms(); + uint64_t now = dc_get_monotonic_ms(); + dc_async_request_t *req; - std::lock_guard lk(g_async_queue.mtx); + /* 第一阶段: 持锁标记超时, 收集超时回调列表 */ + std::vector timeout_cbs; + std::vector timeout_ids; + std::vector timeout_codes; + std::vector timeout_msgs; + std::vector timeout_args; - 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); + { + std::lock_guard lk(g_async_queue.mtx); - if (req->result_cb) - { - req->result_cb(req->request_id, DC_ERR_TIMEOUT, req->err_msg, req->user_arg); - } - } - } + 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 (NULL != req->result_cb) + { + 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]); + } } /** diff --git a/src/public/libdatacenter/src/dc_core.cpp b/src/public/libdatacenter/src/dc_core.cpp index 4ac521b..bf62d78 100644 --- a/src/public/libdatacenter/src/dc_core.cpp +++ b/src/public/libdatacenter/src/dc_core.cpp @@ -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'; } } diff --git a/src/public/libdatacenter/src/dc_event.cpp b/src/public/libdatacenter/src/dc_event.cpp index 5fbe72f..3e88af9 100644 --- a/src/public/libdatacenter/src/dc_event.cpp +++ b/src/public/libdatacenter/src/dc_event.cpp @@ -2,6 +2,8 @@ #include "dc_internal.h" #include +#define EVENT_QUEUE_MAX_SIZE 1024 /**< 每种事件队列最大容量, 超出则丢弃最旧 */ + static std::mutex g_soe_mutex; static std::queue g_soe_queue; static std::vector g_soe_cbs; @@ -42,7 +44,7 @@ static void dc_event_pop_inner(std::mutex &mtx, std::queue &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 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,18 +92,28 @@ void dc_event_queue_pop(void) * 扰动录波事件队列 * ================================================================ */ -/** @brief 向扰动事件队列推送数据 */ +/** @brief 向扰动事件队列推送数据 (超过上限丢弃最旧) */ int dc_disturb_dd_queue_push(void *p) { - if (!p) - { - return DC_ERR_PARAM; - } + if (!p) + { + return DC_ERR_PARAM; + } - std::lock_guard lk(g_dist_mutex); - g_dist_queue.push(p); + std::lock_guard lk(g_dist_mutex); - return DC_OK; + 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; } /** @brief 注册扰动事件队列弹出回调 */ @@ -118,18 +140,28 @@ void dc_disturb_dd_queue_pop(void) * 故障事件队列 * ================================================================ */ -/** @brief 向故障事件队列推送数据 */ +/** @brief 向故障事件队列推送数据 (超过上限丢弃最旧) */ int dc_fault_queue_push(void *p) { - if (!p) - { - return DC_ERR_PARAM; - } + if (!p) + { + return DC_ERR_PARAM; + } - std::lock_guard lk(g_fault_mutex); - g_fault_queue.push(p); + std::lock_guard lk(g_fault_mutex); - return DC_OK; + 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; } /** @brief 注册故障事件队列弹出回调 */ diff --git a/src/public/libdatacenter/src/dc_signal_check.cpp b/src/public/libdatacenter/src/dc_signal_check.cpp index 56ca399..3622b1c 100644 --- a/src/public/libdatacenter/src/dc_signal_check.cpp +++ b/src/public/libdatacenter/src/dc_signal_check.cpp @@ -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,24 +80,137 @@ 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) - { - LOG_E("zone %d out of range\n", zone); - return DC_ERR_PARAM; - } + 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]; + 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; - } + /* 变更检测 */ + 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; + /* 范围校验 (基于 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; } /** @brief 校验遥控值有效性(仅允许 0/1) */ diff --git a/src/public/libdatacenter/src/dc_signal_out.cpp b/src/public/libdatacenter/src/dc_signal_out.cpp index aaa2abf..3bc8e1d 100644 --- a/src/public/libdatacenter/src/dc_signal_out.cpp +++ b/src/public/libdatacenter/src/dc_signal_out.cpp @@ -111,7 +111,16 @@ int dc_set_out_signal_val(const char *saddr, const void *set_data, const char *m return DC_ERR_NOTFOUND; } - strncpy(ps->last_caller_module, module_id, DC_MODULE_MAX_LEN - 1); + /* 记录调用方: 多模块同时修改时设 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); diff --git a/src/public/libdatacenter/src/dc_signal_util.cpp b/src/public/libdatacenter/src/dc_signal_util.cpp index 4e4e267..b5f7d96 100644 --- a/src/public/libdatacenter/src/dc_signal_util.cpp +++ b/src/public/libdatacenter/src/dc_signal_util.cpp @@ -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) {