/** * @file test_datacenter.cpp * @brief 数据中心模块单元测试 * @details 覆盖:生命周期、5 种信号注册/链接、out 延迟回调、yk 同步控制、 * ao SBO 流程、param 多区定值、信号查询、类型工具、事件队列 */ #include "myDatacenter.h" #include "myBase.h" #include #include #include /* ===== 测试辅助 ===== */ static int g_pass = 0; static int g_fail = 0; #define TEST(name) \ do \ { \ printf(" TEST: %s ... ", name); \ } while (0) #define OK() \ do \ { \ printf("OK\n"); \ g_pass++; \ } while (0) #define FAIL(msg) \ do \ { \ printf("FAIL: %s\n", msg); \ g_fail++; \ } while (0) /* ===== 回调测试变量 ===== */ static int g_out_changed = 0; static int g_yk_changed = 0; static int g_ao_changed = 0; static int g_param_changed = 0; static int g_soe_received = 0; static void on_out_changed(const char *saddr, uint8_t data_type, const void *p_new, const void *p_old) { (void)saddr; (void)data_type; (void)p_new; (void)p_old; g_out_changed++; } static void on_yk_changed(const char *saddr, dc_ctrl_step_t step, uint8_t data_type, uint8_t zone, const void *p_data) { (void)saddr; (void)step; (void)data_type; (void)zone; (void)p_data; g_yk_changed++; } static void on_ao_changed(const char *saddr, dc_ctrl_step_t step, uint8_t data_type, uint8_t zone, const void *p_data) { (void)saddr; (void)step; (void)data_type; (void)zone; (void)p_data; g_ao_changed++; } static void on_param_changed(const char *saddr, dc_ctrl_step_t step, uint8_t data_type, uint8_t zone, const void *p_data) { (void)saddr; (void)step; (void)data_type; (void)zone; (void)p_data; g_param_changed++; } static void on_soe_pop(void *p_data) { (void)p_data; g_soe_received++; } /* ===== 测试用例 ===== */ /** * @brief 测试 1:生命周期 */ static void test_lifecycle(void) { TEST("dc_init()"); { int r = dc_init(NULL); if (DC_OK != r) { FAIL("init failed"); return; } OK(); } TEST("dc_init() double"); { int r = dc_init(NULL); if (DC_OK != r) { FAIL("double init should return OK"); return; } OK(); } TEST("dc_cleanup()"); { dc_cleanup(); /* re-init for following tests */ dc_init(NULL); OK(); } } /** * @brief 测试 2:out 信号注册 + 设值 + 延迟回调 */ static void test_out_signal(void) { int yc_val = 100; TEST("dc_signal_out()"); { int r = dc_signal_out("RPC1:YC:1", "test yc", DATA_TYPE_S32, &yc_val, "protocol"); if (DC_OK != r) { FAIL("register failed"); return; } OK(); } TEST("dc_signal_out() duplicate"); { int r = dc_signal_out("RPC1:YC:1", "dup", DATA_TYPE_S32, &yc_val, "protocol"); if (DC_ERR_EXISTS != r) { FAIL("duplicate should return EXISTS"); return; } OK(); } TEST("dc_signal_out_link_with_callback()"); { void *p = NULL; g_out_changed = 0; int r = dc_signal_out_link_with_callback("RPC1:YC:1", &p, on_out_changed, "web"); if (DC_OK != r || p != &yc_val) { FAIL("link failed"); return; } OK(); } TEST("dc_set_out_signal_val() + dc_run_100ms()"); { g_out_changed = 0; yc_val = 200; int r = dc_set_out_signal_val("RPC1:YC:1", &yc_val, "protocol"); if (DC_OK != r) { FAIL("set val failed"); return; } /* 回路阻断:protocol 自身不会被回调 */ dc_run_100ms(); if (g_out_changed != 1) { char msg[64]; snprintf(msg, sizeof(msg), "cb count=%d, expected 1", g_out_changed); FAIL(msg); return; } OK(); } } /** * @brief 测试 3:in 信号注册 */ static void test_in_signal(void) { TEST("dc_signal_in()"); { void *p = NULL; int r = dc_signal_in("RPC1:YC:1:in", "test in", "RPC1:YC:1", &p, "protocol"); if (DC_OK != r || !p) { FAIL("in register failed"); return; } OK(); } TEST("dc_signal_in() bad link"); { void *p = NULL; int r = dc_signal_in("RPC1:YC:99:in", "bad", "RPC1:YC:99", &p, "protocol"); if (DC_ERR_NOTFOUND != r) { FAIL("bad link should return NOTFOUND"); return; } OK(); } } /** * @brief 测试 4:yk 信号 + 同步控制 */ static void test_yk_signal(void) { uint8_t yk_val = 0; TEST("dc_signal_yk()"); { int r = dc_signal_yk("RPC1:YK:1", "test yk", DATA_TYPE_U8, DC_CTRL_DIRECT, &yk_val, on_yk_changed, "web"); if (DC_OK != r) { FAIL("yk register failed"); return; } OK(); } TEST("dc_signal_yk_set_status() DIRECT"); { g_yk_changed = 0; dc_ctrl_t ctrl = {}; ctrl.type = DC_CTRL_DIRECT; ctrl.data_type = DATA_TYPE_U8; uint8_t v = 1; int r = dc_signal_yk_set_status("RPC1:YK:1", DC_STEP_DIRECT, &ctrl, &v, "protocol"); if (DC_OK != r || yk_val != 1) { FAIL("yk set failed"); return; } if (g_yk_changed != 1) { FAIL("yk callback not triggered"); return; } /* 回路阻断:web 自身 */ OK(); } TEST("dc_signal_yk_set_status() SBO flow"); { g_yk_changed = 0; dc_ctrl_t ctrl = {}; ctrl.type = DC_CTRL_SBO; ctrl.data_type = DATA_TYPE_U8; ctrl.p_data = malloc(1); *(uint8_t *)ctrl.p_data = 2; uint8_t sel_val = 2; int r = dc_signal_yk_set_status("RPC1:YK:1", DC_STEP_SELECT, &ctrl, &sel_val, "web"); if (DC_OK != r) { FAIL("SBO SELECT failed"); free(ctrl.p_data); return; } /* DIRECT with matching value */ r = dc_signal_yk_set_status("RPC1:YK:1", DC_STEP_DIRECT, &ctrl, &sel_val, "web"); if (DC_OK != r) { FAIL("SBO DIRECT failed"); free(ctrl.p_data); return; } free(ctrl.p_data); OK(); } } /** * @brief 测试 5:ao 信号 + SBO 控制 */ static void test_ao_signal(void) { float ao_val = 0.0f; TEST("dc_signal_ao()"); { int r = dc_signal_ao("RPC1:AO:1", "test ao", DATA_TYPE_F32, DC_CTRL_SBO, &ao_val, on_ao_changed, "protocol"); if (DC_OK != r) { FAIL("ao register failed"); return; } OK(); } TEST("dc_signal_ao_set_val() SBO"); { g_ao_changed = 0; dc_ctrl_t ctrl = {}; ctrl.type = DC_CTRL_SBO; ctrl.data_type = DATA_TYPE_F32; ctrl.p_data = malloc(4); *(float *)ctrl.p_data = 3.14f; float v = 3.14f; int r = dc_signal_ao_set_val("RPC1:AO:1", DC_STEP_SELECT, &ctrl, &v, "web"); if (DC_OK != r) { FAIL("ao SELECT failed"); free(ctrl.p_data); return; } r = dc_signal_ao_set_val("RPC1:AO:1", DC_STEP_DIRECT, &ctrl, &v, "web"); if (DC_OK != r) { FAIL("ao DIRECT failed"); free(ctrl.p_data); return; } free(ctrl.p_data); if (g_ao_changed < 2) { FAIL("ao callback not triggered"); return; } /* ctrl->step should be reset to READY */ if (ctrl.step != DC_STEP_READY) { FAIL("ctrl step not reset to READY"); return; } OK(); } TEST("dc_signal_ao_set_val_without_check()"); { g_ao_changed = 0; float v = 2.71f; int r = dc_signal_ao_set_val_without_check("RPC1:AO:1", DATA_TYPE_F32, &v, "protocol"); if (DC_OK != r) { FAIL("ao set without check failed"); return; } OK(); } } /** * @brief 测试 6:param 多区定值 */ static void test_param_signal(void) { uint32_t p1 = 10; uint32_t p2 = 20; void *parr[2] = {&p1, &p2}; TEST("dc_signal_param()"); { int r = dc_signal_param("RPC1:PARAM:1", "test param", DATA_TYPE_U32, DC_CTRL_SBO, parr, 2, on_param_changed, "web"); if (DC_OK != r) { FAIL("param register failed"); return; } OK(); } TEST("dc_signal_param_set_val() zone 0"); { g_param_changed = 0; dc_ctrl_t ctrl = {}; ctrl.type = DC_CTRL_SBO; ctrl.data_type = DATA_TYPE_U32; ctrl.p_data = malloc(4); *(uint32_t *)ctrl.p_data = 55; uint32_t v = 55; int r = dc_signal_param_set_val("RPC1:PARAM:1", DC_STEP_SELECT, &ctrl, 0, &v, "web"); if (DC_OK != r) { FAIL("param SELECT failed"); free(ctrl.p_data); return; } r = dc_signal_param_set_val("RPC1:PARAM:1", DC_STEP_DIRECT, &ctrl, 0, &v, "web"); if (DC_OK != r) { FAIL("param DIRECT failed"); free(ctrl.p_data); return; } free(ctrl.p_data); if (p1 != 55) { FAIL("zone0 not updated"); return; } OK(); } } /** * @brief 测试 7:信号查询 */ static void test_query(void) { TEST("dc_get_out_signal_info()"); { char desc[64] = {0}; uint8_t dt = 0; void *p = NULL; int r = dc_get_out_signal_info("RPC1:YC:1", desc, 64, &dt, &p); if (DC_OK != r || dt != DATA_TYPE_S32) { FAIL("query out failed"); return; } OK(); } TEST("dc_get_signal_count()"); { int n = dc_get_signal_count("out"); if (n < 1) { FAIL("out count should be >= 1"); return; } n = dc_get_signal_count("param"); if (n < 1) { FAIL("param count should be >= 1"); return; } OK(); } TEST("dc_get_signal_info_by_id()"); { char saddr[64] = {0}; char desc[64] = {0}; char dtstr[32] = {0}; uint8_t ct = 0; int r = dc_get_signal_info_by_id("out", 0, saddr, 64, desc, 64, dtstr, 32, &ct, NULL, 0); if (DC_OK != r) { FAIL("get by id failed"); return; } OK(); } TEST("dc_get_signal_val()"); { uint32_t v = 42; char buf[32] = {0}; int r = dc_get_signal_val(&v, DATA_TYPE_U32, buf, 32); if (DC_OK != r || 0 != strcmp(buf, "42")) { FAIL("val to str failed"); return; } OK(); } } /** * @brief 测试 8:类型工具 */ static void test_type_util(void) { TEST("dc_get_type_name()"); { const char *n = dc_get_type_name(DATA_TYPE_F32); if (0 != strcmp(n, "float")) { FAIL("type name mismatch"); return; } OK(); } TEST("dc_get_type_id_by_name()"); { uint8_t id = dc_get_type_id_by_name("uint32_t"); if (DATA_TYPE_U32 != id) { FAIL("type id mismatch"); return; } OK(); } TEST("dc_get_type_len()"); { if (4 != dc_get_type_len(DATA_TYPE_F32)) { FAIL("float len should be 4"); return; } if (8 != dc_get_type_len(DATA_TYPE_D64)) { FAIL("double len should be 8"); return; } OK(); } TEST("dc_create_data + dc_destroy_data"); { void *p = dc_create_data(DATA_TYPE_S32); if (!p) { FAIL("create data failed"); return; } dc_destroy_data(p, DATA_TYPE_S32); OK(); } TEST("dc_copy_val + dc_compare_val"); { int a = 10; int b = 0; dc_copy_val(&b, &a, DATA_TYPE_S32); if (b != 10) { FAIL("copy val failed"); return; } if (0 != dc_compare_val(&a, &b, DATA_TYPE_S32)) { FAIL("compare equal failed"); return; } b = 20; if (-1 != dc_compare_val(&a, &b, DATA_TYPE_S32)) { FAIL("compare unequal failed"); return; } OK(); } TEST("dc_set_val_from_str()"); { float f = 0.0f; int r = dc_set_val_from_str(&f, DATA_TYPE_F32, "3.14"); if (DC_OK != r || f < 3.13f || f > 3.15f) { FAIL("set from str failed"); return; } OK(); } TEST("dc_set_val_from_str() IP"); { uint8_t ip[4] = {0}; int r = dc_set_val_from_str(ip, DATA_TYPE_IP, "192.168.1.1"); if (DC_OK != r || ip[0] != 192 || ip[1] != 168 || ip[2] != 1 || ip[3] != 1) { FAIL("IP parse failed"); return; } OK(); } } /** * @brief 测试 9:事件队列 */ static void test_event_queue(void) { TEST("dc_event_queue_push + register"); { int r = dc_event_register_queue_pop(on_soe_pop); if (DC_OK != r) { FAIL("register pop failed"); return; } g_soe_received = 0; int dummy = 99; r = dc_event_queue_push(&dummy); if (DC_OK != r) { FAIL("event push failed"); return; } /* 消耗队列 */ dc_run_100ms(); if (g_soe_received < 1) { FAIL("event not consumed"); return; } OK(); } TEST("dc_disturb_dd_queue_push"); { int dummy = 1; int r = dc_disturb_dd_queue_push(&dummy); if (DC_OK != r) { FAIL("disturb push failed"); return; } OK(); } TEST("dc_fault_queue_push"); { int dummy = 1; int r = dc_fault_queue_push(&dummy); if (DC_OK != r) { FAIL("fault push failed"); return; } OK(); } } /** * @brief 测试 10:异步管理 */ static void test_async_mgmt(void) { TEST("dc_async_pending_count()"); { int n = dc_async_pending_count(); if (n < 0) { FAIL("pending count failed"); return; } OK(); } TEST("dc_async_set_default_timeout()"); { dc_async_set_default_timeout(5000); OK(); } TEST("dc_param_cfg_changed()"); { int r = dc_param_cfg_changed(); if (r < 0) { FAIL("cfg changed failed"); return; } OK(); } } /* ===== 入口 ===== */ int main(void) { printf("=== test_datacenter ===\n\n"); /* 初始化 */ dc_init(NULL); /* 信号注册 */ test_out_signal(); test_in_signal(); test_yk_signal(); test_ao_signal(); test_param_signal(); /* 查询 */ test_query(); /* 工具 */ test_type_util(); /* 事件队列 */ test_event_queue(); /* 异步管理 */ test_async_mgmt(); /* 清理 */ dc_cleanup(); /* 生命周期 */ dc_init(NULL); test_lifecycle(); dc_cleanup(); /* 结果 */ printf("\n=== result: %d pass, %d fail ===\n", g_pass, g_fail); return (g_fail > 0) ? 1 : 0; }