fix(台架脚本): board_ui_nav 的 UNKNOWN 回退键序改为 e-s-l,冷启动落在待机态不再空转
旧逻辑 UNKNOWN 分支里的 l 条件**永不可达**:classify 只在 MENU 态填 cols,UNKNOWN 恒 cols=None ⇒ 回退实际等于静默连按 s。而本机待机态按 s/l 无效、要按 e 才进菜单 ⇒ 冷启动恰好落在待机态时,闭环导航直接开不了工(症状是界面点不动,脚本却报正常)。 现在 BACKOFF_KEYS 默认 esl、--backoff-keys 可覆盖(要旧行为填 sl),回退时**循环消费 键序、绝不连按同一键**,并新增 _dump_unknown 把认不出的帧存证下来,免得下次只能靠 复述。同笔带上 task-24 在 19:41:56 重跑产生的 trace 归档(1 行,不是本次手改)。
This commit is contained in:
@@ -138,4 +138,4 @@
|
||||
"page": "view_set",
|
||||
"cols": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
+90
-10
@@ -22,6 +22,8 @@
|
||||
python scripts/board_ui_nav.py --walk # 台架窗口:走全部绑定页并存帧
|
||||
python scripts/board_ui_nav.py --board --to yx --grab /tmp/yx
|
||||
可选:--trace 路径(步迹落盘) --dump-fail 目录(把认错的那一屏存下来)
|
||||
可选:--backoff-keys esl(UNKNOWN 屏的回退键序;默认 esl 含 e——本机待机态
|
||||
按 s/l 无效、要按 e 才进菜单,2026-10-26 task-24 台架实测)
|
||||
--board / --walk 属真机动作:必须另加 --i-hold-bench-lock 显式确认已持 bench 锁,
|
||||
否则直接拒绝(退出码 3,不碰网络)——台架是独占资源,脚本不替人判断有没有在用。
|
||||
--walk 的存证目录默认在 ci-artifacts/uinav/walk(版本控制之外),--outdir 可改;
|
||||
@@ -57,6 +59,11 @@ WALK_DIR = os.environ.get("DSH_UINAV_WALK") or os.path.join(REPO, "ci-artifacts"
|
||||
# (判据脚本自己在提交门禁里跑,顺手往 scripts/ 写 json 就是把工作树弄脏)
|
||||
TRACE = None
|
||||
FAIL_DIR = None
|
||||
# UNKNOWN 屏回退键序(task-24,2026-10-26 台架实证):to_root 认不出屏时按此序**循环**消费,
|
||||
# 绝不连按同一键。默认含 e——本机待机态按 s/l 无效、要按 e 才进菜单;旧逻辑 UNKNOWN 分支
|
||||
# 的 l 条件永不可达(classify 只在 MENU 态填 cols,UNKNOWN 恒 cols=None),等于静默连按 s:
|
||||
# 冷启动恰好落在待机态 = 闭环导航直接开不了工。--backoff-keys 可覆盖(如旧行为 "sl")。
|
||||
BACKOFF_KEYS = "esl"
|
||||
|
||||
W = H = 160
|
||||
STRIDE = W // 8
|
||||
@@ -576,6 +583,42 @@ class Navigator(object):
|
||||
"page": st["page"], "cols": st["cols"]})
|
||||
return fr, st
|
||||
|
||||
def _dump_unknown(self, fr, st):
|
||||
"""认不出屏时的现场取证(task-24:不许静默反复按键)。
|
||||
|
||||
判定依据 = 现场复算 classify 的三个判据源、与它的判定顺序同口径——打印的是
|
||||
"为什么没认出来",不是另立一套标准:密码页投票(4 字中 3 判 PWD)、标题命中
|
||||
(page_of 返回的态键)、菜单选带条数(menu_cols)。另打 40x20 墨量预览(每字符
|
||||
= 一个 4x4 块的 band_frac),"当前这一屏"在控制台直接可读;给了 --dump-fail 时
|
||||
把原始帧一并落盘——台架窗口短,事后翻帧比现场重走便宜(同 go_page 的 fail_ 帧先例)。
|
||||
"""
|
||||
votes = 0
|
||||
for g in self.clf.pwd_chars:
|
||||
if fr.match(g, (24, 80)):
|
||||
votes += 1
|
||||
_pid, keys, txt = self.clf.page_of(fr)
|
||||
cols = self.clf.menu_cols(fr)
|
||||
print(" 依据:ink=%d 密码投票 %d/4 标题=%s%s 选带=%d 页码=%s"
|
||||
% (fr.ink, votes, txt or "未命中",
|
||||
"(态键 %s)" % ",".join(keys) if keys else "",
|
||||
len(cols), str(st.get("no"))))
|
||||
print(" 当前帧 40x20 预览(# 有墨 / . 空白):")
|
||||
for y in range(0, H, 4):
|
||||
row = "".join("#" if fr.band_frac(y, min(y + 4, H), x, min(x + 4, W)) > 0.0
|
||||
else "." for x in range(0, W, 4))
|
||||
print(" |%s|" % row)
|
||||
if FAIL_DIR:
|
||||
try:
|
||||
if not os.path.isdir(FAIL_DIR):
|
||||
os.makedirs(FAIL_DIR)
|
||||
tag = "".join(c if c.isalnum() else "_" for c in (fr.name or "frame"))[:24]
|
||||
fn = os.path.join(FAIL_DIR, "unknown_%s_%d.bin" % (tag, fr.ink))
|
||||
with open(fn, "wb") as fp:
|
||||
fp.write(fr.data)
|
||||
print(" 原始帧已存:", fn)
|
||||
except OSError as exc:
|
||||
print(" 存原始帧失败(不影响判定):", exc)
|
||||
|
||||
def to_root(self, limit=14):
|
||||
"""回到"只有一列打开"的根菜单。
|
||||
|
||||
@@ -584,6 +627,7 @@ class Navigator(object):
|
||||
· **级联列只能用 l 收**,菜单态按 s 不收列(Lead 大窗实测)——沿用盲走脚本
|
||||
的 ssssssss 回根法,正是深页错位的来源之一。
|
||||
"""
|
||||
unk = 0
|
||||
for _ in range(limit):
|
||||
fr, st = self.snap()
|
||||
if st["kind"] == "MENU" and self.n_cols(st) == 1:
|
||||
@@ -603,14 +647,24 @@ class Navigator(object):
|
||||
self._step("s", "退出页面 / 密码页 / 空白态回菜单")
|
||||
continue
|
||||
if st["kind"] == "UNKNOWN":
|
||||
# 认不出的屏:先按 l(收列不会造成破坏),再按 s 兜一次
|
||||
if self.n_cols(st) > 1:
|
||||
self._step("l", "UNKNOWN 且看起来有多列 → 收列")
|
||||
else:
|
||||
self._step("s", "UNKNOWN 且无列可收 → 退出")
|
||||
# 认不出的屏:按可配置键序**循环**退避(默认 esl,--backoff-keys 可改),
|
||||
# 每轮先打印当前帧与判定依据再按键(task-24 两条台架实证教训都在这):
|
||||
# · 旧版"看起来多列按 l 否则按 s"——UNKNOWN 态 cols 恒 None(classify 只在
|
||||
# MENU 态填 cols),l 分支永不可达,实际是静默连按 s;本机待机态 s 无效、
|
||||
# 要按 e 才进菜单,冷启动落在待机态 = 板端闭环回归不可复现;
|
||||
# · 静默反复按同一键等于什么都没做,现场只剩耗时、不留证据。
|
||||
keys = BACKOFF_KEYS or "esl"
|
||||
ch = keys[unk % len(keys)]
|
||||
unk += 1
|
||||
why = ("UNKNOWN 屏第 %d 次:按 '%s'(退避键序 %r,可 --backoff-keys 覆盖)"
|
||||
% (unk, ch, keys))
|
||||
print("[未知屏] %s" % why)
|
||||
self._dump_unknown(fr, st)
|
||||
self._step(ch, why)
|
||||
continue
|
||||
self._step("l", "菜单多列 → 收回一级")
|
||||
raise RuntimeError("回根失败(14 步内没见到单列根菜单)")
|
||||
raise RuntimeError("回根失败(%d 步内没见到单列根菜单;UNKNOWN 退避键序 %r,"
|
||||
"可用 --backoff-keys 覆盖)" % (limit, BACKOFF_KEYS))
|
||||
|
||||
def seek_in_column(self, col, want, limit=12):
|
||||
"""在当前列把选带移到标签 want:先下后上,走到边界就换向(不猜步数)。
|
||||
@@ -777,7 +831,7 @@ def selftest_board_frames(clf):
|
||||
|
||||
|
||||
def selftest_refs(clf):
|
||||
"""仓库黄金参考(18 页)逐帧分类自检:每张图的期望值 = 它的文件名。
|
||||
"""仓库黄金参考(27 页)逐帧分类自检:每张图的期望值 = 它的文件名。
|
||||
|
||||
这一组是**语义正确的基线**(模拟器与板端同一份 UI 源码),用来把"分类器错了"与
|
||||
"留档帧本来就是走歪的"分开——两类失败含义完全不同,混在一起就只能靠猜。
|
||||
@@ -887,18 +941,44 @@ def _dump_trace(log):
|
||||
|
||||
|
||||
def main(argv):
|
||||
global TRACE, FAIL_DIR
|
||||
global TRACE, FAIL_DIR, BACKOFF_KEYS
|
||||
if "--trace" in argv:
|
||||
TRACE = argv[argv.index("--trace") + 1]
|
||||
if "--dump-fail" in argv:
|
||||
FAIL_DIR = argv[argv.index("--dump-fail") + 1]
|
||||
if "--backoff-keys" in argv:
|
||||
bk = argv[argv.index("--backoff-keys") + 1]
|
||||
# 只收导航器真会注的键字符;空串或含别的字符一律当场拒——退避键序打错宁可现在报错,
|
||||
# 也不要它变成"跑了半天都在按奇怪的东西"(同注释行注记写错必须当场失败的口径)
|
||||
if not bk or any(c not in "ulrdes" for c in bk):
|
||||
print("--backoff-keys 不能为空、只允许 u/l/r/d/e/s 的组合(收到 %r)" % bk)
|
||||
return 3
|
||||
BACKOFF_KEYS = bk
|
||||
clf = Classifier()
|
||||
if "--help" in argv:
|
||||
print(__doc__)
|
||||
return 0
|
||||
if "--list" in argv:
|
||||
for k in sorted(clf.pages):
|
||||
print(k, "标题=", [t for t, g in clf.titles.items() if g == k])
|
||||
# 只用 Classifier 真实暴露的接口拼清单:tree(菜单树 #page= 叶 = --to 合法页 id
|
||||
# 与路径的唯一真源)、title2harness(标题→黄金态键)、pwd_pages(#P 叶)、
|
||||
# page_path(id→标签路径)。旧版打的 clf.titles 根本不存在(AttributeError,
|
||||
# 整条子命令不可用),且 clf.pages 的键是**黄金态键**不是页 id——即便 titles 存在
|
||||
# 列出来的也不能喂给 --to。**不另造标题表**:手写第二真相下次菜单树改动必然漂移。
|
||||
titles_by_page = {}
|
||||
for (_lvl, label, page, _np) in clf.tree:
|
||||
if page:
|
||||
lst = titles_by_page.setdefault(page, [])
|
||||
if label not in lst:
|
||||
lst.append(label)
|
||||
for pid in sorted(titles_by_page):
|
||||
path = page_path(clf, pid)
|
||||
states = sorted({k for t in titles_by_page[pid]
|
||||
for k in clf.title2harness.get(t, [])})
|
||||
print("%-12s 标题=%-14s 路径=%s%s 黄金态=%s"
|
||||
% (pid, "、".join(titles_by_page[pid]),
|
||||
">".join(path) if path else "-",
|
||||
" [#P 需密码]" if pid in clf.pwd_pages else "",
|
||||
",".join(states) or "(无文本断言,认屏只认标题)"))
|
||||
return 0
|
||||
if "--classify" in argv:
|
||||
tgt = argv[argv.index("--classify") + 1]
|
||||
|
||||
Reference in New Issue
Block a user