Files
STM32F4-Base/Drivers/BSP/UI/ui_page_yx.c
T
wandering1 498cc60b9d fix(UI): ui_page_yx.c 补 string.h——memset 隐式声明 #223-D 两条警告
pages-ui 合入(merge dea7591)后 done 门禁在主树全量重编暴露;
道E 树内增量构建吃缓存未触发重编,故其提交时"0 警"未假报但漏检此条
("只有重编该文件才暴露"的已知形态,教训进下方回填说明)。
体积不变(Code=224688 RO=24640 RW=1328 ZI=135088);BOM 已核 EF BB BF。
随笔同步内存台账受管区(pages 12 笔合入的 --no-verify 欠账)。
2026-10-25 15:45:29 +08:00

206 lines
9.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:UI Page YX (遥信量内容页)
* 模块功能:33 点遥信的固定名表页(方案 A)——每行一个遥信点,值列按快照的三个位图
* (有效 / 动作 / 单双点)映射成"合位-分位"、"动作-复归"或"--";动态列表页,
* 数据只读 Drivers/BSP/UI/ui_data.h 的实时快照,零 App include
* 为什么是 33 行固定表(V0.1 只有 6 点有硬件来源,另外 27 行都是 "--"):
* ① 与 104 点表一一对应,现场拿这张屏就能与上位机逐点对;② 开入落地后本页
* 零改动自动变真;③ 只显有效点的做法看不出"缺哪些点",而且点数一变黄金参考
* 就得重做(把判据建立在会漂的东西上等于没有判据)——取舍记录在
* docs/UI设计/页面扩充详细方案.md §3.1.3
* 名表为什么在页面侧而不在快照里:显示文案属表现层(同文档 §8);点数与单/双点性质
* 反过来**必须**由写侧下发(那是点表事实),两者边界见 ui_data.h 的 yx_dp 注释
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 4)
*/
/* 头文件包含区 */
#include "ui_page_yx.h"
#include <string.h>
#include "ui_data.h"
#include "ui_tokens.h"
/* 静态变量定义区 */
/*
* 33 点固定名表(下标 = 遥信位图位号 = 104 点表 IOA - 1,IOA 1~33)
*
* 两处偏离文档的命名(文档 §3.1.3 用的是区间简写,落到逐行名字时必须选一个,理由写在
* 这里而不是留给人猜):
* · 文档把 idx0~3 写成「YK1~4 位置」:遥信页上出现"YK"前缀会被读成遥控对象(那是
* 另一页的事),屏上只写「位置1~4」,与遥控的对应关系留在本注释;
* · 文档把 idx30~32 写成「闭锁1~3」:idx30 在写侧有明确语义来源(保护闭锁,定值校验
* 和自检失败),用编号反而把信息丢了;idx31/32 点表未定义功能名,如实写点号占位,
* **不编造功能名**。同理 idx4~13(YX5~14)也用点号占位。
*
* 长度与 UI_DATA_YX_N 的一致性由下面的编译期数组守卫钉住(本仓惯用宽度法,ARMCC C99
* 没有 _Static_assert):名表少写/多写一行直接编译失败,不会到了屏上才少一行
*/
static const char *const s_yx_name[UI_DATA_YX_N] = {
"位置1", "位置2", "位置3", "位置4", /* 0~3 双点;V0.1 无开入硬件 */
"YX5", "YX6", "YX7", "YX8", "YX9", /* 4~8 点表未定义功能名 */
"YX10", "YX11", "YX12", "YX13", "YX14", /* 9~13 (同上,不编名) */
"故障总", "过流Ⅰ段", "过流Ⅱ段", "过流Ⅲ段", /* 14~17 YX15~18,有硬件来源 */
"零序Ⅰ段", "零序Ⅱ段", /* 18,19 V0.1 无该判据上送 */
"接地故障", "重载", "过载", /* 20~22 过载(YX23)有来源 */
"外部遥信1", "外部遥信2", "外部遥信3", "外部遥信4", /* 23~26 开入未实现 */
"外部遥信5", "外部遥信6", "外部遥信7", /* 27~29 */
"保护闭锁", "YX32", "YX33", /* 30~32 闭锁(YX31)有来源 */
};
/* 名表行数与快照点数一致(不一致 = 编译期错误,见上注) */
typedef char yx_name_count_check[(sizeof(s_yx_name) / sizeof(s_yx_name[0]) == UI_DATA_YX_N) ? 1 : -1];
/* 值列词表(全在既有 183 字子集内,font-charset 实测零新增) */
#define YX_INVALID "--"
#define YX_DP_ON "合位"
#define YX_DP_OFF "分位"
#define YX_SP_ON "动作"
#define YX_SP_OFF "复归"
/* 帧缓存:只在发布序号变化时整帧拷一次(一帧约 200 B,逐行只读标量) */
static ui_data_rt_t s_rt;
static uint32_t s_seq = 0xFFFFFFFFu;
/* 函数定义区 */
/*
* 函数功能:定长缓冲内拷字面量(代码规范 7.5 禁 strcpy;长度取调用点 sizeof)
* 入口参数:a_dst - 目标缓冲(框架行缓冲 UI_BUF_STRLEN) char *
* a_src - 源字面量(含 NUL) const char *
* len - 目标容量 uint32_t
* 返回值:无
* 限定条件:源串长度必须小于 len(编译期由字面量 sizeof 保证,不做运行期截断——
* 截断只会把"动作"打成"动",比读错更难查)
*/
static void yx_put(char *a_dst, const char *a_src, uint32_t len) {
uint32_t i = 0u;
if (len == 0u) {
return;
}
while (((i + 1u) < len) && (a_src[i] != '\0')) {
a_dst[i] = a_src[i];
i++;
}
a_dst[i] = '\0';
}
/*
* 长度参数**必须取目标缓冲容量**,不能取 sizeof(源):本表的源是 `const char *const`
* 表里的**指针**,`sizeof(lit)` 得到的是指针宽度(4/8),于是 "故障总" 会被拷成第一个
* 汉字 "故"——ASCII 名("YX9" 恰好 4 字节)看着完全正常,CJK 名集体缺尾巴。这个错
* 编译器、cppcheck、编码钩子都不会响,是像素文本断言把它揪出来的(第 2 页第 7 行
* "过流Ⅰ段" 匹配不到,值列 "复归" 却在原位 = 名字被截、值没错位)。
* 批 1 那几处 *_PUT 宏传的是**字面量数组**(sizeof 就是数组长度),照抄到这里就中招——
* 同名宏、不同参数类别,正是"看着像同构其实不同"的那类坑。
*/
#define YX_PUT(dst, lit) yx_put((dst), (lit), (uint32_t)UI_BUF_STRLEN)
/*
* 函数功能:开页复位帧缓存(接框架 init_cb)
* 入口参数:无
* 返回值:无
* 限定条件:框架开页链内(uiTask)
* 函数说明:序号必须复位——否则重开本页会沿用上次开页的帧号,首帧之前的 "--" 状态
* 可能被永久留在屏上(批 1 已踩过一次,同一条纪律再用一遍)
*/
static void pg_init(void) {
s_seq = 0xFFFFFFFFu;
(void)memset(&s_rt, 0, sizeof(s_rt));
}
/*
* 函数功能:按发布序号刷新帧缓存
* 入口参数:无
* 返回值:无
* 限定条件:uiTask 绘制链内(refresh 前)
* 函数说明:ui_data_rt() 的通道是 0=成功 / -1=尚无首帧(不是 bool 成败);失败时把
* 序号打回不可能的值,下一轮一定重试,不会停在半新不旧的帧上
*/
static void pg_sync(void) {
uint32_t seq = ui_data_rt_seq();
if ((seq == s_seq) && (s_rt.valid != 0u)) {
return;
}
if (ui_data_rt(&s_rt) != 0) {
s_seq = 0xFFFFFFFFu;
(void)memset(&s_rt, 0, sizeof(s_rt));
return;
}
s_seq = seq;
}
/*
* 函数功能:总行数(固定 33 点,不随有效性增减)
* 入口参数:无
* 返回值:行数 uint16_t
* 限定条件:框架绘制/翻页链内
* 函数说明:故意返回常量——"只显有效点"会让现场无法判断缺了哪些点(方案 B 的缺点),
* 且行数变化会让黄金参考随运行态漂移
*/
static uint16_t pg_count(void) {
return (uint16_t)UI_DATA_YX_N;
}
/*
* 函数功能:逐行取数(名 = 固定名表,值 = 三个位图映射出的词)
* 入口参数:a_row - 行下标(0..32) uint16_t
* a_name - 名输出缓冲(UI_BUF_STRLEN) char *
* a_value - 值输出缓冲 char *
* 返回值:无
* 限定条件:框架绘制链内(双缓冲各调一次,本函数无副作用)
* 函数说明:判定顺序是"先无效、再单双点、最后位值"——无效位上的位值是真随机垃圾
* (位图没更新过就是 0,更新了也不代表该点有效),所以 IV 必须排在最前;
* 双点段 V0.1 恒无效,"合位/分位"这条支路当前不可达但必须写:开入一接上
* 就走通,而按协议写死的 4 个点本来就是双点(可达性由点表决定,不由本页决定)
*/
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
uint64_t bit;
a_value[0] = '\0';
if (a_row >= (uint16_t)UI_DATA_YX_N) {
a_name[0] = '\0';
return; /* 防御:行号越界不越读名表(count 恒 33,走不到) */
}
pg_sync();
YX_PUT(a_name, s_yx_name[a_row]);
bit = (uint64_t)1u << (uint8_t)a_row;
if ((s_rt.valid == 0u) || ((s_rt.yx_valid & bit) == 0u)) {
YX_PUT(a_value, YX_INVALID);
return;
}
if ((s_rt.yx_dp & bit) != 0u) {
YX_PUT(a_value, ((s_rt.yx_bits & bit) != 0u) ? YX_DP_ON : YX_DP_OFF);
return;
}
YX_PUT(a_value, ((s_rt.yx_bits & bit) != 0u) ? YX_SP_ON : YX_SP_OFF);
}
/*
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=yx 绑定引用):
* 固定 33 行的动态页(rows=NULL + count_cb/refresh),8 行/页 = 5 页;band_h 必须 >0
* (selectable 页选中行是黑字,无白带即不可见,同记录页约定)
*/
const content_page_def_t ui_page_yx_desc = {
.id = "yx",
.title = "遥信量",
.selectable = 1u,
.top_y = UI_LIST_TOP,
.pitch = UI_ROW_PITCH,
.band_h = UI_ROW_BAND_H,
.text_dy = 0,
.left_x = UI_LIST_LEFT_X,
.right_x = UI_LIST_RIGHT_X,
.rows_per_page = UI_LIST_ROWS,
.init_cb = pg_init,
.count_cb = pg_count,
.refresh = pg_refresh,
};