a38f862fb9
问题:ui-golden 是逐字节比对(check_content_pages.py 里 data == refdata), 而基准 simulator/tests/ref/ 整目录被 .gitignore 忽略、从未入库。后果不是 "少几个文件",而是这条门禁在任何新克隆 / 新并行工作树里必然报「缺黄金 参考」——只在有老副本的机器上它才是真的回归门禁。本会话已两次实测踩到 (并行工作树里只能现场 --update 造基准,而现场造出来的基准恰好失去意义)。 入库前先证明基线可复现,否则等于把一条天天误报的门禁写进仓库: - 主树 ref/ 生成于 2026-10-11,其间合入 B6~B9 与 UI 令牌化等多次改动; 今天用刚重建的 lvgl_sim 重跑 → 11 页 + 开页-关页往返池水位逐字节全过 - 主树与新工作树两份**独立构建**的 lvgl_sim(不同构建目录、不同绝对路径) 各自生成基准 → 11 个 .bin 的 SHA-256 全部一致 即渲染是 CPU 侧确定性输出,可以当仓库内容。 改动: - .gitignore:整目录忽略 → 只忽略派生的目检 PNG。另加一条 !simulator/tests/ref/*.bin 反选——第 115 行的全仓 *.bin 兜底(挡 sim_netcfg.bin 之类运行产物)会把基线一起挡掉,且反选必须排在它之后 才生效 - 不引入 LFS:11 x 3200 B = 34.4 KB(160x160 单色打包)。走 LFS 反而新增 失效面——没拉 LFS 的克隆会拿 pointer 文本去比像素,把"缺基准"换成 "比不中"。.gitattributes 已有 *.bin binary,check-attr 确认 text unset, 不会被行尾转换改写 - 基线不会被改写型钩子毁掉:trailing-whitespace / end-of-file-fixer 声明 types: [text],位图含 NUL 被判二进制 → 实测 Skipped;索引 blob、磁盘、 主树参照三方哈希 11/11 一致 - 新增 11 个基线;同步 5 份文档与 3 处脚本口径:缺基准的提示从"首次/换机 请加 --update"改为"检出不完整 → git checkout -- simulator/tests/ref/ 取回, 不要用 --update 就地造基准"(旧提示正是自我实现型基准的来源) - 顺带修掉 simulator/tests/README.md 两处与实现不符的描述:基准写成 PNG (实为 .bin)、regen_ref_png.py 写成"重建黄金参考"(它只出目检图,基准 由 check_content_pages.py --update 刷) - .pre-commit-config.yaml 只改注释(原文明确断言 ref/ "永远不会进 git 索引",已被本改动推翻);ui-golden 的 files 面覆盖 simulator/tests/, 基线被改即触发回归,刷参考从此在 git diff 里显形 历史口径按纪律冻结:UI_自动化测试流程 2026-09-10 的"整目录不入库"等 6 处 变更记录行不改写,以新行登记反转。 实测:@build 0 错 0 警(Code=213520,与内存台账一致);ui-golden Passed; pre-commit run --all-files = 36 Passed / 0 Failed(RC=0)。
145 lines
9.2 KiB
Markdown
145 lines
9.2 KiB
Markdown
# 图形绘制流程(SVG / Mermaid)
|
||
|
||
> **范围**:本仓库文档(`docs/`)里的技术配图——协议帧 / 字节布局、分层架构、依赖 / 就绪屏障、流程 / 时序 / 状态机等。
|
||
> **目标**:统一绘图方法与工具链,保证图形**精确、可复现、可 diff、可过 pre-commit**,并与既有 `tools/arch_diagram/` 生成器一致。
|
||
> **沉淀**:本流程已沉淀为 `draw-diagram` skill(`.dsh/skills/draw-diagram/SKILL.md`)——画图时直接加载该 skill 即可。
|
||
> **产物目录**:生成的 `.svg` 统一放 **`docs/images/`**(专门配图目录,产物入库)。
|
||
|
||
---
|
||
|
||
## 一、选型:什么图用什么工具
|
||
|
||
| 图形类型 | 工具 | 说明 |
|
||
|---|---|---|
|
||
| 协议帧 / 字节布局(精确比例) | **SVG**(`gen_frame_svg.py`) | 字段宽须严格 ∝ 字节数 + 逐字段标注 + 偏移标尺 |
|
||
| 闪存 / 内存分区布局(精确比例) | **SVG**(`gen_partition_svg.py`) | 帧图生成器的 MB 计量副本(分区宽 ∝ MB 数 + 偏移标尺) |
|
||
| 分层架构 / 模块依赖 | **SVG**(`gen_arch_svg.py`) | 多节点跨层依赖,Mermaid 会画成"意大利面",手工排布更清晰;单层单职责的栈图用其副本 `gen_storage_stack_svg.py` |
|
||
| 就绪屏障 / 生产者-消费者 | **SVG**(`gen_barrier_svg.py`) | 三栏对齐 + 正交连线,避免交叉 |
|
||
| 流程图 / 时序 / 状态机 / ER | **Mermaid**(Markdown 内嵌) | 拓扑关系类图,节点少、强调连接;渲染器原生支持 |
|
||
| UI 界面 / 像素级样机 | `simulator/` 截图 | 非本流程范围 |
|
||
|
||
**为什么"精确布局图"用 SVG,不用 Mermaid / HTML:**
|
||
|
||
| 维度 | SVG | Mermaid | HTML |
|
||
|---|---|---|---|
|
||
| 字段宽 ∝ 字节数 | ✅ 像素精确定位 | ❌ 盒子按文本自适应 | ⚠️ 可但繁琐 |
|
||
| 连续字节流(无假箭头) | ✅ 分段矩形 | ❌ 自动加有向边 | ✅ |
|
||
| 纯文本(可 diff / lint / pre-commit) | ✅ | ✅(代码块) | ⚠️ |
|
||
| 直接是图片(免截图) | ✅ | ⚠️ 需渲染器 | ❌ 需截图 |
|
||
| 矢量无损(任意 DPI) | ✅ | ✅(渲染后) | ⚠️ |
|
||
| 依赖 | 无(浏览器用系统字体渲染) | 渲染器 | 浏览器 |
|
||
|
||
> 结论:**要精确像素 / 比例的布局图 → SVG 生成器**;**只表达拓扑关系的图 → Mermaid**。
|
||
|
||
---
|
||
|
||
## 二、既有工具(`tools/arch_diagram/`)
|
||
|
||
以下生成器共享同一套约定(§3.2),均为**纯标准库**拼 SVG:
|
||
|
||
| 脚本 | 产物(入库) | 顶部数据区(改这里 → 重跑) |
|
||
|---|---|---|
|
||
| `gen_arch_svg.py` | `docs/images/项目架构总览.svg` | `LAYERS` / `ROWS` / `FOOT` |
|
||
| `gen_barrier_svg.py` | `docs/images/就绪屏障.svg` | `BITS` / `PRODUCERS` / `CONSUMERS` |
|
||
| `gen_frame_svg.py` | `docs/images/RemoDispBus帧格式.svg` | `FIELDS` / `TITLE` / `SUB` / `FOOT` |
|
||
| `gen_storage_stack_svg.py` | `docs/images/存储栈分层.svg` | `L_*` / `ROWS` / `TITLE` / `SUB` / `FOOT` |
|
||
| `gen_partition_svg.py` | `docs/images/物理分区布局.svg` | `FIELDS` / `TITLE` / `SUB` / `FOOT`(MB 计量) |
|
||
| `gen_net_pending_svg.py` | `docs/images/Net层阻塞模型.svg` | `CALLER_STEPS` / `LOOP_STEPS` / `PHASES` |
|
||
| `gen_cfg_arch_svg.py` | `docs/images/配置文件分层架构.svg` | 配置引擎分层 |
|
||
| `gen_net_stack_svg.py` | `docs/images/BSD网络栈分层.svg` | BSD 网络栈分层 |
|
||
| `gen_memory_svg.py` | `docs/images/内存布局_*.svg` | RAM / FLASH 布局 |
|
||
| `gen_watchdog_svg.py` | `docs/images/看门狗心跳模型.svg` | `TASKS` / `REQ_BITS` / `POLL_STEPS` / `TIMELINE` |
|
||
|
||
---
|
||
|
||
## 三、绘制流程
|
||
|
||
### 3.1 精确布局图(协议帧 / 架构 / 依赖)——用生成器
|
||
|
||
1. **挑脚本**:协议帧 → `gen_frame_svg.py`;分层架构 → `gen_arch_svg.py`;就绪屏障 → `gen_barrier_svg.py`。
|
||
2. **改数据**:编辑脚本顶部数据区。配色沿用既有调色板:
|
||
|
||
| 语义 | 主色 | 底色 |
|
||
|---|---|---|
|
||
| 蓝 | `#2563eb` | `#eff6ff` |
|
||
| 青 | `#0d9488` | `#f0fdfa` |
|
||
| 绿 | `#16a34a` | `#f0fdf4` |
|
||
| 琥珀 | `#d97706` | `#fffbeb` |
|
||
| 紫 | `#7c3aed` | `#f5f3ff` |
|
||
| 灰 | `#475569` | `#f1f5f9` |
|
||
| 玫红 | `#e11d48` | `#fff1f2` |
|
||
|
||
3. **重跑**(仓库根目录;纯标准库,任意 python3,统一用 `.venv` 亦可):
|
||
```powershell
|
||
& '.venv\Scripts\python.exe' tools/arch_diagram/gen_frame_svg.py
|
||
```
|
||
成功输出 `OK <字节> bytes, canvas <W>x<H> -> <路径>`。脚本内置 `ElementTree.fromstring` 自检——**XML 不合法会直接抛错**,不会产出坏图。
|
||
4. **目检**:浏览器打开 `docs/images/*.svg`,确认文字不溢出盒外、框 / 线 / 标签对齐、配色正确。
|
||
5. **引用**:在对应 `.md` 里用**相对路径**:根目录下的 md 指向 `docs/images/` 下的图(形如 ``);`docs/UI设计/` 等子目录的 md 则写 `../images/` 前缀的等价写法。
|
||
6. **提交**:脚本(源)+ 生成的 `.svg`(产物)**一起**提交(产物入库,类比生成物 `ui_menu_tab.c`)。
|
||
|
||
### 3.2 SVG 约定(手写 / 新增生成器时遵循)
|
||
|
||
- **纯标准库**:只用 `html` / `os`(+ 需要时 `math`)拼 SVG 字符串;**不引第三方库**(SVG 由浏览器用系统字体渲染,无需 Pillow / 字体文件)。
|
||
- **文本转义**:所有文本经 `html.escape(s, quote=True)`(`& < > " '` → 实体),防止破坏 XML。
|
||
- **字体**:根 `<svg font-family="Microsoft YaHei, Segoe UI, PingFang SC, sans-serif">`(系统字体栈,Win / mac 通用)。
|
||
- **文本宽估算**(自适应盒宽时):`text_w(s, fs)`——CJK / 全角 / 圈码 / 箭头 ≈ `1.0em`,其余 ≈ `0.58em`(**宁大勿小**,防溢出)。
|
||
- **落盘**:`open(OUT, "w", encoding="utf-8", newline="\n")`(UTF-8 无 BOM + LF,符合仓库编码铁律)。
|
||
- **自检**:写盘后 `xml.etree.ElementTree.fromstring(svg)`。
|
||
- **产物位置**:`docs/images/`(专门配图目录;引用它的 `.md` 用相对路径指向)。
|
||
- **命名**:`docs/images/<主题>.svg`(可与同名 `.md` 对应,如 `images/RemoDispBus帧格式.svg` ↔ `RemoDispBus协议与接收设计说明.md` §1.2)。
|
||
|
||
### 3.3 拓扑类图(流程 / 时序 / 状态机)——用 Mermaid
|
||
|
||
在 Markdown 里直接写 ` ```mermaid ` 代码块(GitHub 与多数渲染器原生渲染,**无需生成图片**):
|
||
|
||
````markdown
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant C as 客户端
|
||
participant S as 服务端
|
||
C->>S: LCDMEM 取帧
|
||
S-->>C: 1bpp 位图(3200 B)
|
||
```
|
||
````
|
||
|
||
常用:`flowchart`(流程 / 框图)、`sequenceDiagram`(时序)、`stateDiagram-v2`(状态机)、`erDiagram`(实体关系)。
|
||
> ⚠️ Mermaid 盒子宽度自适应、会自动连线,**只适合"关系"类图**;凡需精确像素 / 比例的布局图(协议帧、分层框)一律走 §3.1 的 SVG 生成器。
|
||
|
||
---
|
||
|
||
## 四、SVG → PNG(可选)
|
||
|
||
默认交付 **SVG**(矢量、可缩放、可 diff、可过门禁)。仅当某个**不渲染 SVG 的查看器**需要位图时再转 PNG:
|
||
|
||
- **最简单**:浏览器打开 `.svg` → 截图 / 另存为图片。
|
||
- **脚本化**:给 `.venv` 装 `cairosvg` 后转(`scale=2` 提高清晰度):
|
||
```powershell
|
||
& '.venv\Scripts\python.exe' -m pip install cairosvg
|
||
& '.venv\Scripts\python.exe' -c "import cairosvg; cairosvg.svg2png(url='docs/images/RemoDispBus帧格式.svg', write_to='remo.png', scale=2)"
|
||
```
|
||
|
||
> PNG 属**本地派生物、不入库**(与 `simulator/tests/ref/*.png` 目检图一致,`.gitignore` 已含
|
||
> `*.png`;注意该目录下作为像素黄金参考的 `.bin` **是入库的**,别顺手一起忽略掉)。
|
||
|
||
---
|
||
|
||
## 五、检查清单(提交前)
|
||
|
||
- [ ] 生成器输出 `OK … bytes`(无异常 = XML 合法)。
|
||
- [ ] 浏览器目检:文字不溢出、框 / 线 / 标签对齐、配色符合调色板。
|
||
- [ ] 引用它的 `.md` 里图片**相对路径**正确(能显示)。
|
||
- [ ] 脚本 + 产物 `.svg` **一并**暂存(产物入库)。
|
||
- [ ] `pre-commit run --files <脚本> <svg> <md>` 全过(无乱码 / 行尾空白 / 编码 OK)。
|
||
|
||
---
|
||
|
||
## 六、变更记录
|
||
|
||
| 日期 | 变更 |
|
||
|---|---|
|
||
| 2026-09-11 | 创建:统一图形绘制流程(选型 / SVG 生成器 / Mermaid / 约定 / 检查清单);新增 `tools/arch_diagram/gen_frame_svg.py`(通用协议帧图)+ `docs/images/RemoDispBus帧格式.svg` |
|
||
| 2026-09-11 | 新增专门配图目录 `docs/images/`:收拢 `docs/` 根与 `docs/UI设计/images/` 下 5 张 SVG;同步更新 3 个生成器 OUT 路径、4 处文档图片引用、`AGENTS.md` 与 `.gitignore`(PNG 预览规则);流程沉淀为 `draw-diagram` skill |
|
||
| 2026-09-11 | 存量 ASCII 框图清理:`STM32F4-Base存储架构说明.md` 两图(存储栈分层 / 物理分区布局)重绘为 SVG(生成器家族新增 `gen_storage_stack_svg.py` / `gen_partition_svg.py`);`RemoDispBus协议与接收设计说明.md` 删 ASCII 帧图(SVG 帧格式图已在,信息无损失);`测试说明/CH395F_Test_Guide.md` 4 图(2 组件架构 + 2 会话时序)转 Mermaid |
|
||
| 2026-10-22 | 澄清 §派生物一句:`simulator/tests/ref/` 下作为像素黄金参考的 `.bin` 已入库,只有派生的 `.png` 目检图仍不入库(原表述把两者混为"参考图不入库",会诱导后来者把整个目录继续忽略掉) |
|