diff --git a/.dsh/skills/id-family-semantic-migration/SKILL.md b/.dsh/skills/id-family-semantic-migration/SKILL.md new file mode 100644 index 00000000..2704018a --- /dev/null +++ b/.dsh/skills/id-family-semantic-migration/SKILL.md @@ -0,0 +1,111 @@ +--- +name: id-family-semantic-migration +description: 编号族整族清退/停发 playbook:把一个「字母+数字」编号族(如保护功能号 L1-x~L5-x、网络条目号 F-*、登记簿旧号 N-x、台架阶段裸形 T1~T3、用例号 TC-*)在全仓代码注释、测试、活文档与门禁配置里改成语义短语,或把「不清退」裁定写成硬口径入档。触发语:清退某族编号、编号改成语义名/语义短语、还有多少号可清、批次二/批次三、id-check 报悬空引用或不可判定超限、"这一族不做了"。 +whenToUse: 用于**整族**处理:清点存量 → 逐族分类 → 分批派单清退 → 用门禁自身计数收口 → 回写 docs/编号规范.md 与变更记录。单条待办行销项写 void 别名用 ledger-row-retire-void-id;某个事实数(页数/叶数/钩子数)变了要同步全仓用 stale-doc-count-sweep;单个文件改名/挪目录用 scripts/rename_ref.py。本技能不覆盖这三类。 +version: 1.0.0 +--- + +# 编号族清退成语义标识 + +口径的唯一来源是仓库自带的编号门禁 `scripts/check_ids.py` 与规范 `docs/编号规范.md`。**不要凭 grep 估数、不要凭印象决定改哪几处、不要把「不做」只划掉一行。** + +## 口径:两套计数都要挑明,先问再比 + +门禁 `ck.refs` 与逐行站点数是**两套合法口径**,谁都没算错: + +| 口径 | 怎么数 | 用途 | +|---|---|---| +| 门禁 refs(去重) | 字面量 × 文件 去重 | 门禁趋势、批次验收线 | +| 逐行站点 | 每行每次出现 | **排工作量、拆单** | + +实测例:保护功能号一族「字面量 28 / 站点 184 / 文件 58」(`ck.refs`)与队友报的「219 站点 / 38 文件」并存且都对。队友报任何合并数(指纹、站点数)之前**先问口径**,不要拿自己的数去否他的数。 + +## Steps + +1. **确认范围与定调**:先读 `docs/编号规范.md` §4.1(族总表,穷举)与 §9(存量与批次),明确本族属主登记处(`owner_docs_glob`)与判据「有脚本按字面量匹配它」。清退 = 全仓改语义;停发 = 游标冻结、只减不增。 +2. **按族实测三口径(字面量 / 站点 / 文件),不用 grep 估数**: + ```bash + cd "D:/Code/DTU Code/STM32F4-Base" && ./.venv/Scripts/python.exe -c " + import sys; sys.path.insert(0, 'scripts') + import check_ids as ci + reg = ci.load_json(ci.REGISTRY_FILE, 'r'); al = ci.load_json(ci.ALIASES_FILE, 'a') + ck = ci.Checker(reg, al, verbose=False); ck.run() + # 族名以 scripts/id_registry.json 的登记名为准(如 'L-PROT'、'FEAT'、'TC'、'N') + for fam in reg['families']: + ck.refs(fam['name']) # 站点数 / 字面量数 / 文件数 + 逐字面量落点 + " + ``` + 把活族清单与每族数字记下来作为**动手前基线**(拆批次与派单依据)。新测试套件发号导致 `id-check` 校验 3 报悬空引用时,**先按号前缀聚合**定位是哪个套件/哪个族(`check_ids.py 2>&1 | grep -a '悬空引用' | ...` 前缀计数),别逐条读。 +3. **逐族先分类再动手**——不是每个命中的裸形都是编号,四类分治(实测 T1/T2/T3 的 41 处里只有部分是编号): + - 真编号 → 语义短语 / 功能名; + - 硬件位号(变压器 T1~T3 等)→ 把目录整目录登记进 `check_ids` 的 `excluded_paths`(如 `excluded_paths += tools/netlist_semantics/`),**不逐处改位号名**; + - Mermaid 流程图节点名 → 改语义名但**节点数与连线关系不变**,图结构不动; + - 数学记号 → 进噪声表;门禁自身的规则名(gitlint `T2 Title has trailing whitespace`)→ 不改。 + 分类前先把剩余每处原文打出来看(`grep -F` 逐文件),否则会把硬件资料改坏。 +4. **映射表由属主文档生成**:从属主文档取「编号 → 功能名/语义短语」全表(实测 42 个定义 / 28 个在用),改名走 `scripts/rename_ids.py`(映射表驱动 + **显式 scope** + **默认干跑** + 自动往 `scripts/id_aliases.json` 写 scoped 旧号→新号条目)。**绝不全局查找替换**:同一裸形字面量可能同时属多个族(实测 `S3` 三义),全局替换等于造新撞号。 +5. **按写域拆道并行派单**(代码与测试半 / 文档半 / 某功能面各一条道),同笔用 `send_message` 下发**统一改写口径**(措辞、是否动字符串字面量、生成器归属、冻结历史行怎么处理),避免各道自定措辞。看板任务只认 `team_task_update` 的 `task_id` / `expected_revision` / `action=reassign` / `owner` 四个字段。 +6. **门禁配置注释也是本族写入面**:`.pre-commit-config.yaml` 的注释里会挂着功能号(实测 `7h) ADC 采样有效性校验(L1-6)`、`7i) 保护定值模型(P1 / L5-1 载体)`、`规划 §3.3 L3-5`)。配置属 Lead 写域,Lead 自己改(注释不占钩子行为、不影响 51 条钩子口径),但**必须计入批次范围并在提交正文写清是 Lead 改的 config**。 +7. **每处替换断言命中数**,不做整文件正则批量改写(批量改写会静默漏改): + ```python + def sub(path, pairs): + p = pathlib.Path(path); t = p.read_text(encoding='utf-8') + for old, new, want in pairs: + got = t.count(old); assert got == want, (path, old, got, want) + t = t.replace(old, new) + p.write_text(t, encoding='utf-8') + ``` + **token 取最小形态**:文档里被反引号/加粗包住的号,替换单位取反引号形(`` `T4` ``)而**不含 `**` 标记**(第 225 行是 `**裸形 `T4`**`,按「裸形 `T4`」替就命中 0)。讲编号的文档改成**形态描述**(如「字母 T 紧跟数字 4」)能把不可判定绝对数还回余量:实测 4 处 → 982 降到 978,且 lesson 不丢。 +8. **同步更新门禁侧配置**:`check_ids` 的 `excluded_paths` 与噪声表按第 3 步的分类结果改,使剩余命中不再是「假编号」;新测试套件发号的悬空引用要先定**定义位**: + - 定义位**只能落一处**——注册表族(`scripts/id_registry.json`)或该套件说明文档,**两处并存比没有定义位更坏**;文档真写出来了(含判据与上板清单)就以文档为准、**撤族**; + - 要建族就照现有同类族克隆(`pattern` / `digit_width` / `owner_docs_glob` / `scope` / `cursor`),别自造字段; + - **抬游标前先确认游标是字符串形态**(`'cursor': 'TC-UIDATA-026'`)——整型比较会静默不生效(实测踩坑),用字符串解析或正则取序号再比。 +9. **生成器文本改动必须同笔重跑产物**:`tools/arch_diagram/gen_*.py` 里的 docstring/文案会进产物,`generated-fresh` 把生成器拷到临时目录重跑逐字节比对,**只改源不改产物必红**。这类站点整包转给生成器属主,「谁改谁跑生成器」,脚本 + 全部 svg 产物同一笔: + ```bash + ./.venv/Scripts/python.exe tools/arch_diagram/gen_net_instability_svg.py 2>&1 | tail -3 + python scripts/ci_local.py --only generated-fresh # PASS 后再目检 diff:只应文字变长、框宽自适应(526→551、右缘未越界、画布不变) + ``` + 不把该站点从批次里悄悄丢掉:登记为「待未完成半批」,收口时核对。 +10. **核队友交付**(按写域并行后必做): + - 文件清单**先展开真实路径再比**:按简称匹配会误报丢失(作者列 `phasor.py`/`adc_health.py` 等 8 个,实为 `test/host/<模块>/run_unit_test.py`);用 `git -c core.quotepath=false diff --name-only` 取全路径做集合差。 + - 合并指纹对不上先问口径:作者按**自定义顺序**算的数 Lead 侧复现不了(`d0a289b7` vs 报的 `a45e7e2b`)。改建 **Lead 自有逐文件 16 位基线**存 `ci-artifacts/lead_lane_b_files.json`,并把**跨道文件单列**(实测 `App/algo/README.md`)——autostash 后按逐文件比对,不假称对上别人的合并数。 +11. **分笔提交走真实 pre-commit 钩子**:`git add` 用显式清单。坑:`git add -- App/algo` 把文档道的 `App/algo/README.md`(26 处)带上车,正文按 48 只文件写就不准了 → 该笔未 push 时 `--amend` 挪到文档笔并补一句归属说明;`git add -A` 会把别道在飞文件卷进来(覆盖率钩子在他编译不过的半成品上报红)→ 撤出他人域后重跑。提交正文每行 ≤80(gitlint B1),别把 ASCII 双引号写成正文首字符。 +12. **收口只认门禁自身计数**,并核**只减不增**: + ```bash + ./.venv/Scripts/python.exe scripts/check_ids.py 2>&1 | grep -aE '扫描|可判定 |不可判定引用' | head -4 + ./.venv/Scripts/python.exe scripts/check_ids.py --strict 2>&1 | tail -1 # [OK] 校验 1~6 无错误 + ``` + 报告「字面量 8787→8299→7828」「不可判定绝对数 1161→984→982→978(上限 1166)」「可判定比例 88.14%(基线 86.0%,容差 0.1pp)」。**绝对数与比例两根线都要报**:把可判定引用改成散文会稀释比值,所以另有不可判定绝对数上限。 +13. **回写 `docs/编号规范.md`**:§4.1 族表(停发/待清栏)、§9 存量表与批次结果表(只减不增)、§9 清点数字按最终实测同步;表格若保留动手前的清点数,**明确标注「不是当前存量,是动手前基线」**,否则就是拿旧数当现状。变更记录加一行(含用户裁定原文)。 +14. **裁定「不做」也要写成可执行硬口径入档**(不是只划掉一行——「不做」最容易被读成「那还可以继续发号」): + - §4.1「已停发」栏新增该族一行:**游标冻结、现存号只减不增、新用例改用语义化测试函数名 + 文档表格行名**,并写清与「允许继续发号」栏为何不打脸; + - §9.1 批次段落写明裁定原文与理由(该族是唯一真被脚本按字面量匹配的族:C 断言标签 + 测试函数名 + 文档用例表三处同一号),并给出**将来真要清退的前置条件**:追溯器(`check_test_traceability.py`)先同时认新旧两种形态、并行核对通过,**不许先改文档再改代码**; + - 全文核「等你裁定 / 需你裁定」悬空表述残留数归 0。 + +## Pitfalls + +- **拿 grep 计数跟人吵口径**:先跑 Checker 拿实测三口径,再谈拆单;两套计数不一致不是有人算错(见上表)。 +- **全局查找替换**:跨族撞号(`S3` 三义)。必走 `rename_ids.py` 映射表 + 显式 scope + 干跑。 +- **未分类就动手**:把变压器位号、Mermaid 节点名、数学记号、gitlint 规则名当编号改坏硬件资料;位号目录进 `excluded_paths`,记号进噪声表。 +- **按简称匹配文件清单** → 误报 8 个文件丢失;比对前展开真实路径。 +- **复现不了队友的合并指纹就怀疑丢文件**:先问口径(自定义顺序不可复现),改落自有逐文件 16 位基线并把跨道文件单列。 +- **替换 token 含 `**` 加粗标记** → 命中数断言 0;取反引号最小形态。 +- **只改生成器文案不重跑产物** → `generated-fresh` 必红(实证 `0f7b5a87` 同类);「反正就是注释,不影响图」是错的,该文本进产物。 +- **`grep 'A\|B'` 空返回当文件干净**:交替竖线在多层 bash 引号里被吃掉,是探针坏不是文件干净 → 反证为空的搜索必须换 `grep -F` 复测。 +- **bash heredoc 里嵌 Python 单引号 / 多层引号** → `SyntaxError: unterminated`;长中文 python 一律 `fs` 落盘 `ci-artifacts/*.py` 再跑。 +- **一次 run_code 串 `5 × wait_agent(118000)` ≈ 590s 整段 abort、进度全丢**;且一次串多条长中文 `send_message` 也撑爆 120s(发出 1 条后 abort,剩余未发)。轮询上限 4×118s,长消息拆开发。 +- **游标整型比较静默不生效**(字符串形态);抬完用 `--strict` 与 plain 双验。 +- **`git add` 目录路径带进别道文件**、`git add -A` 卷进在飞改动;撤销自己的改动只按文件点名 `git restore scripts/id_registry.json`,不碰同树其它道文件。 +- **amend 被 gitlint B1 拦**:正文行 >80,别把 ASCII 双引号写成正文首字符。 +- **改「只动注释」要拿机器证据**:`@build` 0 错 0 警且 `Program Size` 与改前**逐字节同数**(实测三方互证),别口头承诺。 +- **冻结历史与原始证据不改**:boot 串口日志等采集现场是冻结证据,改了就是篡改;历史「修改记录」行按裁定保持改名后形态(号已全仓退役,留一个查无此号的引用比留改过名的条目更坏)。 +- **`*.uvprojx` 抖动不入库**。 + +## Verification + +1. `scripts/check_ids.py --strict` 与 plain **均 rc=0**(`[OK] 校验 1~6 无错误`),悬空引用为 0。 +2. 编号字面量与**不可判定引用绝对数**只降不升、不超 `undecidable_total_ceiling`;可判定比例不低于 `id_registry.json` 基线(86.0%)。 +3. 本族剩余站点复扫归零(`ck.refs` 逐字面量打印);除冻结历史/原始证据行外无残留。 +4. `python scripts/ci_local.py --only generated-fresh` PASS + 目检 svg diff 只有文案与框宽自适应;`check_doc_links.py --all` 通过。 +5. `@build`:`BUILD=0`、0 错 0 警、`Program Size` 与改前同数(零行为改动证据);相关主机组(如 `net_socket 457/457`)全绿。 +6. 真实 pre-commit 钩子全绿(如 `passed=33 failed=0`);提交笔内不含他道文件。 +7. 规范与台账已回写并复跑核数:自己先前写进规范的清点数字要么按最终实测改对,要么标明为动手前基线;「等你裁定」残留 = 0;「不做」裁定已在 §4.1 + §9.1 + 变更记录三处落地。 diff --git a/.dsh/skills/lvgl-simulator/SKILL.md b/.dsh/skills/lvgl-simulator/SKILL.md index 35a95969..9bc70595 100644 --- a/.dsh/skills/lvgl-simulator/SKILL.md +++ b/.dsh/skills/lvgl-simulator/SKILL.md @@ -33,7 +33,7 @@ PC 上编译运行与 MCU **同一份** UI 源码(CMake + MSVC + SDL2,x64, ## UI 改动回归 → 用 `ui-verify` skill(勿在此零散手跑) -5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 47 页黄金参考 + 往返池)→ `@build` → `coding-standard`。 +5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 53 页黄金参考 + 往返池)→ `@build` → `coding-standard`。 RemoDispBus 推流回归单独跑:`.venv\Scripts\python.exe simulator/tests/check_remo_sink.py`。 黄金参考 `tests/ref/` 是**本地生成物不入库**:换机后先 `check_content_pages.py --update` 生成,再 `regen_ref_png.py` 出目检 PNG。 diff --git a/AGENTS.md b/AGENTS.md index c512b4a2..244edc05 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,7 +92,7 @@ ## 提交门禁(pre-commit) -- 任一失败即拦截,共 **51 个钩子**(**口径 = YAML 解析后的 hook 条目数**:自查命令与「声明 / 文档 / 实跑」三数同源核对见 `docs/门禁说明/README.md` §2 首条——⚠ 直接 grep 配置里的声明行会多算一条**注释掉的**候选钩子(= 52),不是口径;**配置注释里也别复述那个 grep 字面量**,它会让文本计数再虚高 1(2026-10-27 实测踩过);加一条钩子就同批改 AGENTS 与门禁索引两处计数。2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条,2026-10-26 加 `generated-fresh` 一条,2026-10-27 加 `nested-comment` 一条;**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败;**2026-10-26 方案 B 同批加了第二根**:不可判定引用**绝对数**超过 `undecidable_total_ceiling` 即失败,因为比值会被"把可判定的引用改成散文"稀释(详见 `docs/门禁说明/id-check.md` §7.6))+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 **22 根专属钩子**(`-unit-test` 后缀 = 有独立 `files:` 触发面、需要秒级归因的那个子集;2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六根)——**这是口径不是缺口**:`test/host/` 实测 40 套带 `run_unit_test.py`,其余 **18 套**(含 `test/host/ui_sink`)没有专属钩子,但每次提交都被 `test-coverage` 总闸全量真跑并守基线回退,**失败照样拦**,只是归因落在总闸输出上;**没有专属钩子 ≠ 没被跑**,18 套台账与补钩子的判据 → `docs/门禁说明/host-unit-tests.md` §6 第 4 条。**Lead 裁定 2026-10-27:不补第 23 根**——读「这套跑没跑」去看总闸输出,别数钩子(原写「23 个 / 含 ui-sink 七个」是双重错:既把 22 说成 23,又宣称 ui-sink 有钩子——配置历史里 `ui-sink-unit-test` 从未存在)+ `test-coverage`(**覆盖率总闸**:跑全部 40 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ `generated-fresh`(**生成器幂等性**:把 `tools/menu_gen/gen_menu.py`、中文字库链、`tools/arch_diagram/gen_*.py` 拷进**系统临时目录**重跑,与工作区**逐字节**必须一致——钩子只读、不写仓库也不 stash 别人在飞的改动;实证 `0f7b5a87` 改了模板文字没重跑生成物,接线前实测另有 5 张 `docs/images/` 配图已漂 → 钉成只减不增基线,**业务生成物零豁免面** → `docs/门禁说明/generated-fresh.md`)+ `nested-comment`(**块注释嵌套**:注释体内再出现 /* 会被 ARMCC 报 9-D,且**按包含者数量刷屏**、只在重编译该 TU 时才暴露;本仓诱因就是「在注释里写作用域/通配路径」,改法是写全目录或用尖括号 ;接线当日实扫 403 个 .c/.h,新发 0、存量 2 处(`test/host/common/` 两份假件头)钉只减不增基线 → `docs/门禁说明/nested-comment.md`)+ `mem-budget`(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ `font-charset`(UI 字符集差集检查,防缺字白框)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 47 页黄金参考)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**)) +- 任一失败即拦截,共 **51 个钩子**(**口径 = YAML 解析后的 hook 条目数**:自查命令与「声明 / 文档 / 实跑」三数同源核对见 `docs/门禁说明/README.md` §2 首条——⚠ 直接 grep 配置里的声明行会多算一条**注释掉的**候选钩子(= 52),不是口径;**配置注释里也别复述那个 grep 字面量**,它会让文本计数再虚高 1(2026-10-27 实测踩过);加一条钩子就同批改 AGENTS 与门禁索引两处计数。2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条,2026-10-26 加 `generated-fresh` 一条,2026-10-27 加 `nested-comment` 一条;**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check`(**编号门禁**,`docs/编号规范.md` §8 八项校验;**已关闸为【增量拦截】**:只拦 `scripts/id_known_backlog.json` 台账之外的新错误级问题,存量按指纹豁免、台账只减不增;另有一根恒生效的线——可判定比例跌破 `id_registry.json` 基线即失败;**2026-10-26 方案 B 同批加了第二根**:不可判定引用**绝对数**超过 `undecidable_total_ceiling` 即失败,因为比值会被"把可判定的引用改成散文"稀释(详见 `docs/门禁说明/id-check.md` §7.6))+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + 主机侧单测 **22 根专属钩子**(`-unit-test` 后缀 = 有独立 `files:` 触发面、需要秒级归因的那个子集;2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六根)——**这是口径不是缺口**:`test/host/` 实测 40 套带 `run_unit_test.py`,其余 **18 套**(含 `test/host/ui_sink`)没有专属钩子,但每次提交都被 `test-coverage` 总闸全量真跑并守基线回退,**失败照样拦**,只是归因落在总闸输出上;**没有专属钩子 ≠ 没被跑**,18 套台账与补钩子的判据 → `docs/门禁说明/host-unit-tests.md` §6 第 4 条。**Lead 裁定 2026-10-27:不补第 23 根**——读「这套跑没跑」去看总闸输出,别数钩子(原写「23 个 / 含 ui-sink 七个」是双重错:既把 22 说成 23,又宣称 ui-sink 有钩子——配置历史里 `ui-sink-unit-test` 从未存在)+ `test-coverage`(**覆盖率总闸**:跑全部 40 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ `generated-fresh`(**生成器幂等性**:把 `tools/menu_gen/gen_menu.py`、中文字库链、`tools/arch_diagram/gen_*.py` 拷进**系统临时目录**重跑,与工作区**逐字节**必须一致——钩子只读、不写仓库也不 stash 别人在飞的改动;实证 `0f7b5a87` 改了模板文字没重跑生成物,接线前实测另有 5 张 `docs/images/` 配图已漂 → 钉成只减不增基线,**业务生成物零豁免面** → `docs/门禁说明/generated-fresh.md`)+ `nested-comment`(**块注释嵌套**:注释体内再出现 /* 会被 ARMCC 报 9-D,且**按包含者数量刷屏**、只在重编译该 TU 时才暴露;本仓诱因就是「在注释里写作用域/通配路径」,改法是写全目录或用尖括号 ;接线当日实扫 403 个 .c/.h,新发 0、存量 2 处(`test/host/common/` 两份假件头)钉只减不增基线 → `docs/门禁说明/nested-comment.md`)+ `mem-budget`(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ `font-charset`(UI 字符集差集检查,防缺字白框)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 53 页黄金参考)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**)) - **范围 = 项目代码**:第三方(`Lib/`、`Middlewares/`、CMSIS、HAL、`lftpd`、`dhara`、`MDK-ARM`)排除;`cppcheck`/`coding-standard` 另排除 `test/`,规范检查再排除 CubeMX 生成的 `Src/`/`Inc/`。**`simulator/` 已纳入全部钩子**(2026-10-11)——生成物只靠 `.gitignore` 挡,**不在 `exclude` 里写目录名**(教训见 `docs/代码规范/pre-commit使用说明.md` §5.5) - ⚠ **pre-commit 4.x 已移除 `always: true`**(实测对 `local` 钩子只告警 `Unexpected key(s)` 并跳过)→ 想"总是跑"的钩子**必须让 `files:` 覆盖到所有会触发它的文件**,没有兜底开关 - **改写型钩子(`trailing-whitespace` + `end-of-file-fixer`)豁免 CubeMX/Keil 生成文件**(配置里 YAML 锚点 `&cubemx-generated`);⚠ **只读检查不受影响**:`cppcheck` / 乱码 / `cubemx-survival` / `task-table` / `mem-numbers` 仍覆盖 `Src/`、`Inc/` @@ -176,7 +176,7 @@ | 存储栈(FatFs / dhara / lftpd / 两条 FatFs 陷阱) | `docs/架构设计/STM32F4-Base存储架构说明.md` | | RAM / 栈预算 / 容量台账 | `docs/内存管理/RAM使用分析与优化方案.md`、`内存内容分配策略.md` | | **每个提交门禁一篇详解**(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径) | `docs/门禁说明/README.md`(51 个钩子 → 27 篇索引 + 1 指针) | -| **要不要给某个东西发编号**(默认:不发,写语义短语)/ 允许用号的 8 个族 / `id-check` 八项校验 | `docs/编号规范.md`(§1 先问三句、§4.1 族总表〔穷举〕、§8 门禁与实测基线) | +| **要不要给某个东西发编号**(默认:不发,写语义短语)/ 允许用号的 7 个族(`TC-*` 已停发——「停发」不等于「暂缓」,也不等于「可以清号」)/ `id-check` 八项校验 | `docs/编号规范.md`(§1 先问三句、§4.1 族总表〔穷举〕与『已停发』栏、§8 门禁与实测基线) | | 提交门禁通用机制(阶段 / stash 陷阱 / 触发面 / 增量拦截三段式) | `docs/代码规范/pre-commit使用说明.md` §2 | | 代码规范 V1.0 / cppcheck 口径 | `docs/代码规范/嵌入式C语言代码规范_V1.0.md`、`cppcheck使用说明.md` | | 自动化测试 / 实机测试 / 板级套件 | `docs/测试说明/自动化测试流程.md`、`实机测试实施指南.md`、`board-test-suite` skill | diff --git a/docs/UI设计/UI_数据供给设计说明.md b/docs/UI设计/UI_数据供给设计说明.md index 3811f224..b9c3dea0 100644 --- a/docs/UI设计/UI_数据供给设计说明.md +++ b/docs/UI设计/UI_数据供给设计说明.md @@ -669,7 +669,7 @@ typedef struct { | 4 | 未标定时页面明确标识(不把码值当伏特) | 目检 + 文本断言(`eng=0` 时带标记) | | 5 | 首帧前显示 `--` 而非 `0.000` | 模拟器:不 publish 直接开页,断言文本 | | 6 | 定值整定全链闭环 | 就地改 → 保存 → 重启 → 值保持 + 校验和自检通过 + 主站 C_RD 一致。**半收口(2026-10-26)**:模拟器侧「改 → 保存 → OK → 回首屏新值已在帧里」已由像素 + 文本断言覆盖(黄金用例 `sparam_save` / `sparam_landed`,越界侧 `sparam_oob`);**重启保持与主站 C_RD 一致只能上板**——模拟器没有 NAND,`apply()` 到 `prot_set_set_sealed()` 就是终点,这条差距已登记在 `docs/测试说明/模拟器差距清单.md`;另注意 40 项里有 13 项 `comm_addr=0`(纯本地项),主站侧根本读不到,不能拿 C_RD 当它们的判据 | -| 7 | 像素回归稳定 | `ui-golden` 连续两次运行零差异。**已验(2026-10-26,当时下限为 27 页)**:27 页连跑两次逐字节一致(含整定 9 态)——**集合现已扩至 47 页**,双次一致需按同口径重跑回填;轮询定时器不影响——无头模式不调 `lv_timer_handler()`(§9.2 注 6) | +| 7 | 像素回归稳定 | `ui-golden` 连续两次运行零差异。**已验(2026-10-26,当时下限为 27 页)**:27 页连跑两次逐字节一致(含整定 9 态)——**集合现已扩至 53 页**,双次一致需按同口径重跑回填;轮询定时器不影响——无头模式不调 `lv_timer_handler()`(§9.2 注 6) | | 9 | 「写不进去」与「改了不动作」两条事实互不冒充 | 反向文本断言(`check_content_pages.py` 的 `ABSENT`:查看页与只读态不得出现「未标定」徽标与「保存」行)+ 台架:未制造该态——要它必须改坏 /prot_set.bin(破坏产品配置存储,用户裁定 2026-10-26 不做),故 LOCK 态目前只有代码级证据与三重反证(ENTER 被正常消费、遥信 YX31=OFF、开机 CORRUPT=0),**没有像素证据** | | 8 | 主机单测覆盖数据模块 | `test/host/ui_data` 全绿并挂 pre-commit 钩子 | diff --git a/docs/UI设计/UI_自动化测试流程.md b/docs/UI设计/UI_自动化测试流程.md index e2913477..45e45274 100644 --- a/docs/UI设计/UI_自动化测试流程.md +++ b/docs/UI设计/UI_自动化测试流程.md @@ -26,7 +26,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | 步 | 脚本/命令 | 门禁内容 | 预期输出 | |---|---|---|---| | 1 | `simulator/tests/check_menu_rows.py` | 菜单 8 状态:选中行白带 / 非选行黑底白字 / 框体 1px 边框 / 往返像素一致(MENU-STATE-03==MENU-STATE-01、MENU-STATE-06==MENU-STATE-01、MENU-STATE-07==MENU-STATE-02)+ MENU-STATE-00 池基线 | `PASS: 8 状态全部断言通过(8 图,池基线 5368 B)` | -| 2 | `simulator/tests/check_content_pages.py` | ① 文本级断言(字形模板匹配,310 条,独立于 `ref/`)→ ② 47 页截图与 `ref/` 黄金参考**逐字节**比对 + 开页-关页往返池水位 | `PASS: 47 内容页 + 往返池水位全部通过`(文本断言先于像素比对,失败即中止) | +| 2 | `simulator/tests/check_content_pages.py` | ① 文本级断言(字形模板匹配,310 条,独立于 `ref/`)→ ② 53 页截图与 `ref/` 黄金参考**逐字节**比对 + 开页-关页往返池水位 | `PASS: 53 内容页 + 往返池水位全部通过`(文本断言先于像素比对,失败即中止) | | 3 | `keil-build` skill(`keil_build.py`) | MCU 端编译(UI 源码双端共用,改一端必须过另一端) | 退出码 0 = BUILD_OK | | 4 | `scripts/check_coding_standard.py` | 代码规范 V1.0 机械检查 | PASS | @@ -49,7 +49,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | `POOL_AFTER_ROUNDTRIP` | check_content_pages.py | **5448 B** | 开页-关页往返后水位 = 基线 + 一次性残留 80 B | > 水位历史:4784 →(2026-09-14 待机模块常驻 +656)5440 / 5520 →(2026-10-11 裁剪 LVGL 控件类 −72)5368 / 5448。 -> 后一次漂移已核实为**有意且更优**:像素逐字节未变(**裁剪当时黄金参考只有 11 页**,在裁剪**前**生成、裁剪后全部一致;该集合现已扩至 47 页), +> 后一次漂移已核实为**有意且更优**:像素逐字节未变(**裁剪当时黄金参考只有 11 页**,在裁剪**前**生成、裁剪后全部一致;该集合现已扩至 53 页), > 且一次性残留仍是 80 B(5448 − 5368)→ 泄漏特征未变,只是 `lv_theme_apply` 不再为已不存在的控件类登记样式条目。 **漂移处理**:菜单/页面结构或样式变更导致门禁失败时,先确认新值合理(残留分解见 @@ -58,7 +58,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 ## 四、黄金参考(入库)与目检 PNG(本地产物) -`simulator/tests/ref/*.bin`(像素黄金参考,47 页 × 3200 B)**随仓库入库**;派生的 `.png` +`simulator/tests/ref/*.bin`(像素黄金参考,53 页 × 3200 B)**随仓库入库**;派生的 `.png` 目检图仍是本地产物(`.gitignore`)。入库的理由:比对是**逐字节**的,基线不入库时任何新克隆 或新工作树跑 `ui-golden` 都必然 FAIL(缺基准),这条门禁就只在"有老副本的机器"上真实存在。 可复现性实测(2026-10-22):两份各自独立构建的 `lvgl_sim`(不同构建目录、相隔 11 天的代码) @@ -145,7 +145,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 ### 7.3 用法与门禁档位 ```powershell -# 认屏快检:47 张黄金参考(+ 若有台架留档帧),秒级,不需要构建产物 +# 认屏快检:53 张黄金参考(+ 若有台架留档帧),秒级,不需要构建产物 & '.venv\Scripts\python.exe' scripts/board_ui_nav.py --selftest-quick # 完整自检:再加全绑定页闭环导航(需模拟器可执行文件,约 30 s) & '.venv\Scripts\python.exe' scripts/board_ui_nav.py --selftest @@ -219,6 +219,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 过密码口径;三张证据 PNG 落 `boot_log_data/` | | 2026-10-22 | **`ref/*.bin` 像素黄金参考改为入库**(撤销 2026-09-10 的"整目录不入库"):`.gitignore` 由忽略整目录收窄为只忽略 `ref/*.png`。动因是这条门禁不可移植——新克隆/新工作树里基准必然缺失,`ui-golden` 报「缺黄金参考」而不是回归结论;本仓实测两次同类踩坑(并行工作树里第一次跑必失败,只能现场 `--update` 造基准,而"现场造的基准"恰好失去意义)。入库前做了可复现性验证:主树(`ref/` 生成于 2026-10-11)与新工作树各自独立构建 `lvgl_sim`,11 个 `.bin` SHA-256 全部一致。§四 与"何时不该更新"随之改写:`--update` = 改基准,会进 `git diff`,须逐页过目并在提交正文说明 | | 2026-10-27 | 内容页黄金参考 **40 → 47 页**(遥测死区与系数 2 页落地,`simulator/tests/ref/` 实测 47 张:7 张新增、存量 40 张逐字节零漂移);本文件的当前态计数同步改 47,历史行 27 → 40 原样保留 | +| 2026-10-27 | 内容页黄金参考 **47 → 53 页**(通讯设置页落地并绑 `#page=comm`;判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53 张,`git diff --name-status 31ca4a1f HEAD -- simulator/tests/ref` 实测只有 6 个 `A`、零 `M` ⇒ 存量 47 张逐字节零漂移);本文件的当前态计数已同步 | | 2026-10-26 | **内容页页数口径统一为 27**:正文里残留的「11 页 / 其余 10 页 / 18 张」按现状改正(判据本体 = `simulator/tests/ref/shot_*.bin` 入库实数 27 个、`check_content_pages.py` 的 `PAGES` 27 项,含整定 9 态);下方变更记录里的「11 页」是当时事实,按历史冻结不动。同类陈述同步在 `测试开发标准流程.md`、`自动化测试流程.md`、`主机单测覆盖率口径.md`、`内容页架构.md`、`simulator/tests/README.md`、`scripts/board_ui_nav.py` 改正 | | 2026-10-26 | **文本级断言规模口径修正**:门禁表里的「50 条」是 2026-09-10 首版数字,现值 **242 条 / 覆盖 27 页中的 24 页**(`pwd` `pwe` `pwc` 三张密码态只进像素比对、未登记文本断言);正文的文本层说明补写了覆盖面与这条盲区。下方变更记录里的 50 条属历史,不动 | | 2026-10-26 | **内容页黄金参考下限 27 → 40 页**(UI 三页落地,机器实测:`simulator/tests/ref/` 入库 `shot_*.bin` 40 个、`check_content_pages.py` 的 `PAGES` 40 项);文本断言随之 **242 → 310 条**、覆盖 **40 页中的 37 页**(`pwd` `pwe` `pwc` 三张密码态仍只进像素比对、无文本断言);27 张旧参考逐字节未变,往返池 5448 B 与菜单常驻堆 5368 B 均未变,字库仍 183 字。本文其余各处与 `AGENTS.md`、`docs/门禁说明/`、`simulator/tests/README.md`、`docs/测试说明/` 三篇同批改齐;**历史行里的 27 / 11 一律保留**(当时下限就是那个数,不改数造连续性)| diff --git a/docs/UI设计/内容页架构.md b/docs/UI设计/内容页架构.md index 4175b8d2..20d8e81e 100644 --- a/docs/UI设计/内容页架构.md +++ b/docs/UI设计/内容页架构.md @@ -453,7 +453,7 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash ## 八、验证与回归 - **门禁**(`ui-verify` skill / `UI_自动化测试流程.md`): - 0) 模拟器构建 0 警告 → 1) 菜单 8 状态像素回归 + 池基线 → 2) 内容页文本断言 + 47 页黄金参考逐字节比对 + 0) 模拟器构建 0 警告 → 1) 菜单 8 状态像素回归 + 池基线 → 2) 内容页文本断言 + 53 页黄金参考逐字节比对 (网口设置 4 态 + 密码页 4 态等)+ 开页-关页往返水位 → 3) Keil 构建 0 错 0 警 → 4) 代码规范检查 - **门禁常量**(脚本头部):`MENU_POOL_BASELINE = 5368 B`、`POOL_AFTER_ROUNDTRIP = 5448 B` - **失败处置**:像素不一致 = 代码 bug(修代码,**禁止**刷参考掩盖);**有意**改视觉 → @@ -481,6 +481,7 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash | 2026-09-15 | 网口设置页保存行改**异步交接**(P1-3 跨任务竞态修复,配置引擎属主 = defaultTask):保存行 ENTER → `net_cfg_save_request()`(置"待保存"标志 + `SYS_EVT_CFG_SAVE` 唤醒属主任务,UI 不再直接触碰 cfg_file 引擎);写盘与"立即应用"由 defaultTask 的 `net_cfg_save_run()` 执行(成功 → OK 并触发 `SYS_EVT_NET_RECONFIG` 免重启重建;失败 → ERR 不触发应用);行值改随交接状态 `net_cfg_save_state()` 显示 ENTER/WAIT/OK/ERR(原为页面局部 `s_saved` 同步结果,删除);无头自检(`--keys`)在注键后同步完成交接保持终态一致;五门禁全绿(像素/文本与改前一致,池水位不变) | | 2026-10-22 | §八 失败处置口径更新:`ref/*.bin` 黄金参考已入库,刷参考会进 `git diff`、必须随改动一并提交并说明理由(详见 `UI_自动化测试流程.md` §四) | | 2026-10-27 | 内容页黄金参考 **40 → 47 页**(遥测死区与系数 2 页落地,`simulator/tests/ref/` 实测 47 张:7 张新增、存量 40 张逐字节零漂移);本文件的当前态计数同步改 47,历史行 27 → 40 原样保留 | +| 2026-10-27 | 内容页黄金参考 **47 → 53 页**(通讯设置页落地并绑 `#page=comm`;判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53 张,`git diff --name-status 31ca4a1f HEAD -- simulator/tests/ref` 实测只有 6 个 `A`、零 `M` ⇒ 存量 47 张逐字节零漂移);本文件的当前态计数已同步 | | 2026-10-26 | 验证与回归段的页数口径修正为 **27 页**黄金参考(`simulator/tests/ref/` 入库 27 个 `shot_*.bin`,含整定 9 态);上方变更记录里的「11 页」是当时事实不动 | | 2026-10-26 | 门禁常量修正为 5368 B / 5448 B(= 两个回归脚本头部现值);本文件正文表早已是 5368/5448,只这一处残留 2026-10-11 裁剪 LVGL 控件类之前的 4784/4864 | | 2026-10-26 | 验证与回归段的黄金参考页数 **27 → 40 页**(UI 三页落地后 `simulator/tests/ref/` 实数 40 张);上方变更记录里的 11 / 27 是各当时下限,保留 | diff --git a/docs/UI设计/页面扩充详细方案.md b/docs/UI设计/页面扩充详细方案.md index 7397f315..442143e9 100644 --- a/docs/UI设计/页面扩充详细方案.md +++ b/docs/UI设计/页面扩充详细方案.md @@ -624,7 +624,7 @@ SNTP 客户端 ──────→ 批4 SNTP设置 | 4 | 无源点显示 `--` | 遥信量无效点、直流量直流通道 | | 5 | 定值整定闭环 | 改 → 保存 → 重启 → 值保持 + 校验和自检通过 + 主站 C_RD 一致。**2026-10-26 半收口**:模拟器侧「改 → 保存 → `OK` → 回首屏新值已在帧里」与「越界被拒、值不落盘」已由像素 + 文本断言覆盖(`sparam_save` / `sparam_landed` / `sparam_oob`);**重启保持与 C_RD 一致只能上板**(模拟器无 NAND)。另注意 40 项里 **13 项 `comm_addr = 0`**(投退、返回系数、过频/低频段、全局软压板)主站不可寻址,不能拿 C_RD 当它们的判据 | | 6 | 分层门禁无新增 | `check_layer_deps.py` 退出码 0,基线 6 → 3。**现状:3**(记录 reader 已削 3 条;写路径走注入式 ops 函数表,未新增 include,见 `docs/UI设计/UI_数据供给设计说明.md` §6.2) | -| 7 | 像素回归稳定 | `ui-golden` 连续两次零差异。**已验(2026-10-26)**:27 页连跑两次逐字节一致——**该下限此后已扩至 47 页**(新增 clock / reset / clear 系列 13 张),47 页上的双次逐字节一致需按同口径重跑一次再回填本行 | +| 7 | 像素回归稳定 | `ui-golden` 连续两次零差异。**已验(2026-10-26)**:27 页连跑两次逐字节一致——**该下限此后已扩至 53 页**(新增 clock / reset / clear 系列 13 张),53 页上的双次逐字节一致需按同口径重跑一次再回填本行 | | 8 | 每个新页有 golden 参考 | `simulator/tests/ref/` 新增对应 `.bin`。**整定 4 页已有 9 张**(含保存/越界/落定/回绕各态);参考图入库,改它 = 改判据,须逐页过目 | --- diff --git a/docs/UI设计/页面补齐规划.md b/docs/UI设计/页面补齐规划.md index cbd198b7..fe002817 100644 --- a/docs/UI设计/页面补齐规划.md +++ b/docs/UI设计/页面补齐规划.md @@ -27,7 +27,7 @@ ### 2.1 规模 > ⚠ **本表是 2026-10-23 立项当天快照**(节点 42 / 有内容页叶子 6 / 空叶子 27 / 黄金参考 11 份均为当日实数,按"完成即冻结"不回改)。 -> 页面扩充落地后现值已变——黄金参考现为 47 份(`simulator/tests/ref/` 入库的 `shot_*.bin` 实数),现状口径见 +> 页面扩充落地后现值已变——黄金参考现为 53 份(`simulator/tests/ref/` 入库的 `shot_*.bin` 实数),现状口径见 > `docs/UI设计/页面扩充详细方案.md` 与 `docs/UI设计/UI_数据供给设计说明.md`。 | 项 | 数 | diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index ef47a2b0..40380e13 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -446,7 +446,7 @@ sequenceDiagram ### 5.9 ADC 采集(FR-ADC)🔶 采集链路已实现(B1);保护/遥测算法与上行待实现 - **需求(B0 已确认,2026-10-10)**:**持续采集** 8 路电气量(Ua/Ub/Uc/U0、Ia/Ib/Ic/I0,**1000 Hz / 20 点/周波**),经保护/遥测算法后**上送 IEC 60870-5-104**(以太网 TCP 2404;遥测上送短浮点、采集上传系数 = 1)。 -- **现状(采集链路 B1 + L1 已实现,2026-10-10)**:启动序列调用一次 `tpafe5160_init()`;**运行期周期 CONVST 重触发已由 TIM6 更新中断(1 kHz)接管**(`Src/main.c` 的 `HAL_TIM_PeriodElapsedCallback`,2026-10-10)+ `adcTask` 经 L1 层 `adc_input_poll()` 消费(`tpafe5160_snapshot()` 8 通道 + STM32 ADC1 9 通道扫描)+ 帧统计,台架 `@test` 确认 `frame_seq` 严格 1000 Hz 递增、`frst_err=0`、`adc1_rc=0` / `adc1_err=0`;STM32 侧 ADC1 **消费者已落地**(`App/algo/adc_input.c` 的 `stm32_scan()`,「STM32 ADC1 启动与扫描」/L1-4,2026-10-10),受双转换器无共同采样触发所限,仅逐通道独立监视。 +- **现状(采集链路 B1 + L1 已实现,2026-10-10)**:启动序列调用一次 `tpafe5160_init()`;**运行期周期 CONVST 重触发已由 TIM6 更新中断(1 kHz)接管**(`Src/main.c` 的 `HAL_TIM_PeriodElapsedCallback`,2026-10-10)+ `adcTask` 经 L1 层 `adc_input_poll()` 消费(`tpafe5160_snapshot()` 8 通道 + STM32 ADC1 9 通道扫描)+ 帧统计,台架 `@test` 确认 `frame_seq` 严格 1000 Hz 递增、`frst_err=0`、`adc1_rc=0` / `adc1_err=0`;STM32 侧 ADC1 **消费者已落地**(`App/algo/adc_input.c` 的 `stm32_scan()`,「STM32 ADC1 启动与扫描」,2026-10-10),受双转换器无共同采样触发所限,仅逐通道独立监视。 - **后果**:上电后仅采集 1 个样本,此后数据永久陈旧。 - **B0 结论**(2026-10-10,详见 `docs/算法/继电保护功能复现规划.md` §1.5):① 持续采集 ② 1000 Hz(20 点/周波),通道 = 4U+4I(点表 `4011~4018`)③ 上行 **IEC 60870-5-104**(2404,非 ModBus RTU 上行)④ ADC 采集链路为遥测/保护的必需来源 → **常驻编译**;原 `APP_ADC_ENABLE` 占位宏零消费点,已于 2026-10-22 删除(将来若要裁剪,需在真实初始化点接入门控并登记进 `App/app/app_cfg.h`)。 - **实现约束**(见 §9):周期 CONVST 重触发 + `adc_task` 消费 `tpafe5160_snapshot()`;**原子快照 API 已就位**(2026-10-10 修三条采集契约缺陷,旧的 `get_buf` / `data_ready` / `clear_ready` 已删 → 取数不再撕裂、重触发不再丢帧、芯片不驱动总线会被 `init()` 首帧自检抓到);栈预算核算;V0.1 通道映射已固化 → `docs/硬件平台/ADC通道映射说明.md`(13/17 路置换;语义 UA/UB/UC/UN、IA/IB/IC/IN、V_ZL2=温度,其余备用;网名 ADC17 = PC4 用 `ADC_CHANNEL_14`,**不是** `ADC_CHANNEL_12`)。⚠ **8 路电气量分属 TPAFE / STM32 两片转换器属 V0.1 硬件缺陷**(跨转换器无同一时刻性、精度与量程不一致,登记簿条目「双转换器无共同采样触发」,V0.2 改版要求全部走 TPAFE)→ 在 V0.1 上主板侧只做**逐通道独立监视**,功率 / 功率因数 / 相角 / 矢量类量一律取子板计量模块结果,不得由主板混算。 @@ -772,7 +772,7 @@ cfg-file-unit-test、test-suite-switch、gitlint。 | 缺口 | 现状 | 影响 | |---|---|---| -| **ADC 采集链路** | ✅ **采集链路已实现(B1 + L1,2026-10-10)**:采样节拍由 TIM6 更新中断(1 kHz)硬件触发;`adcTask` 经 L1 层 `adc_input_poll()` 消费(TPAFE 8 通道快照 + **STM32 ADC1 9 通道扫描**),台架 `@test` 闭环(1000 Hz、`adc1_rc=0`);取数按语义(「STM32 ADC1 启动与扫描」/L1-4,双转换器无共同采样触发下逐通道独立监视) | 数据路径已通;剩余为上层保护/遥测算法与 104 上行 | +| **ADC 采集链路** | ✅ **采集链路已实现(B1 + L1,2026-10-10)**:采样节拍由 TIM6 更新中断(1 kHz)硬件触发;`adcTask` 经 L1 层 `adc_input_poll()` 消费(TPAFE 8 通道快照 + **STM32 ADC1 9 通道扫描**),台架 `@test` 闭环(1000 Hz、`adc1_rc=0`);取数按语义(「STM32 ADC1 启动与扫描」,双转换器无共同采样触发下逐通道独立监视) | 数据路径已通;剩余为上层保护/遥测算法与 104 上行 | | **RS-485 采集链路** | `rs485_task.c` 空桩;`App/protocol/` 仅 README | 无 RS-485 数据收发(B0 定:**对下 ModBus RTU 采集**) | | **DTU 核心转发职责** | **B0 已定(2026-10-10)**:上行 **IEC 60870-5-104**(以太网 TCP 2404,遥测短浮点系数 1);RS-485 对下 ModBus RTU 采集 | 转发目标已明确,待实现 | diff --git a/docs/测试说明/主机单测覆盖率口径.md b/docs/测试说明/主机单测覆盖率口径.md index 40909357..99c9c837 100644 --- a/docs/测试说明/主机单测覆盖率口径.md +++ b/docs/测试说明/主机单测覆盖率口径.md @@ -704,7 +704,7 @@ uint16_t ch395f_link_change_count(void); | 类别 | 体量 | 为什么不做 | 那它靠什么把关 | |---|---|---|---| | 芯片驱动(寄存器/时序) | `ch395f.c`(801)、`gd5f2gq5ue.c`(689)、`sd2506.c`(264)、`tpafe5160.c`(157)、`rs485.c`(79) = **1,990 行** | 把 HAL 与寄存器假到底 ≈ **重写一遍驱动**,测的是自己的桩而不是硬件行为(SPI 分频必须 8、DMA 流、I2C 时序在桩里怎么编都"对") | 板级套件(`board-test-suite` skill)+ `@test` 真机日志 + 4 个 Fault 现场;陷阱逐条登记在 `docs/问题记录/CH395F_Trap_Records.md`、`docs/问题记录/GD5F2GQ5UE_Trap_Records.md`、`docs/问题记录/SD2506_Trap_Records.md` | -| UI 侧 | 17 个 `.c` = **1,942 行** | 与 PC 模拟器是**同一份源码**,行为回归已由 `ui-golden`(菜单 8 状态 + 内容页 47 页像素黄金参考)覆盖;再写单测是第三份重复投资 | `ui-verify` skill 五步 + `docs/UI设计/UI_自动化测试流程.md` | +| UI 侧 | 17 个 `.c` = **1,942 行** | 与 PC 模拟器是**同一份源码**,行为回归已由 `ui-golden`(菜单 8 状态 + 内容页 53 页像素黄金参考)覆盖;再写单测是第三份重复投资 | `ui-verify` skill 五步 + `docs/UI设计/UI_自动化测试流程.md` | | 任务编排 | `default_task.c`(281)、`adc_task.c`(171)、`net_task.c`(123)、`protect_task.c`(90)、`ui_task.c`(71)、`rs485_task.c`(11) = **747 行** | 每个只有 4~5 个函数,主体是"按顺序调别人";真逻辑在被调模块里(那些已测或在 P1/P2)。给编排层写单测最容易写成"断言我调了几次"的自证 | 真机启动锚点计数 + IWDG `required_mask`(`@test`)+ `docs/架构设计/项目启动顺序管理.md` | | 内核前装配 | `app_main.c`(135) | 被测对象本身就是"`osKernelStart` 之前不许调内核 API"的时序,主机侧无法忠实复现(PC 上没有那个约束) | 同上 + `cubemx-survival` 审计 | @@ -772,5 +772,6 @@ P2 5 个 2,226 行、P3 29 个 4,814 行明确不做**(四条对得上:13+5+ 详见 §9「第 3 轮」与 `docs/子系统设计/运行与自检记录设计说明.md` §4.4;合回主干后以 `4288658` 回填内存台账,主干 `pre-commit run --all-files` 39 项全绿。 | | 2026-10-27 | 内容页黄金参考 **40 → 47 页**(遥测死区与系数 2 页落地,`simulator/tests/ref/` 实测 47 张:7 张新增、存量 40 张逐字节零漂移);本文件的当前态计数同步改 47,历史行 27 → 40 原样保留 | +| 2026-10-27 | 内容页黄金参考 **47 → 53 页**(通讯设置页落地并绑 `#page=comm`;判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53 张,`git diff --name-status 31ca4a1f HEAD -- simulator/tests/ref` 实测只有 6 个 `A`、零 `M` ⇒ 存量 47 张逐字节零漂移);本文件的当前态计数已同步 | | 2026-10-26 | UI 侧「不做」理由里的页数口径修正为 **内容页 27 页**(原写 18,与 `simulator/tests/ref/` 入库的 27 个 `shot_*.bin` 不符)| | 2026-10-26 | UI 侧「不做」理由里的黄金参考页数 **27 → 40 页**(与 `simulator/tests/ref/` 入库 40 个 `shot_*.bin` 对齐);历史行保留 | diff --git a/docs/测试说明/测试开发标准流程.md b/docs/测试说明/测试开发标准流程.md index f5b799aa..890291cb 100644 --- a/docs/测试说明/测试开发标准流程.md +++ b/docs/测试说明/测试开发标准流程.md @@ -373,7 +373,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | 层 | 载体 | 抓住什么 | |---|---|---| | 文本层 | `check_content_pages.py` 字形模板匹配(310 条、覆盖当时 37 页,解析字体 cmap / glyph_dsc / bitmap) | 缺字显示空白 / 标签写错等**静默失败**(不依赖 `ref/`,先于像素比对运行、失败即中止防误刷参考) | -| 像素层 | 47 内容页 + 菜单 8 状态,与黄金参考逐字节比对 | 几何 / 样式 / 光标反显 / 整体外观 | +| 像素层 | 53 内容页 + 菜单 8 状态,与黄金参考逐字节比对 | 几何 / 样式 / 光标反显 / 整体外观 | | 池水位层 | `MENU_POOL_BASELINE=5368 B` / `POOL_AFTER_ROUNDTRIP=5448 B` | LVGL 内存池泄漏 / 结构性漂移(量的是 `used`,与 `LV_MEM_SIZE` 总大小无关) | ### 5.3 黄金参考与字体纪律 @@ -645,3 +645,4 @@ CI 就静默停摆(2026-10-11 实测 run 16~21 共 12 个 job 零日志)。 | 2026-10-26 | 三层断言表的文本层规模由「50 条」修正为 **242 条、覆盖 24 页**(现值取自 `simulator/tests/check_content_pages.py` 的 TEXTS 实跑计数;三张密码态无文本断言)| | 2026-10-26 | 三层断言表同步 UI 三页落地后的真值:像素层 **27 → 40 内容页**、文本层 **242 → 310 条(覆盖 37 页)**;上方变更记录里的 27 / 242 属当日事实,保留不动 | | 2026-10-27 | 三层断言表的像素层计数同步真值:**40 → 47 内容页**(判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 47 张 + `check_content_pages.py` 的 `PAGES` 47 项);文本层 310 条 / 覆盖 37 页仍是 2026-10-26 实跑口径,未随新增页重测,留待下次实跑回填;本条只加新行,历史行 27 → 40 原样保留 | +| 2026-10-27 | 三层断言表的像素层计数同步真值:**47 → 53 内容页**(判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53;6 张新增是通讯设置页六态,`31ca4a1f..HEAD` 对 ref 目录只有 6 个 A、零 M)| diff --git a/docs/测试说明/测试文档模板规范.md b/docs/测试说明/测试文档模板规范.md index 8100786b..de6f0b78 100644 --- a/docs/测试说明/测试文档模板规范.md +++ b/docs/测试说明/测试文档模板规范.md @@ -147,15 +147,19 @@ > 新模块立测试文档时先定缩写(2~4 个大写字母),并同步更新本表与 `测试开发标准流程.md` §2.2 的缩写表。 > 注:旧版缩写 `PHY` / `NAND` 已被实际用法 `NET` / `GD5F` 取代(历史文档不回改,新文档按本表)。 -### 6.3 测试用例 ID +### 6.3 测试用例 ID(**已停发新号**,以下形态只用于读现存号) ``` TC-{MOD}-{NNN} ``` -- **分阶段套件**:序号**百位放阶段号**,编号即自文档化——`TC-NET-101` = CH395F Phase 1 第 1 项、`TC-NET-601` = Phase 6 第 1 项、`TC-NET-1101` = Phase 11 第 1 项; -- **无阶段划分套件**:全局顺序编号(如 `TC-STORAGE-03`、`TC-RTC-001`); -- 编号不复用:废弃用例保留编号并注明"已废弃"。 +> ⚠ **本族的号已于 2026-10-27 停发**(用户裁定「批次三,不坐」→ `docs/编号规范.md` §4.1):**新用例不再配 +> TC 号**,标识 = 语义化测试函数名 + 本文档用例表的行名(§6.4 同口径)。下面的形态约定是**读现存号**用的, +> 不是起新号的模板。现存号保留、不清、不复用——测试追溯门禁按字面量读它。 + +- **分阶段套件(历史形态,只读)**:序号**百位放阶段号**,`TC-NET-101` = CH395F Phase 1 第 1 项、`TC-NET-601` = Phase 6 第 1 项、`TC-NET-1101` = Phase 11 第 1 项; +- **无阶段划分套件(历史形态,只读)**:全局顺序编号(如 `TC-STORAGE-03`、`TC-RTC-001`); +- 编号不复用:废弃用例保留编号并注明"已废弃";停发之后**没有"取哪个号"这一步**。 ### 6.4 设计原则 diff --git a/docs/测试说明/自动化测试流程.md b/docs/测试说明/自动化测试流程.md index b0a58ce9..2fc5e7f9 100644 --- a/docs/测试说明/自动化测试流程.md +++ b/docs/测试说明/自动化测试流程.md @@ -119,7 +119,7 @@ - **初始化回归**:`App/task/ui_task.c` 已有 `UI port init: OK` / `UI menu init: OK` / `UI net sink: started` 打印(见 `@test` 命令示例);将其加入 `-Required` 即可经串口自动回归 UI 初始化。 -- **屏幕内容回归**:由 LVGL 模拟器像素回归覆盖(文本级断言 + 47 页黄金参考 + 池水位门禁, +- **屏幕内容回归**:由 LVGL 模拟器像素回归覆盖(文本级断言 + 53 页黄金参考 + 池水位门禁, 见 `docs/UI设计/UI_自动化测试流程.md`);上机屏幕仍需人工目检(L3 层,见 `测试开发标准流程.md` §1)。 ## 扩展用法 @@ -150,5 +150,6 @@ | 2026-10-11 | **身份戳覆盖测试宏**(N-31 复测时发现):切 `test/board/test_config.h` 的套件宏后 `app_main.c` 未被重编,测试镜像与产品镜像顶着同一个 `fw build:` 戳 → 戳校验对"两种构建互换"失明(当时只有 `-CmpFile` 回读发现镜像不同)。修复:`app_main.c` `#include "test_config.h"`(头依赖 → 改宏必重编、戳必刷新)+ 戳串追加 ` suite= dstr=`(`FW_BUILD_VARIANT`,戳内容本身区分构建)。实测:`20:36:11 suite=NONE dstr=off` ↔ `20:36:40 suite=CH395F dstr=on`;`@test` 闭环对新格式 PASS(脚本无需改动,戳按"标记后连续可打印 ASCII"抠取)。失败模式表补一行:戳命中但 `suite=` 与要跑的套件不符 = 烧了产品镜像 | | 2026-10-22 | **自检台账判据改为「只看本次启动新增」**(修两处叠出来的假绿):① 抓取用的 FTP 登录还写着匿名,而设备自鉴权落地起就没有匿名通道 → 抓取**必然** 530 失败,旧实现又把"抓取失败"和"设备侧无此文件"都返回空、一律按"无记录 → PASS"处理 → `--require-selftest-pass` 从写下那天起没真正看过文件;② 就算抓到了,`/log/selftest.log` 是 512 条环形累计历史,"非空即 FAIL"在任何做过故障注入/长跑的设备上永远不可能 PASS(本仓台架实测 29 条记录全是 2026-10-10 ~ 10-14 留下的)。现在:烧录前取基线、启动后只判新增;抓取失败 / 无数据源 / 基线被环覆盖 **一律 FAIL**;`--selftest-scope all` 保留累计口径用于审计与首次建基线;FTP 凭据 `--ftp-user/--ftp-pass`(默认出厂 admin/admin)。凭据与判据两处都过真机:`this-run` → PASS(无新增)、`all` → FAIL(累计 29 条)、无 `device_ip` → FAIL(无数据源)。 | | 2026-10-27 | 内容页黄金参考 **40 → 47 页**(遥测死区与系数 2 页落地,`simulator/tests/ref/` 实测 47 张:7 张新增、存量 40 张逐字节零漂移);本文件的当前态计数同步改 47,历史行 27 → 40 原样保留 | +| 2026-10-27 | 内容页黄金参考 **47 → 53 页**(通讯设置页落地并绑 `#page=comm`;判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53 张,`git diff --name-status 31ca4a1f HEAD -- simulator/tests/ref` 实测只有 6 个 `A`、零 `M` ⇒ 存量 47 张逐字节零漂移);本文件的当前态计数已同步 | | 2026-10-26 | UI 屏幕自动化段的页数口径修正为 **27 页**黄金参考(与 `simulator/tests/ref/` 入库的 27 个 `shot_*.bin` 对齐;旧写 11)| | 2026-10-26 | UI 屏幕自动化段的黄金参考页数 **27 → 40 页**(`simulator/tests/ref/` 入库 `shot_*.bin` 实测 40 个);历史行保留 | diff --git a/docs/算法/继电保护功能复现规划.md b/docs/算法/继电保护功能复现规划.md index e71ce910..9d58bdf9 100644 --- a/docs/算法/继电保护功能复现规划.md +++ b/docs/算法/继电保护功能复现规划.md @@ -37,7 +37,7 @@ | 操作系统 | FreeRTOS V10.3.1(CMSIS-RTOS v2) | | 已实现子系统 | 网络(CH395F)、存储(GD5F2GQ5UE SPI NAND + dhara FTL + FatFs)、时间(SD2506 RTC + `sys_clock`)、配置(cfg_file)、日志、UI(远端显示)、看门狗(IWDG)、FTP | | 采样硬件 | TPAFE5160(16 位、8 通道**同步**、±5 V 双极性、并行 `GPIOG[15:0]`)+ STM32 内部 ADC1(12 位、9 通道**顺序**扫描、0~3.3 V 单极性) | -| 采样软件 | `Drivers/BSP/TPAFE5160/tpafe5160.h` 驱动就绪(原子快照 API);`App/task/adc_task.c` **已实现真实消费者(B1,2026-10-10)**——`tpafe5160_snapshot()` 取数 + 帧统计;**采样节拍由 TIM3 更新事件(1 kHz)硬件触发**(2026-10-10;同一事件经 TRGO 触发 ADC1 + DMA);STM32 侧 **`App/algo/adc_input.c` 已实现语义映射 + ADC1 DMA 乒乓帧消费(「STM32 ADC1 启动与扫描」/L1-4,v2.0)**,`HAL_ADC_Start_DMA` 已出现 | +| 采样软件 | `Drivers/BSP/TPAFE5160/tpafe5160.h` 驱动就绪(原子快照 API);`App/task/adc_task.c` **已实现真实消费者(B1,2026-10-10)**——`tpafe5160_snapshot()` 取数 + 帧统计;**采样节拍由 TIM3 更新事件(1 kHz)硬件触发**(2026-10-10;同一事件经 TRGO 触发 ADC1 + DMA);STM32 侧 **`App/algo/adc_input.c` 已实现语义映射 + ADC1 DMA 乒乓帧消费(「STM32 ADC1 启动与扫描」,v2.0)**,`HAL_ADC_Start_DMA` 已出现 | | 保护算法层 | **L2 相量层已实现并台架闭环**(`App/algo/sample_ring.c` + `phasor.c`:全周傅氏 5 实例、双区交换、每 1 ms 刷新,2026-10-11 `@test`);**L3 元件框架 + 过流/过负荷/过压/欠压/零序过压 + 定值模型软件侧完成**(`prot_elm.c` + `prot_set.c`,2026-10-11,57 项主机单测);**出口 / SOE 记录仍缺失**(P4) | | 已知硬件缺陷 | 登记簿条目「双转换器无共同采样触发」:8 路电气量分属两片转换器 → V0.1 上禁止跨转换器做相位/功率/矢量运算 | diff --git a/docs/编号规范.md b/docs/编号规范.md index f59bc8f9..d09254b7 100644 --- a/docs/编号规范.md +++ b/docs/编号规范.md @@ -82,10 +82,12 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 ### 4.1 允许用号的族(**穷举**——这张表以外一律不许发号) -**允许继续发号**(有脚本按字面量匹配,现状实测 2026-10-27 见 §9.1)——注意 `TC-*` 虽仍被脚本读,但 -其**发号**已按用户裁定 2026-10-27 停掉(游标冻结),下表其余 7 族不受该裁定影响: +**允许继续发号**——**恰好下面 7 行**,判据 = 有脚本按字面量匹配它(现状实测 2026-10-27 见 §9.1)。 +`TC-*` **不在本表**:它的发号已按用户裁定 2026-10-27 停掉(别把那句裁定读成「暂缓」——防误读的三条对照在 +本节末的框里,清点的来龙去脉在 §9.1 批次三)。 + +| 族 | 形态 | 属主登记处 | 用途 | |---|---|---|---| -| `TC-*` | `TC-<模块码>-<序号>` | `docs/测试说明/*_Test_Guide.md`、`test/host/<模块>/*`(代码即登记处) | 用例↔断言双向追溯,要进 C 断言标签 | | `TRAP-*` | `TRAP-<域>-` | `docs/问题记录/*_Trap_Records.md` | 踩坑证词的永久锚点,代码注释反查 | | `L-` | 保护功能号 | `docs/算法/继电保护功能复现规划.md` | 全库唯一"定义与引用都齐"的样板族 | | `F--` | 功能项 | `docs/网络/网络自愈架构设计.md` | 代码注释按号回指设计条目 | @@ -94,18 +96,43 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 | `MENU-STATE-` | UI 状态机键 | `simulator/tests/check_menu_rows.py` | 像素回归脚本按它取状态行 | | `P-` | **本文的设计原则** | 本文 §2 | 就是本文自己的条目 | -**已停发、存量待清**(登记未删只是为了让历史行不报「未登记前缀」,**不得再新增引用**;清点见 §9.1): +> ⚠ **「批次三不做了」只有一层意思:不去清这个族,同时把它的发号口关死。** 它**不是**「TC 族维持原状、 +> 新用例照旧配号」——维持原状的是**存量号**,关掉的是**发号口**。三条对照,防止往任何一边读过头: +> +> - ❌「不做 = 维持原状 = 还能发」——**错**。新用例一律用**语义化测试函数名 + 文档用例表的行名**(§3.3), +> 不再配 TC 号;指认**既有**用例仍可引用现存号(旧号只读,不是许可新配)。 +> - ❌「停发 = 这族要废了,顺手清掉、把号腾出来复用」——**也错**。现存号与它在 `scripts/id_registry.json` +> 里的登记,是 `check_test_traceability.py` 与 40 套 `run_unit_test.py` **按字面量读的追溯把手**, +> 清它等于换机制(§9.1);号不回收、不复用是 §1 的硬规矩。 +> - ❌「registry 里 TC 族还带着活游标,所以门禁允许我发」——**不成立:这条缺口已于 2026-10-27 同日关掉**。 +> 37 个 TC 类族现在**全部** `frozen: true`,其中 **34 个 `TC-HOST-*` 子族的 `cursor` 冻结在裁定日该套件存量 +> 最高号**(5 个游标低于存量的已抬到水位:`TC-PROTELEM-013→015`、`TC-SYSLOG-108→137`、`TC-ADCHEALTH-011→023`、 +> `TC-ADCCAL-017→023`、`TC-I104SRV-041→053`;**游标必须是字符串形态**,整型比较会静默不生效)→ 在套件 +> 登记处写一个比游标更高的 TC 号就是 §8 第 5 项「冻结族越游标」**错误级**,而 `id_known_backlog.json` 台账 +> 0 条指纹 → 增量拦截等于全量拦提交。探针实测(`check_ids` 的 Checker `_check_frozen`):在 `TC-PROTSET-` +> 段存量最高号之上再加一号 → 命中 1 条校验 5;写存量最高号本身 → 零误伤。 +> **还剩一处空白,别把这条机械化读成无死角**:主族 `TC`(没有专属子族的模块码,如 `TC-ADCIN-*`)的 `cursor` +> 仍为 `null`——一个游标表达不了每个模块码的水位,写全族最高号反而放行低号,所以按空白登记(探针实测: +> 给无专属子族的模块码(如 `TC-ADCIN-` 段)写一个新号,门禁不响)。这一面由三条兜:① §8 校验 3——新号没进属主登记处 = 悬空引用(错误级); +> ② §9.1 末段的**冻结水位**(只减不增,涨了就要问「这号是谁在停发之后发的」);③ 评审。**所以给这些模块 +> 补号,即使在文档表格里一并补了定义行,一样违反本裁定。** 要把这面也钉死(按模块码克隆子族,或给族级加 +> 字面量上限)与「真要清退」同属追溯机制改造,按 §7.3 单独开卡,别夹在本批里顺手做。 +> - **唯一的重启入口**:先由用户裁定 → 再回到本节把族放回上面那 7 行、并指出按它做字面量匹配的脚本 → +> 才谈得上发号。顺序倒过来就是 §1.2 那四类事故的由来。 + +**已停发、存量待清**(一律**不得再新增引用**;下表除 `TC-*` 外,登记未删只是为了让历史行不报「未登记前缀」, +清点见 §9.1。`TC-*` 是这一栏的**特例**:登记与现存号都要留着,它是机器把手而不是待清对象,见 §9.1 批次三): | 族 | 状态 | 依据 | |---|---|---| -| `TC-*` | **保留现存号、即日停发**:游标冻结、只减不增,新用例改用语义化测试函数名 + 文档表格行名 | 用户裁定 2026-10-27「批次三,不坐」;本族是 §4.1 判据唯一真被脚本按字面量读的族,清它等于换追溯机制,详见 §9.1 批次三 | +| `TC-*` | **保留现存号、即日停发(机器已冻结)**:34 个 `TC-HOST-*` 子族 `frozen: true`、游标钉在 2026-10-27 存量水位,越游标即 §8 校验 5 错误级;现存号只减不增,新用例改用语义化测试函数名 + 文档表格行名(主族 `TC` 的空白面见上方框) | 用户裁定 2026-10-27「批次三,不坐」;本族是 §4.1 判据唯一真被脚本按字面量读的族,清它等于换追溯机制,详见 §9.1 批次三 | | 族 | 形态 | 存量 | 为什么该清 | |---|---|---|---| | `N-*` | 登记簿 `N-` | 4 处 / 3 个字面量 | 登记簿已于 2026-10-26 全面改语义短语,残留的是漏网的旧引用 | | `TM-*` | 阶段/里程碑 `TM--` | 1 处 | 阶段号本就属 §3.3 明令不许写号的那类 | | `T-TEST` | 台架阶段 **裸形** `T0~T3` | 19 处 / 4 个 | 裸形最危险(同字母多义),且改「台架阶段一」即可 | -| `F-*` | 网络设计条目 `F--` | 38 处 / 12 个 | 代码注释按号回指设计条目,但没有任何脚本匹配它 | +| `F-*` | 网络设计条目 `F--` | 动手前 38 处 / 12 个 → **2026-10-27 批次一收口**:正文 9 处(含孤儿号 `F-DRV-9` 删号)+ 主机侧单测注释 1 处改「条目名」,仅属主文档 §7 历史对照表留 12 条编号 | 代码注释按号回指设计条目,但没有任何脚本匹配它 | - **新增族的唯一正当条件**:有脚本要按字面量匹配它。顺序固定:先改 `id_registry.json`(含属主登记处) → 再写属主文档 → 最后才允许在别处引用。反过来做必然造出悬空号。 @@ -116,6 +143,7 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 | 停发族 | 现在怎么写 | 旧号去哪查 | |---|---|---| | 登记簿 `N-x` / `S-x` / `T-x`、专项 `A-x`、已删报告的 UI 簇 `U1~U5`(2026-10-26 方案 B) | 语义短语——**登记簿表首格本身就是短语**,如「双转换器无共同采样触发」「CI 远端闭环未验证」 | `scripts/id_aliases.json` 标 `retired-to-phrase` | +| 网络设计条目 `F-NET/F-DRV/F-MS-`(12 个,2026-10-27 批次一) | 「§7 + 条目名」短语,如 `(§7「全局读即清搁浅事件」)`;条目名的权威清单 = `docs/网络/网络自愈架构设计.md` §7「条目名」列;孤儿号 `F-DRV-9` 按本节末句**删号留事实**、未补定义 | `scripts/id_aliases.json` 标 `retired-to-phrase`(13 条)+属主文档 §7 编号列仅作历史对照 | | `E-xx` 审计发现、`P0-x`~`P3-x` 审计条目、报告六族 `D/R/F/N/U/S`、复审残号 `S1~S14`/`A-x`/`B1~B10`/`T-x`、模拟器差距 `H-x`/`L-x`、`TM-*` 阶段号、`DP-*`、`gen`/`round`/会话临时号 | 写条目本身("原子快照取数"、"队头阻塞"、"台架日志代次 288"、"第 3 轮台架") | 别名表标 `void`;**部分死号故意不进别名表**,好让门禁继续咬残留引用 | | 规范曾规划却从未启用的前缀族(`DEF`/`WI`/`DR`/`OQ`/`RSK`/`GAP`/`SC`/`GEN`/`MS`/`UL`/`LAT`/`CS` 等) | 同上,写事实 | 无定义,本就不该存在 | @@ -151,6 +179,8 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 ### 5.1 新增编号 先过 §1 三问。确实要发号:① 在 `id_registry.json` 确认族与游标 ② 在属主登记处写定义行 ③ 才允许在 别处引用。**没有属主登记处的号不许存在**——门禁的校验 3 就是照这句实现的。 +**族不在 §4.1 那 7 行之内的,第 ① 步就该走不通**(`TC-*` 已停发:即使在 registry 里查得到它、游标还是活的, +也不构成"能发号"的理由,见 §4.1 末框)。 ### 5.3 代码注释 **优先写短语**(`FTP 鉴权`、`监听生命周期模块化`),别写字母号;确需引用活号时必须带属主文档名。 @@ -252,7 +282,7 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 | `TC-*`(含各模块子族) | **561** | 472 | 77 | 测试用例与断言标签、`test/host/**` | **保留、即日停发**(用户裁定 2026-10-27,见 §9.1 批次三) | | `L-PROT`(保护功能号) | **184** | 28 | 58 | 保护规划与各模块说明 | 批次二:转语义 | | `TRAP-*` | **134** | 25 | 51 | 踩坑记录与驱动注释 | 保留(可检索锚点,用户已裁定) | -| `F-*`(网络设计条目) | **38** | 12 | 20 | `ch395f.h`、`net_events.c` | 批次一:转语义 | +| `F-*`(网络设计条目) | **38** | 12 | 20 | 动手前 `ch395f.h`、`net_events.c`;**收口后代码侧 0 处** | 批次一:**已转语义**(§7 保留 12 条历史对照 + 新增「条目名」列) | | `CODE-RULE-*` | **35** | 20 | 13 | 代码规范门禁 | 保留(门禁按号报错) | | `MENU-STATE-*` | 少量 | — | — | 像素回归脚本 | 保留(脚本按它取状态行) | | `T-TEST`(台架阶段裸形) | **19** | 4 | 12 | 串口/台架脚本与测试流程 | 批次一:转语义 | @@ -264,6 +294,9 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 ### 9.1 三批计划(按「值不值得动门禁」排序) **批次一 · 现在就能清(约 62 个站点 / 20 个字面量,不碰任何门禁)**:`N-*`、`TM-*`、`T-TEST`、`F-*`。 + +> `F-*` 已于 2026-10-27 收口:映射 `scripts/rename_maps/batch7-f-net-semantic.json`(10 条**带上下文**的定点改写),`docs/网络/网络自愈架构设计.md` §7 加「条目名」列作权威对照,13 个旧号进 `scripts/id_aliases.json` 标 `retired-to-phrase`。 +> 作用域刻意只列 2 个文件、且 `old` 全部带上下文——同树里 `docs/硬件平台/` 的 **T1/T2/T3 位号**(网表 ~4200 处 R/C/D/J/X/U/T 位号,本就不在门禁语料内)、`docs/芯片手册/` 的时序记号,以及各文档 Mermaid 图的**单字母节点名**(`F -->`、`EV`、`LN`…)都会被宽松正则顺伤;变更记录行(`BSD_Socket_API_使用指南.md` 的 `| 2026-10-13 |`)按 §8.1 豁免不改写。 这四族**没有任何脚本按字面量匹配**,纯人读,正是「语义标识最好」的适用面;`T-TEST` 还是裸形(`T0~T3`), 最容易与别的字母撞义。改法:号 → 短语(「台架阶段一」「网络自愈:监听生命周期」),残留引用同步清, 别名表标 `void`,台账 `--prune-backlog`。 @@ -284,15 +317,43 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 不可逆试验不值。**结论:机器面自解释的族可以保留;字母号的痛在「人读」侧,解法是别再造给人读的号, 而不是把机器面的把手也拆掉。** -「不做」之后跟着两条硬口径(否则裁定会被当成默许继续扩族): -① **本族即日停发**:游标冻结,现存号只减不增,**新用例一律用语义化测试函数名 + 文档表格行名,不再配 -`TC-` 号**(§4.1 的『已停发』栏已加一行); -② 若将来真要清,按 `scripts/rename_ids.py` + §7.3 五步**单独开卡**,前提是先让追溯器同时认两种形态 +「不做」之后跟着三条硬口径(否则裁定会被当成默许继续扩族): +① **本族即日停发**:现存号只减不增,**新用例一律用语义化测试函数名 + 文档表格行名,不再配 `TC-` 号** + (机器口径已同批落地:见本节末「机器口径已落地」与 §4.1 框内第三条); + 同一笔把 `TC-*` 从 §4.1『允许继续发号』表里**整行移出**(只在『已停发』栏留一行)——留在那张表里, + 脚注写得再重,扫表的人照样照着发号; +② **停发 ≠ 待清**:现存号与 registry 登记一律保留、不清、不复用(机器追溯把手 + §1「不回收、不复用」); + 引用既有用例仍可用旧号,新用例不配新号; +③ 若将来真要清,按 `scripts/rename_ids.py` + §7.3 五步**单独开卡**,前提是先让追溯器同时认两种形态 (新旧并行核对通过,再谈切换),**不许先改文档再改代码**(会凭空造出悬空号,§8 校验 3 会当场拦)。 +**这条裁定的机器口径已落地(2026-10-27 同日补;上一版这里写「停发今天只有规范效力」,那是当日的实测记录,不是笔误,留作历史)**: +`scripts/id_registry.json` 的 **37 个 TC 类族已全部 `frozen: true`**,其中 **34 个 `TC-HOST-*` 子族带游标**,冻结在 +裁定日该套件存量最高号;5 个游标低于存量的已抬到水位(`TC-PROTELEM-013→015`、`TC-SYSLOG-108→137`、 +`TC-ADCHEALTH-011→023`、`TC-ADCCAL-017→023`、`TC-I104SRV-041→053`),裁定理由记在 registry 顶层 +`tc_freeze_2026_10_27`。落地的直接诱因是本族在裁定当天还在长大:另一道在飞的 `time_sync` 改动新增了 +`TC-TIMESYNC-0xx`(撞的是 §8 校验 3 悬空引用而非校验 5——那个模块码没有专属子族,正落在主族 `TC` 的空白面上)。 + +**冻结水位(只减不增)**:TC 类族按门禁 refs 口径(字面量 × 文件去重,属主登记处与本文写法示例不计)—— +动手前基线 **561 站点 / 472 个字面量 / 77 个文件**;本笔落地时工作区实测 **590 / 497 / 81**(站点 / 字面量 / 文件)。 +**差出来的 29 站点全部是裁定之后仍在配号的在飞改动**(`time_sync` 一道的 `TC-TIMESYNC-` 新号),这正是水位 +必须落到纸面的理由:**从本笔起任何一次上涨都要问「这号是谁在停发之后发的」**。重测:跑 `check_ids` 的 Checker, +取 `ck.refs`(注意它是**属性**不是方法),把字面量按 `class == "TC"` 各族 `pattern` 归族后聚合。 +⚠ 上面两个数都是**工作区时点值**(本笔 = 2026-10-27 12:40,含别人在飞的改动)——提交收口时按当时工作区**重打一次**, +别把这个数当长期基线;**也不要为了把数字压回 561 去改别道在飞的文件**。 + +**门禁侧复核**:`check_ids.py --only 5` 校验 5 = 0 处(冻结没有误伤存量);plain 与 `--strict` 的错误级 findings +与本笔之前逐条相同;`id_known_backlog.json` 台账 0 条;可判定比例 87.67%(基线 86.0%)、不可判定绝对数 979(上限 +1,166)。本笔零语料增删,**不动 §8.1 两根护栏基线**(§8.1 的 7,828 处字面量会随在飞改动漂移,那属各自批次的收口)。 + +**还剩下的那一面(别把「机器已冻结」读成全族无死角)**:主族 `TC` 的 `cursor` 仍为 `null`——一个游标表达不了每个 +模块码的水位,写全族最高号反而放行低号,所以按空白登记;这一面由校验 3(新号无定义位 = 悬空,错误级)、上面的 +冻结水位、以及评审三条兜住。**「反正门禁主族咬不到我」不是发号许可。** 要把这面也机械化(按模块码克隆子族,或给 +族级加字面量上限),与「真要清退」一样属追溯机制改造,按 §7.3 单独开卡。 + ### 9.2 别再做什么 「编号说明」这件事本身应该继续变薄:每多一个族,就多一处需要有人记得它的地方;每多发一个号,就多一个 -将来没人敢删的字符串。要新增族之前,请把 §4.1 的「允许继续发号」那 8 行当作反对你的第一手证据。 +将来没人敢删的字符串。要新增族之前,请把 §4.1 的「允许继续发号」那 7 行当作反对你的第一手证据。 ## 10. 速查 - 看到一个不认识的号 → 先查 `id_aliases.json` 与属主登记表;查不到就**删号留事实**,不要补定义。 @@ -309,6 +370,9 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 | 日期 | 变更 | 说明 | |---|---|---| +| 2026-10-27 | **批次一 `F-*` 收口:网络设计 12 个条目号转「条目名」语义短语,§7 表加「条目名」列为权威对照** | 用户指令「把网络设计里的 `F-*` 条目号换成语义短语,别改坏硬件资料的 T1/T2/T3 位号和 Mermaid 节点名」。走 `scripts/rename_ids.py`(映射 `scripts/rename_maps/batch7-f-net-semantic.json`,`--no-alias` 后手工写别名),10 处定点改写=属主文档正文 9 处(含 1 处孤儿号删号留事实)+ `test/host/net_socket/net_socket_test.c` 注释 1 处;孤儿号 `F-DRV-9`(§7 从无定义行)删号留事实、不补定义;13 个旧号标 `retired-to-phrase`。刻意不动:§7 编号列(历史对照)、`BSD_Socket_API_使用指南.md` 变更记录行、`docs/硬件平台/` 位号、Mermaid 节点名。`scripts/id_registry.json` FEAT `cursor_note` 改记停发口径(三模块各有最高号 → 不冻结游标的理由写在里面)。零代码语义变更。 | +| 2026-10-27 | **停发机械化落地:34 个 `TC-HOST-*` 子族游标冻结,§8 校验 5 开始咬新配的 TC 号** | 上一笔把缺口登记为「停发今天只有规范效力」并写明机械化怎么做;本笔照它落地、把话兑现:`scripts/id_registry.json` 37 个 TC 类族全部 `frozen: true`,34 个子族的 `cursor` 冻结在裁定日存量最高号(字符串形态——整型比较静默不生效;5 个低于存量的已抬:`TC-PROTELEM-013→015`、`TC-SYSLOG-108→137`、`TC-ADCHEALTH-011→023`、`TC-ADCCAL-017→023`、`TC-I104SRV-041→053`),理由记在顶层 `tc_freeze_2026_10_27`。探针(Checker `_check_frozen`):在 `TC-PROTSET-` 段存量最高号之上再加一号 → 命中 1 条校验 5,写存量最高号本身 → 零误伤;`check_ids.py --only 5` = 0 处;plain 与 `--strict` 的错误级 findings 与本笔前逐条相同(**12 条 `TC-TIMESYNC-0xx` 悬空引用属另一道在飞的 time_sync 改动,不在本笔写域,也没替它改**);台账 0 条、可判定 87.67%、不可判定 979 / 上限 1,166,不动 §8.1。**没有把主族 `TC` 说成已冻结**:它 `cursor` 仍为 `null`,§4.1 框内第三条与 §9.1 末段把这块空白及其兜底(校验 3 + 冻结水位 564/474/79 只减不增 + 评审)写明,并规定「给这些模块补号即使在文档里补了定义行也算违令」。§9.1 末段由「效力边界」改写为「机器口径已落地」,当日实测留作历史不抹。零代码、零编号增删。 | +| 2026-10-27 | **停发口径收口:`TC-*` 从『允许继续发号』表整行移出,并登记「机器面还没冻结」这个缺口** | 裁定上一笔已入档,但效力写在了会被扫表读过的地方:`TC-*` 仍占着「允许继续发号」表第一行、用途列还写着「要进 C 断言标签」,那句"注意它已停发"只是表前的脚注;而那张表连表头行都在早先瘦身时丢了(GFM 里表格不能紧跟段落,整张塌成正文)。本笔:① 移出该行、补回表头与空行,『允许继续发号』写成**恰好 7 行**;② 表下加防误读框,把「不做了」的两条误读各钉一次——「维持原状所以还能发」与「停发所以顺手清掉/复用号」,并写明唯一重启入口 = 先裁定再回本节改表;③ 实测登记效力边界:registry 37 个 TC 类族仍有 35 个 `frozen: false` 带活游标,§8 第 5 项碰不到它们(且 `cursor` 为空时该校验直接跳过),所以停发今天只有规范效力,机械化单独开卡走 §7.3;④ §5.1 补「不在 7 行内第 ① 步就走不通」、§9.1 两条硬口径扩成三条(新增「停发 ≠ 待清」)并补效力边界段、§9.2 计数 8 → 7。同步:`AGENTS.md` 索引行、`docs/测试说明/测试文档模板规范.md` §6.3、`docs/门禁说明/host-unit-tests.md` §7 号段行(那里原文写着「新增用例要带号」,是最直接的误读源头)。零代码、零编号增删。 | | 2026-10-27 | **批次三裁定「不做」:`TC-*` 保留现状但即日停发** | 用户原话「批次三,不坐」(即不做)。裁定写进 §9.1 批次三(含理由与两条硬口径)、§4.1『已停发』栏加一行、§9 结果表同步。停发的硬口径:游标冻结、现存号只减不增、**新用例一律用语义化测试函数名 + 文档表格行名,不再配 `TC-` 号**;真要清则先让追溯器同时认新旧两种形态、核对通过后单独开卡,不许先改文档再改代码(会造悬空号)。留着的判据依据:`TC-*` 是 §4.1 那条判据(有脚本按字面量匹配)唯一真满足的族,字母号的痛在「人读」侧,解法是不再造给人读的号,而不是拆掉机器面的把手。 | | 2026-10-27 | **语义标识定为唯一默认形态;编号收窄为「机器按字面量匹配」的例外,并首次给出现量清点与三批计划** | 用户原话:「不要用字母加数字的编号,简直太难以理解了,语义标识是最好的」。本笔:① 标题下的一句话从「允许用号的族只有 8 个」改成「语义标识是唯一默认、编号是例外」,并把 §4.1 拆成『允许继续发号』8 族与『已停发、存量待清』4 族(`N-*` / `TM-*` / 台架阶段裸形 / `F-*`)——原表自称穷举,实际漏登记了这三类在用族,属真缺陷。② 新增 §9 存量清点(用门禁自身接口实测:26 族仍有引用、565 个字面量、1,108 个引用站点)与三批计划:批次一 62 站点不碰门禁即可清、批次二保护功能号 184 站点转功能名、批次三 `TC-*` 561 站点需先换追溯机制(等用户裁定)。③ §8.1 基线按 2026-10-27 复测重打:784 文件 / 7,828 字面量、护栏 2 实跑 1,161 / 上限 1,166。 | | 2026-10-26 | **编号治理收口:清死号 → 停发活号 → 本文重写为短版(用户三次裁定)** | 全仓字母+数字编号 10,956 → 8,230 处。① **清死号**:属主登记处已删或从未启用的号一律就地删号留事实、禁补号(文本实删约 1,557 处)。② **停发活号**:登记簿 `N-x`/`S-x`/`T-x`、专项 `A-x`、已删报告的 UI 簇 `U1~U5` 共 20 个号转成语义短语(约 418 处)——登记簿表首格就是短语,旧号进 `scripts/id_aliases.json` 标 `retired-to-phrase`;已销项的死号**故意不进别名表**,让门禁继续咬残留引用。③ **本文瘦身**:619 行 → 229 行,删掉八原则长表、迁移映射表、模块码表与门禁细节(那些各有事实源:registry / 别名表 / `docs/门禁说明/id-check.md`),改成「先问三句再决定要不要发号」+ §4.1 允许用号的族**穷举 = 8 个**。④ **门禁配套**:`check_ids.py` 加变更记录行豁免与第二根护栏 `undecidable_total_ceiling`(比值会被清号动作稀释,绝对数不会),债务台账还清到 0 条 → `--strict` 实为全量拦截,比例基线重打为 86.0%。**本表按用户 2026-10-26 的明确指示只保留最近一条**,更早沿革看 `git log` 与 `docs/门禁说明/id-check.md` §7.5(含其后的方案 B 一节);这是**本文档的例外**,不改变 §5.5对其他文档「冻结历史不回改」的约束。 | diff --git a/docs/网络/网络自愈架构设计.md b/docs/网络/网络自愈架构设计.md index 4edce57e..6159db20 100644 --- a/docs/网络/网络自愈架构设计.md +++ b/docs/网络/网络自愈架构设计.md @@ -13,7 +13,7 @@ |---|---|---| | `I1`~`I8` | 本架构的**不变量**(Invariant)——违反即有实测后果 | §2 | | `E0`~`E19` | **台架实验**编号(E = Experiment):每个实验是「假设 → 方法 → 判据 → 门禁哪个决策」 | §5.2(原始记录已归档,见 §12) | -| `F-NET-nn` / `F-DRV-nn` / `F-MS-nn` | 审计的**编号发现**:NET 层 / 驱动层 / 状态镜像层(**2026-10-27 停发**,指代一律用 §7 表的「一句话结论」条目名) | §7(保留 12 条历史对照) | +| `F-NET-nn` / `F-DRV-nn` / `F-MS-nn` | 审计的**编号发现**:NET 层 / 驱动层 / 状态镜像层(**2026-10-27 停发**,指代一律用 §7 表的「条目名」列) | §7(保留 12 条历史对照 + 条目名对照) | | `M1`~`M5` | 实验固件里的**候选修改代号**:M1 沉降无条件化 · M2 门控换 `net_listener_armed` · M3 可疑门 · M4/M4-lite 删沉降段全局读即清 · M5 `sock_reset` quiescent 排空 | §4.1、§4.3 | | `形态①`~`形态⑤` | `net_reconcile_tick` 的五类**失同步判据** | §4.1 | | `U1`~`U15` | 状态迁移的**"无人负责"组合**(架构欠债清单) | `docs/问题记录/CH395F_Trap_Records.md` 审计遗留章节 | @@ -189,17 +189,17 @@ WCH 官方博客 cnblogs sw111/18451802。 | # | 不变量 | 违反的后果 | |---|---|---| -| I1 | **单一事实源**:芯片状态的权威副本 = 驱动每 Socket 缓存,唯一刷新点 = `net_poll` 每轮全量刷新;net 层 8 态 `net_sock_state_t` 只是"会话投影"(展示/门控),决策点读缓存或主动回读 | 有损镜像产生"镜像说 A、芯片是 B"的决策窗口(F-MS-8) | +| I1 | **单一事实源**:芯片状态的权威副本 = 驱动每 Socket 缓存,唯一刷新点 = `net_poll` 每轮全量刷新;net 层 8 态 `net_sock_state_t` 只是"会话投影"(展示/门控),决策点读缓存或主动回读 | 有损镜像产生"镜像说 A、芯片是 B"的决策窗口(§7「有损镜像决策窗口」) | | I2 | **单一重建所有者**:唯一周期性监听重建决策者 = `net_reconcile_tick` 形态③④;事件处理器与沉降登记**只置事实、不动作** | 多所有者竞争——"谁先看到 DISCONNECT 谁重建"→ 抢先 relisten 吞掉慢消费者的 EOF(B10) | | I3 | **事件只负责提前唤醒,正确性靠回读 + 有界超时** | 纯事件驱动 → 事件被读清消费竞态吞掉后监听静默死亡(TRAP-NET-17;E14 复核:事件非芯片侧丢失,是被消费) | | I4 | **监听常驻 = 软件承诺**(`auto_relisten`),不是芯片属性 | 出向连接被误判孤儿 / 监听永久失效 | | I5 | **复用前清理内联进事务**:`sock_reset` = 排空 RX → CLOSE → 排空事件(quiescent)→ 等 byte0=00H | 带非空 RX 关闭 → 拆链停滞、SYN 被吞、级联自持(E4 变体 C 0/20) | -| I6 | **分层**:驱动持事实与机械;net 层持策略与所有权;应用层零自愈责任 | 应用层重试/手动自愈 = 又一层补丁(F-DRV-9) | -| I7 | **全局中断寄存器唯一读者 = `net_poll`**(F-NET-7;测试/台架代码同样禁自行读取,等事件一律直读本 Socket SINT) | 吞掉其它 Socket 的 CONNECT/DISCONNECT/RECV_OK 边沿事件(B3 级事故) | +| I6 | **分层**:驱动持事实与机械;net 层持策略与所有权;应用层零自愈责任 | 应用层重试/手动自愈 = 又一层补丁(原引用号在 §7 无定义行,删号留事实) | +| I7 | **全局中断寄存器唯一读者 = `net_poll`**(§7「全局读即清搁浅事件」;测试/台架代码同样禁自行读取,等事件一律直读本 Socket SINT) | 吞掉其它 Socket 的 CONNECT/DISCONNECT/RECV_OK 边沿事件(B3 级事故) | | I8 | **不留未证明的复杂度**(TRAP-NET-20 铁律) | 正面执行:U15 内化不立项(E6 命中=0);TSL 变体只作待用补丁(E12) | **去抖 + 冷却**:失配连续 N 轮才动手(分级,见 §4.1)+ 动手后 1s 冷却。去抖与冷却本是 -"镜像世界"的自偿成本(F-MS-9)——判据改读驱动缓存后,探针侧去抖已无必要(缓存无瞬态噪声); +"镜像世界"的自偿成本(§7「去抖冷却自偿成本」)——判据改读驱动缓存后,探针侧去抖已无必要(缓存无瞬态噪声); reconciler 保留分级去抖,因为形态① 要让位 M3 窗、形态② 要覆盖芯片 10ms 级可见窗(E2)。 ## 3. 架构总览 @@ -274,7 +274,7 @@ reconciler 保留分级去抖,因为形态① 要让位 M3 窗、形态② 要 | ② | LISTENING ↔ 芯片已握手(TCP=ESTABLISHED) | `GET_REMOT_IPP` 幽灵甄别 → 补 accept(`handle_connect_event`) | **1 轮**(E2:LISTEN 可见 ≤10ms 无长尾) | 1 次 SPI 读 | | ③ | LISTENING ↔ 监听型 fd 而芯片不在 LISTEN | 登记重建请求(动作在 I2 唯一执行者 `net_listener_tick`) | 2 轮 | 无(转交) | | ④ | 本层 CLOSED(auto_relisten)而芯片**不在** LISTEN | 登记重建请求(同 ③) | 2 轮 | 无(转交) | -| ⑤ | ESTABLISHED ↔ 芯片已回 LISTEN(静默回收,F-NET-2) | RESYNC 语义只修本层(转登端口 + 取消挂起 recv/send),零命令 | 2 轮 | **零** | +| ⑤ | ESTABLISHED ↔ 芯片已回 LISTEN(静默回收;§7「静默回收无人认领」) | RESYNC 语义只修本层(转登端口 + 取消挂起 recv/send),零命令 | 2 轮 | **零** | | U11 | 本层 CLOSED ↔ 芯片**正在** LISTEN | 只重同步本层为 LISTENING(转登端口) | **不去抖、不占冷却** | **零** | ⚠ **U11 不是形态④**。两者判据同族(本层 CLOSED + 监听型 fd)而结论相反: @@ -371,10 +371,10 @@ CLOSED → 由形态④ 接手。关键在两条快路径的判据(`net_pendin (下一轮继续排空残渣);留痕只留首末(每轮都打会 30ms 刷两行)。 - **隔离期持续排空 RX**:中止后残渣(实测 rxdrain=2048 = 芯片单 Socket RX 缓冲满容, E4)会被下一条连接读走 → 隔离期边等沉降窗边排空。 -- **GRACEFUL 无条件化**(M1,F-NET-15):`recv_bytes≠0` 门是背靠背失效循环的放大器 +- **GRACEFUL 无条件化**(M1;§7「沉降门放大器」):`recv_bytes≠0` 门是背靠背失效循环的放大器 ——被陈旧 DISCONNECT 杀死的零数据会话免沉降 → 残渣杀下一条连接、自持循环;无条件化后 `n30` 8%→96.7%(E7b)。⚠ M1 必须与 M4-lite(删沉降段全局读即清)**同批**,否则沉降成为 - 每次断连热路径后放大 F-NET-7 供给面(E7 实测教训)。 + 每次断连热路径后放大「全局读即清」的搁浅面(E7 实测教训)。 ### 4.4 驱动原语 @@ -627,8 +627,8 @@ DISCONNECT 会被 `(void)` 丢掉。但 RECV_OK 是条件位(下轮补回) | **E4** | 裸机 3 复位变体 ×20 轮:先造污染会话(发 3000B 后 RST),复位后**立即**重开监听并上传 4096B 图案载荷,逐字节校验 | 变体 A(排空 RX→CLOSE→排空事件→等 byte0=00H)**20/20**;变体 C(什么都不做)**0/20** 级联自持;实测 `rxdrain=2048`(芯片单 Socket RX 缓冲满容) | §4.4 复位序列定案;两档沉降**都必须排 RX** | | **E5** | `:7003` 上 connect→发 1B→对端 FIN→等 3 s→复连 ×10,找"芯片静默回收回 LISTEN 却不发 DISCONNECT" | 20 次 FIN **全部**正常产生 `DISCONNECTED`;静默回收在累计 ~110 次连接中断里**零复现** | 缺口真实但触发罕见 → 形态⑤ 顺带修,不单独立项 | | **E6** | 插桩固件(`NET_U15_PROBE=1` + `test/host/e6_soak.py`):常态 + 每小时 `:7003`×10 + 压测,探"`GET_RECV_LEN>0` 但 `!readable`"失配 | 命中 **0**(~4 h 常态) | U15 观察项**不立项**(I8);上层盲收保留现状(§4.8) | -| **E7 / E7b** | 候选修改 M1(GRACEFUL 沉降无条件化)+ M2(门控换 `net_listener_armed`),叠加/去掉 M4-lite(删沉降段全局读即清),跑 `n30` | M1+M2+M4-lite 把 `n30` 成功率 **8% → 96.7%**;**M1 单独上会放大 F-NET-7** | 三者即现状(§4.2 门控 / §4.3 沉降);纪律:**M1 与 M4-lite 必须同批** | -| **E8** | 同固件同负载,**只变设备状态**跑 `n30` 矩阵(全新启动 / 累积后 / 仅 ST-Link 复位) | 故障率在 **2%~54%** 间随状态摆动 → 故障率 ∝ 芯片侧残留存量;抓到"风暴签名"三机制(F-NET-17) | M3 可疑门 + `sock_reset` quiescent 排空;"沉降让位救回"红线 | +| **E7 / E7b** | 候选修改 M1(GRACEFUL 沉降无条件化)+ M2(门控换 `net_listener_armed`),叠加/去掉 M4-lite(删沉降段全局读即清),跑 `n30` | M1+M2+M4-lite 把 `n30` 成功率 **8% → 96.7%**;**M1 单独上会放大全局读即清的搁浅面(§7)** | 三者即现状(§4.2 门控 / §4.3 沉降);纪律:**M1 与 M4-lite 必须同批** | +| **E8** | 同固件同负载,**只变设备状态**跑 `n30` 矩阵(全新启动 / 累积后 / 仅 ST-Link 复位) | 故障率在 **2%~54%** 间随状态摆动 → 故障率 ∝ 芯片侧残留存量;抓到"风暴签名"三机制(§7「风暴态三机制」) | M3 可疑门 + `sock_reset` quiescent 排空;"沉降让位救回"红线 | | **E9** | M3 可疑门判据三轮迭代(`0x05/0x00` + `lost_conn_seen==0` + `recv_bytes==0` + 芯片 RX 有在途数据) | 判据定案为**资产门版**;风暴态免疫 + 零误伤 | §4.1.6 M3 门 | | **E10** | 不变量与机制定案后的全量台架回归 | 全绿;暴露 **`net_listener_armed` 必须放行 ESTABLISHED**——漏放行时 i104 的 200 ms 轮询抢不过形态② 去抖而整场饿死 | §4.2 门控定案 | | **E11** | 临时固件开启服务器侧 KeepAlive(`CMD_SET_KEEPALIVE_SN`,60s/5s/3),用"禁用 PC 网卡"造"链路在、对端静默" | 真实可用;判死 **≈30.0 s**(早于手册公式 75 s,以实测为准);静默但**存活**的对端不误杀 | §4.7 KeepAlive 定案 | @@ -661,24 +661,25 @@ DISCONNECT 会被 `(void)` 丢掉。但 RECV_OK 是条件位(下轮补回) ## 7. 编号发现速查(12 条,历史对照表) -> ⚠ **本节的条目号已于 2026-10-27 停发(编号规范 §9 批次一),正文改用条目名指代**; +> ⚠ **本节的条目号已于 2026-10-27 停发(编号规范 §9 批次一),正文/注释改用下表「条目名」列指代**; > 下表保留编号列仅作历史对照(删表会让历史行的引用报「未登记前缀」假错), -> 全仓不得再新增对这些号的引用——代码注释与关联文档已改写为「一句话结论 + 本表落点」的语义形式。 +> 全仓不得再新增对这些号的引用——代码注释与关联文档已改写为「条目名 + §7 落点」的语义形式(2026-10-27 批次一收口: +> 本文件残留 9 处正文引用(其中孤儿号 `F-DRV-9` 就地删号留事实)+ 1 处主机侧单测注释已全部转语义)。 -| 编号 | 一句话结论 | 收口落点 | -|---|---|---| -| F-NET-2 | "本层 ESTABLISHED 而芯片已回 LISTEN"无机制认领 | 对账形态⑤(§4.1) | -| F-NET-3 | `net_relisten` / `net_resync_listen` / `net_can_accept` 是冗余或过期 API 面 | 全部删除;监听门控统一 `net_listener_armed`(§4.2) | -| F-NET-4 | iec104 过期门控 → 丢 CONNECT 探针对 :2404 从不运行 | 门控统一 `net_listener_armed`(§4.2) | -| F-NET-7 | 全局寄存器读即清残留会搁浅其它 Socket 事件 | I7 + `ch395f_sock_events` 护栏(§4.10) | -| F-NET-15 | GRACEFUL 沉降门 `recv_bytes≠0` = 背靠背失效循环放大器 | 沉降无条件化(与删沉降段全局读即清同批,§4.3) | -| F-NET-17 | 风暴态三机制(沉降自再生锁存 / 泵与救回互抢 RX / 芯片 OPEN 卡死放大) | M3 可疑门 + `sock_reset` quiescent + 让位红线(§4.1.6) | -| F-DRV-1 | 同一芯片事实 7 处独立回读、三套去抖参数——单一事实源缺失是补丁膨胀的地基 | 驱动状态缓存 = 单一事实源(I1,§4.4) | -| F-DRV-4 | "close+排空+等 CLOSED"两套同构变体参数漂移 | `ch395f_sock_reset` 统一(§4.4) | -| F-DRV-8 | 命令完成等待是阻塞原语(`HAL_Delay` 忙等、4s 上限) | `cmd_done` 状态回读判据 + 非阻塞状态机(§4.4) | -| F-DRV-13 | 链路事件四个全局位读清后无人消费,运行期拔网线完全静默 | `glob_event_feed` + `link_up` 缓存(§4.4) | -| F-MS-8 | 芯片 11 态 → net 层 8 态有损投影,有损镜像必生决策窗口 | 镜像降级为会话投影(I1、§3.3) | -| F-MS-9 | 去抖/冷却是镜像世界的自偿成本 | 探针去抖已删(读缓存无瞬态);reconciler 保留分级去抖(让位窗需求) | +| 编号(历史对照) | 条目名(现行指代) | 一句话结论 | 收口落点 | +|---|---|---|---| +| F-NET-2 | `静默回收无人认领` | "本层 ESTABLISHED 而芯片已回 LISTEN"无机制认领 | 对账形态⑤(§4.1) | +| F-NET-3 | `冗余监听 API 面` | `net_relisten` / `net_resync_listen` / `net_can_accept` 是冗余或过期 API 面 | 全部删除;监听门控统一 `net_listener_armed`(§4.2) | +| F-NET-4 | `iec104 过期门控` | iec104 过期门控 → 丢 CONNECT 探针对 :2404 从不运行 | 门控统一 `net_listener_armed`(§4.2) | +| F-NET-7 | `全局读即清搁浅事件` | 全局寄存器读即清残留会搁浅其它 Socket 事件 | I7 + `ch395f_sock_events` 护栏(§4.10) | +| F-NET-15 | `沉降门放大器` | GRACEFUL 沉降门 `recv_bytes≠0` = 背靠背失效循环放大器 | 沉降无条件化(与删沉降段全局读即清同批,§4.3) | +| F-NET-17 | `风暴态三机制` | 风暴态三机制(沉降自再生锁存 / 泵与救回互抢 RX / 芯片 OPEN 卡死放大) | M3 可疑门 + `sock_reset` quiescent + 让位红线(§4.1.6) | +| F-DRV-1 | `七处独立回读` | 同一芯片事实 7 处独立回读、三套去抖参数——单一事实源缺失是补丁膨胀的地基 | 驱动状态缓存 = 单一事实源(I1,§4.4) | +| F-DRV-4 | `close 序列两套变体` | "close+排空+等 CLOSED"两套同构变体参数漂移 | `ch395f_sock_reset` 统一(§4.4) | +| F-DRV-8 | `命令完成阻塞原语` | 命令完成等待是阻塞原语(`HAL_Delay` 忙等、4s 上限) | `cmd_done` 状态回读判据 + 非阻塞状态机(§4.4) | +| F-DRV-13 | `链路事件无人消费` | 链路事件四个全局位读清后无人消费,运行期拔网线完全静默 | `glob_event_feed` + `link_up` 缓存(§4.4) | +| F-MS-8 | `有损镜像决策窗口` | 芯片 11 态 → net 层 8 态有损投影,有损镜像必生决策窗口 | 镜像降级为会话投影(I1、§3.3) | +| F-MS-9 | `去抖冷却自偿成本` | 去抖/冷却是镜像世界的自偿成本 | 探针去抖已删(读缓存无瞬态);reconciler 保留分级去抖(让位窗需求) | ## 8. 延后项 diff --git a/docs/门禁说明/README.md b/docs/门禁说明/README.md index 977e3697..4fec3515 100644 --- a/docs/门禁说明/README.md +++ b/docs/门禁说明/README.md @@ -53,7 +53,7 @@ | [`cubemx-survival.md`](cubemx-survival.md) | `cubemx-survival` | CubeMX 重生成会不会把定制静默抹掉(L3 审计兜底) | `.ioc`、`Src/`、`Inc/` | 已写 | | [`test-suite-switch.md`](test-suite-switch.md) | `test-suite-switch` | 板级套件开关必须互斥;全注释 = 产品构建 | `test/board/test_config.h` | 已写 | | [`font-charset.md`](font-charset.md) | `font-charset` | UI 用到的汉字是否都在子集字体里(缺字 = 白框) | `Drivers/BSP/UI/**`、字体脚本 | 已写 | -| [`ui-golden.md`](ui-golden.md) | `ui-golden` | UI 像素回归:菜单 8 状态 + 内容页 47 页黄金参考图比对 | UI 源码、模拟器 | 已写 | +| [`ui-golden.md`](ui-golden.md) | `ui-golden` | UI 像素回归:菜单 8 状态 + 内容页 53 页黄金参考图比对 | UI 源码、模拟器 | 已写 | | [`bom-guard.md`](bom-guard.md) | `bom-guard` | 带 BOM 的文件被编辑工具剥掉 BOM(ARMCC 换解码 → 假语法错) | `Drivers/BSP/UI/**` 等名单内文件 | 已写 | | [`setview-check.md`](setview-check.md) | `setview-check` | 定值查看行表与 `prot_set` 枚举序 / `g_prot_set_meta` 上下限是否同一份口径 | `tools/setview_check/**`、`ui_w_setview.*` | 已写 | | (专篇待补,档位与限度正文在 [`../UI设计/UI_自动化测试流程.md`](../UI设计/UI_自动化测试流程.md) §7.3) | `uinav-selftest` | 板端 UI 导航器认屏快检(黄金参考帧分类;完整闭环档=合入前/上板前手工跑,快检不互替) | `scripts/board_ui_nav.py`、`simulator/tests/ref/*.bin` | 指针 | diff --git a/docs/门禁说明/host-unit-tests.md b/docs/门禁说明/host-unit-tests.md index 799485a4..a8467792 100644 --- a/docs/门禁说明/host-unit-tests.md +++ b/docs/门禁说明/host-unit-tests.md @@ -124,8 +124,12 @@ ## 7. 豁免与基线 - 没有基线:新增或改动的模块必须自身通过。 -- 用例号段:`TC-HOST-<模块码>` 以 `test/host/<目录>/*` 为属主登记处(`docs/编号规范.md`); - 新增用例要带号,否则 `id-check` 会按形态/悬空规则处理。 +- 用例号段:`TC-HOST-<模块码>` 以 `test/host/<目录>/*` 为属主登记处(`docs/编号规范.md` §4.1); + **该号段 2026-10-27 起停发新号**(用户裁定「批次三,不坐」):新增用例**不配号**,写语义化测试函数名, + 现存号只读、不清、不复用。追溯器只双向比对已有集合,**不会**因为新用例没号而报错——别把它当"要带号"。 + **机器口径同日已落地**:`scripts/id_registry.json` 37 个 TC 类族全部 `frozen: true`,34 个子族游标冻结在 2026-10-27 存量最高号, + 在号段上再加新号 = `id-check` 校验 5 错误级;无专属子族的模块码落在主族 `TC`(游标仍为空),那一面由校验 3 + 冻结水位 + 评审兜住 + → `docs/编号规范.md` §4.1 末框与 §9.1 末段。 ## 8. 改这套门禁本身(新增一个模块的宿主单测) diff --git a/docs/门禁说明/ui-golden.md b/docs/门禁说明/ui-golden.md index 708c51f6..250d1b75 100644 --- a/docs/门禁说明/ui-golden.md +++ b/docs/门禁说明/ui-golden.md @@ -1,4 +1,4 @@ -# `ui-golden` — UI 像素回归(菜单 8 状态 + 内容页 47 页黄金参考) +# `ui-golden` — UI 像素回归(菜单 8 状态 + 内容页 53 页黄金参考) > 一句话:改 UI 源码时,把模拟器增量重编 + 两条 golden 回归串成一次提交门禁,**像素对不上就拦**。 > 它是包装器:真正的判据在被包装的两条脚本与参考图里。 @@ -11,7 +11,7 @@ ## 1. 它在查什么 - 菜单侧:整棵菜单树的行渲染结果(8 个状态)与参考图逐像素比对; -- 内容页侧:47 个内容页的渲染结果与参考图比对; +- 内容页侧:53 个内容页的渲染结果与参考图比对; - 参考图 `simulator/tests/ref/*.bin` **就是判据本身**。 ## 2. 为什么要它(有牙的实测) @@ -57,7 +57,7 @@ 1. **参考图即判据**:刷新参考图 = 修改判据。评审时必须盯 `simulator/tests/ref/` 的 diff; 一旦"顺手更新",此后所有回归都只在和错的基线比。**门禁无法阻止这件事**,只能靠评审。 -2. 只覆盖 8 个菜单状态 + 47 个内容页:未列入的状态(密码错误态、超长文本、缺字渲染、 +2. 只覆盖 8 个菜单状态 + 53 个内容页:未列入的状态(密码错误态、超长文本、缺字渲染、 无 SD 卡 / 挂载失败的降级显示)**不会有任何回归**。 3. 跑的是**模拟器那份配置**,不是固件那份 —— 两份 `lv_conf.h` 漂移时,回归结论不能外推到固件 (所以 `lv-conf-sync` 是本门有效性的前提)。 @@ -83,4 +83,5 @@ |---|---| | 2026-10-23 | 首篇逐项文档(含 +3 像素注入实验与"有基线 ≠ 有门禁"的缺口来源) | | 2026-10-27 | 内容页黄金参考 **40 → 47 页**(遥测死区与系数 2 页落地,`simulator/tests/ref/` 实测 47 张:7 张新增、存量 40 张逐字节零漂移);本文件的当前态计数同步改 47,历史行 27 → 40 原样保留 | +| 2026-10-27 | 内容页黄金参考 **47 → 53 页**(通讯设置页落地并绑 `#page=comm`;判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53 张,`git diff --name-status 31ca4a1f HEAD -- simulator/tests/ref` 实测只有 6 个 `A`、零 `M` ⇒ 存量 47 张逐字节零漂移);本文件的当前态计数已同步 | | 2026-10-26 | 内容页黄金参考下限 **27 → 40 页**(UI 三页落地;`simulator/tests/ref/` 入库 `shot_*.bin` 实测 40 个、`check_content_pages.py` 的 `PAGES` 40 项 / 文本断言 310 条覆盖 37 页 / 反向断言 29 条 16 页),标题与「拦不住什么」段同步;未列入状态那一段的覆盖面口径不变 | diff --git a/docs/问题记录/采集与数据处理短板评测.md b/docs/问题记录/采集与数据处理短板评测.md index 6ffc49fa..90dcf16c 100644 --- a/docs/问题记录/采集与数据处理短板评测.md +++ b/docs/问题记录/采集与数据处理短板评测.md @@ -60,11 +60,11 @@ | L1 | 1 kHz 硬件节拍、TPAFE 快照、ADC1 DMA 乒乓、语义映射 | `docs/算法/继电保护功能复现规划.md` §3.1 的「周期 CONVST 重触发」~「通道语义映射」 | | L1 | 采样有效性(静默/丢帧/倒退/错误增量) | 复现规划 §3.1(「采样有效性校验」);`docs/算法/采样有效性校验设计说明.md` | | L1 | 谐波相量入口(IA 的 2/5 次) | 复现规划 §3.1(「二次谐波/五次谐波提取入口」) | -| L2 | 全周傅氏 5 实例、双区原子切换、每拍重算 | 复现规划 §3.2(「全周傅氏系数表」~「相位输出」/L2-6/L2-7) | +| L2 | 全周傅氏 5 实例、双区原子切换、每拍重算 | 复现规划 §3.2(「全周傅氏系数表」~「相位输出」) | | L2.5 | 测频(自适应窗,45~55 Hz 最坏 0.0101 Hz) | 复现规划 §3.3(「过频 / 低频」) | -| L3 | 9 元件(三段过流/过负荷/过压/欠压/零序过压/过频/低频) | 复现规划 §3.3(「三段式相间过流」/L3-3~「零序过压」) | -| L4 | SOE + 动作记录(64 B 定长、CP56 时标、SPSC 环异步落盘) | 复现规划 §3.4(「SOE 记录」/L4-5) | -| L5 | 定值校验和 + 持久化 + 全局软压板 + 采样故障闭锁 | 复现规划 §3.5(「定值校验和」/L5-4) | +| L3 | 9 元件(三段过流/过负荷/过压/欠压/零序过压/过频/低频) | 复现规划 §3.3(「三段式相间过流」~「零序过压」) | +| L4 | SOE + 动作记录(64 B 定长、CP56 时标、SPSC 环异步落盘) | 复现规划 §3.4(「SOE 记录」) | +| L5 | 定值校验和 + 持久化 + 全局软压板 + 采样故障闭锁 | 复现规划 §3.5(「定值校验和」) | | 上行 | 104 总召/变位/遥控/定值读写/时钟同步 | `docs/子系统设计/IEC104上行设计说明.md` | ### 3.2 部分闭环 diff --git a/scripts/board_ui_nav.py b/scripts/board_ui_nav.py index 716638c2..d32ebfe0 100644 --- a/scripts/board_ui_nav.py +++ b/scripts/board_ui_nav.py @@ -993,7 +993,7 @@ def selftest_board_frames(clf): def selftest_refs(clf): - """仓库黄金参考(47 页)逐帧分类自检:每张图的期望值 = 它的文件名。 + """仓库黄金参考(53 页)逐帧分类自检:每张图的期望值 = 它的文件名。 这一组是**语义正确的基线**(模拟器与板端同一份 UI 源码),用来把"分类器错了"与 "留档帧本来就是走歪的"分开——两类失败含义完全不同,混在一起就只能靠猜。