feat(ui-pages): 参数定值控制字软压板四个查看页接 par 快照

四个空叶子(参数定值 → 查看 → 参数/定值/控制字/软压板)接上 par 通道。页面薄、模型厚:
新建 Drivers/BSP/UI/ui_w_setview.{c,h} 收四张视图表 + Q10 换算 + 越界反显,四个页面各是
一份实例 + 四个跳板钩子;渲染与分页复用批 1 的 ui_w_setlist(查看态 commit=NULL:无保存行、
无编辑通路,§3.2.6 查看列口径)。

行表三项口径全部有真源,一项都不在这份文件里拟定
- id = App/algo/prot_set.h 的枚举序。BSP 不 include App 头(分层门禁),所以行表里是裸数字
  + 行尾注枚举名——这份对不上就没人能在 BSP 侧编译期发现,于是从外面钉:见下面的新脚本
- min/max = App/algo/prot_set.c 的 g_prot_set_meta Q10 上下限按该行小数位换算的结果。整定页
  下一批复用同一批表,界内界外一处一份,不会出现「查看看着合法、整定说越界」两套边界
- 显示名/小数位/单位 = docs/UI设计/页面扩充详细方案.md §3.2.2~§3.2.5 四张表

一处偏离文档(不是静默偏离,写进三处:代码注释 / 文档落地注 / 本正文)
- 四个返回系数按 2 位小数显示,文档写 3 位。理由是量值真实性不是排版:Q10 存不下 0.95
  (0.95×1024 截成 972 → 真存 0.94921875),按 3 位显示就多打一位根本不存在的数字;整定单
  上写 0.95 的人看到屏上 0.949 会怀疑装置、甚至反复重输(重输还是 0.949)。2 位才是这份存储
  在自身可分辨精度上的正确读数——文档的 3 位来自「把 Q10 当成十进制存储」这个误会
- 单位挂在值串尾(10.00A / 500ms)而不是塞进显示名:值列只有数字会让 60000 既是 ms 又是 A。
  为此给 ui_w_setlist_row_t 加了 unit 字段(该结构此前无任何消费者,扩展零成本);只挂在
  非编辑态——编辑中是逐位输入串,挂单位会让位光标算错列宽。A/V/ms/Hz 全 ASCII 不占字库

新增可重跑核验:tools/setview_check/check_setview_limits.py
- 从外面把三件事钉住:四张表的 id 恰好覆盖 0..39 各一次;数值行 min/max 等于 meta 上下限的
  换算;kind 与 meta.is_ctl 一致、dec 查表与行表小数位一致、单位只允许 ASCII。改 meta 不改
  行表就红。当前输出:行表 40 行 / dec 40 项 / meta 40 项 -> OK
- 写这个脚本时自己踩的两个坑也留在脚本注释里:① 正则用 \w+ 吃不下带引号的单位,会让整行
  **静默跳过**(少检一行比检查错更难发现)→ 改 \S+ 并加「行数必须等于 40」硬闸;② 脚本要
  打印中文,Windows 控制台按 CP936 写 stdout 会崩在半路(崩在半路 = 一条都没检查)→ 开头
  把 stdout/stderr 锁到 UTF-8

两个只有画面才会暴露的错(都不是静态检查抓到的,都记进了注释)
- ui_data_par() 的通道是 0=成功 / -1=尚无首帧,我按 bool 反义用了:真机与模拟器上都会「永远
  读不到」,整页值列恒 --,而页面看起来完全正常、编译与钩子全绿。看像素图才发现满屏横杠
- 肉眼把 CT 一次额定读成 1000A,字形匹配器证明值列只有 4 个字符位(命中 x=101/107/113/119):
  实际是 100A。断言因此按匹配器能证明的写。顺带一个自洽佐证:PT 10000/100 V 配 CT 100/5 A
  正是 10 kV 馈线终端的变比;控制字页默认值天然两两一组(Ⅰ/Ⅱ 段跳闸、Ⅲ 与过负荷段告警),
  行表若整体错位就不可能复现这个组合——这条已进像素断言,成为 id 对齐的机械证据

无数据时的显示口径
- 参数帧未就位:名字照打、值列打 --(不把 0 当定值显示,也不给整页空白让人以为菜单坏了)
- 越界反显只在 bad_valid=1 时参与:把「没数据」画成「这行有毛病」是假告警,比不画更糟
- Q10 换算不用浮点也不用 64 位除:整数段 + 小数段拆分四舍五入,满位进位(MicroLIB 软浮点与
  __aeabi_ldiv 一进镜像几百字节,模拟器(宿主 CRT)永远照不出这笔代价)

批 1 那个 KEEP_EXTRA 决策在这里第一次兑现
- 本批行表用到的「延时 / 电流 / 压力 / 总 / 配 / 重 / 间 / 未」等字,正是当时成组并入 KEEP_EXTRA
  的 35 个「未知冗余」字里的一批;若当时按 extract 输出重写字符集,这些字形已被洗掉,本批
  上屏就是白框,而 font-charset 只会报「字在清单里没有」——它报不出「字曾被删掉过」
- 本批 font-charset 直接 Passed,未扩字库、未重生成字体

验证
- @build 0 错误 0 警告;模拟器 0 警告;内容页 19 项 PASS(16 页像素 + 四页共 39 条文本断言),
  其余 12 页逐字节不变;菜单 8 状态 PASS;ui-data 83/83、soe 32/32
- 四页出图逐张肉眼确认:值列右缘不越屏(10000V 最宽)、名值不重叠、第 1 页行槽与页脚不撞;
  分页页码正确(定值 18 行 = 3 页、软压板 10 行 = 2 页)
- check_setview_limits.py OK;ui_menu_tab.h 等生成物全部由 gen_menu.py 重生成(手改生成物 = 一定
  会被覆盖,见批 2 那条),uvprojx/CMakeLists 注册由生成器写入,另手工登记 ui_w_setview.c 两处
- 全量钩子 --all-files:见下一行 gate;红项只有同族三条(台账与同码凭证回填归单写者)

体积(相对 b746a1f 台账 217204 / 21504 / 1256 / 132680)
- 实测 Code 221856 / RO 23508 / RW 1300 / ZI 134660
  → Code +4652、RO +2004、RW +44、ZI +1980
- Flash 合计 +6700 B、RAM +2024 B(三批累计)。本批 RO +2120 主要是 40 行行表与名称/单位串,
  ZI +388 是 par 帧缓存(184 B)+ 四个组件实例 + 注册表增量;这是把 40 项真定值上屏的代价,
  不是可省开销

待台架实测:装载真定值后的四页读数(含真机 bad_mask 非 0 时的反显行)、翻页节拍、
PT/CT 变比与 104 上位的定值读回(C_RD)一致性。

