diff --git a/docs/RemoDispBus协议与接收设计说明.md b/docs/UI 设计/RemoDispBus协议与接收设计说明.md similarity index 99% rename from docs/RemoDispBus协议与接收设计说明.md rename to docs/UI 设计/RemoDispBus协议与接收设计说明.md index 5f5d9594..7275c69c 100644 --- a/docs/RemoDispBus协议与接收设计说明.md +++ b/docs/UI 设计/RemoDispBus协议与接收设计说明.md @@ -57,7 +57,7 @@ RemoDispBus 让远端(PC 上的 DTU-RemoteLCD 客户端 / 浏览器页面) **图形版**(SVG 矢量,字段宽 ∝ 字节数;由 `tools/arch_diagram/gen_frame_svg.py` 生成,浏览器打开可无损缩放): -![RemoDispBus 帧格式](images/RemoDispBus帧格式.svg) +![RemoDispBus 帧格式](../images/RemoDispBus帧格式.svg) ### 1.3 字段语义 diff --git a/docs/UI 设计/UI_界面制作流程.md b/docs/UI 设计/UI_界面制作流程.md index 0111cab4..2176b481 100644 --- a/docs/UI 设计/UI_界面制作流程.md +++ b/docs/UI 设计/UI_界面制作流程.md @@ -1,42 +1,42 @@ -# UI 界面制作流程(图片 → HTML → LVGL) +# UI 界面制作流程(参考图 → LVGL) -> **用途**:规范"参考图 → HTML 样机 → LVGL 代码 → 双端验证"的完整管线,规定每一步的**动作、产物、放行条件(gate)**,保证过程可重复、可交接。 -> **适用**:所有 UI 新界面 / 界面大改;小改动走 §11 快速通道。 -> **配套文档**:`UI_设计架构.md`(系统如何工作)、`菜单页面设计.md`(菜单/导航行为与数据流设计)、`LVGL模拟器部署与使用说明.md`(模拟器)、`UI_中文字体生成与配置.md`(字体生成)、`UI_自动化测试流程.md`(P3/P4 回归门禁的可执行版本:脚本 + 池水位常量 + 黄金参考更新)。 -> **首个实例**:界面 01 主界面(根页),样机 `mockups/01_主界面.html`(本文所有示例数字均取自该试点)。 +> **用途**:规范"参考图 → 设计规格 → LVGL 代码 → 双端验证"的完整管线,规定每一步的**动作、产物、放行条件(gate)**,保证过程可重复、可交接。 +> **适用**:所有 UI 新界面 / 界面大改;小改动走 §10 快速通道。 +> **配套文档**:`UI_设计架构.md`(系统如何工作)、`菜单页面设计.md`(菜单/导航行为与数据流设计)、`LVGL模拟器部署与使用说明.md`(模拟器)、`UI_中文字体生成与配置.md`(字体生成)、`UI_自动化测试流程.md`(P2/P3 回归门禁的可执行版本:脚本 + 池水位常量 + 黄金参考更新)。 +> **首个实例**:界面 01 主界面(根页),规格 `mockups/01_主界面_说明.md`(本文所有示例数字均取自该试点)。 + +> **沿革**:旧管线含"HTML 样机"阶段(参考图 → HTML 样机 → 代码),2026-09-13 起移除——设计基准改为**文本规格(布局参数表)+ 参考图**,视觉基准改为**模拟器出图**,用户视觉确认点移到 P2 模拟器窗口/出图(详见变更记录)。 --- ## 一、流程总览 -**五阶段单向管线**:`参考图 → P0 → P1 → P2 → P3 → P4 → P5`。每阶段产出明确**产物**、满足**放行条件(Gate)**才进入下一阶段;其中 **P1、P3 是"比对 → 修正"迭代环**,未收敛不得前进。 +**四阶段单向管线**:`参考图 → P0 → P1 → P2 → P3 → P4`。每阶段产出明确**产物**、满足**放行条件(Gate)**才进入下一阶段;其中 **P2 是"比对 → 修正"迭代环**,未收敛不得前进。 ```mermaid flowchart TD - R(["参考图"]) --> P0["P0 需求录入
→ 屏幕台账 + 问题清单"] - P0 -- "Gate: 屏幕定位/极性已确认 + 阻塞性问题有答案" --> P1["P1 图 → HTML 样机
→ mockups/NN.html + NN_说明.md"] - P1 --> C1{"渲染样机 ↔ 对照原图
收敛?"} - C1 -- "否: 修 HTML(浏览器确认 ↔ 修 HTML)" --> P1 - C1 -- "是(Gate: 样机定稿 + 原图校验过 + 问题有答案 + 字集核对)" --> P2["P2 HTML → LVGL 代码
→ Drivers/BSP/UI 代码 + 字体(重生成)"] - P2 -- "Gate: 参数逐项对账 + 字体重生成 + 内存预算 + 双端注册" --> P3["P3 模拟器验证
→ shot.png(与样机渲染比对)"] - P3 --> C3{"输出图 ↔ 样机渲染
同构?"} - C3 -- "否: 依 HTML 修代码 → 再出图" --> P3 - C3 -- "是(Gate: 导航符合按键语义)" --> P4["P4 MCU 验证
→ 日志 + 上机目检结论"] - P4 -- "Gate: @build 0 错 0 警 + @test PASS + 上机目检通过" --> P5(["P5 归档
→ 变更记录/台账 + 随代码提交(代码+字体+字集+样机+文档)"]) + R(["参考图"]) --> P0["P0 需求录入 + 设计规格
→ 屏幕台账 + NN_说明.md(布局参数表/字符集/问题)"] + P0 -- "Gate: 界面作用/菜单位置/极性已确认 + 阻塞性问题有答案 + 布局验证等式成立 + 字符集核对" --> P1["P1 规格 → LVGL 代码
→ Drivers/BSP/UI 代码 + 字体(重生成)"] + P1 -- "Gate: 参数逐项对账 + 字体重生成 + 内存预算 + 双端注册" --> P2["P2 模拟器验证
→ shot.png(对照参考图 + 规格参数表)"] + P2 --> C2{"输出图 ↔ 参考图
同构?"} + C2 -- "否: 按根因修正(修代码 / 回 P0 修规格)→ 再出图" --> P2 + C2 -- "是(Gate: 导航符合按键语义 + 自动化回归 PASS)" --> P3["P3 MCU 验证
→ 日志 + 上机目检结论"] + P3 -- "Gate: @build 0 错 0 警 + @test PASS + 上机目检通过" --> P4(["P4 归档
→ 变更记录/台账 + 随代码提交(代码+字体+字集+规格+文档)"]) ``` -> 小改动(文案/间距/纯交互/字体重生成)走 §11 快速通道,无需全流程。 +> 小改动(文案/间距/纯交互/字体重生成)走 §10 快速通道,无需全流程。 ### 1.1 各阶段职责分工 | 阶段 | 用户做 | AI 做 | 产物 | |---|---|---|---| -| P0 需求录入 | 提供图片(每批 1~3 屏);说明各屏作用与菜单位置;回答问题 | 图片分析、界面登记、问题清单 | 台账 + Q 清单 | -| P1 图片→HTML | 浏览器打开确认;指出问题**或直接改 HTML** | 度量核对、字符集核对、布局计算、样机制作与修改 | 定稿样机 | -| P2 HTML→代码 | (可选)抽查代码 | 逐参数翻译、字体重生成、内存预算 | 代码 + 字体 | -| P3 模拟器 | (可旁观) | 构建、离线出图、与样机渲染图比对、依 HTML 改码 | 比对结论 | -| P4 MCU | **真机目检**(DTU-RemoteLCD) | @build / @test 执行、日志解析 | 日志 + 结论 | -| P5 归档 | — | 更新变更记录与台账 | 文档 | +| P0 需求录入 + 设计规格 | 提供图片(每批 1~3 屏);说明各屏作用与菜单位置;回答问题;确认布局参数表 | 图片分析、界面登记、字体度量核对、字符集核对、布局计算(验证等式)、规格制作 | 台账 + `_说明.md`(参数表/字符集/问题/映射/翻译要点) | +| P1 规格→代码 | (可选)抽查代码 | 逐参数翻译、字体重生成、内存预算、双端注册 | 代码 + 字体 | +| P2 模拟器 | **视觉确认**(窗口模式/出图),指出差异 | 构建、离线出图、出图与原图逐项比对、依规格改码 | 比对结论 | +| P3 MCU | **真机目检**(DTU-RemoteLCD) | @build / @test 执行、日志解析 | 日志 + 结论 | +| P4 归档 | — | 更新变更记录与台账 | 文档 | + +> 用户视觉确认点由旧"浏览器打开 HTML 样机"改为 **P2 模拟器窗口/出图**:P0 确认**参数表**(数值层面),P2 确认**渲染结果**(视觉层面);渲染结果与意图不符时按 §5.1 根因修正(代码错 → 修代码;参数表错 → 回 P0 修规格再修代码)。 --- @@ -53,7 +53,7 @@ flowchart TD | C5 | 文本 | 一律静态字符串(`lv_label_set_text_static`),禁止动态字符串/strdup | 架构文档 | | C6 | 编码 | `Drivers/BSP/UI/*`、`ui_task.c`、`Lib/lv_conf.h` 为 **UTF-8 带 BOM**;改动只用 Edit 工具,**禁止** PowerShell/cmd 整文件重写(U+FFFD 不可逆) | AGENTS.md 铁律 | | C7 | 代码共用 | `ui_menu.c` / 字体为 MCU 与模拟器共用源,**禁止**在 `simulator/` 复制副本 | 架构文档 | -| C8 | 视觉禁区(1bpp) | 圆角 / 阴影 / 透明度 / 渐变 / 多色 / 抗锯齿 全部禁用;样机与代码都不得引入 | 物理限制 | +| C8 | 视觉禁区(1bpp) | 圆角 / 阴影 / 透明度 / 渐变 / 多色 / 抗锯齿 全部禁用;设计与代码都不得引入 | 物理限制 | | C9 | 双端注册 | 新增 `Drivers/BSP/UI/*.c` 必须**两处**手动注册:Keil `.uvprojx` + `simulator/CMakeLists.txt` 的 `UI_SOURCES` 列表(均为显式列表,无通配扫描)。只注册一端 = 另一端静默缺文件 | 实测(2026-09-03) | ### 2.1 字体度量速查(2026-09-03 实测,字体变更时重测) @@ -66,11 +66,11 @@ flowchart TD | 行高 | `line_height = 13` | 13px | 布局按 13 算,不是 12 | | 基线 | `base_line = 2`(自底部) | — | 垂直定位参考 | -> 样机字体必须用 **SimSun**(设备字体的源字体),浏览器渲染宽度与设备度量一致,预览才"诚实"。 +> 设备字体的源字体为 **SimSun**(上述度量的来源);字符集由仓库根 `ui_cn_chars.txt` 管理。 --- -## 三、P0:需求录入与界面台账 +## 三、P0:需求录入与设计规格 ### 3.1 用户输入 1. 参考图(建议每批 1~3 屏,不必一次给全) @@ -89,38 +89,28 @@ flowchart TD ### 3.3 界面台账 - 编号 `NN`(01 = 试点界面),名称、作用、菜单位置、状态(待做/进行中/已完成) -- 台账随 P5 归档更新 +- 台账随 P4 归档更新 -### 3.4 Gate(进入 P1) -- [ ] 界面作用与菜单位置已确认 -- [ ] 阻塞性问题已回答;非阻塞项已标注默认值(如 "TBD") - -> **试点实例**:01 主界面。用户给一张根页截图:顶部梯形装饰 + 状态行"当前模式:无模式" + 7 行列表(装置信息/实时数据/参数定值/三遥设置/装置维护/通讯参数/记录查询),黑底白字(与现系统同极性)。AI 分析出:7 项与现有 `g_menu_tab` 的映射,其中 3 项疑似重命名(参数定值/三遥设置/通讯参数)→ Q1;"三遥设置"对应现有"遥测量"还是新增"三遥"节点 → Q2;旧菜单"厂家设置/版本信息"未列入 7 项,入口在哪 → Q3;"当前模式"指什么、数据从哪来 → Q4。 - ---- - -## 四、P1:图片 → HTML 样机 - -### 4.1 字体度量核对(字体每次变更时重做) +### 3.4 字体度量核对(字体每次变更时重做) 1. 读 `Drivers/BSP/UI/ui_font_cn_12.c` 头部 Opts 注释 → 字体源(宋体)、尺寸(12)、bpp(1)、**当前字符集** 2. 读 `glyph_dsc[]` 表 → `adv_w ÷ 16` = px(核对 CJK=12 / ASCII=6) 3. 读 `font_dsc` → `line_height=13`、`base_line=2` -4. 与 §2.1 速查不一致 → **全部样机参数重算** +4. 与 §2.1 速查不一致 → **全部规格参数重算** -### 4.2 字符集核对 +### 3.5 字符集核对 1. 收集本屏全部文本中的**去重汉字** 2. 对照现有字符集(字体 `.c` 头部 `--symbols` 行为准;`ui_cn_chars.txt` 为生成物,重生成后一致) 3. 输出:命中列表(现有)/ 新增列表(标红)→ 记入配套 `_说明.md` -4. 新增列表决定 P2 字体重生成的范围 +4. 新增列表决定 P1 字体重生成的范围 > **试点实例**:01 主界面 7 个菜单项共 22 个唯一汉字 → 命中 21(装置信息实时数据参数定值遥设维护通讯记录查询),**新增 1**(三);状态行"当前模式:无模式"新增 5(当前模式无);箭头 **▶**(U+25B6)为新增符号。合计**确定新增 7 个字形**(若状态行用全角冒号":"则再多 1,共 8)。 -### 4.3 布局计算 +### 3.6 布局计算 - 所有值为**设备像素整数** - 列宽:`cell_w = floor((W − 2×边距 − 间隙×(n−1)) / n)`,**1px 余量给最后一列** - 行高:`cell_h = floor((H − 2×边距 − 间隙×(m−1)) / m)` - 必须给出**验证等式**(每项相加 = 屏幕尺寸) -- 文字宽度检查:`字数 × 12px`(步进宽)vs 格内宽(格宽 − 2px 边框)。步进宽超出但**墨迹宽**(约 −1px/端)放得下即可;放不下 → 减字数 / 减列数 / 回 P0 问用户 +- 文字宽度检查:`字数 × 12px`(步进宽)vs 格内宽(格宽 − 2px 边框)。步进宽超出但**墨迹宽**(约 −1px/端)放得下即可;放不下 → 减字数 / 减列数 / 回 3.1 问用户 > **试点实例(7 行列表)**: > `160 = 顶边距2 + 顶装饰5 + 隙4 + 状态行13 + 隙5 + 列表框128 + 底边距3` @@ -129,72 +119,52 @@ flowchart TD > > 推论:行通栏 144px,文字**左对齐**(左留白 4px)+ 字体自带侧边距 = 视觉留白;行尾 ▶ 右对齐(右留白 4px)。选中行整行反色(白底黑字)。 -### 4.4 样机制作 -**文件**:`docs/UI 设计/mockups/NN_界面名.html`(**仅页面**)+ 配套 `NN_界面名_说明.md`(规格) +### 3.7 设计规格 `NN_界面名_说明.md` +**文件**:`docs/UI 设计/mockups/NN_界面名_说明.md`(`mockups/` 目录仅存"原始参考图 + 设计规格";骨架见附录 A) -**HTML 样机(只渲染页面本身,无其他可见内容)**: - -| 要求 | 规定 | -|---|---| -| 自包含 | 内联 CSS,无外部依赖,**无 JS**,双击即开(Chrome/Edge 最佳,需 SimSun) | -| **仅页面** | HTML 只渲染 160×160 页面;**不出现**标题/说明区/图例等任何非页面可见内容。规格、核对、问题等一律写入配套 `_说明.md` | -| 视图 | ① 1:1 实际大小(160×160,所见即真机)② 4x 放大(细节检查);选中/告警等状态若属设计一部分则直接画在页面内,不单列 | -| 4x 实现 | `--u` CSS 变量(1:1 取 `1px`,4x 取 `4px`),全部尺寸 `calc(N*var(--u))`,与 1:1 **同源**标记 | -| 字体 | `font-family: SimSun, NSimSun, serif`(与设备字体同源同宽) | -| 颜色 | 仅 `#000` / `#fff` | -| 定位 | 绝对定位 + 精确 px(对 LVGL `lv_obj_set_pos` 翻译最忠实);确属流式布局才用 flex | -| 文字对齐 | 默认左对齐(见 §4.3 推论);垂直居中 | -| 1bpp 禁区 CSS | `border-radius` / `box-shadow` / `opacity` / 渐变 / `transform` / z-index 层叠 —— 出现即违规 | - -**配套 `NN_界面名_说明.md`(规格,顺序固定)**: -1. **布局参数表** —— 每个 px 值都列出(P2 翻译的依据,必须与样机 CSS 完全一致)+ 验证等式 +**规格(顺序固定)**: +1. **布局参数表** —— 每个 px 值都列出(P1 翻译的依据,必须与验证等式自洽)+ 验证等式 2. **行/元素内容与状态** 3. **字符集核对结果** —— 命中 / 新增 -4. **待确认问题** —— 沿用 P0 的 Q 编号 +4. **待确认问题** —— 沿用 3.2 的 Q 编号 5. **映射建议**(菜单类界面:条目 → 子树) 6. **LVGL 翻译要点** —— 本屏具体的翻译提示(控件选择、状态、内存注意点) -**文件头注释**(HTML 顶部,简短):样机规则 + `状态:制作中 / 已定稿 (YYYY-MM-DD)` + 指向配套 `_说明.md` +> **规格是唯一设计基准**:P1 照参数表翻译代码;P2 出图与原图不符时,先查代码是否忠实实现参数表——代码错 → 修代码;参数表本身偏离原图/用户意图 → 回本阶段修规格,再修代码。 +> 规格定稿(文件头标 `已定稿 (日期)`)后,改动须用户明确提出。 -### 4.5 原图校验循环(渲染 → 目视比对 → 修正) -> 目的:消除"AI 读图不准"的风险。样机完成后渲染成图,与原图并排目视比对,有差异就改,循环到基本一致。**不做像素级对齐**(字体字形本就不同,像素 diff 无意义)。 - -1. **渲染样机成图**:`python tools/ui_mock/render_mockup.py `(无头 Chrome/Edge 截 1:1 视图,得 160×160 PNG) -2. **AI 读两图比对**:直接读 `原图.png` 与渲染出的 PNG,目视逐项核对——装饰形状/位置、状态行位置与文案、列表框位置/大小、行高、选中行、箭头位置 -3. **有差异 → 改 HTML → 重新渲染 → 再比对**,循环 -4. **基本一致**:结构与原图无明显出入(允许字体字形微差)→ 交用户浏览器最终确认 -5. 用户指出问题,或**直接修改 HTML** 后告知(HTML 即设计文件,用户改 = 设计变更) -6. **定稿**:几何/文案/状态/参数与用户意图一致 → 文件头注释标 `已定稿 (日期)`,此后改动须用户明确提出 - -### 4.6 Gate(进入 P2) -- [ ] 样机已定稿 -- [ ] **原图校验通过**:渲染图与原图目视比对基本一致(§4.5 循环收敛) -- [ ] 待确认问题全部有答案或显式默认值 +### 3.8 Gate(进入 P1) +- [ ] 界面作用与菜单位置已确认 +- [ ] 阻塞性问题已回答;非阻塞项已标注默认值(如 "TBD") +- [ ] 布局计算完成,验证等式成立 - [ ] 字符集核对完成(新增字清单确定) +- [ ] 用户已确认规格(参数表/内容/状态) + +> **试点实例**:01 主界面。用户给一张根页截图:顶部梯形装饰 + 状态行"当前模式:无模式" + 7 行列表(装置信息/实时数据/参数定值/三遥设置/装置维护/通讯参数/记录查询),黑底白字(与现系统同极性)。AI 分析出:7 项与现有 `g_menu_tab` 的映射,其中 3 项疑似重命名(参数定值/三遥设置/通讯参数)→ Q1;"三遥设置"对应现有"遥测量"还是新增"三遥"节点 → Q2;旧菜单"厂家设置/版本信息"未列入 7 项,入口在哪 → Q3;"当前模式"指什么、数据从哪来 → Q4。 --- -## 五、P2:HTML → LVGL 代码 +## 四、P1:规格 → LVGL 代码 -### 5.1 翻译对照表(CSS → LVGL v9) +### 4.1 翻译对照表(规格参数 → LVGL v9) -| 样机(HTML/CSS) | LVGL(设备端) | 备注 | +| 规格 / 设计描述 | LVGL(设备端) | 备注 | |---|---|---| | 160×160 白底画布 | display(I1)+ 屏幕背景色 | 极性随全局决策(Q2 类问题) | -| `left/top` 绝对定位 | `lv_obj_set_pos(o, x, y)` | 整数 px | -| `width/height` | `lv_obj_set_size(o, w, h)` | | -| flex row / row-wrap | `lv_obj_set_flex_flow(o, LV_FLEX_FLOW_ROW[_WRAP])` | | -| `gap` | `lv_obj_set_style_pad_gap(o, g, 0)` | | +| 元素位置 `x, y` | `lv_obj_set_pos(o, x, y)` | 整数 px,绝对坐标 | +| 元素尺寸 `w×h` | `lv_obj_set_size(o, w, h)` | | +| 横向/纵向排布 | `lv_obj_set_flex_flow(o, LV_FLEX_FLOW_ROW[_WRAP])` | | +| 间距 `gap` | `lv_obj_set_style_pad_gap(o, g, 0)` | | | 1px 边框 | `lv_style_set_border_width(st,1)` + `border_color` | | | 仅底边框 | `lv_style_set_border_side(st, LV_BORDER_SIDE_BOTTOM, 1)` | v9 分边设置 | -| 背景色 | `lv_style_set_bg_color` + `lv_style_set_bg_opa` | 1bpp 仅纯黑/纯白 | +| 黑/白背景 | `lv_style_set_bg_color` + `lv_style_set_bg_opa` | 1bpp 仅纯黑/纯白 | | 文本 | `lv_label_create` + `lv_label_set_text_static` | 静态字符串(C5) | -| 文字对齐 | 标签定位 / `lv_label_set_long_mode` | 默认左对齐 | +| 文字对齐 | 标签定位 / `lv_label_set_long_mode` | 默认左对齐(见 §3.6 推论) | | 选中反色 | `LV_STATE_CHECKED` 样式(bg/文字色互换) | 条目对象**自身**带 CHECKED(防白底白字,架构 §5.5) | -| `display:none` | `LV_OBJ_FLAG_HIDDEN` | 页面/状态切换 | -| 圆角/阴影/透明/渐变 | **禁止翻译**(C8) | 样机里就不该有 | +| 隐藏状态 | `LV_OBJ_FLAG_HIDDEN` | 页面/状态切换 | +| 圆角/阴影/透明/渐变 | **禁止**(C8) | 规格里就不该有 | -### 5.2 文件策略与双端注册 +### 4.2 文件策略与双端注册 - **首选原地扩展** `ui_menu.c`(共用源,改一处两端生效) - 确需新文件(模块独立,如曲线页渲染器)时,**两处注册**(C9): 1. Keil:`MDK-ARM/*.uvprojx` 手动添加 @@ -202,15 +172,15 @@ flowchart TD - 新**页面类型**(内容页/编辑页)扩展现有"容器持久、条目动态"架构,**禁止**另起第二套架构 - 字体文件 `ui_font_cn_12.c` 两端均已注册 → 重生成是纯内容替换,无需注册 -### 5.3 翻译纪律 -1. **逐参数翻译**:代码中每个 px 值必须能在样机"布局参数表"中找到出处;禁止"看着办" +### 4.3 翻译纪律 +1. **逐参数翻译**:代码中每个 px 值必须能在规格"布局参数表"中找到出处;禁止"看着办" 2. 样式沿用现有 `s_style_flat` 模式(静态样式、一次性初始化、后加优先覆盖主题) 3. 焦点对象恒为**页面容器**;条目不入输入组(架构 §5.4) 4. 容器**永不删除**(indev 释放事件 use-after-free,架构 §5.2);只建/毁框体(行自绘、无对象) 5. 编码纪律(C6):Edit 工具精确替换;文件保持 UTF-8 BOM 不变 6. 注释按代码规范 V1.0:文件头模块块 + 函数头块(功能/参数/返回/限定/说明) -### 5.4 字体重生成(固定顺序,不得跳过或调换) +### 4.4 字体重生成(固定顺序,不得跳过或调换) ``` 1. 代码落库(新界面字符串已写入 ui_menu.c 等 UI 源码) 2. python tools/font_gen/extract_cn.py # 扫描 UI 源码字符串 → 仓库根 ui_cn_chars.txt(自动含新字;纯标准库,任意 python3) @@ -220,180 +190,179 @@ flowchart TD - 一次性环境准备:`npm i -g lv_font_conv`;fonttools/pillow 统一装进仓库 `.venv`(`.venv\Scripts\python.exe -m pip install -r requirements.txt`,见 AGENTS.md「Python 环境」)。PATH 默认 python 是 msys64 的、无第三方库,勿用它跑 make_font.py - `ui_cn_chars.txt`(仓库根,受 git 跟踪)与 `ui_font_cn_12.c` **随代码一起提交** - 双端 `LV_FONT_DEFAULT` 均指向 `&ui_font_cn_12` → 重生成后两端自动生效 -- P1 已知的新增字若含生僻字(宋体可能无此字形)→ 在写码前先手动验证字形,避免"代码写完才发现是空白" +- P0 已知的新增字若含生僻字(宋体可能无此字形)→ 在写码前先手动验证字形,避免"代码写完才发现是空白" - **已处理实例:▶(U+25B6)箭头**——宋体无此字形,`lv_font_conv` 静默跳过 → 真机方框。`make_font.py` 已内置 `merge_arrow_glyph()` 自动从 Segoe UI Symbol 补回(幂等),重生成字体后箭头不丢。换用自带 ▶ 的字体时可忽略 -### 5.5 内存预算 +### 4.5 内存预算 - 估算:`对象数 × 单价`(`lv_obj` ≈ 150B、`lv_label` ≈ 200B 量级,以实测为准) - **实测点**:建页前后打 `lv_mem_monitor` 池水位(沿用现有 `menu build start/done` 日志模式) - 红线:**最坏路径**(根页 + 最深下钻 + 当前内容页)< 40KB 且留 ≥25% 余量 -- 超预算 → 回到 P1 减元素 / 改按需建毁策略;不得靠加大 `LV_MEM_SIZE` 硬扛(RAM 总预算紧张) +- 超预算 → 回到 P0 减元素 / 改按需建毁策略;不得靠加大 `LV_MEM_SIZE` 硬扛(RAM 总预算紧张) -### 5.6 Gate(进入 P3) -- [ ] 代码与样机参数逐项一致(自查对照表) +### 4.6 Gate(进入 P2) +- [ ] 代码与规格参数逐项一致(自查对照表) - [ ] 字体已重生成且 diff 检查通过 - [ ] 内存预算通过(估算 + 代码中埋好水位日志) - [ ] 新文件(如有)双端注册完成 --- -## 六、P3:模拟器验证 +## 五、P2:模拟器验证 -### 6.1 动作 +### 5.1 动作 1. `cd simulator; cmake --build build --config Debug`(改过 `CMakeLists.txt` 才需重新 `cmake -B`) 2. **离线出图**(模拟器页面渲染完成后): - - `.\build\Debug\lvgl_sim.exe --screenshot shot.bin` → 离屏渲染首帧,输出 I1(1bpp) 位图 - - `python i1_to_png.py shot.bin -o shot.png --scale 4` → 160×160 PNG(放大便于比对) -3. **读两图比对**:模拟器 `shot.png` vs 样机渲染图 `mockups/render_NN.png`,按 §6.2 检查单逐项核对 - (样机渲染图由 P1 的 `tools/ui_mock/render_mockup.py` 产出,即本屏"输入图片") -4. **不合适 → 依 HTML 样机改 LVGL 代码**(HTML 为设计基准)→ 重构建 → 重新出图 → 再比对,循环至一致 + - `.\build\Debug\lvgl_sim.exe --screenshot shot.bin` → 离屏渲染首帧,输出 I1(1bpp) 位图 + - `python i1_to_png.py shot.bin -o shot.png --scale 4` → 160×160 PNG(放大便于比对) +3. **读两图比对**:模拟器 `shot.png` vs **参考图**(`mockups/NN_..._原始图片.png`),按 §5.2 检查单逐项核对(参考图比例可能不同:几何以规格参数表的设备 px 为准,不照参考图像素) +4. **不符 → 根因修正**(本阶段核心迭代环): + - 代码与规格参数表不一致(位置/尺寸/颜色错)→ 修代码 + - 代码忠实于参数表、但参数表本身偏离参考图/用户意图 → 回 P0 修规格,再修代码 + - 重构建 → 重新出图 → 再比对,循环至收敛 5. 交互行为(导航/滚动/选中)在窗口模式 `.\build\Debug\lvgl_sim.exe` 内按键验证(离线出图只覆盖首帧静态画面) 6. **自动化回归**(任何 UI 改动必跑,脚本已入库无需重写):仓库根目录执行 - `python simulator/tests/check_menu_rows.py` + `python simulator/tests/check_content_pages.py`, - 全部门禁与常量说明见 `UI_自动化测试流程.md` + `python simulator/tests/check_menu_rows.py` + `python simulator/tests/check_content_pages.py`, + 全部门禁与常量说明见 `UI_自动化测试流程.md` -### 6.2 比对检查单 +> **循环目的**:消除"AI 读图不准"的风险——参数表是 AI 对参考图的理解,出图与原图比对才能暴露读图错误(此时是参数表问题而非代码问题,回 P0 修)。 + +### 5.2 比对检查单 | 项 | 通过标准 | |---|---| -| 几何 | 位置/尺寸/边框与 1:1 视图逐格一致(容差 1px) | -| 文本 | 内容完整、**无空白块**(空白 = 缺字 → 回 §5.4)、宽度/位置正确 | +| 几何 | 位置/尺寸/边框与规格布局参数表逐格一致(容差 1px);整体结构/比例与原图一致 | +| 文本 | 内容完整、**无空白块**(空白 = 缺字 → 回 §4.4)、宽度/位置正确 | | 状态 | 选中反色正确(极性正确:黑底白字↔白底黑字不反) | | 导航 | ↑↓ 移动(环绕)、→/Enter 进入、←/Esc 返回;滚动条行为正常 | | 日志 | `menu build done: used=…` 在预算内;无 ERROR/FAIL | -### 6.3 已知不阻塞差异(不修) -- 浏览器灰边抗锯齿 vs 1bpp 硬边 -- 浏览器 TTF 渲染 vs 位图字体的字形微差(同源宋体,形状近似即可) +### 5.3 已知不阻塞差异(不修) +- 参考图为高分辨率截图/手工图,字形与线宽与设备 1bpp 渲染不完全一致(形状近似即可,几何以参数表为准) - 系统 DPI 缩放导致的窗口显示大小差异 -### 6.4 Gate(进入 P4) -- [ ] 模拟器出图 `shot.png` 与样机渲染图 `render_NN.png` 逐项同构(§6.2,忽略 6.3) +### 5.4 Gate(进入 P3) +- [ ] 模拟器出图 `shot.png` 与规格参数表逐项一致、与参考图结构一致(§5.2,忽略 5.3) - [ ] 导航行为符合键位语义表(架构 §3.4) - [ ] 自动化回归 PASS:`check_menu_rows.py` + `check_content_pages.py`(`UI_自动化测试流程.md`) --- -## 七、P4:MCU 验证 +## 六、P3:MCU 验证 1. `@build` → **0 错误 0 警告**(AGENTS.md 强制;有警告看 `MDK-ARM/build_log.txt`) 2. `@test`(一键 `test/run_boot_test.ps1`): - - 采集窗口**先于烧录武装**(脚本 `-FlashCmd` 分支已保证顺序) - - Required:`UI port init: OK;UI menu init: OK` + 本界面定制标记(按屏增补,如页头标题字样) - - Forbidden:`FAIL;ERROR;HardFault` + - 采集窗口**先于烧录武装**(脚本 `-FlashCmd` 分支已保证顺序) + - Required:`UI port init: OK;UI menu init: OK` + 本界面定制标记(按屏增补,如页头标题字样) + - Forbidden:`FAIL;ERROR;HardFault` 3. **真机目检**(用户执行,DTU-RemoteLCD @ TCP 7003):极性 / 边框 / 文字 / 选中态 / 滚动 4. 串口日志:池水位、任务栈高水位(`uxTaskGetStackHighWaterMark`,uiTask 预算 16384 words) -### Gate(进入 P5) +### Gate(进入 P4) - [ ] @build 0错0警 - [ ] @test PASS - [ ] 真机目检通过(用户确认) --- -## 八、P5:归档 +## 七、P4:归档 1. 更新 `docs/UI 设计/UI_设计架构.md`: - - 变更记录(日期 + 一行) - - 受影响章节:文件地图 / 关键参数 / 菜单架构 / 键位语义 -2. HTML 样机**保留**于 `mockups/`(定稿版 = 设计档案,后续改界面须先改样机再改代码) + - 变更记录(日期 + 一行) + - 受影响章节:文件地图 / 关键参数 / 菜单架构 / 键位语义 +2. 设计规格 `NN_界面名_说明.md` **保留**于 `mockups/`(定稿版 = 设计档案,后续改界面须先改规格再改代码) 3. 台账:该界面状态 → 已完成(日期) -4. 提交检查:代码 + 字体(`ui_font_cn_12.c`)+ `ui_cn_chars.txt` + 样机 HTML + 文档 一起进 git +4. 提交检查:代码 + 字体(`ui_font_cn_12.c`)+ `ui_cn_chars.txt` + 规格 + 文档 一起进 git --- -## 九、批量生产策略 +## 八、批量生产策略 1. **试点先行**:01 主界面走完全流程,校准各阶段耗时、翻译损耗点、内存基线(试点已完成 P1,后续阶段数据回填本文档) 2. **按界面族批量**: - - 列表页(复用现有级联框自绘架构)→ 数据显示页(数值行框架)→ 参数编辑页(编辑/确认交互)→ 曲线/状态页 - - 每族先做**族内第一屏**产出可复用的页面框架,再批量铺同族界面 + - 列表页(复用现有级联框自绘架构)→ 数据显示页(数值行框架)→ 参数编辑页(编辑/确认交互)→ 曲线/状态页 + - 每族先做**族内第一屏**产出可复用的页面框架,再批量铺同族界面 3. **每批收尾**:字体统一重生成(各屏字符集**并集**,一次生成)→ 全量回归(已完成界面重跑 @test) 4. 字符集只增不减;字体文件每次**全量重生成**,禁止手工拼合 -### 9.1 样机投入分流(按屏型定 HTML 投入度) +### 8.1 规格投入分流(按屏型) -> **两条正交轴**:HTML 样机只解决“**长什么样**”(静态外观);“**怎么动 / 数据从哪来**”(行为 / 导航 / 数据驱动)靠设计文档(`菜单页面设计.md`)+ LVGL 模拟器(动态 oracle)。两轴互补——HTML 是“设计先行”的确定性工具,**不是代码生成器**,别指望它加速行为代码。 +> **两条正交轴**:设计规格(布局参数表)只解决"**长什么样**"(静态外观);"**怎么动 / 数据从哪来**"(行为 / 导航 / 数据驱动)靠设计文档(`菜单页面设计.md`)+ LVGL 模拟器(动态 oracle)。两轴互补——规格是"设计先行"的确定性工具,**不是代码生成器**,别指望它加速行为代码。 -按屏型决定 P1 样机投入(收益从高到低): +按屏型决定 P0 规格投入(收益从高到低): -| 屏型 | 例 | P1 样机投入 | 行为 / 数据靠什么定 | +| 屏型 | 例 | P0 规格投入 | 行为 / 数据靠什么定 | |---|---|---|---| -| 静态、元素多的内容页 | 数据显示 / 参数定值 / 装置信息 | **完整** HTML + `_说明.md`(收益最大:几何钉死→抄写 + 缺字/1bpp 排雷前移 + P3 客观视觉基准) | 数据绑定 + 少量交互 → 设计文档 | -| 导航 / 交互重的屏 | 菜单(横向级联框)/ 参数编辑 | HTML 只做**静态首帧**样子;交互重头放**设计文档** | 设计文档定状态机/级联框/键位语义 + 模拟器验证行为 | +| 静态、元素多的内容页 | 数据显示 / 参数定值 / 装置信息 | **完整**参数表逐项(收益最大:几何钉死→抄写 + 缺字/1bpp 排雷前移 + P2 客观视觉基准) | 数据绑定 + 少量交互 → 设计文档 | +| 导航 / 交互重的屏 | 菜单(横向级联框)/ 参数编辑 | 参数表只做**静态首帧**样子;交互重头放**设计文档** | 设计文档定状态机/级联框/键位语义 + 模拟器验证行为 | -- **HTML 的真实价值**(确定但偏窄,集中在 P1/P3):① 几何从“推导”降为“抄写” ② 缺字 / 1bpp 禁区在 P1 排雷(比 P4 真机发现便宜得多) ③ P3 提供客观视觉基准(`render_NN.png`) ④ 用户浏览器廉价确认。 -- **HTML 帮不上**(本工程 C 的复杂度大头,与 HTML 无关):LVGL 管线(离屏渲染 / 输入组 / 焦点 / 容器持久)、导航状态机、数据驱动(`g_menu_tab` 一表驱动)、运行时内存水位。这些靠 LVGL 功底 + `UI_设计架构.md` + 模拟器。 +- **规格的真实价值**(确定但偏窄,集中在 P0/P2):① 几何从"推导"降为"抄写" ② 缺字 / 1bpp 禁区在 P0 排雷(比 P3 真机发现便宜得多) ③ P2 提供客观几何基准 ④ 用户可在参数表层面廉价确认设计。 +- **规格帮不上**(本工程 C 的复杂度大头,与规格无关):LVGL 管线(离屏渲染 / 输入组 / 焦点 / 容器持久)、导航状态机、数据驱动(`g_menu_tab` 一表驱动)、运行时内存水位。这些靠 LVGL 功底 + `UI_设计架构.md` + 模拟器。 --- -## 十、常见坑速查 +## 九、常见坑速查 | 坑 | 现象 | 对策 | |---|---|---| | PowerShell 重写含中文文件 | 整文件 U+FFFD 乱码(不可逆) | 只许 Edit 工具(C6) | -| 用了字体子集外的字 | 该字显示空白 | P1 字符集核对 + P2 字体重生成 | -| 字体缺字形(如宋体无 ▶) | 该字符显示成方框 □ | `make_font.py` 已内置 `merge_arrow_glyph()` 自动补回;其余缺字形同法(从有该字的字体提取位图合并,§5.4) | +| 用了字体子集外的字 | 该字显示空白 | P0 字符集核对 + P1 字体重生成 | +| 字体缺字形(如宋体无 ▶) | 该字符显示成方框 □ | `make_font.py` 已内置 `merge_arrow_glyph()` 自动补回;其余缺字形同法(从有该字的字体提取位图合并,§4.4) | | 新 .c 只注册了 Keil(或只改了 CMake) | 单端编译缺函数/静默缺失 | 双端注册(C9) | | 模拟器主循环调 `SDL_PollEvent` | 键盘全部无响应 | 事件只由 LVGL SDL 驱动内部轮询(架构 §5.7) | | 选中行"白底白字" | 选中文字不可见 | 历史坑(2026-09-02):子 label 自身 card 样式总命中、继承不到父选中色;现行行自绘(绘制时给色、无样式继承)已根除(架构 §5.5) | | 按键处理里删除焦点容器 | use-after-free 崩溃 | 容器持久不删(架构 §5.2) | -| LVGL 池爆 40KB | 对象创建失败/画面残缺 | §5.5 预算 + 按需建毁 | +| LVGL 池爆 40KB | 对象创建失败/画面残缺 | §4.5 预算 + 按需建毁 | | 极性配反 | 黑屏/白屏/不可读 | 模拟器 mono 主题 `dark_mode` 参数 + MCU 端背景色一致 | -| 步进宽接近/超出格宽的标签居中 | 亚像素错位/不对称 | 左对齐 + 字体侧边距(§4.3) | +| 步进宽接近/超出格宽的标签居中 | 亚像素错位/不对称 | 左对齐 + 字体侧边距(§3.6) | | 关闭了 `LV_DRAW_SW_COMPLEX`(省代码) | `lv_line` 斜线不绘制(如主界面顶部"浅 V"两腿消失,只剩水平线) | `LV_DRAW_SW_COMPLEX` 两端显式 =1;`LV_DRAW_SW_CIRCLE_CACHE_SIZE` 须非零(默认 4,否则零长数组 C2466)(架构 §关键约束 7) | | 斜线用细 `line_width`(如 2px) | 1bpp 阈值吃掉斜线边缘,腿偏细、比设计稿细 | 斜线加粗(腿 3px),或按段拆 `lv_line` 分粗细(如"浅 V"两腿 3 / 水平线 2) | -| 样机用了花哨 CSS | P2 翻译不出来 | 1bpp 禁区(C8),P1 阶段就拦下 | +| 规格引入翻译不出来的效果 | P1 翻译不出来 | 1bpp 禁区(C8),P0 阶段就拦下 | | UI 源文件(UTF-8 BOM)里 `snprintf` 格式串含中文 | 模拟器 cl.exe 报 C4819(代码页 936 误读 UTF-8 字节) | 格式化输出用 `lv_snprintf`(LVGL 自带,双端一致),勿用标准库 `snprintf` | | 常驻样式用动态 `lv_style_t` + set_prop | LVGL 池永久占用:首次 set_prop 分配 `values_and_props` 且永不释放(x64 实测 88 B/个) | 常驻样式一律常量样式:`LV_STYLE_CONST_INIT(var, props[])` + `lv_style_const_prop_t[]`(属性存 Flash、零池分配,见架构 §5.6) | --- -## 十一、小改动快速通道(简化流程) +## 十、小改动快速通道(简化流程) | 改动类型 | 流程 | |---|---| -| 文案修改 | 改样机 → 改代码 → **字符集核对**(有新字则重生成字体)→ P3 比对 | -| 间距/尺寸调整 | 改样机参数表 → 改代码 → P3/P4 合并为一轮验证 | -| 纯交互调整(键位/滚动) | 改样机/配套说明 → 改代码 → P3 验证行为 | -| 字体重生成 | 任何引入新字的改动后强制执行 §5.4(**最常见的漏项**) | +| 文案修改 | 改规格 → 改代码 → **字符集核对**(有新字则重生成字体)→ P2 比对 | +| 间距/尺寸调整 | 改规格参数表 → 改代码 → P2/P3 合并为一轮验证 | +| 纯交互调整(键位/滚动) | 改规格/设计文档 → 改代码 → P2 验证行为 | +| 字体重生成 | 任何引入新字的改动后强制执行 §4.4(**最常见的漏项**) | --- -## 附录 A:样机 HTML 模板骨架(仅页面) +## 附录 A:设计规格模板骨架(NN_界面名_说明.md) -```html - - - - - -界面 NN | <名称> - - - - -
…标记…
-
…同标记…
- - +```markdown +# 界面 NN:<名称>(设计规格) + +> 状态:制作中 / 已定稿 (YYYY-MM-DD) +> 参考图:mockups/NN_<名称>原始图片.png +> 本文件为设计基准:P1 照参数表翻译代码;P2 出图对照参数表 + 原图 + +## 一、布局参数表 +| 元素 | x | y | w | h | 边框 | 内容/状态 | +|---|---|---|---|---|---|---| +| 页头 | … | … | … | … | … | … | + +验证等式:`160 = 顶边距2 + … + 底边距3`(各项相加 = 屏幕尺寸) + +## 二、行/元素内容与状态 +… + +## 三、字符集核对 +- 命中(__):… +- 新增(__):… + +## 四、待确认问题 +- Q1:… + +## 五、映射建议(菜单类界面) +… + +## 六、LVGL 翻译要点 +… ``` ## 附录 B:单界面生产检查单(复制使用) @@ -402,27 +371,26 @@ flowchart TD 界面 NN:<名称> 负责人:____ 开始:____ P0 [ ] 台账登记(编号/作用/菜单位置) -P1 [ ] 图片分析(结构/元素/文本逐字/比例/缺失) + [ ] 图片分析(结构/元素/文本逐字/比例/缺失) [ ] 字体度量核对(与 §2.1 一致) [ ] 字符集核对:__ 命中 / __ 新增 [ ] 布局计算(验证等式:____________) - [ ] 样机 HTML 产出(仅页面:1:1 / 4x) - [ ] 配套 NN_名称_说明.md 产出(布局/内容/字符集/问题/映射/LVGL 要点) - [ ] 原图校验:渲染成图 + 读两图目视比对,循环至基本一致(§4.5) - [ ] 用户浏览器确认定稿(日期:____) -P2 [ ] 代码翻译(与样机参数逐项一致) + [ ] NN_名称_说明.md 规格产出(参数表/内容/字符集/问题/映射/LVGL 要点) + [ ] 用户确认规格(日期:____) +P1 [ ] 代码翻译(与规格参数逐项一致) [ ] 字体重生成(extract → make_font → diff 检查) [ ] 新文件双端注册(如适用,C9) [ ] 内存预算(实测水位:____KB / 40KB) -P3 [ ] 模拟器构建 0 错误 +P2 [ ] 模拟器构建 0 错误 [ ] 离线出图:--screenshot shot.bin → i1_to_png.py → shot.png - [ ] shot.png 与样机渲染图(render_NN.png)逐项比对一致(几何/文本/状态);不一致依 HTML 改码再出图(§6.1) + [ ] shot.png 与参考图/规格参数表逐项比对(几何/文本/状态);不符按根因(修码/修规格)再出图(§5.1) [ ] 导航行为验证(窗口模式按键) -P4 [ ] @build 0 错 0 警 + [ ] 自动化回归:check_menu_rows.py + check_content_pages.py PASS +P3 [ ] @build 0 错 0 警 [ ] @test PASS(Required:____ / Forbidden:FAIL;ERROR;HardFault) [ ] 真机目检通过 -P5 [ ] UI_设计架构.md 变更记录更新 - [ ] 台账状态 → 已完成(代码+字体+字符集+样机+文档 一并提交) +P4 [ ] UI_设计架构.md 变更记录更新 + [ ] 台账状态 → 已完成(代码+字体+字符集+规格+文档 一并提交) ``` --- @@ -437,8 +405,9 @@ P5 [ ] UI_设计架构.md 变更记录更新 | 2026-09-03 | 顶部"开口浅 V"装饰两腿不渲染:根因 `simulator/lv_conf.h` 设 `LV_DRAW_SW_COMPLEX 0`(省代码)→ `lv_line` 斜段被跳过。改为两端显式 `LV_DRAW_SW_COMPLEX 1` + `LV_DRAW_SW_CIRCLE_CACHE_SIZE 4`;三图(原图/HTML/截图)顶部装饰现已一致 | | 2026-09-03 | 顶部"开口浅 V"两腿偏细(1bpp 斜线边缘被阈值吃掉):`ui_menu.c` 装饰拆 3 段 `lv_line`,两腿加粗 `MENU_DECO_LEG_W`=3、水平线 `MENU_DECO_HORIZ_W`=2(`deco_line_apply` 统一样式),三图腿粗现已一致(约 2~3px) | | 2026-09-03 | §一 流程总览重画为清晰的纵向管线图:每阶段标注 产物 + Gate,P1/P3 的"比对→修正"迭代环显式标出(⟲);补全原先遗漏的 P1 原图校验环/`NN_说明.md` 产物、P2 双端注册、P3 导航 Gate,与各阶段细则(§3~§8)对齐 | -| 2026-09-04 | §九 新增 9.1“样机投入分流(按屏型)”:明确 HTML 样机=静态外观轴(设计先行、非代码生成器),设计文档 + 模拟器=行为/数据轴;静态内容页走完整样机管线、导航/交互重屏 HTML 只做静态首帧、行为靠 `菜单页面设计.md`;配套文档补列 `菜单页面设计.md` | -| 2026-09-07 | §一 五阶段管线图由 ASCII 转 Mermaid flowchart:P1/P3“比对→修正”迭代环由行内 ⟲ 注记改为显式判定节点 + 回边(未收敛不得前进更直观);各 Gate 条件与产物标注保留、语义不变 | +| 2026-09-04 | §九 新增 9.1"样机投入分流(按屏型)":明确 HTML 样机=静态外观轴(设计先行、非代码生成器),设计文档 + 模拟器=行为/数据轴;静态内容页走完整样机管线、导航/交互重屏 HTML 只做静态首帧、行为靠 `菜单页面设计.md`;配套文档补列 `菜单页面设计.md` | +| 2026-09-07 | §一 五阶段管线图由 ASCII 转 Mermaid flowchart:P1/P3"比对→修正"迭代环由行内 ⟲ 注记改为显式判定节点 + 回边(未收敛不得前进更直观);各 Gate 条件与产物标注保留、语义不变 | | 2026-09-09 | 内容页三屏(装置信息/交流量/直流量,`ui_content_page`)落地后回填:C3 字符集 78→97 汉字(193 字形);§十 新增两坑——UI BOM 源文件 `snprintf` 中文格式串触发 C4819(改 `lv_snprintf`)、常驻动态样式池永久占用(改 `LV_STYLE_CONST_INIT` 常量样式,x64 实测省 88 B/个) | | 2026-09-09 | 用户密钥密码页落地后回填:C3 字符集 97→102 汉字(198 字形,补"本/用/户/密/钥");新文案走 P4 字体重生成 + `check_content_pages.py` 新增密码页 4 状态黄金参考 | | 2026-09-10 | 网口设置内容页落地后回填:C3 字符集 102→111 字符(208 字形,补网口设置/地址/掩码等,并修复 `lv_font_conv` 丢弃的低码位符号 U+FF0C 全角逗号 → 页脚逗号不再空白);新文案走 P4 字体重生成 + `check_content_pages.py` 新增网口设置 4 状态黄金参考(可编辑/只读/编辑光标反显/保存 OK);模拟器 `main` 调 `net_cfg_load` 使参考图显示工厂默认值 | +| 2026-09-13 | 移除 HTML 样机阶段:五阶段 → **四阶段管线**(P0 需求录入+设计规格 / P1 规格→代码 / P2 模拟器 / P3 MCU / P4 归档);设计基准改为"规格布局参数表 + 参考图",P2 比对基准由"样机渲染图"改为"参考图 + 规格参数表"(根因分流:修代码 / 回 P0 修规格);用户视觉确认点由 HTML 浏览器移到 P2 模拟器窗口/出图;删除 `mockups/01_主界面.html`、`render_01.png`、`tools/ui_mock/render_mockup.py`,保留 `NN_说明.md` 规格与原始参考图;`NN_说明.md` 升格为唯一设计基准(P1 照参数表翻译代码,P2 出图对照参数表 + 原图);`UI_自动化测试流程.md` 门禁编号同步改为 P2/P3 | diff --git a/docs/UI 设计/UI_自动化测试流程.md b/docs/UI 设计/UI_自动化测试流程.md index 12f08424..7b24b18b 100644 --- a/docs/UI 设计/UI_自动化测试流程.md +++ b/docs/UI 设计/UI_自动化测试流程.md @@ -1,7 +1,7 @@ # UI 自动化测试流程(模拟器回归) > **用途**:所有涉及 UI 的改动(菜单结构 / 内容页 / 字体 / 样式 / 几何)改完代码后,按本文跑一遍全部门禁再提交。脚本已入库,直接调用即可,无需临时重写。 -> **定位**:`UI_界面制作流程.md` P3/P4 门禁的可执行版本;设计期样机比对(P1/P2)仍走该文档与 `tools/ui_mock/`。 +> **定位**:`UI_界面制作流程.md` P2/P3 门禁的可执行版本;设计阶段(P0 规格 / P1 编码)走 `UI_界面制作流程.md`。 --- @@ -88,7 +88,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 ## 六、与其他文档的关系 -- `UI_界面制作流程.md`:P1 样机生成(`tools/ui_mock/render_mockup.py`)→ P2 设计评审 → **P3 本文回归** → P4 MCU 上机目检 +- `UI_界面制作流程.md`:P0 需求录入 + 设计规格 → P1 规格→代码 → **P2 本文回归** → P3 MCU 上机目检 → P4 归档 - `菜单页面设计.md` §9:内容页对象树 / 几何表 / 内存预算(本文数字的来源) - `simulator/README.md` §3.5/§3.6:无头模式(`--keys`/`--screenshot`)与回归脚本说明 @@ -101,3 +101,4 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | 2026-09-10 | `check_content_pages.py` 新增**文本级断言层**(字形模板匹配 50 条,独立于 `ref/`,先于像素比对运行、失败即中止防误刷参考);内容页扩至 11 页(+网口设置 4 态);§二/§四同步 | | 2026-09-10 | 新增 `simulator/tests/regen_ref_png.py`(由 `ref/*.bin` 批量重生成目检 PNG,默认 `--scale 6 --invert`);`ref/*.png` 加入 `.gitignore`(本地调试产物不入库);§四 补"重生成目检 PNG"步骤(修复"刷了 `.bin` 参考但 `.png` 仍是旧渲染 → 看似乱码") | | 2026-09-10 | `simulator/tests/ref/` **整目录不入库**(`.bin` 黄金参考 + `.png` 目检图均为本地生成物):`git rm --cached` 原有 3 个 `.bin`,`.gitignore` 改为忽略整目录;首次/换机后先 `--update` 生成参考;`--update` 先跑文本断言防误刷;§四 重写 | +| 2026-09-13 | 管线同步:`UI_界面制作流程.md` 移除 HTML 样机阶段(五阶段 → 四阶段 P0~P4),本文定位改为"P2/P3 门禁的可执行版本";§六 关系描述更新为 P0 规格 → P1 编码 → P2 本文回归 → P3 上机目检 → P4 归档;移除 `tools/ui_mock/` 引用 | diff --git a/docs/UI 设计/mockups/01_主界面.html b/docs/UI 设计/mockups/01_主界面.html deleted file mode 100644 index 6071ca04..00000000 --- a/docs/UI 设计/mockups/01_主界面.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - -界面 01 | 主界面 - - - - - -
- - - -
当前模式 : 无模式
-
-
装置信息
-
实时数据▶
-
参数定值▶
-
三遥设置▶
-
装置维护▶
-
通讯参数▶
-
记录查询▶
-
-
- - -
- - - -
当前模式 : 无模式
-
-
装置信息
-
实时数据▶
-
参数定值▶
-
三遥设置▶
-
装置维护▶
-
通讯参数▶
-
记录查询▶
-
-
- - - diff --git a/docs/UI 设计/mockups/01_主界面_说明.md b/docs/UI 设计/mockups/01_主界面_说明.md index f4579ed3..792cf0ad 100644 --- a/docs/UI 设计/mockups/01_主界面_说明.md +++ b/docs/UI 设计/mockups/01_主界面_说明.md @@ -1,8 +1,8 @@ # 界面 01:主界面(根页)— 规格说明 -> 配套样机:`01_主界面.html`(仅渲染页面本身)。本文件为设计规格(source of truth):布局参数、字符集核对、待确认问题、映射、LVGL 翻译要点。 +> 本文件为设计规格(source of truth):布局参数、字符集核对、待确认问题、映射、LVGL 翻译要点。P1 照参数表翻译代码,P2 出图对照参数表 + 原图。 > 规则:160×160 设备像素 | 1bpp 纯黑白 | SimSun 12px(汉字 12px/字,ASCII 6px/字,行高 13) -> 布局依据:原图 `01_主界面原始图片.png`(628×631 @ 3.925x 缩放)实测 + 渲染目视比对。 +> 布局依据:原图 `01_主界面原始图片.png`(628×631 @ 3.925x 缩放)实测 + 模拟器出图目视比对。 --- @@ -67,11 +67,11 @@ 注:旧菜单中的"厂家设置"(L0,含 元件设置/恢复默认/版本显示/版本信息/装置遥调/装置记录)未出现在新 7 项列表中,入口待 Q3 确认。 -## 六、LVGL 翻译要点(阶段 2 用) +## 六、LVGL 翻译要点(P1 用) 1. 列表 = 持久容器 `lv_obj`(x=0, y=44, 99×107,1px 边框)+ 7 个行子 `lv_obj`(高 15,绝对定位 `lv_obj_set_pos`,固定布局更可控) 2. 行 = `lv_obj` + `lv_label`(静态文本,左对齐 x=8)+ `lv_label`("▶",x=58,紧跟文字);选中行 = `LV_STATE_CHECKED` 整行反色(白底黑字,高亮铺满框宽) 3. 状态行 = `lv_label`(x=17, y=20,文本由 App 层供给,Q4) 4. 顶部装饰 = 开口浅 V(4 段折线:(7,7)→(16,17)→(145,17)→(158,7)):LVGL 可用 3 条 `lv_obj` 线段拼合(斜线用 `lv_line`),或预渲染 1bpp 位图 `lv_image`(更省对象、更精确) -5. 1bpp 禁区(本样机均未使用):圆角 / 阴影 / 透明度 / 渐变 / 多色 -6. 内存:根页 = 1 列表容器 + 7 行 ×(行 obj + 2 label)+ 状态 label + 装饰 ≈ 20+ 个对象,为最高频页面,阶段 2 需核算 40KB LVGL 池占用 +5. 1bpp 禁区(本设计均未使用):圆角 / 阴影 / 透明度 / 渐变 / 多色 +6. 内存:根页 = 1 列表容器 + 7 行 ×(行 obj + 2 label)+ 状态 label + 装饰 ≈ 20+ 个对象,为最高频页面,P1 需核算 40KB LVGL 池占用 diff --git a/docs/UI 设计/mockups/render_01.png b/docs/UI 设计/mockups/render_01.png deleted file mode 100644 index 0a4b1c4f..00000000 Binary files a/docs/UI 设计/mockups/render_01.png and /dev/null differ diff --git a/docs/图形绘制流程.md b/docs/图形绘制流程.md index 708e6184..e503f058 100644 --- a/docs/图形绘制流程.md +++ b/docs/图形绘制流程.md @@ -16,7 +16,7 @@ | 分层架构 / 模块依赖 | **SVG**(`gen_arch_svg.py`) | 多节点跨层依赖,Mermaid 会画成"意大利面",手工排布更清晰;单层单职责的栈图用其副本 `gen_storage_stack_svg.py` | | 就绪屏障 / 生产者-消费者 | **SVG**(`gen_barrier_svg.py`) | 三栏对齐 + 正交连线,避免交叉 | | 流程图 / 时序 / 状态机 / ER | **Mermaid**(Markdown 内嵌) | 拓扑关系类图,节点少、强调连接;渲染器原生支持 | -| UI 界面 / 像素级样机 | `simulator/` 截图、`tools/ui_mock/` | 非本流程范围 | +| UI 界面 / 像素级样机 | `simulator/` 截图 | 非本流程范围 | **为什么"精确布局图"用 SVG,不用 Mermaid / HTML:** diff --git a/simulator/README.md b/simulator/README.md index 3808fd5d..c0f208d9 100644 --- a/simulator/README.md +++ b/simulator/README.md @@ -178,7 +178,7 @@ cmake --build build --config Debug ### 3.5 截图比对设计稿(离线出图) -窗口模式只适合人工目检。要把渲染结果存成 PNG 与设计稿(`docs/UI 设计/mockups/render_01.png`)逐像素比对,用离屏截图 + 转 PNG: +窗口模式只适合人工目检。要把渲染结果存成 PNG 与设计参考(`docs/UI 设计/mockups/` 下的原始参考图)做目视比对,用离屏截图 + 转 PNG: ```powershell cd simulator diff --git a/tools/ui_mock/render_mockup.py b/tools/ui_mock/render_mockup.py deleted file mode 100644 index 081285e1..00000000 --- a/tools/ui_mock/render_mockup.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- -""" -render_mockup.py — 把 mockup HTML 的 1:1 视图渲染成 160×160 PNG(无头 Chrome/Edge) - -用法: - python tools/ui_mock/render_mockup.py [--pad 24] [--size 160] - -原理: - mockup 的 body 有 padding(默认 24px),1:1 视图是第一个 flex 子项,位于 (pad, pad), - 尺寸 160×160。用无头浏览器截整页,再裁出 (pad, pad, pad+160, pad+160) 即得纯页面位图。 - 浏览器抗锯齿会在边缘产生灰阶;对比前由 compare_mockup.py 统一二值化(阈值 128)。 -""" -import argparse -import os -import subprocess -import sys -from pathlib import Path - -from PIL import Image -import numpy as np - -BROWSER_CANDIDATES = [ - r"C:\Program Files\Google\Chrome\Application\chrome.exe", - r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", - r"C:\Program Files\Microsoft\Edge\Application\msedge.exe", - r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe", -] - - -def find_browser(): - for c in BROWSER_CANDIDATES: - if os.path.exists(c): - return c - sys.exit("未找到 Chrome/Edge(用于无头渲染)") - - -def render(html_path, out_png, pad=24, size=160): - browser = find_browser() - html_abs = Path(html_path).resolve() - if not html_abs.exists(): - sys.exit("找不到 HTML: %s" % html_abs) - out_abs = Path(out_png).resolve() - out_abs.parent.mkdir(parents=True, exist_ok=True) - - win = pad + size + pad + 12 # 留一点余量 - cmd = [ - browser, - "--headless", - "--disable-gpu", - "--hide-scrollbars", - "--force-device-scale-factor=1", - "--window-size=%d,%d" % (win, win), - "--screenshot=%s" % out_abs, - html_abs.as_uri(), - ] - r = subprocess.run(cmd, capture_output=True, text=True, timeout=90) - if not out_abs.exists(): - sys.exit("截图失败:\n%s\n%s" % (r.stdout, r.stderr)) - - img = Image.open(out_abs).convert("L") - box = (pad, pad, pad + size, pad + size) - page = img.crop(box) - arr = np.array(page) - dark = float((arr < 128).mean()) - page.save(out_abs) - print("OK %s -> %s (%dx%d, 黑像素 %.1f%%)" % (html_abs, out_abs, size, size, dark * 100)) - return out_abs - - -if __name__ == "__main__": - ap = argparse.ArgumentParser() - ap.add_argument("html") - ap.add_argument("out") - ap.add_argument("--pad", type=int, default=24, help="mockup body padding(1:1 视图左上偏移)") - ap.add_argument("--size", type=int, default=160) - a = ap.parse_args() - render(a.html, a.out, pad=a.pad, size=a.size)