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:
@@ -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 自检记录 */
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 页面跳板提供,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -3566,11 +3566,36 @@
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\UI\ui_w_soe.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>ui_w_setview.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\UI\ui_w_setview.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>ui_page_soe.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\UI\ui_page_soe.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>ui_page_view_param.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\UI\ui_page_view_param.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>ui_page_view_plate.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\UI\ui_page_view_plate.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>ui_page_view_set.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\UI\ui_page_view_set.c</FilePath>
|
||||
</File>
|
||||
<File>
|
||||
<FileName>ui_page_view_word.c</FileName>
|
||||
<FileType>1</FileType>
|
||||
<FilePath>..\Drivers\BSP\UI\ui_page_view_word.c</FilePath>
|
||||
</File>
|
||||
</Files>
|
||||
</Group>
|
||||
<Group>
|
||||
|
||||
@@ -497,6 +497,15 @@ typedef struct {
|
||||
> 三条纪律在页面侧被验证:① 页面只读快照、零 App include(分层基线不变);② 帧缓存按 `seq`
|
||||
> 门控整帧拷一次,逐行只读标量;③ 品质位判空一律 `--`,"有数但不可信"与"没数"同形。参数帧
|
||||
> `par` 的首批消费者是定值/参数查看页(本批未接,`bad_mask`/`locked` 的屏上表达随那 4 页落)。
|
||||
>
|
||||
> 【2026-10-25 落地进度·三】`par` 的首批消费者已落(同分支批 3):**参数 / 定值 / 控制字 / 软压板**四个查看页
|
||||
> (`ui_w_setview` 四张行表 + `ui_w_setlist` 组件,查看态 `commit=NULL` 无保存行)。Q10 → 显示值的换算落在**表现层**
|
||||
> (本契约 §8 的分工:写侧只搬原值),换算式在 `ui_w_setview.c` 里做整数拆分而非浮点/64 位除(MicroLIB
|
||||
> 软浮点与 `__aeabi_ldiv` 一进镜像几百字节,模拟器永远照不出这笔代价)。`bad_mask` 的屏上表达 =
|
||||
> 该行反显高亮(且只在 `bad_valid=1` 时参与,把没数据画成有毛病是假告警);`locked` 的屏上表达是
|
||||
> **禁编辑**,查看页本来就无编辑通路,它随整定 4 页一起显形。四个返回系数按 2 位小数显示的理由见
|
||||
> `docs/UI设计/页面扩充详细方案.md` §3.2.2 落地注;行表与 `prot_set` 枚举序/上下限的一致性由
|
||||
> `tools/setview_check/check_setview_limits.py` 核验(BSP 侧看不到 App 头,物理上无法自证,必须有人从外面钉)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -212,6 +212,20 @@ V0.1 **无直流采样通道**(`ADC_SEM` 只有 8 路电气 + 温度),点
|
||||
| 7 | `过频返回` | `PS_RET_OF` | — | 3 |
|
||||
| 8 | `低频返回` | `PS_RET_UF` | — | 3 |
|
||||
|
||||
> 【2026-10-25 落地注·四张视图表(道E task-15 批 3)】四个查看页已接(`ui_page_view_param` /
|
||||
> `_set` / `_word` / `_plate`,共用 `Drivers/BSP/UI/ui_w_setview.c` 的四张行表 + `ui_w_setlist`
|
||||
> 组件)。三条落地时的偏离与理由都写进了代码注释与提交正文,不留在这儿等人猜:
|
||||
> 1. **返回系数按 2 位小数显示**(本表写 3 位)。这不是排版取舍,是量值真实性问题:Q10 存不
|
||||
> 下 0.95(0.95×1024 截成 972 → 真存 0.94921875)。按 3 位显示就多打一位根本不存在的数字,
|
||||
> 整定单上写 0.95 的人看到屏上 0.949 会怀疑装置、甚至反复重输(重输还是 0.949)。2 位才是
|
||||
> 这份存储在自身可分辨精度上的正确读数。本表「小数位」列按文档口径保留原样,偏离登记在
|
||||
> `tools/setview_check/check_setview_limits.py` 的文档串里(该脚本连 meta 上下限一起核)。
|
||||
> 2. **单位挂在值串尾**(`10.00A` / `500ms`),不塞进显示名:值列只有数字会让 `60000` 既是 ms
|
||||
> 又是 A;A/V/ms/Hz 全 ASCII,不占中文字库、不触发 font-charset。为此给 `ui_w_setlist_row_t`
|
||||
> 加了 `unit` 字段(NULL = 不挂;只挂在非编辑态——编辑中是逐位输入串,挂单位会算错位光标)。
|
||||
> 3. **上下限不自己拟定**:行表 min/max = `g_prot_set_meta` 的 Q10 上下限按该行小数位换算的结果,
|
||||
> 事后由上述脚本核验(改了 meta 不改行表就会红)。
|
||||
|
||||
#### 3.2.3 定值视图(18 项)
|
||||
|
||||
| 行 | 显示名 | 枚举 | 单位 | 小数位 |
|
||||
|
||||
@@ -102,8 +102,13 @@ set(UI_SOURCES
|
||||
${UI_DIR}/ui_w_setlist.c
|
||||
${UI_DIR}/ui_page_action.c
|
||||
${UI_DIR}/ui_data.c # 数据快照存侧(道E task-14;写侧 App/app/ui_data_feed.c 不进模拟器,见 scripts/check_registration.py)
|
||||
${UI_DIR}/ui_w_soe.c # SOE 记录组件(道E task-14;reader 注入点待绑定,未绑定按空态渲染)
|
||||
${UI_DIR}/ui_w_soe.c # SOE 记录组件(道E task-14;reader 已在批 2 两端绑定)
|
||||
${UI_DIR}/ui_w_setview.c # 定值查看视图模型(道E task-15 批 3:四张视图表 + Q10 换算 + 越界反显)
|
||||
${UI_DIR}/ui_page_soe.c
|
||||
${UI_DIR}/ui_page_view_param.c
|
||||
${UI_DIR}/ui_page_view_plate.c
|
||||
${UI_DIR}/ui_page_view_set.c
|
||||
${UI_DIR}/ui_page_view_word.c
|
||||
)
|
||||
|
||||
# 运行/自检记录引擎(双端共享核心 + 平台存储后端;MCU 端编 sys_log_mcu.c,见 Keil 工程)
|
||||
|
||||
@@ -44,6 +44,12 @@ PAGES = [
|
||||
# SOE 记录(node 51,记录查询下第 1 项):6 下进记录查询 → e → 首项 SOE记录 → e;
|
||||
# 数据 = simulator/main.c 的固定 3 条假记录(含 type 7 未标定闭锁这条非元件支路)
|
||||
("soe", "dddddde e"),
|
||||
# 参数定值 → 查看 的四个只读页(批 3):dd 进"参数定值"→ e 进(选中"整定")→ d 到"查看"
|
||||
# → e 进(选中"参数")→ e 开页;定值/控制字/软压板 依次多按 d 选到该行再 e
|
||||
("vparam", "ddedee"),
|
||||
("vset", "ddedede"),
|
||||
("vword", "ddededde"),
|
||||
("vplate", "ddededdde"),
|
||||
("pwd", "ddeee"), # 密码页初始态:3 框 000、光标首框
|
||||
("pwe", "ddeee uru"), # 密码页调值+移光标:110、光标第 2 框
|
||||
("pwk", "ddeee uruuruuue"), # 密码页输 123:u / r / u,u / r / u,u,u / e → 关页进空白终点框(菜单屏)
|
||||
@@ -106,6 +112,84 @@ TEXTS = {
|
||||
("r5名", "未标定闭锁", (94, 108), False),
|
||||
("页脚", "第 1 页,共 1 页", (140, 160), False),
|
||||
],
|
||||
# ── 批 3:参数定值 → 查看 四页(数据 = prot_set 出厂默认,值串带单位)──
|
||||
# 名与值两列都断:值列断的是换算后的小数位与单位——光看像素,没人会去核 0.95 是不是被
|
||||
# Q10 存成了 0.94921875。控制字页还多钉一层:Ⅰ/Ⅱ 段默认是「跳闸」、Ⅲ 与过负荷段默认是
|
||||
# 「告警」,这种两两一组恰好把行表与枚举序对齐钉住(行表整体错位就不可能出现该组合)
|
||||
"vparam": [
|
||||
("标题", "参数", (0, 24), False),
|
||||
("r0名", "PT一次", (24, 38), True),
|
||||
("r0值", "10000V", (24, 38), True),
|
||||
("r1名", "PT二次", (38, 52), False),
|
||||
("r1值", "100V", (38, 52), False),
|
||||
# r2 值我先前肉眼读成 "1000A",字形匹配器证明值列只有 4 个字符位(命中 x=101/107/113/119):
|
||||
# CT 一次额定是 100 A —— PT 10000/100 V 加 CT 100/5 A 正是 10 kV 馈线终端的变比自洽。
|
||||
# 断言写"匹配器能证明的",不写"我看图觉得的"(这类差异肉眼发现不了,只有匹配器会响)
|
||||
("r2名", "CT一次", (52, 66), False), ("r2值", "100A", (52, 66), False),
|
||||
("r3名", "CT二次", (66, 80), False),
|
||||
("r3值", "5A", (66, 80), False),
|
||||
("r4名", "过量返回", (80, 94), False),
|
||||
("r4值", "0.95", (80, 94), False),
|
||||
("r5名", "欠量返回", (94, 108), False),
|
||||
("r5值", "1.05", (94, 108), False),
|
||||
("r6名", "过频返回", (108, 122), False),
|
||||
("r6值", "0.99", (108, 122), False),
|
||||
("r7名", "低频返回", (122, 136), False),
|
||||
("r7值", "1.01", (122, 136), False),
|
||||
("页脚", "第 1 页,共 1 页", (140, 160), False),
|
||||
],
|
||||
"vset": [
|
||||
("标题", "定值", (0, 24), False),
|
||||
("r0名", "Ⅰ段电流", (24, 38), True),
|
||||
("r0值", "10.00A", (24, 38), True),
|
||||
("r1名", "Ⅰ段延时", (38, 52), False),
|
||||
("r1值", "0ms", (38, 52), False),
|
||||
("r2名", "Ⅱ段电流", (52, 66), False),
|
||||
("r2值", "5.00A", (52, 66), False),
|
||||
("r3名", "Ⅱ段延时", (66, 80), False),
|
||||
("r3值", "500ms", (66, 80), False),
|
||||
("r4名", "Ⅲ段电流", (80, 94), False),
|
||||
("r4值", "2.00A", (80, 94), False),
|
||||
("r5名", "Ⅲ段延时", (94, 108), False),
|
||||
("r5值", "3000ms", (94, 108), False),
|
||||
("r6名", "过负荷电流", (108, 122), False),
|
||||
("r6值", "1.00A", (108, 122), False),
|
||||
("r7名", "过负荷延时", (122, 136), False),
|
||||
("r7值", "10000ms", (122, 136), False),
|
||||
("页脚", "第 1 页,共 3 页", (140, 160), False), # 18 行 = 3 页
|
||||
],
|
||||
"vword": [
|
||||
("标题", "控制字", (0, 24), False),
|
||||
("r0名", "Ⅰ段跳闸", (24, 38), True),
|
||||
("r0值", "跳闸", (24, 38), True),
|
||||
("r1名", "Ⅱ段跳闸", (38, 52), False),
|
||||
("r1值", "跳闸", (38, 52), False),
|
||||
("r2名", "Ⅲ段跳闸", (52, 66), False),
|
||||
("r2值", "告警", (52, 66), False),
|
||||
("r3名", "过负荷跳闸", (66, 80), False),
|
||||
("r3值", "告警", (66, 80), False),
|
||||
("页脚", "第 1 页,共 1 页", (140, 160), False),
|
||||
],
|
||||
"vplate": [
|
||||
("标题", "软压板", (0, 24), False),
|
||||
("r0名", "Ⅰ段投入", (24, 38), True),
|
||||
("r0值", "投入", (24, 38), True),
|
||||
("r1名", "Ⅱ段投入", (38, 52), False),
|
||||
("r1值", "投入", (38, 52), False),
|
||||
("r2名", "Ⅲ段投入", (52, 66), False),
|
||||
("r2值", "投入", (52, 66), False),
|
||||
("r3名", "过负荷投入", (66, 80), False),
|
||||
("r3值", "投入", (66, 80), False),
|
||||
("r4名", "过压投入", (80, 94), False),
|
||||
("r4值", "投入", (80, 94), False),
|
||||
("r5名", "低压投入", (94, 108), False),
|
||||
("r5值", "投入", (94, 108), False),
|
||||
("r6名", "零序投入", (108, 122), False),
|
||||
("r6值", "投入", (108, 122), False),
|
||||
("r7名", "过频投入", (122, 136), False),
|
||||
("r7值", "投入", (122, 136), False),
|
||||
("页脚", "第 1 页,共 2 页", (140, 160), False), # 10 行 = 2 页,次页还有低频投入与保护总投入
|
||||
],
|
||||
# 直流量:V0.1 板上无直流采样通道 -> 温度取码值并显式带"码"前缀,通道行如实"未配置"
|
||||
"dc": [
|
||||
("标题", "直流量", (0, 24), False),
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
模块名称:定值查看行表一致性检查
|
||||
模块功能:把 Drivers/BSP/UI/ui_w_setview.c 的四张视图表当数据核,比对真源
|
||||
App/algo/prot_set.h 的枚举序与 App/algo/prot_set.c 的 g_prot_set_meta:
|
||||
① id 必须恰好覆盖 0..39 各一次(漏 / 重 / 错位都会在这里炸;页面上只有当前
|
||||
可见的 8 行会被像素断言看到,错位要翻到第 2、3 页才可能被人发现)
|
||||
② 数值行 min/max 必须等于 meta 上下限按该行小数位换算后的值——行表里的上下限
|
||||
就是从 meta 换算来的,本脚本是那条换算的可重跑证明(改 meta 不改行表就红)
|
||||
③ 二态行必须带串表、数值行必须不带;单位串只允许 ASCII(不进中文字库)
|
||||
④ 小数位与 id 查表一致(组件 get 只收到 id,两处不一致就会显示错位数字)
|
||||
已知偏离(写死在下面,不做静默豁免):四个返回系数按 2 位小数显示,文档 §3.2.2 写 3 位。
|
||||
理由是 Q10 存不下 0.95:0.95*1024 截成 972,真存 0.94921875,按 3 位显示会打出一位根本
|
||||
不存在的数字——整定单写 0.95 的人看到 0.949 会怀疑装置,甚至反复重输(重输还是 0.949)。
|
||||
用法:在仓库根执行
|
||||
.venv/Scripts/python.exe tools/setview_check/check_setview_limits.py
|
||||
退出码:0 = 一致;1 = 不一致(打印具体行与期望值);2 = 解析锚点丢失(结构改了,脚本要跟着改)
|
||||
创建日期:2026-10-25(道E task-15 批 3)
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Windows 控制台默认按 CP936 写 stdout:本脚本要打印中文,先把输出锁到 UTF-8——否则同一份
|
||||
# 脚本在别人的机器上会崩在半路(崩在半路 = 一条都没检查出来,比 FAIL 更难发现)
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ENUM_H = "App/algo/prot_set.h"
|
||||
META_C = "App/algo/prot_set.c"
|
||||
TABLES = "Drivers/BSP/UI/ui_w_setview.c"
|
||||
RATIO_IDS = (25, 26, 38, 39) # 四个返回系数:本文档字符串里说明过的 2 位小数偏离
|
||||
|
||||
|
||||
def q10(tok):
|
||||
m = re.match(r"PROT_SET_Q10\(([-0-9.]+)\)$", tok.strip())
|
||||
v = float(m.group(1)) if m else float(tok.strip())
|
||||
return int(round(v * 1024)) if m else int(v)
|
||||
|
||||
|
||||
def scaled(v, d):
|
||||
ip = v >> 10
|
||||
fp = v & 0x3FF
|
||||
p10 = 10 ** d
|
||||
fr = (fp * p10 + 512) >> 10
|
||||
if fr >= p10:
|
||||
ip += 1
|
||||
fr -= p10
|
||||
return ip * p10 + fr
|
||||
|
||||
|
||||
def load_meta():
|
||||
h = open(ENUM_H, encoding="utf-8").read()
|
||||
body = h.split("typedef enum {")[1].split("PS_ID_COUNT")[0]
|
||||
ids = [x.strip() for x in re.findall(r"^\s+(PS_[A-Z0-9_]+)[, ]", body, re.M)]
|
||||
c = open(META_C, encoding="utf-8").read()
|
||||
blk = c.split("g_prot_set_meta[PS_ID_COUNT] = {")[1].split("\n};")[0]
|
||||
blk = re.sub(r"/\*.*?\*/", "", blk, flags=re.S)
|
||||
ent = re.findall(r"\{([^{}]*)\}", blk, re.S)
|
||||
if len(ent) != len(ids):
|
||||
print("[FATAL] 枚举 %d 项 / meta %d 项不一致" % (len(ids), len(ent)))
|
||||
return None
|
||||
meta = {}
|
||||
for name, e in zip(ids, ent):
|
||||
p = [x.strip() for x in e.split(",")]
|
||||
if len(p) != 5:
|
||||
print("[FATAL] %s 的 meta 行解析成 %d 段" % (name, len(p)))
|
||||
return None
|
||||
meta[name] = (1 if p[1].startswith("1") else 0, q10(p[2]), q10(p[3]), q10(p[4]))
|
||||
return (ids, meta)
|
||||
|
||||
|
||||
def load_tables():
|
||||
t = open(TABLES, encoding="utf-8").read()
|
||||
rows = []
|
||||
# 行表一行的形状:{ "名", NNu, UI_W_SETLIST_KIND_XXX, Du, min, max, opts, unit }, /* PS_名 */
|
||||
# opts/unit 两种写法都要吃下:NULL 或 "V" 这类带引号串;用 \S+ 而不是 \w+,否则带引号
|
||||
# 的单位匹配不上会让整行**静默跳过**(少检查一行比检查错更难发现)
|
||||
pat = re.compile(r"^\s*\{\s*\"(.*?)\",\s*(\d+)u,\s*UI_W_SETLIST_KIND_(NUM|ENUM),\s*(\d+)u,\s*(-?\d+),\s*(-?\d+),\s*(\S+),\s*(\S+?)\s*\},\s*/\*\s*(PS_[A-Z0-9_]+)\s*\*/", re.M)
|
||||
for m in pat.finditer(t):
|
||||
rows.append((m.group(9), int(m.group(2)), m.group(3), int(m.group(4)),
|
||||
int(m.group(5)), int(m.group(6)), m.group(7), m.group(8)))
|
||||
dm = re.search(r"s_dec\[UI_DATA_SET_N\] = \{(.*?)\};", t, re.S)
|
||||
if dm is None or not rows:
|
||||
print("[FATAL] 行表/查表锚点解析失败(行表写法改了要同步改本脚本)")
|
||||
return None
|
||||
dec = [int(x) for x in re.findall(r"\d+", dm.group(1))]
|
||||
return (rows, dec)
|
||||
|
||||
|
||||
def main():
|
||||
lm = load_meta()
|
||||
if lm is None:
|
||||
return 2
|
||||
ids, meta = lm
|
||||
lt = load_tables()
|
||||
if lt is None:
|
||||
return 2
|
||||
rows, dec = lt
|
||||
bad = []
|
||||
seen = []
|
||||
if len(rows) != len(ids):
|
||||
print("[FAIL] 行表解析到 %d 行,但枚举有 %d 项——正则与行表写法脱节了,宁可报错也不要少检" % (len(rows), len(ids)))
|
||||
return 1
|
||||
for ps, rid, kind, d, mn, mx, opts, unit in rows:
|
||||
if rid < 0 or rid >= len(ids):
|
||||
bad.append("%s: id %d 越界" % (ps, rid)); continue
|
||||
if ids[rid] != ps:
|
||||
bad.append("%s: id=%d 实际指向 %s(枚举序对不上)" % (ps, rid, ids[rid]))
|
||||
seen.append(rid)
|
||||
is_ctl = meta[ps][0]
|
||||
if kind == "ENUM":
|
||||
if opts == "NULL":
|
||||
bad.append("%s: 二态行没有串表" % ps)
|
||||
if dec[rid] != 0:
|
||||
bad.append("%s: 二态项在 dec 表里应为 0,实为 %d" % (ps, dec[rid]))
|
||||
if not is_ctl:
|
||||
bad.append("%s: meta 说它是模拟量项,行表却当二态行" % ps)
|
||||
else:
|
||||
if opts != "NULL":
|
||||
bad.append("%s: 数值行不该带串表" % ps)
|
||||
if dec[rid] != d:
|
||||
bad.append("%s: 行表小数位 %d 与 dec 查表 %d 不一致" % (ps, d, dec[rid]))
|
||||
emn = scaled(meta[ps][2], d)
|
||||
emx = scaled(meta[ps][3], d)
|
||||
if (mn, mx) != (emn, emx):
|
||||
bad.append("%s: 上下限 (%d,%d) != meta 换算 (%d,%d)(d=%d)" % (ps, mn, mx, emn, emx, d))
|
||||
if is_ctl:
|
||||
bad.append("%s: meta 说它是投退/枚举项,行表却当数值行" % ps)
|
||||
if unit != "NULL":
|
||||
u = ""
|
||||
if unit.startswith('"'):
|
||||
u = unit.strip('"')
|
||||
else:
|
||||
bad.append("%s: 单位字段写法不认识:%s" % (ps, unit))
|
||||
if u and any(ord(ch) > 127 for ch in u):
|
||||
bad.append("%s: 单位含非 ASCII 字符 %r(会进中文字库,上屏还可能缺字)" % (ps, u))
|
||||
if sorted(seen) != list(range(len(ids))):
|
||||
dup = [x for x in set(seen) if seen.count(x) > 1]
|
||||
miss = sorted(set(range(len(ids))) - set(seen))
|
||||
bad.append("id 覆盖不完整:重复 %s 缺失 %s" % (dup, miss))
|
||||
ratios = [r for r in seen if r in RATIO_IDS]
|
||||
if sorted(ratios) != sorted(RATIO_IDS):
|
||||
bad.append("四个返回系数(%s)没有全在数值视图里,偏离白名单需要同步更新" % (RATIO_IDS,))
|
||||
for m in bad:
|
||||
print("[FAIL] " + m)
|
||||
print("行表 %d 行 / dec %d 项 / meta %d 项 -> %s" % (len(rows), len(dec), len(ids), "OK" if not bad else "FAIL"))
|
||||
return 1 if bad else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+4
-4
@@ -29,10 +29,10 @@
|
||||
控制字 #P
|
||||
软压板 #P
|
||||
查看
|
||||
参数
|
||||
定值
|
||||
控制字
|
||||
软压板
|
||||
参数 #page=view_param
|
||||
定值 #page=view_set
|
||||
控制字 #page=view_word
|
||||
软压板 #page=view_plate
|
||||
三遥设置
|
||||
遥测死区 #P
|
||||
遥测系数 #P
|
||||
|
||||
Reference in New Issue
Block a user