gate: only mem-budget/mem-numbers/size-gate pending lead ledger backfill
(同族三条,root cause 一条:台账与同码凭证回填归单写者)
像素基线单独提交(下一笔新增 4 个 shot_v*.bin):本笔单独 checkout 时 ui-golden 会报缺参考,
属预期拆分;上面的钩子结论取自「本笔 + 下一笔」的合并树。
自查:12 个新增/改动 UI 源文件 BOM 与 HEAD 对齐(本批 edit 又剥掉 4 次,逐次补回);CR = 0;
U+FFFD = 0;C 字面量纯 ASCII(中文只在带 BOM 文件里)。
This commit is contained in:
2026-10-25 14:13:05 +08:00
parent 8ee3be4c97
commit 45393be3d5
21 changed files with 1291 additions and 7 deletions
+8
View File
@@ -17,11 +17,19 @@
#include "ui_page_runlog.h"
#include "ui_page_selftest.h"
#include "ui_page_soe.h"
#include "ui_page_view_param.h"
#include "ui_page_view_plate.h"
#include "ui_page_view_set.h"
#include "ui_page_view_word.h"
const ui_page_entry_t g_ui_page_map[UI_PAGE_MAP_COUNT] = {
{ 1u, &ui_page_device_info_desc }, /* node 1 装置信息 */
{ 3u, &ui_page_ac_desc }, /* node 3 交流量 */
{ 4u, &ui_page_dc_desc }, /* node 4 直流量 */
{ 13u, &ui_page_view_param_desc }, /* node 13 参数 */
{ 14u, &ui_page_view_set_desc }, /* node 14 定值 */
{ 15u, &ui_page_view_word_desc }, /* node 15 控制字 */
{ 16u, &ui_page_view_plate_desc }, /* node 16 软压板 */
{ 30u, &ui_page_net_cfg_desc }, /* node 30 网口设置 */
{ 33u, &ui_page_soe_desc }, /* node 33 SOE记录 */
{ 39u, &ui_page_selftest_desc }, /* node 39 自检记录 */
+1 -1
View File
@@ -25,7 +25,7 @@ typedef struct {
const content_page_def_t *desc; /* 页面描述符(Flash const) */
} ui_page_entry_t;
#define UI_PAGE_MAP_COUNT 7u /* 绑定了内容页的叶子数 */
#define UI_PAGE_MAP_COUNT 11u /* 绑定了内容页的叶子数 */
extern const ui_page_entry_t g_ui_page_map[UI_PAGE_MAP_COUNT];
+94
View File
@@ -0,0 +1,94 @@
/*
* 模块名称:UI Page view_param (参数查看内容页)
* 模块功能:参数定值 → 查看 → 参数 的内容页——8 行数值表(PT/CT 一次二次额定 + 4 个返回系数),每页 8 行、1 页;
* 数据 = 参数快照(Q10)按行表小数位换算,越界项由 bad 回调反显;
* 本文件零 App include、零写路径(查看页没有"保存"这件事)
* 为什么这么薄:行表与取数在 ui_w_setview(四个查看页共用一份视图模型),
* 渲染与反显在 ui_w_setlist(与整定页同一组件)——本文件只把视图编号
* 和标准骨架几何令牌接进框架的跳板,改一次口径四处一致
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3)
*/
/* 头文件包含区 */
#include "ui_page_view_param.h"
#include "ui_tokens.h"
#include "ui_w_setlist.h"
#include "ui_w_setview.h"
/* 静态变量定义区 */
/* 本页实例(组件状态:编辑态等;查看页只用行游标与反显,实例仍须独占一份,
* 与别的页面共用会互相踩状态——同 ui_w_records 的实例化约定) */
static ui_w_setlist_t s_sl;
/* 函数定义区 */
/*
* 函数功能:开页绑定视图(行表 + 取数/反显回调一次配齐;接框架 init_cb)
* 入口参数:无
* 返回值:无
* 限定条件:框架开页链内调用(uiTask)
* 函数说明:每次开页都重新 attach——组件实例内残留的编辑态/游标会跨页带过来,
* 开页复位比"信任上一次已经清干净"可靠
*/
static void pg_init(void) {
ui_w_setview_attach(&s_sl, UI_W_SETVIEW_PARAM);
}
/*
* 函数功能:数据行数(框架 count_cb;查看视图没有保存行)
* 入口参数:无
* 返回值:行数 uint16_t
* 限定条件:框架绘制/翻页链内
*/
static uint16_t pg_count(void) {
return ui_w_setlist_count(&s_sl);
}
/*
* 函数功能:逐行取数(框架 refresh;内部先按发布序号同步参数帧)
* 入口参数:a_row / a_name / a_value - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
ui_w_setview_refresh(&s_sl, a_row, a_name, a_value);
}
/*
* 函数功能:自绘层(越界行反显;框架内置区块画完后调用)
* 入口参数:a_layer / a_clip - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_draw(lv_layer_t *a_layer, const lv_area_t *a_clip) {
ui_w_setlist_draw(&s_sl, a_layer, a_clip);
}
/*
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=view_param 绑定引用)
* edit_capable=0 + 无 key_cb + key_intercept=0:查看页不接受任何写交互,
* ↑↓ 移选与 ← 关页全走框架内置(§3.2.7 表里"权限不足 ENTER 一律忽略"在
* 查看页等价于"根本没有 ENTER 这条路")
*/
const content_page_def_t ui_page_view_param_desc = {
.id = "view_param",
.title = "参数",
.selectable = 1u,
.edit_capable = 0u,
.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,
.draw_cb = pg_draw,
};
+27
View File
@@ -0,0 +1,27 @@
/*
* 模块名称:UI Page view_param (参数查看内容页)
* 模块功能:参数查看叶子节点的内容页声明——向框架 ui_content_page 提供页面描述符
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3;规格 =
* docs/UI设计/页面扩充详细方案.md §3.2.6 查看列)
*/
#ifndef UI_PAGE_VIEW_PARAM_H
#define UI_PAGE_VIEW_PARAM_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ui_content_page.h"
/* 页面描述符(定义在 ui_page_view_param.c;生成物按菜单树 #page=view_param 引用本符号) */
extern const content_page_def_t ui_page_view_param_desc;
#ifdef __cplusplus
}
#endif
#endif /* UI_PAGE_VIEW_PARAM_H */
+94
View File
@@ -0,0 +1,94 @@
/*
* 模块名称:UI Page view_plate (软压板查看内容页)
* 模块功能:参数定值 → 查看 → 软压板 的内容页——10 行二态表(9 个段投退 + 保护总投:投入/退出),每页 8 行、2 页;
* 数据 = 参数快照(Q10)按行表小数位换算,越界项由 bad 回调反显;
* 本文件零 App include、零写路径(查看页没有"保存"这件事)
* 为什么这么薄:行表与取数在 ui_w_setview(四个查看页共用一份视图模型),
* 渲染与反显在 ui_w_setlist(与整定页同一组件)——本文件只把视图编号
* 和标准骨架几何令牌接进框架的跳板,改一次口径四处一致
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3)
*/
/* 头文件包含区 */
#include "ui_page_view_plate.h"
#include "ui_tokens.h"
#include "ui_w_setlist.h"
#include "ui_w_setview.h"
/* 静态变量定义区 */
/* 本页实例(组件状态:编辑态等;查看页只用行游标与反显,实例仍须独占一份,
* 与别的页面共用会互相踩状态——同 ui_w_records 的实例化约定) */
static ui_w_setlist_t s_sl;
/* 函数定义区 */
/*
* 函数功能:开页绑定视图(行表 + 取数/反显回调一次配齐;接框架 init_cb)
* 入口参数:无
* 返回值:无
* 限定条件:框架开页链内调用(uiTask)
* 函数说明:每次开页都重新 attach——组件实例内残留的编辑态/游标会跨页带过来,
* 开页复位比"信任上一次已经清干净"可靠
*/
static void pg_init(void) {
ui_w_setview_attach(&s_sl, UI_W_SETVIEW_PLATE);
}
/*
* 函数功能:数据行数(框架 count_cb;查看视图没有保存行)
* 入口参数:无
* 返回值:行数 uint16_t
* 限定条件:框架绘制/翻页链内
*/
static uint16_t pg_count(void) {
return ui_w_setlist_count(&s_sl);
}
/*
* 函数功能:逐行取数(框架 refresh;内部先按发布序号同步参数帧)
* 入口参数:a_row / a_name / a_value - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
ui_w_setview_refresh(&s_sl, a_row, a_name, a_value);
}
/*
* 函数功能:自绘层(越界行反显;框架内置区块画完后调用)
* 入口参数:a_layer / a_clip - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_draw(lv_layer_t *a_layer, const lv_area_t *a_clip) {
ui_w_setlist_draw(&s_sl, a_layer, a_clip);
}
/*
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=view_plate 绑定引用)
* edit_capable=0 + 无 key_cb + key_intercept=0:查看页不接受任何写交互,
* ↑↓ 移选与 ← 关页全走框架内置(§3.2.7 表里"权限不足 ENTER 一律忽略"在
* 查看页等价于"根本没有 ENTER 这条路")
*/
const content_page_def_t ui_page_view_plate_desc = {
.id = "view_plate",
.title = "软压板",
.selectable = 1u,
.edit_capable = 0u,
.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,
.draw_cb = pg_draw,
};
+27
View File
@@ -0,0 +1,27 @@
/*
* 模块名称:UI Page view_plate (软压板查看内容页)
* 模块功能:软压板查看叶子节点的内容页声明——向框架 ui_content_page 提供页面描述符
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3;规格 =
* docs/UI设计/页面扩充详细方案.md §3.2.6 查看列)
*/
#ifndef UI_PAGE_VIEW_PLATE_H
#define UI_PAGE_VIEW_PLATE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ui_content_page.h"
/* 页面描述符(定义在 ui_page_view_plate.c;生成物按菜单树 #page=view_plate 引用本符号) */
extern const content_page_def_t ui_page_view_plate_desc;
#ifdef __cplusplus
}
#endif
#endif /* UI_PAGE_VIEW_PLATE_H */
+94
View File
@@ -0,0 +1,94 @@
/*
* 模块名称:UI Page view_set (定值查看内容页)
* 模块功能:参数定值 → 查看 → 定值 的内容页——18 行数值表(三段过流/过负荷/过压/低压/零序/过频/低频 定值与延时),每页 8 行、3 页;
* 数据 = 参数快照(Q10)按行表小数位换算,越界项由 bad 回调反显;
* 本文件零 App include、零写路径(查看页没有"保存"这件事)
* 为什么这么薄:行表与取数在 ui_w_setview(四个查看页共用一份视图模型),
* 渲染与反显在 ui_w_setlist(与整定页同一组件)——本文件只把视图编号
* 和标准骨架几何令牌接进框架的跳板,改一次口径四处一致
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3)
*/
/* 头文件包含区 */
#include "ui_page_view_set.h"
#include "ui_tokens.h"
#include "ui_w_setlist.h"
#include "ui_w_setview.h"
/* 静态变量定义区 */
/* 本页实例(组件状态:编辑态等;查看页只用行游标与反显,实例仍须独占一份,
* 与别的页面共用会互相踩状态——同 ui_w_records 的实例化约定) */
static ui_w_setlist_t s_sl;
/* 函数定义区 */
/*
* 函数功能:开页绑定视图(行表 + 取数/反显回调一次配齐;接框架 init_cb)
* 入口参数:无
* 返回值:无
* 限定条件:框架开页链内调用(uiTask)
* 函数说明:每次开页都重新 attach——组件实例内残留的编辑态/游标会跨页带过来,
* 开页复位比"信任上一次已经清干净"可靠
*/
static void pg_init(void) {
ui_w_setview_attach(&s_sl, UI_W_SETVIEW_SET);
}
/*
* 函数功能:数据行数(框架 count_cb;查看视图没有保存行)
* 入口参数:无
* 返回值:行数 uint16_t
* 限定条件:框架绘制/翻页链内
*/
static uint16_t pg_count(void) {
return ui_w_setlist_count(&s_sl);
}
/*
* 函数功能:逐行取数(框架 refresh;内部先按发布序号同步参数帧)
* 入口参数:a_row / a_name / a_value - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
ui_w_setview_refresh(&s_sl, a_row, a_name, a_value);
}
/*
* 函数功能:自绘层(越界行反显;框架内置区块画完后调用)
* 入口参数:a_layer / a_clip - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_draw(lv_layer_t *a_layer, const lv_area_t *a_clip) {
ui_w_setlist_draw(&s_sl, a_layer, a_clip);
}
/*
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=view_set 绑定引用)
* edit_capable=0 + 无 key_cb + key_intercept=0:查看页不接受任何写交互,
* ↑↓ 移选与 ← 关页全走框架内置(§3.2.7 表里"权限不足 ENTER 一律忽略"在
* 查看页等价于"根本没有 ENTER 这条路")
*/
const content_page_def_t ui_page_view_set_desc = {
.id = "view_set",
.title = "定值",
.selectable = 1u,
.edit_capable = 0u,
.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,
.draw_cb = pg_draw,
};
+27
View File
@@ -0,0 +1,27 @@
/*
* 模块名称:UI Page view_set (定值查看内容页)
* 模块功能:定值查看叶子节点的内容页声明——向框架 ui_content_page 提供页面描述符
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3;规格 =
* docs/UI设计/页面扩充详细方案.md §3.2.6 查看列)
*/
#ifndef UI_PAGE_VIEW_SET_H
#define UI_PAGE_VIEW_SET_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ui_content_page.h"
/* 页面描述符(定义在 ui_page_view_set.c;生成物按菜单树 #page=view_set 引用本符号) */
extern const content_page_def_t ui_page_view_set_desc;
#ifdef __cplusplus
}
#endif
#endif /* UI_PAGE_VIEW_SET_H */
+94
View File
@@ -0,0 +1,94 @@
/*
* 模块名称:UI Page view_word (控制字查看内容页)
* 模块功能:参数定值 → 查看 → 控制字 的内容页——4 行二态表(*_TRIP:告警/跳闸),每页 8 行、1 页;
* 数据 = 参数快照(Q10)按行表小数位换算,越界项由 bad 回调反显;
* 本文件零 App include、零写路径(查看页没有"保存"这件事)
* 为什么这么薄:行表与取数在 ui_w_setview(四个查看页共用一份视图模型),
* 渲染与反显在 ui_w_setlist(与整定页同一组件)——本文件只把视图编号
* 和标准骨架几何令牌接进框架的跳板,改一次口径四处一致
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3)
*/
/* 头文件包含区 */
#include "ui_page_view_word.h"
#include "ui_tokens.h"
#include "ui_w_setlist.h"
#include "ui_w_setview.h"
/* 静态变量定义区 */
/* 本页实例(组件状态:编辑态等;查看页只用行游标与反显,实例仍须独占一份,
* 与别的页面共用会互相踩状态——同 ui_w_records 的实例化约定) */
static ui_w_setlist_t s_sl;
/* 函数定义区 */
/*
* 函数功能:开页绑定视图(行表 + 取数/反显回调一次配齐;接框架 init_cb)
* 入口参数:无
* 返回值:无
* 限定条件:框架开页链内调用(uiTask)
* 函数说明:每次开页都重新 attach——组件实例内残留的编辑态/游标会跨页带过来,
* 开页复位比"信任上一次已经清干净"可靠
*/
static void pg_init(void) {
ui_w_setview_attach(&s_sl, UI_W_SETVIEW_WORD);
}
/*
* 函数功能:数据行数(框架 count_cb;查看视图没有保存行)
* 入口参数:无
* 返回值:行数 uint16_t
* 限定条件:框架绘制/翻页链内
*/
static uint16_t pg_count(void) {
return ui_w_setlist_count(&s_sl);
}
/*
* 函数功能:逐行取数(框架 refresh;内部先按发布序号同步参数帧)
* 入口参数:a_row / a_name / a_value - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
ui_w_setview_refresh(&s_sl, a_row, a_name, a_value);
}
/*
* 函数功能:自绘层(越界行反显;框架内置区块画完后调用)
* 入口参数:a_layer / a_clip - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
*/
static void pg_draw(lv_layer_t *a_layer, const lv_area_t *a_clip) {
ui_w_setlist_draw(&s_sl, a_layer, a_clip);
}
/*
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=view_word 绑定引用)
* edit_capable=0 + 无 key_cb + key_intercept=0:查看页不接受任何写交互,
* ↑↓ 移选与 ← 关页全走框架内置(§3.2.7 表里"权限不足 ENTER 一律忽略"在
* 查看页等价于"根本没有 ENTER 这条路")
*/
const content_page_def_t ui_page_view_word_desc = {
.id = "view_word",
.title = "控制字",
.selectable = 1u,
.edit_capable = 0u,
.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,
.draw_cb = pg_draw,
};
+27
View File
@@ -0,0 +1,27 @@
/*
* 模块名称:UI Page view_word (控制字查看内容页)
* 模块功能:控制字查看叶子节点的内容页声明——向框架 ui_content_page 提供页面描述符
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3;规格 =
* docs/UI设计/页面扩充详细方案.md §3.2.6 查看列)
*/
#ifndef UI_PAGE_VIEW_WORD_H
#define UI_PAGE_VIEW_WORD_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ui_content_page.h"
/* 页面描述符(定义在 ui_page_view_word.c;生成物按菜单树 #page=view_word 引用本符号) */
extern const content_page_def_t ui_page_view_word_desc;
#ifdef __cplusplus
}
#endif
#endif /* UI_PAGE_VIEW_WORD_H */
+7 -1
View File
@@ -146,7 +146,13 @@ void ui_w_setlist_refresh(ui_w_setlist_t *a_inst, uint16_t a_row,
ui_w_editor_format(&a_inst->ed, a_value, UI_BUF_STRLEN); /* 编辑实时串 */
}
else {
ui_w_editor_fmt_scaled(a_inst->get(r->id), r->decimals, a_value, UI_BUF_STRLEN);
/* 单位后缀(r->unit NULL = 不挂):先格式化到临时串再合成,一次界内写回,
* 不用 strcat 类函数(代码规范 7.5;且长度不足时会静默截半,屏幕上更难查) */
char fmt_buf[UI_BUF_STRLEN];
ui_w_editor_fmt_scaled(a_inst->get(r->id), r->decimals, fmt_buf, UI_BUF_STRLEN);
(void)lv_snprintf(a_value, UI_BUF_STRLEN, "%s%s", fmt_buf,
(r->unit != NULL) ? r->unit : "");
}
return;
}
+4
View File
@@ -63,6 +63,10 @@ typedef struct {
int32_t min; /* 数值行下限(含;>=0;二态行忽略) */
int32_t max; /* 数值行上限(含;二态行忽略) */
const char *const *opts; /* 二态行:NUL 结尾 Flash 字符串串表(数值行 NULL) */
const char *unit; /* 数值行单位后缀(Flash,NULL = 不挂;二态行忽略)
* 单位是"屏上的话",跟视图表走、不进 App 快照;只挂在
* 非编辑态的数值串尾——编辑中是逐位输入串,挂单位会让
* ui_w_editor 的位光标算错列宽(其契约:串内只有数字与点) */
} ui_w_setlist_row_t;
/* 数据回调组(均 uiTask 上下文、页面绘制/按键链内调用;实现由批 2 页面跳板提供,
+381
View File
@@ -0,0 +1,381 @@
/*
* 模块名称:UI 定值查看视图模型(ui_w_setview)
* 模块功能:参数 / 定值 / 控制字 / 软压板四张视图表与它们的取数、换算、越界反显——
* 供四个查看页薄跳板共用;声明与分层理由见 ui_w_setview.h
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3)
*/
/* 头文件包含区 */
#include <string.h>
#include "ui_w_setview.h"
#include "ui_data.h"
#include "ui_tokens.h" /* UI_BUF_STRLEN:框架行缓冲长度(无帧占位串要写进同一缓冲) */
/* 静态变量定义区 */
/* 二态行的串表(下标 = 记录值;0/1 的语义来自 App/algo/prot_set.h 的 meta 注释:
* *_TRIP 0 = 告警 / 1 = 跳闸,*_EN 0 = 退出 / 1 = 投入;串表以 NULL 结尾,
* 组件按串表长度决定 ENTER 循环切换的范围) */
const char *const s_opts_trip[3] = { "告警", "跳闸", NULL };
const char *const s_opts_plate[3] = { "退出", "投入", NULL };
/*
* 四张视图表(Flash const)。行表的三项口径全部有真源,不在本文件里自由发挥:
* · id = App/algo/prot_set.h 的 prot_set_id_t 枚举序(BSP 不 include App 头,
* 故写裸数字并把枚举名注在行尾;对不上由主机单测与像素值两处暴露)
* · min/max = App/algo/prot_set.c 的 g_prot_set_meta 上下限(Q10)换算到本行
* decimals 后的整数——**照抄换算结果,不自己拟定**;整定页复用同一批表,
* 界内外一处一份,不会出现"查看看着合法、整定说越界"的两套边界
* · 显示名/小数位/单位 = docs/UI设计/页面扩充详细方案.md §3.2.2~§3.2.5 四张表;
* **唯一偏离**:四个返回系数按 2 位小数显示(文档写 3 位)。这不是排版取舍而是
* 量值真实性问题——Q10 存不下 0.95:0.95×1024 截成 972,真存的是 0.94921875,
* 按 3 位显示就打出一位根本不存在的"9",整定单上写 0.95 的人看到 0.949 会怀疑
* 装置、甚至反复重输(重输也还是 0.949)。2 位显示 0.95 才是这份存储在它自己可
* 分辨精度上的正确读数;文档的 3 位来自"把 Q10 当成十进制存储"这个误会
* 单位挂在值串尾(A/V/ms/Hz 全 ASCII,不占中文字库):值列只有数字会让"60000"既是
* ms 又是 A,读者只能靠猜;行表带 unit 字段是本次给组件加的(NULL = 不挂)
*/
/* 参数视图(8 行 = 1 页) */
static const ui_w_setlist_row_t s_rows_param[8] = {
{ "PT一次", 27u, UI_W_SETLIST_KIND_NUM, 0u, 100, 500000, NULL, "V" }, /* PS_PT_PRI */
{ "PT二次", 28u, UI_W_SETLIST_KIND_NUM, 0u, 1, 200, NULL, "V" }, /* PS_PT_SEC */
{ "CT一次", 29u, UI_W_SETLIST_KIND_NUM, 0u, 1, 10000, NULL, "A" }, /* PS_CT_PRI */
{ "CT二次", 30u, UI_W_SETLIST_KIND_NUM, 0u, 1, 10, NULL, "A" }, /* PS_CT_SEC */
{ "过量返回", 25u, UI_W_SETLIST_KIND_NUM, 2u, 50, 100, NULL, NULL }, /* PS_RET_OVER */
{ "欠量返回", 26u, UI_W_SETLIST_KIND_NUM, 2u, 100, 150, NULL, NULL }, /* PS_RET_UNDER */
{ "过频返回", 38u, UI_W_SETLIST_KIND_NUM, 2u, 90, 100, NULL, NULL }, /* PS_RET_OF */
{ "低频返回", 39u, UI_W_SETLIST_KIND_NUM, 2u, 100, 110, NULL, NULL }, /* PS_RET_UF */
};
/* 定值视图(18 行 = 3 页) */
static const ui_w_setlist_row_t s_rows_set[18] = {
{ "Ⅰ段电流", 2u, UI_W_SETLIST_KIND_NUM, 2u, 0, 10000, NULL, "A" }, /* PS_OC1_CUR */
{ "Ⅰ段延时", 3u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_OC1_DLY */
{ "Ⅱ段电流", 6u, UI_W_SETLIST_KIND_NUM, 2u, 0, 10000, NULL, "A" }, /* PS_OC2_CUR */
{ "Ⅱ段延时", 7u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_OC2_DLY */
{ "Ⅲ段电流", 10u, UI_W_SETLIST_KIND_NUM, 2u, 0, 10000, NULL, "A" }, /* PS_OC3_CUR */
{ "Ⅲ段延时", 11u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_OC3_DLY */
{ "过负荷电流", 14u, UI_W_SETLIST_KIND_NUM, 2u, 0, 10000, NULL, "A" }, /* PS_OL_CUR */
{ "过负荷延时", 15u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_OL_DLY */
{ "过压定值", 17u, UI_W_SETLIST_KIND_NUM, 1u, 0, 2000, NULL, "V" }, /* PS_OV_VOL */
{ "过压延时", 18u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_OV_DLY */
{ "低压定值", 20u, UI_W_SETLIST_KIND_NUM, 1u, 0, 2000, NULL, "V" }, /* PS_UV_VOL */
{ "低压延时", 21u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_UV_DLY */
{ "零序定值", 23u, UI_W_SETLIST_KIND_NUM, 1u, 0, 2000, NULL, "V" }, /* PS_3U0_VOL */
{ "零序延时", 24u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_3U0_DLY */
{ "过频定值", 33u, UI_W_SETLIST_KIND_NUM, 2u, 5000, 5500, NULL, "Hz" }, /* PS_OF_HZ */
{ "过频延时", 34u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_OF_DLY */
{ "低频定值", 36u, UI_W_SETLIST_KIND_NUM, 2u, 4500, 5000, NULL, "Hz" }, /* PS_UF_HZ */
{ "低频延时", 37u, UI_W_SETLIST_KIND_NUM, 0u, 0, 60000, NULL, "ms" }, /* PS_UF_DLY */
};
/* 控制字视图(4 行 = 1 页) */
static const ui_w_setlist_row_t s_rows_word[4] = {
{ "Ⅰ段跳闸", 1u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_trip, NULL }, /* PS_OC1_TRIP */
{ "Ⅱ段跳闸", 5u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_trip, NULL }, /* PS_OC2_TRIP */
{ "Ⅲ段跳闸", 9u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_trip, NULL }, /* PS_OC3_TRIP */
{ "过负荷跳闸", 13u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_trip, NULL }, /* PS_OL_TRIP */
};
/* 软压板视图(10 行 = 2 页) */
static const ui_w_setlist_row_t s_rows_plate[10] = {
{ "Ⅰ段投入", 0u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_OC1_EN */
{ "Ⅱ段投入", 4u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_OC2_EN */
{ "Ⅲ段投入", 8u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_OC3_EN */
{ "过负荷投入", 12u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_OL_EN */
{ "过压投入", 16u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_OV_EN */
{ "低压投入", 19u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_UV_EN */
{ "零序投入", 22u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_3U0_EN */
{ "过频投入", 32u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_OF_EN */
{ "低频投入", 35u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_UF_EN */
{ "保护总投入", 31u, UI_W_SETLIST_KIND_ENUM, 0u, 0, 0, s_opts_plate, NULL }, /* PS_GLOBAL_EN */
};
/*
* 每个定值项的显示小数位(下标 = 枚举序;二态项为 0,不参与数值换算)
* 为什么要单独一张表而不是从行表里找:组件的 get(id) 回调只给 id 不给行号,
* 同一 id 只会出现在一张视图里(四张表恰好覆盖 0..39 各一次,由主机单测钉住),
* 所以这张表是无歧义的,且比遍历四张表便宜
*/
static const uint8_t s_dec[UI_DATA_SET_N] = {
0, 0, 2, 0, 0, 0, 2, 0, 0, 0,
2, 0, 0, 0, 2, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 2, 2, 0, 0, 0,
0, 0, 0, 2, 0, 0, 2, 0, 2, 2,
};
/* 无参数帧时值列的占位串(与全仓"读不到就 --"同一口径) */
#define SV_NO_DATA "--"
/* 参数帧缓存(四个查看页共用一份:同屏只开一页,无并发读者) */
static ui_data_par_t s_par;
static uint32_t s_par_seq = 0xFFFFFFFFu; /* 与任何合法发布序号都不同 */
static uint8_t s_par_ok;
/* 当前视图的行表(bad 回调只收到行号,需要它把行号换回 id 再查位图) */
static const ui_w_setlist_row_t *s_cur_rows;
static uint16_t s_cur_n; /* 当前视图行数(bad 回调的边界) */
/* 行表长度(编译期算,别手写数字——手写数字与表长不一致是最难查的错) */
#define SV_ROW_N(tbl) ((uint16_t)(sizeof(tbl) / sizeof((tbl)[0])))
/* 函数定义区 */
/*
* 函数功能:Q10 原值 → 指定小数位的定点显示值(四舍五入)
* 入口参数:a_q10 - Q10 原值(工程量 × 1024) int32_t
* a_dec - 小数位 0..3 uint8_t
* 返回值:工程量 × 10^a_dec int32_t
* 限定条件:|a_q10| 在 int32 范围内且换算后仍在 int32 内(行表生成时已按
* g_prot_set_meta 的最大量程核过:最大项 PT 一次额定 500000 V,d=0)
* 函数说明:不用浮点也不用 64 位除法——MicroLIB 的 soft-float 与 __aeabi_ldiv
* 一进镜像就是几百字节,而模拟器(宿主 CRT)永远照不出这笔代价(本仓
* 反复记录的同一条口径)。做法是拆整数段与小数段:
* v = ip×1024 + fp(fp∈[0,1023],负数用算术右移取 floor,fp 仍非负),
* 小数段按 10^d 缩放后四舍五入,满 10^d 就进位回整数段。
* 误差上界半个最低显示位——与 prot_set 自己的 Q10 精度同源,
* 屏上显示 10.00A 而实际 10.0004A 不是本页造成的,是存储精度
*/
static int32_t sv_q10_to_scaled(int32_t a_q10, uint8_t a_dec) {
static const int32_t p10[4] = { 1, 10, 100, 1000 };
int32_t ip;
uint32_t fp;
int32_t r;
ip = a_q10 >> 10;
fp = (uint32_t)a_q10 & 0x3FFu;
r = ip * p10[a_dec];
if (a_dec != 0u) {
uint32_t fr = (fp * (uint32_t)p10[a_dec] + 512u) >> 10; /* 作用域收在这里 */
if (fr >= (uint32_t)p10[a_dec]) { /* 小数段进位(如 0.9996 → 1.000) */
r += p10[a_dec];
fr -= (uint32_t)p10[a_dec];
}
r += (int32_t)fr;
}
return r;
}
/*
* 函数功能:读某定值项的显示值(组件 get 回调)
* 入口参数:a_id - 行表登记的 id(= 枚举序) uint8_t
* 返回值:工程量 × 10^decimals int32_t
* 限定条件:ui_w_setview_sync() 已先跑过且 ui_w_setview_ready() 为真
* (页面在值列打 "--" 之前不会走到这里)
* 函数说明:id 越界(>39)返回 0 属防御性死支——行表是本文件内 const,越界 id
* 只可能来自代码改动错误,由主机单测在构建期前拦,不靠运行期兜
*/
static int32_t sv_get(uint8_t a_id) {
if (a_id >= (uint8_t)UI_DATA_SET_N) {
return 0;
}
return sv_q10_to_scaled(s_par.q10[a_id], s_dec[a_id]);
}
/*
* 函数功能:只读视图的写回调占位(组件契约要求非 NULL)
* 入口参数:a_id/a_val - 未使用
* 返回值:无
* 限定条件:查看视图不可编辑,组件不会调到这里
* 函数说明:刻意空实现——查看页**没有任何**该写进 RAM 工作副本的语义;
* 真要在查看页改值,得先把整定批次的 commit/state 一起接上
*/
static void sv_set_unused(uint8_t a_id, int32_t a_val) {
(void)a_id;
(void)a_val;
}
/*
* 函数功能:某数据行是否越界(组件 bad 回调 → 反显高亮)
* 入口参数:a_row - 数据行下标(不含保存行) uint16_t
* 返回值:true = 反显;false = 正常 bool
* 限定条件:uiTask 绘制链内;s_cur_rows 已由 attach 设定
* 函数说明:位图来自 prot_set_bad_mask()(上一次整表装载/提交后仍在量程外的项),
* 经 par 帧 bad_mask + bad_valid 下发。bad_valid=0 表示位图无意义
* (App 侧只在位图非 0 时置 1),此时一律不反显——把"没数据"画成
* "这行有问题"是假告警,比不画更糟
*/
static bool sv_bad(uint16_t a_row) {
const ui_w_setlist_row_t *r;
uint64_t bit;
if ((s_cur_rows == NULL) || (s_par_ok == 0u) || (s_par.bad_valid == 0u)) {
return false;
}
if (a_row >= s_cur_n) {
return false; /* 行号越界(组件不会给,防御:越界不反显也不越读) */
}
r = &s_cur_rows[a_row];
if (r->id >= (uint8_t)UI_DATA_SET_N) {
return false;
}
bit = (uint64_t)1u << r->id;
return (s_par.bad_mask & bit) != 0u;
}
/*
* 函数功能:按发布序号取参数帧快照(接各查看页 refresh 前调用)
* 入口参数:无
* 返回值:无
* 限定条件:uiTask 上下文(页面绘制链内);只读快照层,不写任何东西
* 函数说明:序号没变就一帧都不重拷(一帧约 184 B;四个查看页共用这份缓存——同屏
* 只可能开着一页,彼此之间不存在并发读者)。读取失败把序号打回不可能的值,
* 下一轮一定重试,不会停在半新不旧的帧上
*/
void ui_w_setview_sync(void) {
uint32_t seq = ui_data_par_seq();
if ((seq == s_par_seq) && (s_par_ok != 0u)) {
return; /* 序号未变:一帧都不重拷 */
}
/* ui_data_par() 的通道是 0=成功 / -1=尚无首帧 / -2=参数无效(快照层的既有约定,
* 不是布尔成败)——把它当 bool 用会反义:模拟器与真机上都会永远"读不到",
* 于是整页值列恒 "--",而页面看起来完全正常(本函数第一版就踩了这一条,
* 是看像素图才发现满屏横杠,静态检查看不出来) */
if (ui_data_par(&s_par) != 0) {
s_par_ok = 0u;
s_par_seq = 0xFFFFFFFFu; /* 失败态不认序号,下一轮一定重试 */
(void)memset(&s_par, 0, sizeof(s_par));
return;
}
s_par_seq = seq;
s_par_ok = (s_par.valid != 0u) ? 1u : 0u;
}
/*
* 函数功能:逐行取数(页面 refresh 的直直通:先同步参数帧,再走组件)
* 入口参数:a_inst - 实例 ui_w_setlist_t *;a_row - 行下标 uint16_t
* a_name/a_value - 框架行缓冲(UI_BUF_STRLEN) char *
* 返回值:无
* 限定条件:框架绘制链内(uiTask)
* 函数说明:**参数帧未就位时只把值列打成 "--",名字照打**——空值会被读成"这项是 0",
* 整页空白会让人以为菜单坏了;打出"名字 + --"才说得出真正的事实
* ("这张表在等参数帧")。帧就位后走组件:数值行按小数位 + 单位,
* 二态行打串表词
*/
void ui_w_setview_refresh(ui_w_setlist_t *a_inst, uint16_t a_row,
char *a_name, char *a_value) {
ui_w_setview_sync();
if (ui_w_setview_ready() != 0u) {
ui_w_setlist_refresh(a_inst, a_row, a_name, a_value);
return;
}
a_name[0] = '\0';
a_value[0] = '\0';
if ((a_inst != NULL) && (a_inst->rows != NULL) && (a_row < a_inst->count)) {
(void)lv_snprintf(a_name, UI_BUF_STRLEN, "%s", a_inst->rows[a_row].name);
}
(void)lv_snprintf(a_value, UI_BUF_STRLEN, "%s", SV_NO_DATA);
}
/*
* 函数功能:参数帧是否已就位
* 入口参数:无
* 返回值:1 = 有有效帧可显示;0 = 尚无首帧或读取失败 uint8_t
* 限定条件:uiTask 上下文;纯查询,无副作用
* 函数说明:返回 uint8_t 而不是 bool 是**本层约定**(bsp-err 的 err-type:BSP 公开 API
* 不得返回 bool——bool 把"为什么失败"整条丢掉,而这里虽然确实不需要原因,
* 也不能给整层开一个按名字豁免的例外;先例见 ui_w_setlist_key 的键分发标志)。
* 调用方拿它决定值列打换算值还是打 "--",没有错误要上报
*/
uint8_t ui_w_setview_ready(void) {
return s_par_ok;
}
/*
* 函数功能:取某视图的行表与行数(结构自测用;页面不需要,attach 时才自己取)
* 入口参数:a_view - 视图编号 ui_w_setview_t;a_n - 行数输出(不可为 NULL) uint16_t *
* 返回值:行表首地址;视图号非法返回 NULL const ui_w_setlist_row_t *
* 限定条件:只读;行表是 const Flash 表
* 函数说明:存在的理由是让主机单测能把四张表当成数据来核(id 是否恰好覆盖 0..39
* 各一次、二态行有没有串表、小数位与 dec 查表是否一致)。这些约束在
* 编译期看不出来,页面上也只有当前可见的 8 行会被像素断言看到——
* 第 2、3 页的行错了对齐,屏幕上要翻两页才看得见,而人不会去翻
*/
const ui_w_setlist_row_t *ui_w_setview_table(ui_w_setview_t a_view, uint16_t *a_n) {
if (a_n == NULL) {
return NULL;
}
switch (a_view) {
case UI_W_SETVIEW_PARAM:
*a_n = SV_ROW_N(s_rows_param);
return s_rows_param;
case UI_W_SETVIEW_SET:
*a_n = SV_ROW_N(s_rows_set);
return s_rows_set;
case UI_W_SETVIEW_WORD:
*a_n = SV_ROW_N(s_rows_word);
return s_rows_word;
case UI_W_SETVIEW_PLATE:
*a_n = SV_ROW_N(s_rows_plate);
return s_rows_plate;
default:
*a_n = 0u;
return NULL;
}
}
/*
* 函数功能:取 id → 显示小数位的查表(结构自测用)
* 入口参数:a_n - 表长输出(= UI_DATA_SET_N) uint16_t *
* 返回值:表首地址 const uint8_t *
* 限定条件:只读
*/
const uint8_t *ui_w_setview_dec_table(uint16_t *a_n) {
if (a_n != NULL) {
*a_n = (uint16_t)UI_DATA_SET_N;
}
return s_dec;
}
/*
* 函数功能:把某个查看视图绑到定值表实例(行表 + 取数 + 反显一次配齐)
* 入口参数:a_inst - 页面持有的实例(可为 NULL,NULL 直接返回) ui_w_setlist_t *
* a_view - 视图编号 ui_w_setview_t
* 返回值:无
* 限定条件:页面 init_cb 内调用(uiTask);每次开页都重新绑,不信任上一次的残留
* 函数说明:先 memset 再填 —— 组件实例里的编辑态跨页留着就是脏状态;视图号非法
* 给空表(页面退化成 0 行空页,比按错表导航安全)
*/
void ui_w_setview_attach(ui_w_setlist_t *a_inst, ui_w_setview_t a_view) {
if (a_inst == NULL) {
return;
}
(void)memset(a_inst, 0, sizeof(*a_inst));
switch (a_view) {
case UI_W_SETVIEW_PARAM:
a_inst->rows = s_rows_param;
a_inst->count = SV_ROW_N(s_rows_param);
break;
case UI_W_SETVIEW_SET:
a_inst->rows = s_rows_set;
a_inst->count = SV_ROW_N(s_rows_set);
break;
case UI_W_SETVIEW_WORD:
a_inst->rows = s_rows_word;
a_inst->count = SV_ROW_N(s_rows_word);
break;
case UI_W_SETVIEW_PLATE:
a_inst->rows = s_rows_plate;
a_inst->count = SV_ROW_N(s_rows_plate);
break;
default:
a_inst->rows = NULL;
a_inst->count = 0u;
break;
}
s_cur_rows = a_inst->rows;
s_cur_n = a_inst->count;
a_inst->get = sv_get;
a_inst->set = sv_set_unused;
a_inst->commit = NULL; /* NULL = 查看视图:无保存行、禁编辑(§3.2.6) */
a_inst->state = NULL;
a_inst->bad = sv_bad;
ui_w_setlist_reset(a_inst);
}
+110
View File
@@ -0,0 +1,110 @@
/*
* 模块名称:UI 定值查看视图模型(ui_w_setview)
* 模块功能:参数 / 定值 / 控制字 / 软压板**四张视图表**与它们的取数、越界反显——
* 把"40 项定值怎么分组上屏、Q10 怎么换算成带小数位的显示值"收在一处,
* 供四个查看页薄跳板(ui_page_view_*)共用;整定页(下一批)复用同一批表,
* 只是再绑 set/commit/state 三个回调
* 分层:本模块只读 Drivers/BSP/UI/ui_data.h 的参数快照,**不 include 任何 App 头**
* (行表里的 id 是**裸数字** + 注释标注 PS_* 名,因为 prot_set_id_t 属 App 层)
* ——id 与 App/algo/prot_set.h 枚举序的一致性由 test/host/ui_data 的主机单测
* 在两侧头文件同时可见的地方钉住(BSP 侧看不到 App 头,物理上无法自证)
* 值语义:q10[i] = 工程量 × 1024(App/app/ui_data_feed.c 只搬原值不换算,换算属表现层,
* docs/UI设计/UI_数据供给设计说明.md §8),本模块按行表 decimals 换算成
* "工程量 × 10^decimals" 交给 ui_w_setlist(其值通道契约)
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-15 批 3;规格 =
* docs/UI设计/页面扩充详细方案.md §3.2.1~§3.2.3/§3.2.6)
*/
#ifndef UI_W_SETVIEW_H
#define UI_W_SETVIEW_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include "ui_w_setlist.h"
/* 四个视图编号 */
typedef enum {
UI_W_SETVIEW_PARAM = 0, /* 参数视图 8 行(PT/CT 额定 + 4 个返回系数) */
UI_W_SETVIEW_SET, /* 定值视图 18 行(电流/电压/频率/延时) */
UI_W_SETVIEW_WORD, /* 控制字视图 4 行(*_TRIP:告警/跳闸) */
UI_W_SETVIEW_PLATE, /* 软压板视图 10 行(*_EN + 保护总投:投入/退出) */
UI_W_SETVIEW_VIEWS /* 哨兵 = 视图个数(不是有效视图号) */
} ui_w_setview_t;
/*
* 函数功能:绑定一个查看视图到定值表实例(rows/count/get/bad 一次配齐)
* 入口参数:a_inst - 页面持有的实例(static,一页一份) ui_w_setlist_t *
* a_view - 视图编号 ui_w_setview_t
* 返回值:无
* 限定条件:页面 init_cb 内调用(uiTask 上下文)
* 函数说明:查看视图与整定视图的**唯一**差别就是这里绑不绑 set/commit/state:
* commit=NULL → 组件不画保存行、ENTER 无效(§3.2.6 查看列口径);
* set 契约写"必填",故绑一个空实现而不是 NULL——组件在只读态确实不会调它,
* 但把"当前不会调"当成"可以是 NULL"等于把安全性押在下一次重构上
*/
void ui_w_setview_attach(ui_w_setlist_t *a_inst, ui_w_setview_t a_view);
/*
* 函数功能:按发布序号取参数帧快照(页面 refresh 前调用)
* 入口参数:无
* 返回值:无
* 限定条件:uiTask 上下文(页面绘制链内)
* 函数说明:序号没变就不重拷(一帧约 184 B;四个查看页共用这一份缓存——同屏只可能
* 开着一页,缓存互相之间不存在并发读者)
*/
void ui_w_setview_sync(void);
/*
* 函数功能:参数帧是否已就位
* 入口参数:无
* 返回值:1 = 有有效帧可显示;0 = 尚无首帧或读取失败 uint8_t(取 uint8_t 是 BSP 公开
* API 的 err-type 约定,不是错误通道;定义处有完整理由)
* 限定条件:uiTask 上下文;纯查询,无副作用
* 函数说明:页面据此决定值列打 "--" 还是打换算值——**无数据不许打 0**:
* "0.00A" 会被读成"这台装置的定值就是 0",与"还不知道"是两件事
*/
uint8_t ui_w_setview_ready(void);
/*
* 函数功能:逐行取数(接框架 refresh;内含 ui_w_setview_sync() 与无帧占位)
* 入口参数:a_inst - 已 attach 的实例 ui_w_setlist_t *
* a_row - 全局行下标 uint16_t
* a_name - 名输出缓冲(UI_BUF_STRLEN) char *
* a_value - 值输出缓冲 char *
* 返回值:无
* 限定条件:框架绘制链内调用(uiTask)
* 函数说明:页面**不要**直接调 ui_w_setlist_refresh()——那样会绕过"无帧打 --"
* 这一步,参数帧还没到就把 0 当成定值显示出去
*/
void ui_w_setview_refresh(ui_w_setlist_t *a_inst, uint16_t a_row,
char *a_name, char *a_value);
/*
* 函数功能:取某视图的行表与行数(供主机单测做结构自测;页面正常不需要调用)
* 入口参数:a_view - 视图编号 ui_w_setview_t;a_n - 行数输出(不可为 NULL) uint16_t *
* 返回值:行表首地址;视图号非法或 a_n 为空返回 NULL const ui_w_setlist_row_t *
* 限定条件:只读
*/
const ui_w_setlist_row_t *ui_w_setview_table(ui_w_setview_t a_view, uint16_t *a_n);
/*
* 函数功能:取 id → 显示小数位查表(供主机单测核对行表与查表一致)
* 入口参数:a_n - 表长输出(= UI_DATA_SET_N) uint16_t *
* 返回值:表首地址 const uint8_t *
* 限定条件:只读
*/
const uint8_t *ui_w_setview_dec_table(uint16_t *a_n);
#ifdef __cplusplus
}
#endif
#endif /* UI_W_SETVIEW_H */