feat(UI): W6 第一波——设计令牌层、页面注册表生成器与 UI 门禁补强

启动 docs/任务规划/README.md 的 W6 UI 扩展簇(用户裁定,覆盖 2026-10-11
的"本轮不排期")。四个工作包,全部验证通过。

## U1 设计令牌层(零行为变化)
新建 Drivers/BSP/UI/ui_tokens.h(20 个宏),框架 + 7 个页面 + App/task/
ui_task.c 全部接入。屏幕几何**复用 ui_port.h 既有 UI_SCREEN_W/H**,不重复
定义;令牌头自身只 include ui_port.h,不把 LVGL 拖进全部消费者。
补收了报告没列但代码里真重复的量:UI_TEXT_BAND_H 15(7 次)、列表左右边界
与每页行数(各 4 个页面逐字复制)、推流协议几何与显示几何同源(此前硬编码
3200 且注释写着"须与 UI_FB_SIZE 一致"——典型漂移隐患)、UI_TASK_TICK_MS
(ui_port_step 与 osDelay 必须同值,原先靠注释维系)。
故意不收页面私有几何与"同形不同义"的值(device_info 的 pitch=15 与列表
UI_ROW_PITCH=14 合并即改像素)。

## U3 页面注册表与生成器(删手工分发)
ui_page_registry.{c,h} 由生成器产出,**手工 menu_content_page_for() 已删
除**(仓库内仅存 2 处历史说明注释)。密码页按代码实况**单独出口**
(ui_page_registry_password())——它非节点绑定,任何 #P 叶子进同一页。
顺带落地此前判定"U1 阶段做不了"的共享 flat 样式 ui_style.{c,h}。
生成器除产注册表外**自动把新 .c 写进 uvprojx 与 CMakeLists.txt**,且
**双向对账**:加页打印 registered、删页打印 pruned,不留死条目;并双向
校验(#page 找不到实例报错、实例未被任何 #page 引用也报错)。

## U4 门禁补强(35 个钩子)
新增 font-charset 钩子(scripts/check_font_charset.py):复用
tools/font_gen 的同一套提取与 cmap 解析规则(importlib 直载,零第二套
规则),A 段查源码在用 vs 字符集、B 段查字符集 vs 字体 cmap。
registration-check 补第 ④ 类:实测原规则对"进了两端构建但没接描述符表"
的页面**全绿**——desc 被链接器剥离、编译链接全过、菜单永远打不开的死代码。
仍照不出的两项已如实写进文档(枚举漏加归 @build、菜单树漏挂归 golden)。

## 字体子集瘦身(D 案,4 留 3 删)
ui_cn_chars.txt 122→119 字(恰删 上/电/重,零新增)。异/常/片/芯 进
KEEP_EXTRA 带理由白名单:它们曾在 9eb1bd63 被写进中文自检描述落盘
/log/selftest.log(定长 64B 记录跨固件持久),删字形会使历史故障记录
渲染空白且不可逆。保留字按**必需**处理,反向有牙——谁只跑 extract_cn
把它们静默洗掉,钩子直接 FAIL 并逐字打印理由。
--strict 口径为"未知冗余 = 0"而非"冗余 = 0"。

## 验证
@build 0 错 0 警;ER_IROM1 232,052 B(较 HEAD −228 B)、RW_IRAM1
75,232 B **零增量**、RW_CCM 87.9% 未变。golden:菜单 8 状态全像素一致 +
内容页 11 页逐字节一致,**参考图 sha256 差异 0 条**(未用 --update 刷
基线);池水位菜单基线 5368 B、开页-关页往返 5448 B 与 U1 时相同。
38 个在途修改文件逐个核对 BOM/CRLF/U+FFFD 全部与 HEAD 一致。
L0 33 钩子 + L0.5 16 runner / 1104 断言全绿;check_doc_links --all
142 篇、无法判定 0;mem-budget / mem-numbers 已回填。

## 单位成本实测(U5 排期依据)
临时页 zzprobe 绑到"遥信量"叶子 → 只跑生成器 → 带页 @build 0/0 →
注册表出现 {5u, &ui_page_zzprobe_desc} → 回滚后 6 个产出文件 zzprobe
残留计数均 0、重编回基线。
**结论:新增一页 = 3 处人工(新 .c + 新 .h + 菜单树那一行加
 #page=<名>)+ 1 条生成器命令,手工编辑注册文件 0 处。**
据此修正报告原口径"8 处降到 1 处"——那个 1 只在"1 处对既有文件的修改"
意义下成立,页面自身 2 个文件本就要写,真实处数是 3。

## 文档同步与两处自我修正
§0.1 UI 行 B → B+;§6.1「内容页」「设计令牌」两行按实况回填;§6.5 U1/U3
状态更新;§6 顶部加"建议栏不可照抄"总警示(本轮四次证伪:ui_port.h 已有
同名宏、LV_COLOR_BLACK/WHITE 不存在且极性反了、27 页实为目标页数现存 7
个、生成器草图漏密码页非节点绑定)。
两处改口:① §6.5 U4 从我上一版**误写的 3/3 改回 2/3**——"golden 脚本早已
存在"不等于"已进自动门禁",两个 golden 脚本至今未挂 pre-commit,该缺口
单列为待办;② 原计划"分三笔提交"**作废**,U1 与 U3 改同一批文件、文件内
交叠,硬切会产生不可编译的中间态,故整波一笔。
This commit is contained in:
2026-10-20 21:14:14 +08:00
parent 9d06798f2d
commit dafc18f5ed
47 changed files with 1447 additions and 450 deletions
+242
View File
@@ -0,0 +1,242 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""check_font_charset.py - UI 字符集差集门禁(防"缺字显示白框",报告 §6.5 U4 残余)。
背景:
本工程唯一中文字体 `Drivers/BSP/UI/ui_font_cn_12.c` 是**子集**字体:只包含
`ui_cn_chars.txt` 列出的码位。生成链(font-regen skill 四步):
UI 源码 --extract_cn.py--> ui_cn_chars.txt --make_font.py--> ui_font_cn_12.c
`make_font.py` 的 `verify_coverage()` 只在**生成期**校验"txt ⊆ 字体 cmap";
若开发者改了 UI 字符串却忘了重跑 extract_cn / make_font,提交期**无任何门禁**
能拦住——缺字上机渲染为空白(白框),无日志无报错,只能靠肉眼截图发现。
本钩子把这条约定机械化(U4 三钩子之二,golden 已建、registration-check 见 §2.18)。
判定口径(两段链条,缺一环即白框):
A. 必需字符集(源码在用 ∪ KEEP_EXTRA 保留字) vs ui_cn_chars.txt
必需、txt 无 -> [GAP] FAIL(字体必然缺此字形 → 白框;保留字被剔同样 FAIL——
防未来 extract_cn 按"源码已不用"静默删掉已落盘记录的用字)
txt 有、非必需 -> [WARN] 不拦(未知冗余字形只费 Flash 不致白框;--strict 升级为
FAIL;KEEP_EXTRA 内的字不计入冗余——刻意保留 ≠ 未知冗余)
B. ui_cn_chars.txt vs ui_font_cn_12.c 的 cmap
txt 有、字体无 -> [GAP] FAIL(lv_font_conv 对缺字形/低码位会**静默丢字**)
A 通过(txt ⊇ 必需)+ B 通过(字体 ⊇ txt)⟹ 字体 ⊇ 源码在用与刻意保留的每个字。
刻意保留清单 KEEP_EXTRA(同构 check_registration.py 的 KNOWN_GAPS:存量 + 逐条理由):
extract_cn 是**静态扫描**,照不出运行期**已落盘数据**里的汉字——典型如
/log/selftest.log 的定长 64 B 记录(跨固件持久,自检页直接渲染 desc)。这类字
源码已不用、但删字形会让**历史审计记录**渲染空白且不可逆;收益(每字 ~200 B RO,
占 1 MB Flash 万分之几)与代价完全不对称(同 2026-10-15 内存优化簇"收益不抵风险"
整簇暂缓的裁定逻辑)。新增条目必须写理由与出处;确认相关落盘数据已不存在后方可
删条目(删条目后下次 font-regen 自然瘦身)。
⚠ font-regen 流程:extract_cn 重生成 txt 时**不会**带上保留字(源码已不用),须把
KEEP_EXTRA 的字并回 ui_cn_chars.txt 再跑 make_font.py,否则本钩子 A 段 FAIL 并
逐字给出理由——刻意设计(静默丢失 = 事故),不是误报。
单一规则源(关键设计):
本脚本**不自创**第二套解析规则——字符集提取复用 `tools/font_gen/extract_cn.py`
的 `SCAN` 清单与 `collect()` 状态机(跳过注释/字符常量、只取双引号串内非 ASCII),
cmap 覆盖判定复用 `tools/font_gen/make_font.py` 的 `verify_coverage()`(含稀疏
unicode_list 与补回字形的单码位 cmap 条目)。两套规则必然漂移,这正是本项目
反复在防的(见 pre-commit使用说明.md §5.5 教训)。
用法(仓库根目录):
& '.venv\\Scripts\\python.exe' scripts/check_font_charset.py
& '.venv\\Scripts\\python.exe' scripts/check_font_charset.py --strict
# 注入测试用覆盖(不改动仓库文件即可复现"缺字"场景):
# --chars <副本> 模拟字符集文件缺/多字
# --font <副本> 模拟字体 cmap 丢字形
退出码:
0 = 通过(必需字符——源码在用 ∪ KEEP_EXTRA——全部在字体里;未知冗余仅 WARN)
1 = 失败(存在白框方向的缺口:必需字不在 txt(含 KEEP_EXTRA 保留字被剔),或 txt 字不在字体;--strict 时未知冗余也算)
2 = 环境错误(字符集文件 / 字体文件 / 生成器脚本缺失)
创建日期:2026-10-15(U4 残余,报告 §6.5 / §7 交付物清单"字符集差集检查"行)
"""
import argparse
import importlib.util
import os
import re
import sys
# --- 仓库根(scripts/ 的上一级)---
REPO_ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
TOOLS_DIR = os.path.join(REPO_ROOT, "tools", "font_gen")
# --- 刻意保留的冗余字(同构 check_registration.py 的 KNOWN_GAPS:逐条理由 + 出处)---
# 这些字**源码已不用**,但 2026-09-14~09-20 窗口的固件把它们写进了 /log/selftest.log
# 定长 64 B 记录(跨固件持久,自检页渲染 desc)。删字形 = 历史故障行永久渲染空白,
# 而收益只有每字 ~200 B RO——不对称,故保留并登记(Lead 裁定 2026-10-15,task-6 D 案)。
# 钩子按"必需"对待:txt/字体里缺任一保留字 → FAIL(防下次 font-regen 静默再删)。
KEEP_EXTRA = {
"异": "9eb1bd63(2026-09-14) 中文自检描述 \"NAND异常/ADC异常/时钟芯片异常/通讯异常/"
"文件系统异常\" 曾落盘 /log/selftest.log;6f51c62a(2026-09-20 串口输出禁中文) "
"改 ASCII,但已落盘记录跨固件持久,删字形使历史故障行渲染空白",
"常": "同 \"异常\" 系记录(时钟芯片异常/文件系统异常 等,9eb1bd63 写入,落盘持久)",
"芯": "9eb1bd63 写入的 \"时钟芯片异常\" 落盘 /log/selftest.log(SD2506 时钟芯片故障"
"路径当时可触发,记录持久)",
"片": "同 \"时钟芯片异常\"(9eb1bd63 写入,落盘持久)",
}
def load_tool(name):
"""按文件路径加载 tools/font_gen/<name>.py(无包结构,importlib 直载)。
两个生成器模块级只有常量与函数定义(main 均有 __name__ 守卫),导入零副作用。
"""
path = os.path.join(TOOLS_DIR, name + ".py")
if not os.path.isfile(path):
print("[ENV-ERROR] 生成器缺失: %s" % path)
sys.exit(2)
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def fmt_char(c):
"""字符 -> `你(U+4F60)` 形态;GBK 控制台打不出的符号由 stdout replace 兜底。"""
return "%s(U+%04X)" % (c, ord(c))
def rel(path):
"""仓库相对路径;跨盘符(--font/--chars 指向临时目录)时退回原路径。"""
try:
return os.path.relpath(path, REPO_ROOT)
except ValueError:
return path
def fmt_chars(chars):
return " ".join(fmt_char(c) for c in sorted(chars))
def extract_source_chars(extract_cn):
"""复用 extract_cn 的 SCAN 清单 + collect() 状态机提取源码实际用到的字符集。"""
chars = set()
n = 0
for f in extract_cn.SCAN:
if not os.path.exists(f):
print(" skip (not found): %s" % os.path.relpath(f, REPO_ROOT))
continue
chars |= extract_cn.collect(open(f, encoding="utf-8-sig", errors="ignore").read())
n += 1
return chars, n
def check_font_coverage(make_font, font_path):
"""复用 make_font.verify_coverage 判定"字符集文件 ⊆ 字体 cmap"。
返回 None = 通过;返回 (缺字列表 | None, 原始报错) —— 缺字列表由报错里的
U+XXXX 码位反解而来(verify_coverage 只打印码位,这里补出字形便于定位)。
"""
try:
make_font.verify_coverage(font_path)
return None
except SystemExit as e:
msg = str(e)
codes = re.findall(r"U\+([0-9A-Fa-f]{4,6})", msg)
missing = [chr(int(x, 16)) for x in codes] if codes else None
return missing, msg
except OSError as e:
print("[ENV-ERROR] 读取字体失败: %s" % e)
sys.exit(2)
def main():
# Windows GBK 控制台打不出 ▶ 等符号:降级替换,避免 UnicodeEncodeError 崩溃
# (与 make_font.py 同款处理;pre-commit 经管道捕获时同理)
try:
sys.stdout.reconfigure(errors="replace")
except Exception:
pass
ap = argparse.ArgumentParser(description="UI 字符集差集门禁(源码 ↔ ui_cn_chars.txt ↔ ui_font_cn_12.c)")
ap.add_argument("--chars", default=None,
help="覆盖字符集文件路径(默认 ui_cn_chars.txt;注入测试用)")
ap.add_argument("--font", default=None,
help="覆盖字体 .c 路径(默认 Drivers/BSP/UI/ui_font_cn_12.c;注入测试用)")
ap.add_argument("--strict", action="store_true",
help="未知冗余字(txt 有、源码无、且不在 KEEP_EXTRA)也按 FAIL 处理(供审计/瘦身排期)")
args = ap.parse_args()
extract_cn = load_tool("extract_cn")
make_font = load_tool("make_font")
chars_path = args.chars or make_font.CHARS_FILE
font_path = args.font or make_font.OUT
for path, label in ((chars_path, "字符集文件"), (font_path, "字体文件")):
if not os.path.isfile(path):
print("[ENV-ERROR] %s缺失: %s" % (label, path))
return 2
# --- A 段:必需字符(源码在用 ∪ KEEP_EXTRA)vs 字符集文件 ---
src, nfiles = extract_source_chars(extract_cn)
txt = set(open(chars_path, encoding="utf-8").read().strip())
keep = set(KEEP_EXTRA)
missing_src = sorted(src - txt) # 白框方向 -> FAIL
missing_keep = sorted(keep - txt - src) # 保留字被静默剔除 -> FAIL(防历史落盘记录变白框)
extra_in_txt = sorted(txt - src - keep) # 未知冗余 -> WARN(--strict 升级)
kept_present = sorted((keep & txt) - src) # 保留字在位且源码不用 -> [KEEP] 信息行
# --- B 段:字符集文件 vs 字体 cmap(复用 verify_coverage,其事实源 = CHARS_FILE)---
make_font.CHARS_FILE = chars_path # 允许 --chars 注入;默认即仓库根 ui_cn_chars.txt
gaps = []
warns = []
keeps = []
keep_reasons = []
if missing_src:
gaps.append("[GAP] 字符集缺字 %d 个(UI 源码在用、不在 %s -> 字体必然无此字形,上机白框): %s"
% (len(missing_src), rel(chars_path), fmt_chars(missing_src)))
if missing_keep:
gaps.append("[GAP] 刻意保留字被剔除 %d 个(在 KEEP_EXTRA、不在 %s -> 历史落盘记录"
"(如 /log/selftest.log)将渲染空白,不可逆;把这些字并回字符集后重跑 make_font): %s"
% (len(missing_keep), rel(chars_path), fmt_chars(missing_keep)))
keep_reasons = [" KEEP_EXTRA 理由 %s: %s" % (fmt_char(c), KEEP_EXTRA[c])
for c in missing_keep]
if kept_present:
keeps.append("[KEEP] 刻意保留 %d 个(源码已不用但按 KEEP_EXTRA 保留,不计冗余;"
"理由见脚本头部与字典): %s" % (len(kept_present), fmt_chars(kept_present)))
if extra_in_txt:
line = ("[WARN] 字符集未知冗余 %d 个(在 txt、源码已不用且不在保留清单 -> 只费 Flash "
"不致白框;重跑 font-regen 可瘦身): %s" % (len(extra_in_txt), fmt_chars(extra_in_txt)))
if args.strict:
gaps.append(line.replace("[WARN]", "[GAP] (--strict)", 1))
else:
warns.append(line)
result = check_font_coverage(make_font, font_path)
if result is not None:
missing, msg = result
if missing:
gaps.append("[GAP] 字体缺字 %d 个(在字符集、不在 %s 的 cmap -> lv_font_conv 静默丢字,上机白框): %s"
% (len(missing), rel(font_path), fmt_chars(missing)))
else:
gaps.append("[GAP] 字体覆盖校验失败: %s" % msg)
# --- 输出 ---
print("== UI 字符集差集检查 ==")
print("扫描 UI 源码: %d 个文件 | 源码在用: %d 字 | 保留清单: %d 字 | 字符集: %d 字"
% (nfiles, len(src), len(keep), len(txt)))
for k in keeps:
print(k)
for w in warns:
print(w)
for g in gaps:
print(g)
for kr in keep_reasons:
print(kr)
if gaps:
print("修复:按 font-regen skill 四步重跑(extract_cn.py -> 并回 KEEP_EXTRA -> make_font.py -> diff 检查),"
"ui_cn_chars.txt 与 ui_font_cn_12.c 随代码一起提交")
print("结果: FAIL(%d 项缺口,%d 个 WARN)" % (len(gaps), len(warns)))
return 1
print("结果: PASS(必需字符(源码在用 ∪ 保留清单)全部在字体子集内%s%s)"
% (";%d 个保留字在位" % len(kept_present) if kept_present else "",
";%d 个未知冗余仅 WARN" % len(extra_in_txt) if warns else ""))
return 0
if __name__ == "__main__":
sys.exit(main())
+76 -4
View File
@@ -10,12 +10,20 @@
历史上 `App/task/rs485_task.c`、`Drivers/BSP/NET/lftpd/lftpd_log.c` **两处都没注册**
= 写了却从不参与任何一端编译的**死代码**,误导读者以为功能已生效。
本脚本做什么(三类缺口):
本脚本做什么(四类缺口):
扫描 `App/`、`Drivers/BSP/`、`Src/` 下全部 `.c`(排除第三方),对照两份注册表:
① 存在但未进 uvprojx -> 固件不编译(死代码 / 漏注册)
② 存在但未进 CMakeLists -> 模拟器不编译(**仅当该文件本应进模拟器时**才是缺口;
MCU-only 文件不进模拟器是**正确**的,见下方豁免)
③ 注册了但文件不存在(反向)-> 幽灵注册(改名/删除后残留的注册条目)
另对 UI 页面模块(U4 门禁补强,2026-10-15,报告 §6.5):
④ `Drivers/BSP/UI/ui_page_*.c` 定义了 `ui_page_<name>_desc`,但该符号在 UI 目录内
除自身 .c/.h 外无任何引用 -> 页面未接入
`ui_content_page.c` 的 `s_defs[]` 描述符表:两端**编译
链接全过**(desc 无人引用,链接器整段剥离),菜单永远
打不开这一页 = 又一形态的"写了却从不生效"死代码。
①②③ 对此完全照不出(2026-10-15 实测),故补本类;
即"新增 UI 页面必须过 registration-check"的机械口径。
豁免机制(关键):
模拟器只建模 **UI + 网络配置共享核心**;其余模块(MCU 驱动 / 固件任务 / 纯算法 /
@@ -237,6 +245,66 @@ def parse_cmake():
return sources
# --- UI 页面模块 → 描述符表接线(④,U4 门禁补强)---
UI_DIR_REL = "Drivers/BSP/UI"
def check_ui_page_wiring():
"""每个定义 `ui_page_<name>_desc` 的 `ui_page_*.c`,其 desc 必须被 UI 目录内其他文件引用。
UI 页面的"注册"有**两层**:构建注册(uvprojx + CMakeLists,①② 类)与框架注册
(`ui_content_page.h` 枚举 + `ui_content_page.c` 的 `s_defs[]` 表)。漏了后者的页面
两端照常编译链接(desc 无引用 → 链接器剥离),①②③ 全绿但菜单打不开这一页。
判定:页面模块 = `ui_page_*.c` 中定义 `<stem>_desc =` 的文件(不定义 desc 的
`ui_page_*.c` 不是页面模块——未来 U3 生成的 `ui_page_registry.c` 承载表引用,
本规则依旧成立且不会把它误判为页面);引用面 = `Drivers/BSP/UI/` 下全部 .c/.h,
排除该页自身 .c/.h(自身头文件里的 extern 声明不算接线)。
"""
ui_dir = os.path.join(REPO_ROOT, UI_DIR_REL.replace("/", os.sep))
if not os.path.isdir(ui_dir):
return [], 0
# 1) 页面模块清单:ui_page_*.c 中定义 `<stem>_desc =` 的
pages = {} # sym -> repo-rel .c
for fn in sorted(os.listdir(ui_dir)):
if not (fn.startswith("ui_page_") and fn.endswith(".c")):
continue
try:
text = open(os.path.join(ui_dir, fn), encoding="utf-8-sig", errors="replace").read()
except OSError:
continue
sym = fn[:-2] + "_desc"
if re.search(r"\b%s\b\s*=" % re.escape(sym), text):
pages[sym] = "%s/%s" % (UI_DIR_REL, fn)
if not pages:
return [], 0
# 2) 引用面扫描:UI 目录全部 .c/.h,排除各页自身 .c/.h
referenced = {sym: False for sym in pages}
for dirpath, _dirnames, filenames in os.walk(ui_dir):
for fname in filenames:
if not fname.endswith((".c", ".h")):
continue
rel_p = to_repo_rel(os.path.join(dirpath, fname))
try:
text = open(os.path.join(dirpath, fname), encoding="utf-8-sig", errors="replace").read()
except OSError:
continue
for sym, own_c in pages.items():
if rel_p in (own_c, own_c[:-2] + ".h"):
continue
if re.search(r"\b%s\b" % re.escape(sym), text):
referenced[sym] = True
gaps = []
for sym, rel_c in sorted(pages.items()):
if not referenced[sym]:
gaps.append("[GAP] %s | 页面未接入描述符表(%s 在 %s/ 内除自身外无引用 -> "
"编译得过但菜单打不开,死代码;新增 UI 页面必须过本项)"
% (rel_c, sym, UI_DIR_REL))
return gaps, len(pages)
def main():
ap = argparse.ArgumentParser(description="双端注册反向校验(uvprojx + CMakeLists)")
ap.add_argument("--strict", action="store_true",
@@ -290,17 +358,21 @@ def main():
if not os.path.isfile(os.path.join(REPO_ROOT, f.replace("/", os.sep))):
gaps.append("[GAP] %s | 幽灵注册(CMakeLists)" % f)
# ④ UI 页面模块未接入描述符表(框架注册,U4;①②③ 照不出的盲区)
ui_gaps, ui_pages = check_ui_page_wiring()
gaps.extend(ui_gaps)
# 输出
print("== 双端注册反向校验 ==")
print("扫描 .c: %d | uvprojx(扫描范围): %d | CMakeLists(扫描范围): %d"
% (len(scanned), len(uv_c), len(cmake_c)))
print("扫描 .c: %d | uvprojx(扫描范围): %d | CMakeLists(扫描范围): %d | UI 页面模块: %d"
% (len(scanned), len(uv_c), len(cmake_c), ui_pages))
for w in warns:
print(w)
for g in gaps:
print(g)
if gaps:
print("结果: FAIL(%d 个新缺口/幽灵注册,%d 个存量 WARN)" % (len(gaps), len(warns)))
print("结果: FAIL(%d 个新缺口/幽灵注册/页面未接线,%d 个存量 WARN)" % (len(gaps), len(warns)))
return 1
if warns:
print("结果: PASS(无新缺口;%d 个存量缺口已白名单 WARN,N-23 Lead 处置中)" % len(warns))
+1
View File
@@ -3,3 +3,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
2026-10-13 211456 18760 1104 154232 230216 22.0 97912 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
2026-10-14 212296 18740 1112 154456 231036 22.0 98144 74.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
2026-10-15 213540 18740 1136 131520 232280 22.2 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
2026-10-20 213472 18580 1140 131516 232052 22.1 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
1 date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
3 2026-10-13 211456 18760 1104 154232 230216 22.0 97912 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
4 2026-10-14 212296 18740 1112 154456 231036 22.0 98144 74.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
5 2026-10-15 213540 18740 1136 131520 232280 22.2 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
6 2026-10-20 213472 18580 1140 131516 232052 22.1 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736