/* * 模块名称:UI SOE 记录页共享组件实现(ui_w_soe) * 模块功能:见 Drivers/BSP/UI/ui_w_soe.h——事件类型/元件显示名映射 + 每记录 2 行 * 渲染 + 单条读盘缓存;数据经注入 reader 取,未注入按空态渲染 * 适用范围:Drivers/BSP/UI * 说明:与 ui_w_records.c 同构(同一份"分页读记录 → 两行/条 + 实例缓存 + 空态 * 伪行"骨架),差别只在记录形态是二进制解码后的视图结构 * 作者:王建锋 * 创建日期:2026-10-25 * 修改记录: * v1.0 2026-10-25 王建锋 创建(道E task-14) */ /* 头文件包含区 */ #include #include "ui_w_soe.h" /* 私有宏定义区 */ /* * 框架文本缓冲宽度(真源 Drivers/BSP/UI/ui_tokens.h 的 UI_BUF_STRLEN = 32)。 * 这里**有意不 include ui_tokens.h**:该头经 ui_port.h 牵入整条 LVGL 头链,而本组件 * 是纯数据 + 纯文本搬运、一个 LVGL 调用都没有——容量宏本地化之后, * test/host/ui_data 可以直接把本 .c 编进主机单测核对显示名映射(同 * Drivers/BSP/UI/ui_w_records.h 把 ts/desc 容量宏本地化的同一先例与理由: * 容量不足只会退化成占位显示,不会越界读)。框架令牌若加宽,本宏同步加宽。 */ #define SOE_BUF_MAX 32u #define SOE_INVALID_STR "--" /* 未知编号/越界/读失败的统一占位(ASCII) */ #define SOE_EMPTY_STR "无记录" /* 空态伪行(与文本记录页同字) */ #define SOE_NO_CLOCK_STR "时钟无效" /* 时标行值列:IV 位不显示猜测时间 */ /* 静态变量定义区 */ /* 注入的 SOE reader(模块级单例:SOE 落盘本就是全局一份,两端各只有一份实现; * NULL = 未注入 → 空态。当前两端均未绑定,理由见头注"说明") */ static const ui_soe_reader_t *s_reader; /* * 事件类型显示名(下标 = UI_W_SOE_T_*;表长 = UI_W_SOE_T_COUNT) * 词长分两档是**版式约束**不是措辞偏好:值列可用宽约 59 px(4 汉字), * 名列约 94 px(7 汉字)——"动作/返回"是元件行的值列词,其余四类 4 字词 * 落在非元件事件的名列(口径 docs/UI设计/页面扩充详细方案.md §2.3 第 4 条) */ static const char *const s_type_name[UI_W_SOE_T_COUNT] = { SOE_INVALID_STR, /* 0 未知/未映射(含读失败) */ "动作", /* 1 元件动作 */ "返回", /* 2 元件返回 */ "压板退出", /* 3 软压板退出 */ "压板投入", /* 4 软压板投入 */ "采样闭锁", /* 5 采样故障闭锁 */ "采样恢复", /* 6 采样故障恢复 */ "未标定闭锁", /* 7 未标定闭锁(照 App/log/soe_rec.h 与 数据可信 卡5 的原词, * 不缩成"标定闭锁"——那个写法可以读成"把标定闭锁了", * 而这一行要说的是"因未标定而闭锁",一字之差含义相反) */ "标定复归", /* 8 标定复归 */ }; /* 元件显示名(下标 = UI 元件编号 = 保护元件枚举序;越界不入表) */ static const char *const s_elm_name[UI_W_SOE_ELM_N] = { "过流Ⅰ段", /* 0 */ "过流Ⅱ段", /* 1 */ "过流Ⅲ段", /* 2 */ "过负荷", /* 3 */ "过压", /* 4 */ "欠压", /* 5 */ "零序过压", /* 6 */ "过频", /* 7 */ "低频", /* 8 */ }; /* 函数定义区 */ /* * 函数功能:注入 SOE reader */ void ui_w_soe_bind(const ui_soe_reader_t *a_reader) { s_reader = a_reader; } /* * 函数功能:把定长源串写入框架文本缓冲 * 入口参数:a_dst - 框架给的名/值缓冲(宽 = SOE_BUF_MAX,与框架 UI_BUF_STRLEN 同值) char * * a_src - 源串(Flash 字面量或实例缓存) const char * * src_bytes - 源缓冲总宽(含 NUL,整块拷入以带上终止符) size_t * 返回值:无 * 限定条件:两者非 NULL;src_bytes 超 SOE_BUF_MAX 即按上限截断拷贝 * 函数说明:不用 strcpy/sprintf(门禁 CODE-RULE-06 禁用不安全函数);本组件 * 只做"整块搬运",不做格式化——格式化属页面行表(表现层)的事 */ static void soe_put(char *a_dst, const char *a_src, size_t src_bytes) { if ((a_dst == NULL) || (a_src == NULL)) { return; } if (src_bytes > SOE_BUF_MAX) { src_bytes = SOE_BUF_MAX; } (void)memcpy(a_dst, a_src, src_bytes); } /* * 函数功能:事件类型编号 → 显示名 */ const char *ui_w_soe_type_name(uint8_t a_type) { if (a_type >= UI_W_SOE_T_COUNT) { return SOE_INVALID_STR; } return s_type_name[a_type]; } /* * 函数功能:元件编号 → 显示名 */ const char *ui_w_soe_elm_name(uint8_t a_elm) { if (a_elm >= UI_W_SOE_ELM_N) { return SOE_INVALID_STR; } return s_elm_name[a_elm]; } /* * 函数功能:总行数(每条记录 2 行;空态 = 单条伪行"无记录") */ uint16_t ui_w_soe_count(ui_w_soe_t *a_inst) { uint32_t n; (void)a_inst; n = (s_reader != NULL) ? (uint32_t)s_reader->count() : 0u; if (n == 0u) { return 1u; /* 空态伪行(页脚"共 1 页",避免"共 0 页") */ } return (uint16_t)(n * 2u); } /* * 函数功能:逐行取数(偶数行 = 时标,奇数行 = 元件 + 事件类型) */ void ui_w_soe_refresh(ui_w_soe_t *a_inst, uint16_t a_row, char *a_name, char *a_value) { uint32_t n; uint32_t rec; uint8_t ok; a_value[0] = '\0'; n = (s_reader != NULL) ? (uint32_t)s_reader->count() : 0u; if (n == 0u) { (void)memcpy(a_name, SOE_EMPTY_STR, sizeof(SOE_EMPTY_STR)); return; } rec = (uint32_t)a_row / 2u; ok = 0u; if (a_inst->cache_valid != 0u && rec == a_inst->last_rec) { ok = 1u; /* 同一条记录的两行共用一次读盘 */ } else if (s_reader != NULL) { if ((s_reader->get)(rec, &a_inst->rec) == 0) { a_inst->last_rec = rec; a_inst->cache_valid = 1u; ok = 1u; } else { a_inst->cache_valid = 0u; /* 越界/读失败:本行退化为占位,不留脏缓存 */ } } if (ok == 0u) { (void)memcpy(a_name, SOE_INVALID_STR, sizeof(SOE_INVALID_STR)); return; } if (((uint16_t)a_row % 2u) == 0u) { /* 时标行:IV 位或未对时 → 名列 "--"、值列"时钟无效",绝不显示猜测时间 */ if ((a_inst->rec.flags & UI_W_SOE_FLG_IV) != 0u || a_inst->rec.ts[0] == '\0') { (void)memcpy(a_name, SOE_INVALID_STR, sizeof(SOE_INVALID_STR)); (void)memcpy(a_value, SOE_NO_CLOCK_STR, sizeof(SOE_NO_CLOCK_STR)); } else { soe_put(a_name, a_inst->rec.ts, sizeof(a_inst->rec.ts)); } return; } /* 内容行:元件事件 = 元件名 + 动作/返回;非元件事件 = 类型名(名列) */ if (a_inst->rec.elm != UI_W_SOE_ELM_NONE) { const char *elm = ui_w_soe_elm_name(a_inst->rec.elm); soe_put(a_name, elm, strlen(elm) + 1u); /* 只有动作/返回两类是元件级事件;其余编号(理论不该出现)仍给类型名 */ if (a_inst->rec.type == UI_W_SOE_T_ELM_ASSERT || a_inst->rec.type == UI_W_SOE_T_ELM_RETURN) { soe_put(a_value, ui_w_soe_type_name(a_inst->rec.type), strlen(ui_w_soe_type_name(a_inst->rec.type)) + 1u); } } else { const char *tn = ui_w_soe_type_name(a_inst->rec.type); soe_put(a_name, tn, strlen(tn) + 1u); } }