docs(编号): 测试验证层级文档面清号 + 修上一批改名留下的双写与错族疤(27 篇)
同族第三道(文档面)。上一批只改了 10 篇测试文档,本笔清掉余下 27 篇的层级号,并修 那批机械替换留下的两类疤。 - 层级号改语义名:各单元测试说明的「**门禁层级** | L0.5 层(介于 L0 静态门禁与 L1 板端套件之间)」→「主机侧纯逻辑单测(介于静态门禁与板端测试套件之间)」; 「板端(L1)」「L1/L2」「L3 人工」等表头与列值同批改写;测试文档模板规范的模板 字段同步(它决定以后每篇测试说明怎么写这一栏)。 - 「模拟器回归」旧号(原文标 L3/L2)**不映射**成新语义名,就地写「模拟器回归」: 它与「主机侧回归」是两回事,硬映射等于造一处新的口径错。 - 修双写疤 17 处(替换串套替换串的产物):文档道改完 Lead 点名的 8 处后中途失败, Lead 接手余下 9 处——测试开发标准流程 7、CI README 1、门禁说明/host-unit-tests 1、 任务规划/测试与架构专项 1。 - 修**错族替换疤**(比双写更坏:字面改对、意思改错):ADC输入层单元测试说明与 相量层单元测试说明的「介于 L0 静态门禁与 语义取数与有效性板端套件之间」——上一批 把测试层的 L1 换成了采集链层名;本笔随该行改写一并修正。产品需求 §7.4 同类问题 在上一笔(代码道)修。 - 变更记录历史行按「行内换号、日期与技术事实不动」处理(与「卡」族先例一致), 换算表与撞族警示在 scripts/archive/id_gate/编号治理历史.md §10 / §10.1。 - 刻意未动:docs/芯片手册(外部手册原文)、UI 菜单层级残留 2 处与采集链旧层名残留 (已登记 task-12)、test/board 的链路时延段号残留(task-11)。 - 门禁:check_doc_links --all --sections-strict 通过(195 篇、章节引用 3292 处); id-check-new --worktree rc=0(剩 2 处 WARN 是变更记录行里的已停发测试专项号, 不拦提交,本笔不越族去动它);112 个改动文件 U+FFFD=0、无 CR、BOM 与 HEAD 一致; git diff --check 干净。
This commit is contained in:
@@ -523,7 +523,7 @@ typedef struct {
|
||||
|
||||
## 9. 可测试性
|
||||
|
||||
### 9.1 主机单测(L0.5)
|
||||
### 9.1 主机侧纯逻辑单测
|
||||
|
||||
`ui_data` 是纯内存模块,天然可测:
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ git commit
|
||||
|
||||
### 2.3 快速自查入口
|
||||
|
||||
- 提交前等价自查(静态门禁 + L0b + 主机侧纯逻辑单测):`scripts/ci_local.py`(用 `.venv` 解释器跑)。
|
||||
- 提交前等价自查(静态门禁 + 提交信息门禁 + 主机侧纯逻辑单测):`scripts/ci_local.py`(用 `.venv` 解释器跑)。
|
||||
- 单独跑某一条:`-m pre_commit run <hook-id> --files <改动的文件>`;全库:`--all-files`。
|
||||
- 临时跳过只在紧急情况,且必须在提交说明里写明跳过了什么、为什么、随后怎么补——**跳之前先跑 §2.4 自查表**(`--no-verify` 一次跳过 49 条,连 `gitlint` 一起跳,见 §2.2「两个阶段」行的 49/50 口径)。
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ CH395F 套件 5 档构建全部链接通过,最坏态 `RW_IRAM1` 91,088 B / 69
|
||||
且**撕裂在单测里测不出来**(时序相关),必须在设计上消除;
|
||||
3. **DE 收尾必须有超时路径覆盖**(RS485 半双工特有陷阱)。
|
||||
|
||||
**完成判据**:B2 设计文档体现上述三条;协议解析函数有对应 主机侧纯逻辑单测单测。(B1 侧:ADC 无协议解析面,其"同一时刻性"由原子快照保证、已闭环。)
|
||||
**完成判据**:B2 设计文档体现上述三条;协议解析函数有对应主机侧纯逻辑单测。(B1 侧:ADC 无协议解析面,其"同一时刻性"由原子快照保证、已闭环。)
|
||||
|
||||
### 2.3 「体积门禁 CI 强制路径」残余:CI 体积门禁盲区(✅ 2026-10-25 销项)
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
|
||||
| # | 事项 | 优先级 | 问题与证据 | 处置方向 | 出处 |
|
||||
|---|---|---|---|---|---|
|
||||
| CI 远端闭环未验证 | **CI 远端闭环未验证**(runner 本机已就绪) | 中 | **剩余两件**:③ 推送远端跑一次两 job 闭环验证(**跑通证据尚无留档**,故仍按"未验证"对待);④ 第二机交叉验证(不同工具安装路径,验 `DTU_*` 环境变量方案,→ `../环境与部署/本地工具路径环境变量方案.md` 变更记录 TODO)。①② 已就绪,见 `../CI/远程CI完整指南.md` §0 仓库状态行 | 完成前**提交前仍须在本地跑 `scripts/ci_local.py` 或 `pre-commit run --all-files` 自查**(L0 唯一保障,不能因"已配 CI"而放宽) | `.gitea/workflows/ci.yml`;`../CI/远程CI完整指南.md`;`../测试说明/测试开发标准流程.md` §9 |
|
||||
| CI 远端闭环未验证 | **CI 远端闭环未验证**(runner 本机已就绪) | 中 | **剩余两件**:③ 推送远端跑一次两 job 闭环验证(**跑通证据尚无留档**,故仍按"未验证"对待);④ 第二机交叉验证(不同工具安装路径,验 `DTU_*` 环境变量方案,→ `../环境与部署/本地工具路径环境变量方案.md` 变更记录 TODO)。①② 已就绪,见 `../CI/远程CI完整指南.md` §0 仓库状态行 | 完成前**提交前仍须在本地跑 `scripts/ci_local.py` 或 `pre-commit run --all-files` 自查**(静态门禁唯一保障,不能因"已配 CI"而放宽) | `.gitea/workflows/ci.yml`;`../CI/远程CI完整指南.md`;`../测试说明/测试开发标准流程.md` §9 |
|
||||
|
||||
## 5. RS-485、内存与仓库观察
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
| 通讯参数段 | `App/protocol/iec104/iec104_comm.c/h`(道G task-8) | `[comm]` 段注册 + ca/端口 getter 合法带自愈 / setter 守卫 | cfg_file 引擎(只注册不属主) |
|
||||
|
||||
> 三个纯逻辑模块 + 真实数据源(`App/algo/` 全纯逻辑)可整体在主机侧编译 →
|
||||
> L0.5 单测覆盖**全协议栈**(`test/host/iec104/`),与 `test/host/prot_elm/` 同模式。
|
||||
> 主机侧纯逻辑单测覆盖**全协议栈**(`test/host/iec104/`),与 `test/host/prot_elm/` 同模式。
|
||||
|
||||
## 3. 点表(V0.1)
|
||||
|
||||
@@ -229,8 +229,8 @@
|
||||
|
||||
| 层 | 内容 |
|
||||
|---|---|
|
||||
| L0.5 | `test/host/iec104/`:APCI 重组/坏帧、STARTDT 握手、总召全流程(含 RBT/分片/预算)、COV、k/w 窗、t1/t2/t3(假时基)、遥控两段式与 PN、YK12 软压板两段转真 + 置脏(B7)、定值两段写 + set_sealed 后 selfcheck、C_RD(含本地项不可寻址)、C_CS(含闰年)、错地址/错类型 EVI;**板端默认源真实链**(ring→phasor→freq→meas 快照,非桩):值/品质透传、CLIPPED→IV、0x401D 绑频(收敛 ±0.02 Hz / 未新鲜 IV)、死区运行时化 + 2 点确认/回摆撤销、seq 去重(104 改读快照与频率点绑定,TC-IEC104-025~028)+ 运行参数段路由/两段式/RMW(TC-IEC104-029)+ `[comm]` 段真链(道G task-8,TC-IEC104-031~033:注册/出厂模板/沉降重载/带外自愈/setter 守卫/ca 端到端 = C_RD 正答与旧写死 ca=1 转 COT13+EVI);`test/host/iec104_serv/` 钉端口与 ca 取值来源(TC-I104SRV-001b/c);`test/host/cfg_file/` 钉段/键容量上限(TC-CONFIG-056/057);`test/host/run_par/`:[run_par] 注册/落盘往返/守卫/系数直通合法带(TC-RUNPAR-001~008);`test/host/prot_set/`:blob 往返 + 损坏整表拒绝(all-or-nothing)|
|
||||
| L0 | `iec104-unit-test` 钩子 + cppcheck/coding-standard |
|
||||
| 主机侧纯逻辑单测 | `test/host/iec104/`:APCI 重组/坏帧、STARTDT 握手、总召全流程(含 RBT/分片/预算)、COV、k/w 窗、t1/t2/t3(假时基)、遥控两段式与 PN、YK12 软压板两段转真 + 置脏(B7)、定值两段写 + set_sealed 后 selfcheck、C_RD(含本地项不可寻址)、C_CS(含闰年)、错地址/错类型 EVI;**板端默认源真实链**(ring→phasor→freq→meas 快照,非桩):值/品质透传、CLIPPED→IV、0x401D 绑频(收敛 ±0.02 Hz / 未新鲜 IV)、死区运行时化 + 2 点确认/回摆撤销、seq 去重(104 改读快照与频率点绑定,TC-IEC104-025~028)+ 运行参数段路由/两段式/RMW(TC-IEC104-029)+ `[comm]` 段真链(道G task-8,TC-IEC104-031~033:注册/出厂模板/沉降重载/带外自愈/setter 守卫/ca 端到端 = C_RD 正答与旧写死 ca=1 转 COT13+EVI);`test/host/iec104_serv/` 钉端口与 ca 取值来源(TC-I104SRV-001b/c);`test/host/cfg_file/` 钉段/键容量上限(TC-CONFIG-056/057);`test/host/run_par/`:[run_par] 注册/落盘往返/守卫/系数直通合法带(TC-RUNPAR-001~008);`test/host/prot_set/`:blob 往返 + 损坏整表拒绝(all-or-nothing)|
|
||||
| 静态门禁 | `iec104-unit-test` 钩子 + cppcheck/coding-standard |
|
||||
| 台架 | `@test` 锚点(104 服务启动/监听行);**PC 主站客户端 `test/host/iec104_master.py`(纯标准库,B6 2026-10-12 就绪)**:STARTDT/总召四组+actterm/C_SE 定值两段写→YX15·16 变位(COT=3)/恢复复归/YK11 两段正答/C_RD 读回,台架往返 **18/18 PASS**(证据 `../测试说明/boot_log_data/20261012_B6_iec104_roundtrip_log.txt`);**B7 持久化模式 `--b7-pre/--b7-post/--b7-mark/--b7-check`**(中间 ST-LINK 复位):YK12 两段投退→保护退出/联动、**定值与压板态跨复位保持**(含非默认 7.5 往返),**21/21 + 13/13 + 4/4+4/4 PASS**(证据 `../测试说明/boot_log_data/20261012_B7_iec104_persist_log.txt`);**B9 SOE 腿**(同工具同轮回归):自发 YX 断言 **M_SP_TB_1 带 ts**(工具解析 type 30/1 双型)+ FTP 下载 `/log/soe.log` 经 `test/host/soe/decode_soe.py` 解码断言(校验和/seq 单调/CP56↔epoch UTC 交叉),pre 21/21 + post 13/13 不回归;lib60870 级第三方全流程归 B10 |
|
||||
|
||||
## 8. V0.1 限制与待办(如实登记)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
> 描述,已清理;实施史与逐次决策见 §10 与文末变更记录,未删)。
|
||||
> **相关文档**:
|
||||
> [`运行与自检记录设计说明.md`](运行与自检记录设计说明.md)(复位记录与 `selftest.log`)、
|
||||
> [`../测试说明/看门狗单元测试说明.md`](../测试说明/看门狗单元测试说明.md)(L0.5 判定状态机单测)、
|
||||
> [`../测试说明/看门狗单元测试说明.md`](../测试说明/看门狗单元测试说明.md)(主机侧纯逻辑单测,判定状态机)、
|
||||
> [`../测试说明/实机测试实施指南.md`](../测试说明/实机测试实施指南.md)(故障注入台架流程)、
|
||||
> [`../架构设计/项目启动顺序管理.md`](../架构设计/项目启动顺序管理.md)(启动时序与就绪屏障)、
|
||||
> [`../测试说明/ST-LINK_CLI_复位使用说明.md`](../测试说明/ST-LINK_CLI_复位使用说明.md)(复位语义)、
|
||||
@@ -28,7 +28,7 @@
|
||||
| 启动时刻 | **defaultTask 存储 bring-up 之后**(`task_wd_start()`);`MX_IWDG_Init()` 只填参数不启动 | `default_task.c:175`、`Src/iwdg.c` 的 `USER CODE BEGIN 0` |
|
||||
| 复位归因 | 停喂 → IWDG 自然溢出 → `RCC_CSR.IWDGRSTF`(不用 `NVIC_SystemReset`) | `task_wd_poll()` |
|
||||
| 复位原因上报 | `main.c` 锁存 `g_rcc_csr_latched` + 清 `RMVF` → `app_main.c` 打 `reset cause:` 并把看门狗复位记入 `selftest.log` | `app_main.c:117-130` |
|
||||
| 主机侧单测 | 39 断言(L0.5,pre-commit `watchdog-unit-test`) | `test/host/watchdog/` |
|
||||
| 主机侧单测 | 39 断言(主机侧纯逻辑单测,pre-commit `watchdog-unit-test`) | `test/host/watchdog/` |
|
||||
| 重生成审计 | 21 项 / 1 警告 / 0 失败(含 iwdg 延后启动、看门狗挂载、复位锁存) | `scripts/check_cubemx_survival.py` |
|
||||
|
||||
---
|
||||
@@ -291,7 +291,7 @@ static HAL_StatusTypeDef iwdg_deferred_start(const IWDG_HandleTypeDef *a_h) { (v
|
||||
|
||||
### 4.7 模拟器与主机侧
|
||||
|
||||
- **模拟器不编译本模块**(`simulator/` 无 `task_watchdog.c`,也无 `--watchdogtest`)——原计划的模拟器侧看门狗自检已降级,判定逻辑改由 **L0.5 主机侧单测**覆盖(39 断言,见 §7)。
|
||||
- **模拟器不编译本模块**(`simulator/` 无 `task_watchdog.c`,也无 `--watchdogtest`)——原计划的模拟器侧看门狗自检已降级,判定逻辑改由**主机侧纯逻辑单测**覆盖(39 断言,见 §7)。
|
||||
- 不可建模的部分:真实复位、LSI 漂移、`RCC_CSR` 标志——必须真机(§7)。
|
||||
|
||||
---
|
||||
@@ -441,7 +441,7 @@ snapshot == required → miss_streak=0、证据门闩复位、喂狗
|
||||
| V6 | 测试套件共存 | 🟡 部分 | 2026-10-02 曾出现测试构建 IWDG 复位循环,已按 §3.5 掩码规则修复;未跑完整套件用例矩阵 |
|
||||
| V7 | 上电复位辨识 | 🟡 未单独注入 | `reset cause:` 每次开机都在打(POR 串已实现),未做专门断电对照 |
|
||||
| V8 | 引脚复位辨识 | ⏳ 未做 | 未做定向注入(`external pin (NRST)` 分支已实现) |
|
||||
| 主机单测 | 判定状态机主机侧单测 | ✅ 39/39 | `test/host/watchdog/run_unit_test.py`(L0.5,pre-commit `watchdog-unit-test`) |
|
||||
| 主机单测 | 判定状态机主机侧单测 | ✅ 39/39 | `test/host/watchdog/run_unit_test.py`(主机侧纯逻辑单测,pre-commit `watchdog-unit-test`) |
|
||||
| 门禁触发 | 门禁接入 | ✅ | 改 `App/util/task_watchdog*.{c,h}` 或 `test/host/watchdog/**` 触发 |
|
||||
| 模拟器 | `--watchdogtest` | ⏳ **已降级不做** | 位图与判定逻辑已由主机单测覆盖;模拟器不编译本模块 |
|
||||
|
||||
@@ -700,8 +700,8 @@ enter/exit 段还能读出"此刻仍在段内已多久"——**永久阻塞的
|
||||
| 2026-09-29 | **第二次重生成验证 + 机制修正** | 审计脚本**实测有效**(准确检出 5 处覆盖)。⚠️ **关键发现**:CubeMX 换了一代中断函数模板(旧 `HardFault_Handler`/`HardFault_Report` 自造区 → 新函数体内 `HardFault_IRQn 0`/`W1_HardFault_IRQn 0`),**"生成区外的自造标记区"同样不被保留**——上一轮假设被证伪。据此升级恢复方案:报告实现迁入 `USER CODE BEGIN 0`(标准保护位);Handler 本体改为生成区整函数替换(`#define` 改名保留生成实体,链接器自动移除,`.map` 已验证)+ `HARD-FAULT-ANCHOR` 锚点。同步修正审计脚本(`hardfault-handler` 改 `wholefile` + 锚点)、`UNPROTECTED_LEGACY` 清单、§10.1/§10.2 全文。`@build` 0 错误 0 警告 |
|
||||
| 2026-09-29 | **3 个 Fault 升级为崩溃采集** | `MemManage` / `BusFault` / `UsageFault` 由空 `while(1)`(死机静默)升级为与 `HardFault` 同一套采集:生成区整函数替换(各带独立锚点 `MEMFAULT-ANCHOR` / `BUSFAULT-ANCHOR` / `USAGEFAULT-ANCHOR`)+ `#define X X_Gen` 改名保留生成实体(链接器各移除 2 字节)。新增 `fault_handler_enable()` 使能 `SHCSR` 后三者(默认只开 HardFault,不开则故障被升级、看不出根因),声明入 `Inc/stm32f4xx_it.h`、调用在 `app_main_init()` 第一行。因 ARMCC `__asm` 取不到 C 数据符号,改为 per-fault C 入口 `fault_entry_{hard,mem,bus,usage}` 带故障名调 `hard_fault_report(tag, frame)`。**刻意不启用** `UNALIGN_TRP` / `DIV_0_TRP`(避免把普通 bug 升级成死机)。审计脚本新增 `fault-handlers-3` / `fault-handler-enable` 两项、`UNPROTECTED_LEGACY` 清掉 3 个 Fault(WARN 2→1)、清单 16→18 项。`@build` 0 错误 0 警告;审计 18 通过 / 1 警告 / 0 失败 |
|
||||
| 2026-10-01 | **查证 `fault_handler_enable()` 的 CubeMX 可行性** | 用户问"能否通过 CubeMX 配置"→ **否**。查 CubeMX 安装目录模板源码得三重证据:① `tpl_main_c.ftl` 的 `MX_NVIC_Init()` 只在 `systemHandler=="false"` 时生成 `HAL_NVIC_EnableIRQ`,而 `NVIC-STM32F417_Modes.xml` 中三个 Fault 都标 `Y`(=systemHandler)→ 永不生成使能代码;② **全部模板中 `SHCSR` 只出现 1 次**且是 MPU 清理的 `&= ~MEMFAULTENA`(禁用),**无任何 `\|=` 置位**;③ `.ioc` 无 MPU(`Mcu.IP` 列表),且 `HAL_MPU_Enable` 只置 `MEMFAULTENA` 一位。实测印证:`HAL_MspInit()` 里三个 Fault 一行代码都没生成。结论:只能手工写,但属 **L2**(`USER CODE BEGIN 1` 区)→ 重生成不丢,有审计项兜底。另新增警示:`.ioc` 的 `NVIC.MemoryManagement_IRQn=true` **不代表已使能**(第 1 段只是"向量可见")。写入 §10.2 与四个 Fault 采集小节 |
|
||||
| 2026-10-01 | **新增 §7.3 主机侧单元测试(L0.5)** | 硬件访问收敛到薄平台层(`App/util/task_watchdog.h` 末段声明 3 个 `__weak` 钩子:喂狗/启动/取 tick),MCU 端行为零变化(`@build` 0/0)。新建 `test/host/watchdog/`(`watchdog_test.c` 39 用例 + `run_unit_test.py` MSVC 运行器 + `stub/` 三桩头),`#include` 生产源码以保证测的是真代码。挂 pre-commit `watchdog-unit-test` 钩子。新增 `docs/测试说明/看门狗单元测试说明.md`;`App/util/README.md`、`test/host/README.md`、`测试开发标准流程.md`(新增 L0.5 层)同步。顺带改善:`s_last_tick` 由函数局部 static 提为文件 static(可测性 + 模块状态归一)。§5.1 文件表与 §10 步骤表登记;§7.2 S1 优先级降低(位图逻辑已由 U1 覆盖) |
|
||||
| 2026-10-02 | **P0 根因修复:IWDG 超时被静默配成复位默认值(比设计短 5 倍)** | round 3~8 反复出现的"空 STOR(0 字节截断)→ IWDG 误复位"**根因不是"poller 自身停摆"**。真因:`Src/iwdg.c` 为实现**延后启动**(§4.5 决策 C)删掉了 `HAL_IWDG_Init()`(`IWDG-DEFER-START-ANCHOR`),而 `HAL_IWDG_Init()` 是**唯一**会把 `hiwdg.Init.Prescaler/Reload` 写进 IWDG 寄存器的路径——删掉后 `MX_IWDG_Init()` 只填结构体,`WD_IWDG_PRESCALER`/`WD_IWDG_RELOAD` 沦为**死宏**,寄存器停在**复位默认值** `PR=0`(÷4)/`RLR=4095`。实测(SWD 回读)实际超时 = 4×4096/LSI ≈ **349~512 ms**,而设计是 64×1250/LSI ≈ **1701~2498 ms**。两个后果:① `WD_MISS_TOLERANCE`(8×100ms=800ms)**永远等不到**就被 IWDG 复位 → **停喂判定分支成死代码**(这正是 §7"STALL 从未执行"的真正原因,当时据此误判为"poller 停摆");② 任何 >400ms 的心跳缺口都直接误复位(空 STOR 的 FTL flush 使 NET/FTP/UI 心跳缺 ~400ms)。**修复**:`wd_platform_start()`(`__weak`,**不在 CubeMX 生成区**,重生成不丢)补上按设计值配置:解锁(`KR=0x5555`) → `PR` → 等 `SR.PVU` 清 → `RLR` → 等 `SR.RVU` 清 → `KR=0xCCCC`(不等同步会静默丢弃后写的那只,SWD 实测)。`.map` 证实 `Removing stm32f4xx_hal_iwdg.o(i.HAL_IWDG_Init), (94 bytes)`,故这是全固件**唯一**写 PR/RLR 的点。**验证**:SWD 现场改回设计值后,同一空 STOR 由 **5/5 复位 → 100% 成功**;固件烧录后 3 次开机回读均为 `PR=4/RLR=1249`。`@build` 0/0;本地 CI L0+L0.5 全绿(278/278 断言) |
|
||||
| 2026-10-01 | **新增 §7.3 主机侧单元测试** | 硬件访问收敛到薄平台层(`App/util/task_watchdog.h` 末段声明 3 个 `__weak` 钩子:喂狗/启动/取 tick),MCU 端行为零变化(`@build` 0/0)。新建 `test/host/watchdog/`(`watchdog_test.c` 39 用例 + `run_unit_test.py` MSVC 运行器 + `stub/` 三桩头),`#include` 生产源码以保证测的是真代码。挂 pre-commit `watchdog-unit-test` 钩子。新增 `docs/测试说明/看门狗单元测试说明.md`;`App/util/README.md`、`test/host/README.md`、`测试开发标准流程.md`(新增主机侧纯逻辑单测层)同步。顺带改善:`s_last_tick` 由函数局部 static 提为文件 static(可测性 + 模块状态归一)。§5.1 文件表与 §10 步骤表登记;§7.2 S1 优先级降低(位图逻辑已由 U1 覆盖) |
|
||||
| 2026-10-02 | **P0 根因修复:IWDG 超时被静默配成复位默认值(比设计短 5 倍)** | round 3~8 反复出现的"空 STOR(0 字节截断)→ IWDG 误复位"**根因不是"poller 自身停摆"**。真因:`Src/iwdg.c` 为实现**延后启动**(§4.5 决策 C)删掉了 `HAL_IWDG_Init()`(`IWDG-DEFER-START-ANCHOR`),而 `HAL_IWDG_Init()` 是**唯一**会把 `hiwdg.Init.Prescaler/Reload` 写进 IWDG 寄存器的路径——删掉后 `MX_IWDG_Init()` 只填结构体,`WD_IWDG_PRESCALER`/`WD_IWDG_RELOAD` 沦为**死宏**,寄存器停在**复位默认值** `PR=0`(÷4)/`RLR=4095`。实测(SWD 回读)实际超时 = 4×4096/LSI ≈ **349~512 ms**,而设计是 64×1250/LSI ≈ **1701~2498 ms**。两个后果:① `WD_MISS_TOLERANCE`(8×100ms=800ms)**永远等不到**就被 IWDG 复位 → **停喂判定分支成死代码**(这正是 §7"STALL 从未执行"的真正原因,当时据此误判为"poller 停摆");② 任何 >400ms 的心跳缺口都直接误复位(空 STOR 的 FTL flush 使 NET/FTP/UI 心跳缺 ~400ms)。**修复**:`wd_platform_start()`(`__weak`,**不在 CubeMX 生成区**,重生成不丢)补上按设计值配置:解锁(`KR=0x5555`) → `PR` → 等 `SR.PVU` 清 → `RLR` → 等 `SR.RVU` 清 → `KR=0xCCCC`(不等同步会静默丢弃后写的那只,SWD 实测)。`.map` 证实 `Removing stm32f4xx_hal_iwdg.o(i.HAL_IWDG_Init), (94 bytes)`,故这是全固件**唯一**写 PR/RLR 的点。**验证**:SWD 现场改回设计值后,同一空 STOR 由 **5/5 复位 → 100% 成功**;固件烧录后 3 次开机回读均为 `PR=4/RLR=1249`。`@build` 0/0;本地 CI 静态门禁+主机侧纯逻辑单测全绿(278/278 断言) |
|
||||
| 2026-10-03 | **V2/V4/V5 台架验收闭环(N-03 销项)** | 三用例全部通过。**V2 任务饿死**:`netTask` 循环插 `while(1)` → 800ms 声明 `[WD] task stall: mask=0x00000001 req=0x00000037`,≤2.5s 触发 IWDG 复位(`reset cause: IWDG`,`RCC_CSR=0x24000000`)。⚠️ 首轮实测**不复位**(stall 已声明、`miss` 累至 1157≈115s):根因是**停摆证据落盘自身喂狗**——`wd_stall_evidence()`→`sys_log_evidence()` 在停喂窗口里每 100ms 轮都写 NAND(`/log/wdev.log` + `SYSLOG_RUN` flush 诱导),经 `nand_ftl_wd_feed()` **直接重装 IWDG**,"停喂"语义被证据机制抵消。修复:加 `s_stall_evidenced` **每 episode 门闩**(每轮停摆只落盘一次,此后不再触碰 NAND)→ 复测复位正常。**V4 关中断死循环**:`__disable_irq()` 后插 `while(1)` → 设备每 ~4.9s 稳定复位(20+ 周期,`reset cause: IWDG`),证实 IWDG **独立于中断**(PRIMASK 屏蔽 TIM7/SysTick/PendSV 仍能复位)。**V5 长期不误杀**:FTP 512KB 上/下传 + `e02_probe` 网络探针持续 **30 min** → **120/120 次 FTP 传输全 PASS(MATCH)、零 IWDG 复位**(唯一复位为起始烧录的 `software (NVIC_SystemReset)`)。Step 4 堆告警全程未触发(`min_ever=4728 B` > 3072 B)。**遗留观察(已登记 `待处理内容.md`)**:V5 期间 44 次 `task stall`(`mask=0x00000001` 持续 ≥800ms)**100% 与 `e02_probe` 的 30s 挂起客户端→`time_sync` 掉线+监听重建(`listen FAIL`×2,约 4s)重合**;隔离实验证实「仅 FTP(无探针)120s = 0 次 stall」→ 该停摆为**探针场景专属**,非一般负载误杀。因缺口(~1s)< IWDG 最坏超时(1701ms)而未复位,与 §4.3 包含关系一致 |
|
||||
| 2026-10-09 | **IWDG 延后启动由 L3 升级为 L2:不再需要每次重生成后手工删那一句** | 起因是提问"能否在初始化函数下方的自定义区里先反初始化"。查证:**反初始化不可行**——IWDG 一旦启动就**无法停止**(参考手册:唯一停止手段是复位),故不能在 `IWDG_Init 2` 区撤销。改用等价且免手工的做法:`Src/iwdg.c` 的 `USER CODE BEGIN 0`(标准保护位)放空实现 `iwdg_deferred_start()` + `#define HAL_IWDG_Init(a_h) iwdg_deferred_start(a_h)`,把**本编译单元里**那句生成调用 `HAL_IWDG_Init(&hiwdg)` 改名到空实现 → 生成区保持 CubeMX 原样、**重生成零 diff**。`.map` 证实 `Removing stm32f4xx_hal_iwdg.o(i.HAL_IWDG_Init), (94 bytes)`、`iwdg.o(i.MX_IWDG_Init)` 只引用 `.bss`(空实现被编译器消掉)。审计项 `iwdg-defer-start` 相应由 `wholefile`+锚点注释改为 `protected`(区 `0`)+ 代码符号断言(`iwdg_deferred_start` / `#define HAL_IWDG_Init`——`protected` 比对前会剥掉注释,锚点注释不能当断言)。§10.2 硬教训②与 L3 存量清单同步:L3 只剩 4 个 Fault Handler 实体(同一招可根治,尚未做)。`@build` 0 错误 0 警告;审计 21 通过 / 1 警告 / 0 失败 |
|
||||
| 2026-10-09 | **4 个 Fault Handler 由 L3 升级为 L2:重生成完全零 diff** | 用与 iwdg 同一招。`Src/stm32f4xx_it.c` 的 4 个 `__asm void X_Handler` 从**生成区**搬进 `USER CODE BEGIN 0`,且必须写在文件头那 4 个 `#define X X_Gen` **之前**(写在之后会被自己的宏改名);配套的 `#undef X` 随之删除(不再需要)。生成区恢复 CubeMX 原样(`void HardFault_Handler(void)` + `HardFault_IRQn 0` / `W1_HardFault_IRQn 0` 标记区),4 个锚点注释(`HARD-FAULT-ANCHOR` / `MEMFAULT-ANCHOR` / `BUSFAULT-ANCHOR` / `USAGEFAULT-ANCHOR`)全部退役。`.map` 复核:`startup_stm32f407xx.o(RESET) refers to stm32f4xx_it.o(.emb_text) for HardFault_Handler`(向量表仍指向我们的 `__asm` 版)、`.emb_text → i.fault_entry_* → i.hard_fault_report`、4 个 `*_Gen` 各 `Removing ... (2 bytes)`。审计项 `hardfault-handler` / `fault-handlers-3` 由 `wholefile`+锚点改为 `protected`(区 `0`)+ 代码符号断言。**L3 清单清空**,重生成预期零 diff。`@build` 0 错误 0 警告;审计 21 通过 / 1 警告 / 0 失败 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ADC 输入层单元测试说明(主机侧)
|
||||
|
||||
> **模块**:ADC Input Layer(语义取数与预处理,`App/algo/adc_input.c`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层)——在 PC 上离线验证语义通道映射、
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证语义通道映射、
|
||||
> STM32 ADC1 的 DMA 乒乓帧消费(含半区选择)、TPAFE 快照消费与取数守卫,零硬件、零烧录、
|
||||
> 毫秒级、不占 MCU 的 RAM。
|
||||
> **状态**:✅ 已落地(**101** 用例,`/W4` 0 告警,已挂 pre-commit 钩子)
|
||||
@@ -97,7 +97,7 @@ test/host/adc_input/adc_input_test.c
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发范围** | `App/algo/adc_input.c` / `adc_input.h`,或 `test/host/adc_input/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||||
| **门禁层级** | **L0.5 层**(介于 L0 静态门禁与 语义取数与有效性板端套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **pre-commit 钩子** | `adc-input-unit-test`(本地钩子,`.pre-commit-config.yaml` §7e) |
|
||||
| **汇总行** | `=== ADC Input Tests: 70/70 PASSED (failed=0) ===` |
|
||||
| **与板端测试的关系** | 互补:本单测保证**映射 / 契约 / 故障路径**正确;板端保证 **HAL 配置(Rank 顺序、采样时间、引脚)** 与真机一致 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 卷锁单元测试说明(主机侧)
|
||||
|
||||
> **模块**:Volume Lock(`App/fs/vol_lock.c` + `App/fs/vol_lock.h`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层)——在 PC 上离线验证 NAND FatFs 卷串行化
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证 NAND FatFs 卷串行化
|
||||
> 互斥锁的创建 / 获取 / 释放契约,零硬件、零烧录、毫秒级、不占 MCU 的 RAM。
|
||||
> **状态**:✅ 已落地(33 断言点,`/W4` 0 告警,已挂 pre-commit 钩子)
|
||||
> **日期**:2026-10-10
|
||||
@@ -107,7 +107,7 @@ test/host/vol_lock/vol_lock_test.c
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发范围** | `App/fs/vol_lock.c` / `vol_lock.h`,或 `test/host/vol_lock/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||||
| **门禁层级** | **L0.5 层**(介于 L0 静态门禁与 L1 板端套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **pre-commit 钩子** | `vol-lock-unit-test`(本地钩子,`.pre-commit-config.yaml` §7f) |
|
||||
| **汇总行** | `=== Volume Lock Tests: 33/33 PASSED (failed=0) ===` |
|
||||
| **与板端测试的关系** | 互补:本单测保证**锁的调用契约**(fail-open / 幂等 / 透传);板端保证**真实的 FTL 并发不互踩**(dhara journal 竞争),后者需真机 + FTP 并发压测 |
|
||||
@@ -194,8 +194,8 @@ test/host/vol_lock/vol_lock_test.c
|
||||
|
||||
| 层次 | 判据 | 手段 |
|
||||
|---|---|---|
|
||||
| 本单测(L0.5) | 锁的**调用契约**(fail-open / 幂等 / 透传 / 判空) | 主机侧桩注入 |
|
||||
| 板端(L1) | 卷访问**真的串行**了(无 dhara journal 竞争、无 IWDG 误复位) | 真机 + FTP 与 syslog 并发压测 |
|
||||
| 本单测(主机侧纯逻辑单测) | 锁的**调用契约**(fail-open / 幂等 / 透传 / 判空) | 主机侧桩注入 |
|
||||
| 板端测试套件 | 卷访问**真的串行**了(无 dhara journal 竞争、无 IWDG 误复位) | 真机 + FTP 与 syslog 并发压测 |
|
||||
|
||||
`vol_lock.h` 记录的历史根因(2026-10-02:defaultTask 与 ftpTask 并发 FTL 写 → dhara journal
|
||||
竞争 → 恢复旋转饥饿喂狗点 → IWDG 误复位)只能由板端判据复现;本单测负责**防止契约被改坏**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 开机账本单元测试说明(主机侧)
|
||||
|
||||
> **模块**:Boot Stats(`App/util/boot_stats.c` + `App/util/boot_stats.h`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层)——在 PC 上离线验证"上电次数"这块
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证"上电次数"这块
|
||||
> RTC 用户 SRAM 账本的**四态判定、记录校验、I2C 失败分支、幂等与饱和**,以及复位原因
|
||||
> 优先级链的唯一出处。零硬件、零烧录、毫秒级。
|
||||
> **状态**:✅ 已落地(21 断言点,MSVC `/W4` 与 cppcheck 均 0 告警)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 时间同步单元测试说明(主机侧)
|
||||
|
||||
> **模块**:Time Sync(`App/sync/time_sync.c` + `App/sync/time_sync.h`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层)——在 PC 上离线验证 TCP 时间同步协议
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证 TCP 时间同步协议
|
||||
> 状态机(分片喂狗延时 / 监听建立与退避 / 时间包重组 / 任务主循环),零硬件、零烧录、
|
||||
> 毫秒级、不占 MCU 的 RAM。
|
||||
> **状态**:✅ 已落地(80 断言点,`/W4` 0 告警,已挂 pre-commit 钩子)
|
||||
@@ -115,7 +115,7 @@ test/host/time_sync/time_sync_test.c
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发范围** | `App/sync/time_sync.c` / `time_sync.h`、`App/util/task_watchdog.h`,或 `test/host/time_sync/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||||
| **门禁层级** | **L0.5 层**(介于 L0 静态门禁与 L1 板端套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **pre-commit 钩子** | `time-sync-unit-test`(本地钩子,`.pre-commit-config.yaml` §7g) |
|
||||
| **汇总行** | `=== Time Sync Tests: 80/80 PASSED (failed=0) ===` |
|
||||
| **与板端测试的关系** | 互补:本单测保证**延时节奏与重组逻辑**符合设计;板端保证**真实 TCP 时序 + IWDG 不误复位**(该心跳缺口的实际余量只能由板端长跑判定) |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
> **性质**:**登记册**(living document)。新增模拟器后端差异时必须在此登记一行,不得只写在别处。
|
||||
> **上游依据**:`docs/网络/Net层阻塞模型架构设计.md` §10(net 层 pending 化现状)。
|
||||
> **相关**:`UI设计/LVGL模拟器部署与使用说明.md`(部署与使用)、`simulator/README.md`(目录与架构)、
|
||||
> `测试说明/测试开发标准流程.md`(L3 门禁在测试分层中的位置)。
|
||||
> `测试说明/测试开发标准流程.md`(模拟器回归门禁在测试分层中的位置)。
|
||||
|
||||
---
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
> 模拟器覆盖的是 **「纯逻辑 + 平台抽象之上」的那一层**:UI 场景/渲染/导航、配置引擎、日志记录格式、
|
||||
> `net_*` BSD API 之上的协议状态机。它**不覆盖**「硬件时序 / 中断 / 真实复位 / CH395F 与 NAND 芯片行为」。
|
||||
> 因此 **L3(模拟器回归)与 L1/L2(板端套件 / `@test` 闭环)互补而非替代**——见 §3 的"什么时候必须回真机"。
|
||||
> 因此 **模拟器回归与板端测试套件 / 主机侧回归(`@test` 闭环)互补而非替代**——见 §3 的"什么时候必须回真机"。
|
||||
|
||||
---
|
||||
|
||||
@@ -63,10 +63,10 @@
|
||||
|
||||
| 差距 | 模拟器行为 | 真机行为 | 影响 | 回归补测 |
|
||||
|---|---|---|---|---|
|
||||
| **IWDG 硬件行为** | **不编译看门狗模块**(`simulator/` 无 `task_watchdog.c`,也无 IWDG 桩) | 独立看门狗,LSI 时钟驱动,超时真复位 | **真实复位行为、LSI 漂移、`RCC_CSR` 标志均不可建模**;位图/判定逻辑也不在模拟器内跑——改由主机侧单测覆盖 | 真机 `@test`(复位原因日志 + 故障注入 V2/V4)+ 看门狗主机侧单测(L0.5,39 断言)<br>`看门狗设计说明.md` §4.7 / §7 |
|
||||
| **IWDG 硬件行为** | **不编译看门狗模块**(`simulator/` 无 `task_watchdog.c`,也无 IWDG 桩) | 独立看门狗,LSI 时钟驱动,超时真复位 | **真实复位行为、LSI 漂移、`RCC_CSR` 标志均不可建模**;位图/判定逻辑也不在模拟器内跑——改由主机侧单测覆盖 | 真机 `@test`(复位原因日志 + 故障注入 V2/V4)+ 看门狗主机侧纯逻辑单测(39 断言)<br>`看门狗设计说明.md` §4.7 / §7 |
|
||||
| **CH395F 芯片** | 无(Winsock 代其 BSD API) | SPI 寄存器/命令/中断、PHY 协商、真实以太网帧 | 寄存器时序、命令回复、中断风暴、PHY 100M 全双工强制 | 板端 CH395F 套件(52 断言点)+ `@test` |
|
||||
| **GD5F NAND** | 无 | SPI NAND:BBT 扫描、ECC、坏块管理、dhara FTL | 坏块/ECC/写入失败等路径完全不可测 | 板端 GD5F 套件(76 断言点)+ `@test` |
|
||||
| **SD2506 RTC** | 无 | I2C RTC:时间读写、闹钟、温度、电池电压 | 时间源相关(`sys_clock_init` 降级路径)在模拟器上不可触发 | 板端 RTC 套件(18 断言点)+ `sys_clock` L0.5(已覆盖算术面) |
|
||||
| **SD2506 RTC** | 无 | I2C RTC:时间读写、闹钟、温度、电池电压 | 时间源相关(`sys_clock_init` 降级路径)在模拟器上不可触发 | 板端 RTC 套件(18 断言点)+ `sys_clock` 主机侧纯逻辑单测(已覆盖算术面) |
|
||||
| **开机账本 boot_stats(上电次数 / 复位原因快照)** | **不编译** `App/util/boot_stats.c`(依赖 SD2506 用户 SRAM 与 `RCC_CSR`);UI「运行报告」页的四个数在模拟器上是 `simulator/main.c` 的**固定假数据**(`boot_count=7`、`seq_state=延续`、`rst_cause=上电复位`、104 四枚 3 / 128 / 0 / 4096、`uptime_s=0`) |`boot_stats_init()` 读 SRAM → 自增 → 写回 → 快照驻留;四个 104 观测计数由 iec104Task 真实累加(跨重连不清零、断电归零) | 「自增有没有真落 SRAM」「电池域掉电后是否落 RESTARTED」「写失败是否只少计不重复」「104 计数是否跨重连累计」四类事在模拟器上**结构上不可见**;黄金参考只拍得到显示口径(范围标签在不在屏上、值有没有串行、`boot_ready=0` 是否显 `--` 而不是 0) | 主机单测 `test/host/boot_stats/`(SRAM 状态机与四态判定)+ 真机 `@test`:上电一次看 `boot_count` +1、拔电池后落 RESTARTED、IWDG 复位后 `rst_cause=IWDG`、主站连一次看 `conn_up` +1 |
|
||||
| **TPAFE5160 ADC** | 无 | SPI ADC + BUSY EXTI 中断 | 中断驱动采集链路 | 待功能实现(上游 B1) |
|
||||
| **RS-485 半双工** | 无 | UART4 + DE 方向控制,DWT 微秒延时 | DE 收发切换时序(特有陷阱) | 待功能实现(上游 B2) |
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
## 3. 什么时候必须回真机(决策规则)
|
||||
|
||||
模拟器回归(L3)通过后,**满足以下任一条件即必须走真机**(`@test` / 板端套件):
|
||||
模拟器回归通过后,**满足以下任一条件即必须走真机**(`@test` / 板端套件):
|
||||
|
||||
1. **改动触及硬件访问层**:`Drivers/BSP/` 下任何驱动的寄存器/时序/中断代码
|
||||
2. **改动触及 §2.3 硬件专属差距的任一子系统**的初始化或运行时行为
|
||||
@@ -96,7 +96,7 @@
|
||||
5. **涉及启动段(`osKernelStart` 之前)**(启动段硬件初始化)
|
||||
6. **涉及内存预算**:新增任务/缓冲/栈(堆后端不同;模拟器堆不可作依据)
|
||||
|
||||
反之,**纯 UI 场景/渲染/导航、配置解析算术、日志记录编解码格式**的改动,模拟器 + L0.5 可给出足够信心。
|
||||
反之,**纯 UI 场景/渲染/导航、配置解析算术、日志记录编解码格式**的改动,模拟器 + 主机侧纯逻辑单测可给出足够信心。
|
||||
|
||||
---
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
- **何时登记**:新增/修改 `simulator/sim/` 下任何后端、或发现"某逻辑模拟器走了不同分支"时,**必须**在 §2 加一行。
|
||||
- **格式**:沿用本表列(差距 / 模拟器行为 / 真机行为 / 影响 / 回归补测)。
|
||||
- **联动**:若该差距影响 L3 门禁的可信度,须同步在 `UI设计/UI_自动化测试流程.md` 引用本清单。
|
||||
- **联动**:若该差距影响模拟器回归门禁的可信度,须同步在 `UI设计/UI_自动化测试流程.md` 引用本清单。
|
||||
- **收敛**:当某差距被后续工作**消除**时,不删行——改标 ~~删除线~~ + 注明消除日期与提交,保留"曾经差在哪"的历史。
|
||||
|
||||
---
|
||||
|
||||
+21
-21
@@ -31,7 +31,7 @@
|
||||
| 主机侧回归 | `@test` 串口日志标记 + LVGL 模拟器像素 / 池水位回归 | 启动序列、UI 渲染、LVGL 内存池水位 |
|
||||
| 人工目检 | 上机屏幕、PC 端交互工具 | 视觉内容、自动化无法覆盖的场景 |
|
||||
|
||||
> **主机侧纯逻辑单测与 主机侧回归的区别**:主机侧回归的模拟器回归依赖 SDL / LVGL 与图形输出;主机侧纯逻辑单测是**纯计算**单测
|
||||
> **主机侧纯逻辑单测与主机侧回归的区别**:主机侧回归的模拟器回归依赖 SDL / LVGL 与图形输出;主机侧纯逻辑单测是**纯计算**单测
|
||||
> (无窗口、无硬件、秒级),专测"判定逻辑"。目前仅看门狗模块使用,见
|
||||
> [`看门狗单元测试说明.md`](看门狗单元测试说明.md)。适格对象:算法 / 状态机 / 协议编解码 /
|
||||
> 参数校验等**无副作用**的模块。
|
||||
@@ -45,7 +45,7 @@
|
||||
|---|---|
|
||||
| 任何源码提交 | G0 pre-commit(提交时自动触发)+ G1 `@build` |
|
||||
| App / Drivers 功能代码(板端行为可观测) | G0 + G1 + G2 `@test`(模块标记) |
|
||||
| **有 主机侧纯逻辑单测单测的模块(如看门狗)** | G0(含该模块单测钩子,自动)+ G1 `@build`(+ 台架项另按需 G2) |
|
||||
| **有主机侧纯逻辑单测的模块(如看门狗)** | G0(含该模块单测钩子,自动)+ G1 `@build`(+ 台架项另按需 G2) |
|
||||
| UI(菜单 / 内容页 / 字体 / 样式 / 几何) | G0 + G1 + G2 + G3 ui-verify 五步 |
|
||||
| UI 新增中文文案 | 另需 font-regen(字体重生成 + 回归),**先于提交** |
|
||||
| `test/` 套件或 PC 端工具代码 | G1 + G2(套件标题标记 / 工具自测) |
|
||||
@@ -270,16 +270,16 @@ static void phaseN_run(void) {
|
||||
| **`test/run_all_suites.py`** | `test/` | **多套件回归编排** + **`--check-build` 只编译不烧录**(纯离机,验证 4 套件可编译性);见 §3.6 |
|
||||
| `.dsh/skills/serial-boot-test/scripts/verify_log.py` | `test/host/` | 日志标记校验(Required / Forbidden → PASS / FAIL)+ 可选套件汇总行校验 |
|
||||
| **`test/host/common/unit_test_harness.py`** | `test/host/common/` | **主机侧纯逻辑单测通用单测 harness**:工具链探测 / `INCLUDE`-`LIB` 拼接 / 编译运行 / 退出码契约。各模块运行器只做声明式配置 |
|
||||
| **`test/host/watchdog/run_unit_test.py`** | `test/host/watchdog/` | **主机侧纯逻辑单测主机侧单测**:声明 `TestBuild` 后调 harness,穷举看门狗判定状态机(零硬件、秒级)。**需本机装有 VS(`cl.exe`)**,见 §4.2 环境要求 |
|
||||
| **`test/host/sys_clock/run_unit_test.py`** | `test/host/sys_clock/` | **主机侧纯逻辑单测主机侧单测**:穷举 `sys_clock` 时间换算 / 星期 / 惰性再基准化 / RTC 故障降级(100 断言,首批主机侧单测) |
|
||||
| **`test/host/cfg_file/run_unit_test.py`** | `test/host/cfg_file/` | **主机侧纯逻辑单测主机侧单测**:INI 解析 / 值编解码 / 段注册 / load-save-poll 契约,内存虚拟 FS 驱动(139 断言,首批主机侧单测) |
|
||||
| **`test/host/nand_port/run_unit_test.py`** | `test/host/nand_port/` | **主机侧纯逻辑单测主机侧单测**:离线编译真实 `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` + 真实 dhara,以 **RAM 假芯片**驱动,149 断言钉死 is_free 整页扫描与六个入口的越界守卫 |
|
||||
| **`test/host/watchdog/run_unit_test.py`** | `test/host/watchdog/` | **主机侧纯逻辑单测**:声明 `TestBuild` 后调 harness,穷举看门狗判定状态机(零硬件、秒级)。**需本机装有 VS(`cl.exe`)**,见 §4.2 环境要求 |
|
||||
| **`test/host/sys_clock/run_unit_test.py`** | `test/host/sys_clock/` | **主机侧纯逻辑单测**:穷举 `sys_clock` 时间换算 / 星期 / 惰性再基准化 / RTC 故障降级(100 断言,首批主机侧单测) |
|
||||
| **`test/host/cfg_file/run_unit_test.py`** | `test/host/cfg_file/` | **主机侧纯逻辑单测**:INI 解析 / 值编解码 / 段注册 / load-save-poll 契约,内存虚拟 FS 驱动(139 断言,首批主机侧单测) |
|
||||
| **`test/host/nand_port/run_unit_test.py`** | `test/host/nand_port/` | **主机侧纯逻辑单测**:离线编译真实 `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` + 真实 dhara,以 **RAM 假芯片**驱动,149 断言钉死 is_free 整页扫描与六个入口的越界守卫 |
|
||||
| **`scripts/ci_local.py`** | `scripts/` | **本地跑与 CI 等价的门禁**(静态门禁 + 主机侧纯逻辑单测):自动选用装了 pre-commit 的解释器;`.map` 自适应跳过 `mem-numbers`;退出码 0/1/2 与 CI 对齐。见 §9.3 |
|
||||
| `.serialtest/config.json` | 仓库 | **串口端口单一配置源**(当前 COM6 / 115200);换端口只改这个文件 |
|
||||
| `test/host/ftp_*.py`(4 个)/ `ch395f_socket_test.py` / `time_sync_server.py` | `test/host/` | PC 端交互测试工具(FTP 客户端 / socket 回显 / 对时推送) |
|
||||
| `simulator/tests/*.py`(4 个) | `simulator/tests/` | LVGL 模拟器回归脚本(见 §5) |
|
||||
|
||||
### 4.2 主机侧纯逻辑单测主机侧单元测试(纯逻辑,无硬件)
|
||||
### 4.2 主机侧纯逻辑单测(纯逻辑,无硬件)
|
||||
|
||||
**适用对象**:**无副作用**的纯逻辑模块——算法、状态机、协议编解码、参数校验等
|
||||
(例:看门狗的位图判定 / 掩码 / 计数阈值 / 整数回绕)。
|
||||
@@ -312,9 +312,9 @@ static void phaseN_run(void) {
|
||||
|
||||
- 平台层的抽取必须**零行为变化、零额外开销**(MCU 端默认实现即原逻辑;`@build` 须 0/0);
|
||||
- 测试专用钩子(如静态态复位)用 `#ifdef <模块>_UNIT_TEST` 守卫,MCU 构建不编入(零符号);
|
||||
- 单测**不能替代**板端验证:真机寄存器 / 复位原因 / 时序仍须 板端测试套件/主机侧回归台架闭环。
|
||||
- 单测**不能替代**板端验证:真机寄存器 / 复位原因 / 时序仍须走板端测试套件 / 主机侧回归台架闭环。
|
||||
|
||||
#### 4.2.1 主机侧纯逻辑单测适格模块台账(2026-10-02 加)
|
||||
#### 4.2.1 主机侧纯逻辑单测:适格模块台账(2026-10-02 加)
|
||||
|
||||
**问题**:主机侧纯逻辑单测起步阶段只有"看门狗"一个先例,**下一个该测谁**靠临时判断,易遗漏高价值模块。
|
||||
|
||||
@@ -511,9 +511,9 @@ flowchart LR
|
||||
|
||||
| 层 | 上 CI? | 载体 | 理由 |
|
||||
|---|---|---|---|
|
||||
| **静态门禁** 静态门禁 | ✅ | `pre-commit run --all-files`(全部钩子) | 纯 Python + cppcheck,零硬件 |
|
||||
| **主机侧纯逻辑单测** 主机侧纯逻辑单测 | ✅ | `test/host/**/run_unit_test.py`(MSVC 离线编译) | 纯标准库 + `cl.exe`,零硬件 |
|
||||
| 板端测试套件板端套件 | ⛔ | `test/board/` | 需真机 + 串口 + ST-LINK |
|
||||
| **静态门禁** | ✅ | `pre-commit run --all-files`(全部钩子) | 纯 Python + cppcheck,零硬件 |
|
||||
| **主机侧纯逻辑单测** | ✅ | `test/host/**/run_unit_test.py`(MSVC 离线编译) | 纯标准库 + `cl.exe`,零硬件 |
|
||||
| 板端测试套件 | ⛔ | `test/board/` | 需真机 + 串口 + ST-LINK |
|
||||
| 主机侧回归 `@test` 闭环 | ⛔ | 烧录 + 串口采集 | 同上 |
|
||||
| 人工目检 | ⛔ | 上机屏幕 | 人的判断 |
|
||||
|
||||
@@ -545,8 +545,8 @@ flowchart LR
|
||||
|
||||
| job | 覆盖层 | 一句话职责 |
|
||||
|---|---|---|
|
||||
| `l0-gates` | **静态门禁** | `pre-commit run --all-files`(cppcheck / 编码规范 / 文档一致性 / 主机侧单测钩子)+ **提交信息门禁**(gitlint,见上方 🔴);无 `.map` 时自适应跳过 `mem-numbers` |
|
||||
| `l05-host-unit-tests` | **主机侧纯逻辑单测** | 探测 MSVC 后遍历 `test/host/**/run_unit_test.py` 逐个跑并聚合断言数;**未装 MSVC 时降级为 WARNING 跳过**(不硬失败,但等于该层裸奔) |
|
||||
| `static-gates` | **静态门禁** | `pre-commit run --all-files`(cppcheck / 编码规范 / 文档一致性 / 主机侧单测钩子)+ **提交信息门禁**(gitlint,见上方 🔴);无 `.map` 时自适应跳过 `mem-numbers` |
|
||||
| `host-unit-tests` | **主机侧纯逻辑单测** | 探测 MSVC 后遍历 `test/host/**/run_unit_test.py` 逐个跑并聚合断言数;**未装 MSVC 时降级为 WARNING 跳过**(不硬失败,但等于该层裸奔) |
|
||||
|
||||
触发:`push`(`paths-ignore: docs/** / **/*.md / .workbuddy-ai/**` → **纯文档改动不会触发 CI**)
|
||||
与 `pull_request`,另留 `workflow_dispatch` 手动触发;同分支 `cancel-in-progress: true`。
|
||||
@@ -561,8 +561,8 @@ CI 只在 push / PR 时跑,且需 runner 就绪。**提交前自查、或 runn
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py # 静态门禁 + 提交信息门禁 + 主机侧纯逻辑单测(与 CI 等价)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --l0-only # 只跑 静态门禁(含 提交信息门禁 提交信息门禁)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --l05-only # 只跑 主机侧纯逻辑单测
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --static-only # 只跑静态门禁(含提交信息门禁)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --host-unit-only # 只跑主机侧纯逻辑单测
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --commits 20 # 提交信息校验最近 20 条
|
||||
```
|
||||
|
||||
@@ -570,7 +570,7 @@ CI 只在 push / PR 时跑,且需 runner 就绪。**提交前自查、或 runn
|
||||
仍不可用时退出码 2 并列出**全部已尝试路径**。
|
||||
⚠️ 该候选表与 CI 侧 **`scripts/ci_find_python.ps1`** 必须**同序同步**——2026-10-11 的 静态门禁全灭就是
|
||||
workflow 单方面把候选收窄成"PATH python + 仓库 `.venv`"(runner 上两者都是死路)导致的。
|
||||
- **提交信息门禁 提交信息门禁**:与 CI 的 `ci_gitlint_range.ps1` 同语义(`--all-files` 跑不到 commit-msg stage)。
|
||||
- **提交信息门禁**:与 CI 的 `ci_gitlint_range.ps1` 同语义(`--all-files` 跑不到 commit-msg stage)。
|
||||
- **`.map` 自适应与 CI 完全一致**:本地有 `.map` 就照常跑 `mem-numbers`,没有就跳过。
|
||||
- 退出码:`0` 通过 / `1` 门禁或单测失败 / `2` 环境错误。
|
||||
|
||||
@@ -617,9 +617,9 @@ CI 就静默停摆(2026-10-11 实测 run 16~21 共 12 个 job 零日志)。
|
||||
| `docs/测试说明/串口助手设计说明.md` | 串口调试工具(Python 串口助手) |
|
||||
| `docs/测试说明/自动化测试流程.md` | @test 闭环细节 |
|
||||
| `docs/测试说明/测试文档模板规范.md` | 测试文档模板与命名 |
|
||||
| `docs/测试说明/看门狗单元测试说明.md` | 看门狗判定状态机(主机侧纯逻辑单测主机侧) |
|
||||
| `docs/测试说明/系统时钟单元测试说明.md` | 系统时钟换算(主机侧纯逻辑单测主机侧) |
|
||||
| `docs/测试说明/配置文件单元测试说明.md` | 配置引擎解析/契约(主机侧纯逻辑单测主机侧) |
|
||||
| `docs/测试说明/看门狗单元测试说明.md` | 看门狗判定状态机(主机侧纯逻辑单测) |
|
||||
| `docs/测试说明/系统时钟单元测试说明.md` | 系统时钟换算(主机侧纯逻辑单测) |
|
||||
| `docs/测试说明/配置文件单元测试说明.md` | 配置引擎解析/契约(主机侧纯逻辑单测) |
|
||||
|
||||
## 变更记录
|
||||
|
||||
@@ -632,7 +632,7 @@ CI 就静默停摆(2026-10-11 实测 run 16~21 共 12 个 job 零日志)。
|
||||
| 2026-10-02 | **S6**:`test/run_all_suites.py` 新增 **`--check-build`**(只编译不烧录,纯离机,不需要 MCU/串口/ST-LINK);新增 **§3.6.1** 说明其解决的问题(测试构建无独立门禁,2026-08~09 CH395F 套件实例)、判定语义与用法。**实测 4 套件全部 `BUILD_OK: 0 errors, 0 warnings`**(即 T-4 提到的"CH395F 不可编译"已修复) |
|
||||
| 2026-10-02 | **S1-b + S2**:新增 **§4.2.1「主机侧纯逻辑单测适格模块台账」**(判据 + 模块状态表 + "两条实证":sys_clock / cfg_file 首测即各抓一处生产缺陷);§4.1 登记 `test/host/cfg_file/` 新单测。同步新增 [`配置文件单元测试说明.md`](配置文件单元测试说明.md)(含抓出的 `s_encode` 返回值误判 P0 缺陷记录) |
|
||||
| 2026-10-02 | **S3 + S14**:§9.1 第 2 条改正(原文"强制跑 `mem-numbers`"自相矛盾 → 改为"按 `.map` 有无自适应跳过")并加 🔴 门禁盲区声明;§9.2 补 S3 结果可见性(`Tee-Object` 落盘 + `$GITHUB_STEP_SUMMARY` + `::notice::` 汇总行,不用 `actions/upload-artifact`)。另:§4.1 登记 `test/host/sys_clock/` 新单测(S1-a) |
|
||||
| 2026-10-01 | 新增 **§9 CI 持续集成(Gitea Actions,T-6)**:`.gitea/workflows/ci.yml`(`l0-gates` + `l05-host-unit-tests`,Windows runner);**静态门禁 + 主机侧纯逻辑单测上 CI、板端测试套件/主机侧回归/人工目检留本地**;刻意不跑 `@build`、`.map` 缺失时自适应跳过 `mem-numbers`;新增 `scripts/ci_local.py` 本地等价命令(§4.1 登记);§1 补 CI 说明;`docs/任务规划/README.md` T-6 销项 |
|
||||
| 2026-10-01 | 新增 **§9 CI 持续集成(Gitea Actions,T-6)**:`.gitea/workflows/ci.yml`(`static-gates` + `host-unit-tests`,Windows runner);**静态门禁 + 主机侧纯逻辑单测上 CI、板端测试套件/主机侧回归/人工目检留本地**;刻意不跑 `@build`、`.map` 缺失时自适应跳过 `mem-numbers`;新增 `scripts/ci_local.py` 本地等价命令(§4.1 登记);§1 补 CI 说明;`docs/任务规划/README.md` T-6 销项 |
|
||||
| 2026-10-01 | §3.6 新增 **`test/run_all_suites.py`** 多套件回归编排(`try/finally` 无条件还原套件开关;T-5);`test/README.md` 同步 |
|
||||
| 2026-10-01 | §3.2 断言宏改为**集中内核**:新增 `test/board/test_common.h`(`test_stats_t` + `TEST_CHECK_IMPL`/`TEST_REPORT_IMPL`),4 套件各自用一行宏绑定自身统计变量(T-4;零行为变化、零 RAM 增量、`@build` 0/0,四套件均 0/0 验证)。附 `test_common.h` 不 include `dbg_log.h` 的原因(`DBG_TAG` 冻结 → `#47-D`) |
|
||||
| 2026-10-01 | 新增 §7.1 钩子 **`test-suite-switch`**(测试套件开关=产品构建,防测试固件静默提交)+ §3.1 铁律(跑完套件回归务必改回产品构建);依据 `docs/任务规划/README.md` T-1(测试架构评审 P0)。同步更新 `../代码规范/pre-commit使用说明.md` §2.13 |
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|---|---|
|
||||
| `测试开发标准流程.md` | 测试开发总流程(规划 → 实现 → 门禁 → 沉淀);本文是其"测试规划(文档先行)"环节的可执行模板 |
|
||||
| `自动化测试流程.md` | `@test` 闭环命令、参数与失败模式;本文"回归接入"章节引用它 |
|
||||
| `docs/UI设计/UI_自动化测试流程.md` | UI 类改动的模拟器回归门禁(L2 层) |
|
||||
| `docs/UI设计/UI_自动化测试流程.md` | UI 类改动的模拟器回归门禁 |
|
||||
| `docs/问题记录/<模块>_Trap_Records.md` | 陷阱记录;测试文档"已知陷阱"章节引用它 |
|
||||
|
||||
---
|
||||
@@ -61,7 +61,7 @@
|
||||
| `@test` 标记 | 套件结尾汇总行(如 `=== Storage Tests:`)或阶段标题行;**必须与串口实际输出逐字一致**(`verify_log.py` 子串匹配) |
|
||||
| 禁止模式 | 默认 `FAIL;ERROR;HardFault;assert`,可按模块增补 |
|
||||
| PC 端脚本 | `test/*.py` 工具清单与调用方式 |
|
||||
| 门禁层级 | L1(板端套件)/ L2(串口标记 + 模拟器回归)/ L3(人工目检),见 `测试开发标准流程.md` §1 |
|
||||
| 门禁层级 | 板端测试套件 / 主机侧回归(串口标记 + 模拟器回归)/ 人工目检,见 `测试开发标准流程.md` §1 |
|
||||
|
||||
> 标记字符串一律**从代码 `DBG_*` 实际输出复制**(含大小写 / 冒号 / 空格);代码改了输出文案必须同步改文档(§7.6)。
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
| **测试步骤** | 操作步骤(编号列表,动作具体到 API 或操作) |
|
||||
| **预期结果** | 系统应表现的行为 |
|
||||
| **通过标准** | 可量化的判定条件 |
|
||||
| **机器可判** | 串口标记 / TEST_CHECK / 像素参考 / 池水位 / L3 人工(见 §7.4) |
|
||||
| **机器可判** | 串口标记 / TEST_CHECK / 像素参考 / 池水位 / 人工目检(见 §7.4) |
|
||||
| **覆盖原则** | 设计原则短语(可选,关联设计原则) |
|
||||
```
|
||||
|
||||
@@ -200,7 +200,7 @@ TC-{MOD}-{NNN}
|
||||
| 返回值 / 寄存器 / 数据 | 板端 `XXX_TEST_CHECK` 断言 | 驱动命令、协议交互、数据完整性 |
|
||||
| 像素参考 | LVGL 模拟器回归(`simulator/tests/`) | UI 渲染、字体、样式 |
|
||||
| 内存池水位 | 模拟器池基线 / 往返常量 | LVGL 对象泄漏、结构漂移 |
|
||||
| L3 人工 | 人工目检 / 交互 | 屏幕观感等无法机器判定的项(必须写目检步骤) |
|
||||
| 人工目检 | 人工目检 / 交互 | 屏幕观感等无法机器判定的项(必须写目检步骤) |
|
||||
|
||||
- 好的示例:`70/70 成功率(100%)`、`串口显示 "PHY_CHANGE: 0x01"`、`10/10 回显匹配`、`=== Storage Tests: 11/11 PASSED (failed=0) ===`
|
||||
- 差的示例:`功能正常`、`系统稳定`
|
||||
@@ -257,7 +257,7 @@ TC-{MOD}-{NNN}
|
||||
|---|---|
|
||||
| `@test` 标记 | `=== XXX Tests:`(套件结尾汇总行的确切字符串) |
|
||||
| PC 端脚本 | `test/xxx_test.py` |
|
||||
| 门禁层级 | L1 + L2 |
|
||||
| 门禁层级 | 板端测试套件 + 主机侧回归 |
|
||||
|
||||
## 2. 阶段 1:[阶段名]
|
||||
|
||||
@@ -282,7 +282,7 @@ TC-{MOD}-{NNN}
|
||||
| **测试步骤** | 1. ... |
|
||||
| **预期结果** | ... |
|
||||
| **通过标准** | ...(可量化) |
|
||||
| **机器可判** | 串口标记 / TEST_CHECK / 像素参考 / 池水位 / L3 人工 |
|
||||
| **机器可判** | 串口标记 / TEST_CHECK / 像素参考 / 池水位 / 人工目检 |
|
||||
|
||||
## 附录 A:通过/失败汇总矩阵
|
||||
|
||||
@@ -330,4 +330,4 @@ TC-{MOD}-{NNN}
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-09-13 | 补充完善:模块缩写表更新为项目实际用法(`NET`/`GD5F`/`STO`/`RTC`/`FTP`,取代旧版 `PHY`/`NAND`,历史文档不回改);概述新增"套件与入口"与"回归接入"章节(衔接 `test_config.h` 套件互斥与 `@test` 闭环,标记须与串口实际输出逐字一致);用例表新增"机器可判"字段(四通道 + L3 人工层);类型枚举新增"集成测试";TC 序号约定"分阶段套件百位放阶段号"(TC-NET-601 = Phase 6);新增附录 C API 覆盖;陷阱引用路径修正为 `docs/问题记录/`;新增 §7.6 文档维护规范与文末变更记录章节 |
|
||||
| 2026-09-13 | 补充完善:模块缩写表更新为项目实际用法(`NET`/`GD5F`/`STO`/`RTC`/`FTP`,取代旧版 `PHY`/`NAND`,历史文档不回改);概述新增"套件与入口"与"回归接入"章节(衔接 `test_config.h` 套件互斥与 `@test` 闭环,标记须与串口实际输出逐字一致);用例表新增"机器可判"字段(四通道 + 人工目检层);类型枚举新增"集成测试";TC 序号约定"分阶段套件百位放阶段号"(TC-NET-601 = Phase 6);新增附录 C API 覆盖;陷阱引用路径修正为 `docs/问题记录/`;新增 §7.6 文档维护规范与文末变更记录章节 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 相量层单元测试说明(主机侧)
|
||||
|
||||
> **模块**:Phasor Layer(全周傅氏相量计算,`App/algo/phasor.c` + `App/algo/sample_ring.c`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层)——在 PC 上离线验证全周傅氏相量计算与
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证全周傅氏相量计算与
|
||||
> 采样环缓冲,零硬件、零烧录、毫秒级、不占 MCU 的 RAM。
|
||||
> **状态**:✅ 已落地(57 用例,`/W4` 0 告警,已挂 pre-commit 钩子)
|
||||
> **日期**:2026-10-10
|
||||
@@ -84,7 +84,7 @@ test/host/phasor/phasor_test.c
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发范围** | `App/algo/phasor.c` / `phasor.h`、`App/algo/sample_ring.c` / `sample_ring.h`,或 `test/host/phasor/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||||
| **门禁层级** | **L0.5 层**(介于 L0 静态门禁与 语义取数与有效性板端套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **pre-commit 钩子** | `phasor-unit-test`(本地钩子,`.pre-commit-config.yaml` §7d) |
|
||||
| **汇总行** | `=== Phasor Tests: 57/57 PASSED (failed=0) ===` |
|
||||
| **与板端测试的关系** | 互补:本单测保证**算法正确性**;板端保证 **1 kHz 采样节拍与 TPAFE 快照链路**(「周期 CONVST 重触发」/「TPAFE 快照消费」)正常 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 看门狗单元测试说明(主机侧)
|
||||
|
||||
> **模块**:Task Watchdog(IWDG + 任务心跳位图,`App/util/task_watchdog.c`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层,见 §1.5)——在 PC 上离线验证
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(见 §1.5)——在 PC 上离线验证
|
||||
> 判定状态机,零硬件、零烧录、秒级、不占 MCU 的 RAM。
|
||||
> **状态**:✅ 已落地(47 断言,`@build` 0/0,已挂 pre-commit 钩子)
|
||||
> **日期**:2026-10-01(2026-10-25 v1.5 按位分账改造,见变更记录)
|
||||
@@ -74,7 +74,7 @@ test/host/watchdog/watchdog_test.c
|
||||
> 该工具链逻辑**不在本模块目录内**(2026-10-01 抽取):已沉淀为通用 harness
|
||||
> [`../../test/host/common/unit_test_harness.py`](../../test/host/common/unit_test_harness.py),
|
||||
> 本模块的 `run_unit_test.py` 只**声明** `TestBuild`(源文件 / 桩目录 / 汇总行标记)。
|
||||
> 新增 L0.5 单测时直接复用 harness,见 [`../../test/host/common/README.md`](../../test/host/common/README.md)。
|
||||
> 新增主机侧纯逻辑单测时直接复用 harness,见 [`../../test/host/common/README.md`](../../test/host/common/README.md)。
|
||||
|
||||
### 1.4 运行方式
|
||||
|
||||
@@ -90,7 +90,7 @@ test/host/watchdog/watchdog_test.c
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发范围** | `App/util/task_watchdog.c` / `task_watchdog.h`,或 `test/host/watchdog/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||||
| **门禁层级** | **L0.5 层**(介于 L0 静态门禁与 L1 板端套件之间):主机侧、秒级、纯逻辑,比板端套件便宜、比静态检查更强 |
|
||||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑,比板端套件便宜、比静态检查更强 |
|
||||
| **pre-commit 钩子** | `watchdog-unit-test`(本地钩子,`.pre-commit-config.yaml` §7) |
|
||||
| **汇总行** | `=== Task Watchdog Tests: N/N PASSED (failed=0) ===` |
|
||||
| **与板端测试的关系** | 互补:本单测保证**判定逻辑**正确;板端 `@test` 故障注入(V2/V4,见 `docs/子系统设计/看门狗设计说明.md` §7 验证记录)保证**真机复位链路**正确 |
|
||||
@@ -169,9 +169,9 @@ if (s_miss_streak >= WD_MISS_TOLERANCE) { DBG_ERROR("[WD] task stall: ..."); }
|
||||
|
||||
| 层 | 覆盖 | 状态 |
|
||||
|---|---|---|
|
||||
| **本单测(L0.5)** | 判定状态机全部逻辑分支与边界 | ✅ 已落地 |
|
||||
| 板端故障注入(L1/L2) | 真机:单任务卡死 → 停喂 → IWDG 真复位 → `RCC_CSR` 记 `IWDGRSTF` | ✅ 已台架验收(V2/V4,2026-10-03) |
|
||||
| 长跑(L2) | ≥30 min 无假复位(`miss_count` 稳定) | ✅ 已台架验收(V5,30 min / 120×FTP 零复位,2026-10-03) |
|
||||
| **本单测(主机侧纯逻辑单测)** | 判定状态机全部逻辑分支与边界 | ✅ 已落地 |
|
||||
| 板端故障注入(板端测试套件 / 主机侧回归) | 真机:单任务卡死 → 停喂 → IWDG 真复位 → `RCC_CSR` 记 `IWDGRSTF` | ✅ 已台架验收(V2/V4,2026-10-03) |
|
||||
| 长跑(主机侧回归) | ≥30 min 无假复位(`miss_count` 稳定) | ✅ 已台架验收(V5,30 min / 120×FTP 零复位,2026-10-03) |
|
||||
|
||||
> 本单测**不能**替代板端验证:IWDG 实际溢出时间、LSI 频率漂移、`SHCSR`/复位原因
|
||||
> 寄存器的真机行为,仍需 `@test` 台架闭环确认。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 系统时钟单元测试说明(主机侧)
|
||||
|
||||
> **模块**:System Clock(墙钟时间维护,`App/util/sys_clock.c`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层)——在 PC 上离线验证时间换算与
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证时间换算与
|
||||
> 墙钟基准逻辑,零硬件、零烧录、秒级、不占 MCU 的 RAM。
|
||||
> **状态**:✅ 已落地(20 用例 / 100 断言点,`@build` 0/0,已挂 pre-commit 钩子)
|
||||
> **日期**:2026-10-02
|
||||
@@ -92,7 +92,7 @@ test/host/sys_clock/sys_clock_test.c
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发范围** | `App/util/sys_clock.c` / `sys_clock.h`、`Drivers/BSP/SD2506/sd2506.h`,或 `test/host/sys_clock/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||||
| **门禁层级** | **L0.5 层**(介于 L0 静态门禁与 L1 板端套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **pre-commit 钩子** | `sys-clock-unit-test`(本地钩子,`.pre-commit-config.yaml` §7b) |
|
||||
| **汇总行** | `=== System Clock Tests: N/N PASSED (failed=0) ===` |
|
||||
| **与板端测试的关系** | 互补:本单测保证**换算与基准逻辑**正确;板端 `SD2506_Test_Guide.md` 保证**真机 I2C 读写链路**正确 |
|
||||
@@ -248,8 +248,8 @@ TC-SYSCLK-019 的期望星期由 Python `datetime.date(...).weekday()` 独立生
|
||||
|
||||
| 层 | 覆盖 | 状态 |
|
||||
|---|---|---|
|
||||
| **本单测(L0.5)** | 换算逻辑 / 星期 / 再基准化 / 故障降级 | ✅ 已落地(100 断言点) |
|
||||
| 板端(L1/L2) | 真机 SD2506 I2C 读写、掉电保持、与 PC 对时联动 | 见 [`SD2506_Test_Guide.md`](SD2506_Test_Guide.md) |
|
||||
| **本单测(主机侧纯逻辑单测)** | 换算逻辑 / 星期 / 再基准化 / 故障降级 | ✅ 已落地(100 断言点) |
|
||||
| 板端测试套件 / 主机侧回归 | 真机 SD2506 I2C 读写、掉电保持、与 PC 对时联动 | 见 [`SD2506_Test_Guide.md`](SD2506_Test_Guide.md) |
|
||||
|
||||
> 本单测**不替代**板端验证:I2C 时序、BCD 编解码、掉电保持、温度读等真机行为,
|
||||
> 仍需 `@test` 台架闭环确认。
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
- **初始化回归**:`App/task/ui_task.c` 已有 `UI port init: OK` / `UI menu init: OK` / `UI net sink: started`
|
||||
打印(见 `@test` 命令示例);将其加入 `-Required` 即可经串口自动回归 UI 初始化。
|
||||
- **屏幕内容回归**:由 LVGL 模拟器像素回归覆盖(文本级断言 + 60 张黄金参考 + 池水位门禁,
|
||||
见 `docs/UI设计/UI_自动化测试流程.md`);上机屏幕仍需人工目检(L3 层,见 `测试开发标准流程.md` §1)。
|
||||
见 `docs/UI设计/UI_自动化测试流程.md`);上机屏幕仍需人工目检(见 `测试开发标准流程.md` §1)。
|
||||
|
||||
## 扩展用法
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 配置文件引擎单元测试说明(主机侧)
|
||||
|
||||
> **模块**:Config File Engine(通用配置文件引擎,`App/cfg/cfg_file.c`)
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**(L0.5 层)——在 PC 上离线验证 INI 解析、
|
||||
> **测试类型**:**主机侧纯逻辑单元测试**——在 PC 上离线验证 INI 解析、
|
||||
> 值类型编解码、段注册校验与 load/save/poll 契约,零硬件、零烧录、毫秒级、不占 MCU 的 RAM。
|
||||
> **状态**:✅ 已落地(20 用例 / 139 断言点,`@build` 0/0,已挂 pre-commit 钩子)
|
||||
> **日期**:2026-10-02
|
||||
@@ -96,7 +96,7 @@ test/host/cfg_file/cfg_file_test.c
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| **触发范围** | `App/cfg/cfg_file.c` / `cfg_file.h`、`App/cfg/cfg_file_store.h`、`App/log/sys_log.h`,或 `test/host/cfg_file/**`、`test/host/common/unit_test_harness.py` 变动 |
|
||||
| **门禁层级** | **L0.5 层**(介于 L0 静态门禁与 L1 板端套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **门禁层级** | **主机侧纯逻辑单测**(介于静态门禁与板端测试套件之间):主机侧、秒级、纯逻辑 |
|
||||
| **pre-commit 钩子** | `cfg-file-unit-test`(本地钩子,`.pre-commit-config.yaml` §7c) |
|
||||
| **汇总行** | `=== Config File Tests: N/N PASSED (failed=0) ===` |
|
||||
| **与板端测试的关系** | 互补:本单测保证**解析/编解码/契约逻辑**正确;板端保证 **NAND 上 FatFS 真实读写链路**正确 |
|
||||
@@ -205,7 +205,7 @@ test/host/cfg_file/cfg_file_test.c
|
||||
|
||||
### 4.3 与板端套件的分工
|
||||
|
||||
| 关注点 | 主机侧 L0.5(本单测) | 板端 L1 |
|
||||
| 关注点 | 主机侧纯逻辑单测(本单测) | 板端测试套件 |
|
||||
|---|---|---|
|
||||
| INI 解析 / 值编解码 / 契约逻辑 | ✅ 穷举 | 不覆盖 |
|
||||
| 段注册校验 / 故障注入 | ✅ 穷举 | 不覆盖 |
|
||||
|
||||
@@ -122,7 +122,7 @@ D:/Code/DTU Code/
|
||||
| `new` | 建分支 + 工作树 + junction + 台账 | 可并行 |
|
||||
| 编码 | 在新空间里改代码 | 可并行 |
|
||||
| 单测产物 | `%TEMP%\dtu_host\<工作树哈希>\` 分家 + 每跑独占 `runs\<pid>-<ns>\` | **不需要锁,但必须按树分家**(旧固定共享名跨树互踩:A 树可能执行 B 树编出的同名 exe,summary 相同不可辨;build/bench 是真独占,继续用锁) |
|
||||
| 门禁 | `scripts/ci_local.py`(L0 + L0.5,纯软件) | **可并行** |
|
||||
| 门禁 | `scripts/ci_local.py`(静态门禁 + 主机侧纯逻辑单测,纯软件) | **可并行** |
|
||||
| 构建 | `@build`(UV4 增量,需持 `build` 锁) | **必须串行** |
|
||||
| 台架 | `@test` / 板级套件(需先持台架锁,见 §7) | **必须串行** |
|
||||
| 提交 | 本地 `git commit`(多行正文用 `-F <UTF-8 无 BOM 文件>`) | 可并行 |
|
||||
@@ -204,7 +204,7 @@ D:/Code/DTU Code/
|
||||
`@build`(UV4 增量)在新空间里是**独占目录**,但 **UV4 不能两个实例同时 `-b`**(2026-10-12 用户确认,
|
||||
无需实测)——因此构建**同样纳入串行锁**(`build`),与台架锁并列。
|
||||
|
||||
> 结论:任务期真正可并行的只有**编码与静态门禁**(`ci_local.py` 的 L0 / L0.5,纯软件、零硬件);
|
||||
> 结论:任务期真正可并行的只有**编码与静态门禁**(`ci_local.py` 的静态门禁 / 主机侧纯逻辑单测,纯软件、零硬件);
|
||||
> `@build` 与烧录/台架都必须排队。
|
||||
|
||||
### 7.3 并行道与写范围规则(规划层 ↔ 执行层映射)
|
||||
@@ -268,7 +268,7 @@ D:/Code/DTU Code/
|
||||
|
||||
| 层级 | 内容 | 时机 |
|
||||
| ------- | ------------------------------------ | ------------------------------ |
|
||||
| 任务期 | `scripts/ci_local.py`(L0 + L0.5,可并行) | 每个任务各自跑 |
|
||||
| 任务期 | `scripts/ci_local.py`(静态门禁 + 主机侧纯逻辑单测,可并行) | 每个任务各自跑 |
|
||||
| 任务期 | `@build`(0 错 0 警,**持 `build` 锁串行**) | 每个任务各自跑 |
|
||||
| **新空间首次** | `@build` 一次,生成 `.map` | 见下表下注释:不先构建,体积类门禁必然红 |
|
||||
| 任务期(可选) | `@test` / 板级套件(持 `bench` 锁) | 需真机验证的任务 |
|
||||
@@ -277,7 +277,7 @@ D:/Code/DTU Code/
|
||||
|
||||
> ⚠ **新开工作树后先 `@build` 一次再谈门禁**(实测 2026-10-25):`.map` 是每棵树各自的构建产物、
|
||||
> 不入版本库,所以依赖它的事实源的门禁——`mem-numbers`、`mem-budget`、以及 `ci_local.py` 的
|
||||
> `L0c`——在从未构建过的新空间里**必然红**。这不是代码缺陷,脚本此时也会把这句原因打出来
|
||||
> 体积门禁——在从未构建过的新空间里**必然红**。这不是代码缺陷,脚本此时也会把这句原因打出来
|
||||
> (`check_mem_numbers.py` / `ram_baseline.py` 的提示文案)。顺带一条同源提醒:`done` 的门禁
|
||||
> 排在 `@build` 之前(见下文陷阱条目),同一族误报在回收期也会重现一次。
|
||||
|
||||
|
||||
+10
-10
@@ -11,18 +11,18 @@
|
||||
|---|---|---|---|---|
|
||||
| `DTU_KEIL_UV4` | MCU 构建机必需 | UV4.exe 完整路径,例如 `D:\Tools\Keil\UV4\UV4.exe` | `keil_build.py`(`@build`) | 不设即报错(exit 2);不从 C 盘旧路径猜测 |
|
||||
| `DTU_STLINK_CLI` | 复位/烧录/回读机必需 | `ST-LINK_CLI.exe` 完整路径,例如 `C:\Program Files (x86)\STMicroelectronics\STM32 ST-LINK Utility\ST-LINK Utility\ST-LINK_CLI.exe` | `stlink_flash.py` | 未设时回退 PATH(`where ST-LINK_CLI.exe` 首个命中);已设但无效 = 硬报错 |
|
||||
| `DTU_PRECOMMIT_PYTHON` | 可选(覆盖用) | 含 `pre_commit` 模块的 `python.exe` 完整路径 | `ci_local.py` L0、CI `l0-gates` | 未设时按 §1.1 探测(当前解释器 → `.venv` → PATH);已设但无效 = 硬报错 |
|
||||
| `DTU_PRECOMMIT_PYTHON` | 可选(覆盖用) | 含 `pre_commit` 模块的 `python.exe` 完整路径 | `ci_local.py` 静态门禁、CI `static-gates` | 未设时按 §1.1 探测(当前解释器 → `.venv` → PATH);已设但无效 = 硬报错 |
|
||||
| `DTU_GRAPHVIZ_DOT` | 可选(覆盖用) | `dot.exe` 完整路径 | `gen_refgraph.py` | 未设时只查 PATH;无 Graphviz 时降级为"生成 .dot/.json、跳过渲染" |
|
||||
| `DTU_MSVC_CL` | 可选(覆盖用) | x64 `cl.exe` 完整路径(`bin\Hostx64\x64` 下) | `unit_test_harness.py` L0.5、CI `l05` | 未设时按 VS 安装根自动探测(2019/2022/2026 各 edition);已设但不符 `bin\Hostx64\x64\cl.exe` = 硬报错 |
|
||||
| `DTU_WINDOWS_KITS_ROOT` | 可选(覆盖用) | `Windows Kits\10` 根目录(须含 `Include`/`Lib`) | `unit_test_harness.py` L0.5、CI `l05` | 未设时从 `%ProgramFiles*%` 推导根自动探测;已设但无 `Include`/`Lib` = 硬报错 |
|
||||
| `DTU_MSVC_CL` | 可选(覆盖用) | x64 `cl.exe` 完整路径(`bin\Hostx64\x64` 下) | `unit_test_harness.py` 主机侧纯逻辑单测、CI `host-unit-tests` | 未设时按 VS 安装根自动探测(2019/2022/2026 各 edition);已设但不符 `bin\Hostx64\x64\cl.exe` = 硬报错 |
|
||||
| `DTU_WINDOWS_KITS_ROOT` | 可选(覆盖用) | `Windows Kits\10` 根目录(须含 `Include`/`Lib`) | `unit_test_harness.py` 主机侧纯逻辑单测、CI `host-unit-tests` | 未设时从 `%ProgramFiles*%` 推导根自动探测;已设但无 `Include`/`Lib` = 硬报错 |
|
||||
| `DTU_VSWHERE` | 可选(覆盖用) | `vswhere.exe` 完整路径 | `build_sim.ps1` | 未设时用 `%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe` 推导;已设但无效 = 硬报错 |
|
||||
| `DTU_CPPCHECK` | 可选(覆盖用) | `cppcheck.exe` 完整路径 | CI `l0-gates` cppcheck 步骤 | 未设时用 PATH(`Get-Command`);已设但无效 = 硬报错。本地 `ci_local.py` L0 不读该变量:cppcheck 钩子按 pre-commit `entry: cppcheck` 从 PATH 解析 |
|
||||
| `DTU_CPPCHECK` | 可选(覆盖用) | `cppcheck.exe` 完整路径 | CI `static-gates` cppcheck 步骤 | 未设时用 PATH(`Get-Command`);已设但无效 = 硬报错。本地 `ci_local.py` 静态门禁不读该变量:cppcheck 钩子按 pre-commit `entry: cppcheck` 从 PATH 解析 |
|
||||
|
||||
> 变量值只存路径本身(不加引号);可执行文件变量存 `.exe` 完整路径,目录变量(Kits 根)存目录。
|
||||
> 不设任何变量时全部走自动发现(PATH / 系统目录推导 / `.venv` / `vswhere`);变量只用于覆盖非标准安装位置。
|
||||
> 字体系统目录(`simsun.ttc`)不设 DTU 变量:由 `%WINDIR%\Fonts\` 推导,`make_font.py` 的 `--font` 参数仍最高优先。
|
||||
|
||||
### 1.1 Python 优先级特别说明(`ci_local.py` / CI L0)
|
||||
### 1.1 Python 优先级特别说明(`ci_local.py` / CI 静态门禁)
|
||||
|
||||
`pre-commit` 是门禁的**执行环境**而非"工具地址",优先级与其余工具不同:
|
||||
|
||||
@@ -117,13 +117,13 @@ $env:DTU_STLINK_CLI = 'D:\Tools\ST-LINK Utility\ST-LINK_CLI.exe'
|
||||
带 harness 的主机单测,在 3.14.7 下 exit 0);
|
||||
2. 真正会碰第三方的是**下游脚本里的惰性导入**——典型是字体链 `tools/font_gen/make_font.py` 在函数体内
|
||||
`from fontTools.ttLib import TTCollection`。它会不会被走到静态判不了,所以
|
||||
`scripts/ci_local.py` 的 L0a 预检把这类只列成**候选**(实测列出 `font-charset` / `generated-fresh`
|
||||
`scripts/ci_local.py` 的钩子解释器预检把这类只列成**候选**(实测列出 `font-charset` / `generated-fresh`
|
||||
两条经下游引入 fontTools),摊开给人看、不作判决;判决只留给「模块级 import + 裸 python +
|
||||
该解释器确实没装 + 脚本不自带解释器解析」四事齐备那种必然崩。
|
||||
|
||||
> ⚠ 本节 2026-10-27 的早先版本写作「42 条里唯一需要第三方包的那条自带解释器解析」。那是我拿一条宽松
|
||||
> 正则(连注释与字符串里的包名都算命中)审计出的**保守过头的结论**,条目数也因 `nested-comment` 钩子
|
||||
> 落地从 42 变 43。现在这条口径由 L0a 预检每次跑门禁前重算,人不再需要记这个数。
|
||||
> 落地从 42 变 43。现在这条口径由钩子解释器预检每次跑门禁前重算,人不再需要记这个数。
|
||||
|
||||
⇒ 写新钩子时二选一,别开第三条路:要么脚本只用标准库,要么在脚本内部按
|
||||
「显式变量 > 当前解释器 > 仓库 `.venv` > PATH」自行解析并**探针校验**(先例与理由见
|
||||
@@ -150,7 +150,7 @@ $env:DTU_STLINK_CLI = 'D:\Tools\ST-LINK Utility\ST-LINK_CLI.exe'
|
||||
- 只有全坏透才删 `.venv` 重来;删前记下 `pip freeze`,并记住本仓 `.venv` 基底是 Anaconda
|
||||
(换机复制不出来,见上面 §5.2 依赖表与 §5.3 第 5 条)。
|
||||
|
||||
**本笔把上面这些机械化了一半**:`scripts/ci_local.py` 新增 **L0a 钩子解释器预检**(`--preflight-only`
|
||||
**本笔把上面这些机械化了一半**:`scripts/ci_local.py` 新增 **钩子解释器预检**(`--preflight-only`
|
||||
可单跑,也是 `ci_local.py` 每次的第一段),报清"钩子跑在谁身上 / 裸 python 落到谁 / 那个解释器缺什么 /
|
||||
哪些只是候选",四事齐备即 exit 2 并指名到钩子。红-绿在临时假仓库上验过(同一份配置里 bad-hook 判 FAIL、
|
||||
good-hook 判 OK,整体 `rc = 2`)。**它不改环境、不装包、不写仓库**,也不替代上面的修复死顺序——
|
||||
@@ -186,6 +186,6 @@ good-hook 判 OK,整体 `rc = 2`)。**它不改环境、不装包、不写
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-27 | **新增 §5.2 动环境的失效顺序与回滚(原插成 §5.4,本笔按依赖表→死顺序→遗留账重排)+ §5.1 二次修正(道G task-33 修复分支)**:四种现场(删/换 `.venv`、只换包、换 PATH 首位、改 requirements 约束)逐行钉死"第一现场 / 症状 / 还能动什么",回滚按现场分条不混用;`scripts/ci_local.py` 同批加 **L0a 钩子解释器预检**(`--preflight-only` 单跑,也是每次门禁的第一段:报"钩子跑在谁身上 / 裸 python 落到谁 / 缺哪些包 / 哪些是惰性候选",四事齐备 exit 2 指名到钩子,红-绿在临时假仓库验过)。§5.1 那条「42 条里唯一需要第三方包的那条自带解释器解析」被更严的口径推翻:按**模块级 import** 判是 0 条,条目数也因 `nested-comment` 落地变 43;fontTools 属下游**惰性**导入,预检只列候选不判决。 | 2026-10-27 | **新增 §5 解释器实测矩阵(道G task-33,只读诊断,未动任何环境)**:三个解释器(仓库 `.venv` / PATH 首位 / PATH 次位)逐个探针实测,钉住三条事实——① `.venv` **不是残缺**:`pre-commit` 与 `identify` 的 dist-info 齐全(`pip show` 可查),`identify-2.6.19` 的 RECORD 里本就没有 `tags.py`,标签常量在该版本的 `identify.identify` 里,pre-commit 自己也是从那里取(旧写法 `identify.tags` 在这个版本对上必失败,全仓零引用);② 钩子执行环境被 `.git/hooks/pre-commit` 的 `INSTALL_PYTHON` 钉死为 `.venv`,而 `language: system` 的 42 条 entry 用裸 `python` → 落到 PATH 首位那个**零第三方包**的解释器,今天没炸是「42 条全只用标准库 + 唯一需要第三方包的那条自带解释器解析」两件事同时成立,据此写下新增 entry 的硬规则(§5.1);③ 「查不到 pre-commit」的现场是查了 PATH 上的解释器。另附 §5.2「默认不动环境;确需修复时的死顺序与回滚(只补不重建、freeze 备份放仓外、按 freeze 反向回滚、禁止删 `.venv` 重来)」与 §5.3 三条待属主的账。| 2026-10-02 | **文档改造**:删除规划阶段内容(设计结论、路径耦合盘点、代码改造映射、分阶段推广与验收、风险与边界、后续任务拆分),保留并重组为现行机制说明——§1 变量清单(含 Python 优先级特别说明)、§2 解析与失败语义、§3 本机配置方法(会话/用户/runner 三级)、§4 验收判据、变更记录;章节重编号,外部引用同步(`gen_refgraph.py` §3→§1、`ci_local.py` §3.1→§1.1、`Gitea_Runner部署指南.md` §5.3→§3.3) |
|
||||
| 2026-10-02 | **实施完成**:阶段 A~D 落地。① 10 处调用点全部改为"`DTU_*` 变量优先 + 自动发现回退"(显式值无效 = 硬报错、不静默回退):`build/download/reset.bat`(goto 标签式解析,纯 ASCII)、`ci_local.py`(`DTU_PRECOMMIT_PYTHON` 严格校验)、`ci.yml`(`DTU_PRECOMMIT_PYTHON`/`DTU_CPPCHECK`/`DTU_MSVC_CL`/`DTU_WINDOWS_KITS_ROOT`,run 块纯 ASCII)、`gen_refgraph.py`(`DTU_GRAPHVIZ_DOT`→PATH,保留无渲染降级)、`unit_test_harness.py`(`DTU_MSVC_CL`/`DTU_WINDOWS_KITS_ROOT` + `%ProgramFiles*%` 推导取代静态盘符列表)、`build_sim.ps1`(`DTU_VSWHERE`→`%ProgramFiles(x86)%` 推导,去字面 C 盘回退)、`make_font.py`(`%WINDIR%` 推导);`_build.cmd` 确认零调用后删除。② 本机按 §3.2 配置用户级 `DTU_KEIL_UV4`/`DTU_STLINK_CLI`(其余工具本机均可自动发现,未设变量),验证:`@build` BUILD_OK 0/0、`ci_local.py` L0 15 钩子 + L0.5 278/278 断言全绿、refgraph 重生成(含修复 `引用关系图.json` 末尾换行缺失导致的 end-of-file-fixer 抖动,生成器补 `f.write("\n")`)、`build_sim.ps1` vswhere 推导命中 VS2026(当时模拟器 CMake 配置期暴露**既有**问题:`App/cfg_file.c` 已移入 `App/cfg/` 但 `simulator/CMakeLists.txt` 未同步——与环境变量改造无关;2026-10-02 已修复:CMakeLists 同步 `App/cfg/` + `test/board/` 引用,模拟器全量构建通过)、`reset.bat list` 识别探针。③ 文档收口:`AGENTS.md`/`MDK-ARM/README.md`/`项目部署环境要求.md`(新增 §2.1.1)/`Gitea_Runner部署指南.md`(新增 §5.1.3 机器级变量)/`CI配置指南.md`/`pre-commit使用说明.md`/字体/测试流程/引用图说明/ST-LINK 说明/实机规划/`scripts/README.md` 共 12 篇同步为变量口径。④ `requirements.txt` 增 `pre-commit==4.6.2`(重建 `.venv` 即可跑 L0)。⑤ 全量 `pre-commit --all-files` + `ci_local.py` 通过。待办:新 workflow 推送远程后跑一次 CI 两 job 闭环验证;第二台不同安装路径机器的交叉验证(操作清单已固化入 `项目部署环境要求.md` §2.12) |
|
||||
| 2026-10-27 | **新增 §5.2 动环境的失效顺序与回滚(原插成 §5.4,本笔按依赖表→死顺序→遗留账重排)+ §5.1 二次修正(道G task-33 修复分支)**:四种现场(删/换 `.venv`、只换包、换 PATH 首位、改 requirements 约束)逐行钉死"第一现场 / 症状 / 还能动什么",回滚按现场分条不混用;`scripts/ci_local.py` 同批加 **钩子解释器预检**(`--preflight-only` 单跑,也是每次门禁的第一段:报"钩子跑在谁身上 / 裸 python 落到谁 / 缺哪些包 / 哪些是惰性候选",四事齐备 exit 2 指名到钩子,红-绿在临时假仓库验过)。§5.1 那条「42 条里唯一需要第三方包的那条自带解释器解析」被更严的口径推翻:按**模块级 import** 判是 0 条,条目数也因 `nested-comment` 落地变 43;fontTools 属下游**惰性**导入,预检只列候选不判决。 | 2026-10-27 | **新增 §5 解释器实测矩阵(道G task-33,只读诊断,未动任何环境)**:三个解释器(仓库 `.venv` / PATH 首位 / PATH 次位)逐个探针实测,钉住三条事实——① `.venv` **不是残缺**:`pre-commit` 与 `identify` 的 dist-info 齐全(`pip show` 可查),`identify-2.6.19` 的 RECORD 里本就没有 `tags.py`,标签常量在该版本的 `identify.identify` 里,pre-commit 自己也是从那里取(旧写法 `identify.tags` 在这个版本对上必失败,全仓零引用);② 钩子执行环境被 `.git/hooks/pre-commit` 的 `INSTALL_PYTHON` 钉死为 `.venv`,而 `language: system` 的 42 条 entry 用裸 `python` → 落到 PATH 首位那个**零第三方包**的解释器,今天没炸是「42 条全只用标准库 + 唯一需要第三方包的那条自带解释器解析」两件事同时成立,据此写下新增 entry 的硬规则(§5.1);③ 「查不到 pre-commit」的现场是查了 PATH 上的解释器。另附 §5.2「默认不动环境;确需修复时的死顺序与回滚(只补不重建、freeze 备份放仓外、按 freeze 反向回滚、禁止删 `.venv` 重来)」与 §5.3 三条待属主的账。| 2026-10-02 | **文档改造**:删除规划阶段内容(设计结论、路径耦合盘点、代码改造映射、分阶段推广与验收、风险与边界、后续任务拆分),保留并重组为现行机制说明——§1 变量清单(含 Python 优先级特别说明)、§2 解析与失败语义、§3 本机配置方法(会话/用户/runner 三级)、§4 验收判据、变更记录;章节重编号,外部引用同步(`gen_refgraph.py` §3→§1、`ci_local.py` §3.1→§1.1、`Gitea_Runner部署指南.md` §5.3→§3.3) |
|
||||
| 2026-10-02 | **实施完成**:阶段 A~D 落地。① 10 处调用点全部改为"`DTU_*` 变量优先 + 自动发现回退"(显式值无效 = 硬报错、不静默回退):`build/download/reset.bat`(goto 标签式解析,纯 ASCII)、`ci_local.py`(`DTU_PRECOMMIT_PYTHON` 严格校验)、`ci.yml`(`DTU_PRECOMMIT_PYTHON`/`DTU_CPPCHECK`/`DTU_MSVC_CL`/`DTU_WINDOWS_KITS_ROOT`,run 块纯 ASCII)、`gen_refgraph.py`(`DTU_GRAPHVIZ_DOT`→PATH,保留无渲染降级)、`unit_test_harness.py`(`DTU_MSVC_CL`/`DTU_WINDOWS_KITS_ROOT` + `%ProgramFiles*%` 推导取代静态盘符列表)、`build_sim.ps1`(`DTU_VSWHERE`→`%ProgramFiles(x86)%` 推导,去字面 C 盘回退)、`make_font.py`(`%WINDIR%` 推导);`_build.cmd` 确认零调用后删除。② 本机按 §3.2 配置用户级 `DTU_KEIL_UV4`/`DTU_STLINK_CLI`(其余工具本机均可自动发现,未设变量),验证:`@build` BUILD_OK 0/0、`ci_local.py` 静态门禁 15 钩子 + 主机侧纯逻辑单测 278/278 断言全绿、refgraph 重生成(含修复 `引用关系图.json` 末尾换行缺失导致的 end-of-file-fixer 抖动,生成器补 `f.write("\n")`)、`build_sim.ps1` vswhere 推导命中 VS2026(当时模拟器 CMake 配置期暴露**既有**问题:`App/cfg_file.c` 已移入 `App/cfg/` 但 `simulator/CMakeLists.txt` 未同步——与环境变量改造无关;2026-10-02 已修复:CMakeLists 同步 `App/cfg/` + `test/board/` 引用,模拟器全量构建通过)、`reset.bat list` 识别探针。③ 文档收口:`AGENTS.md`/`MDK-ARM/README.md`/`项目部署环境要求.md`(新增 §2.1.1)/`Gitea_Runner部署指南.md`(新增 §5.1.3 机器级变量)/`CI配置指南.md`/`pre-commit使用说明.md`/字体/测试流程/引用图说明/ST-LINK 说明/实机规划/`scripts/README.md` 共 12 篇同步为变量口径。④ `requirements.txt` 增 `pre-commit==4.6.2`(重建 `.venv` 即可跑静态门禁)。⑤ 全量 `pre-commit --all-files` + `ci_local.py` 通过。待办:新 workflow 推送远程后跑一次 CI 两 job 闭环验证;第二台不同安装路径机器的交叉验证(操作清单已固化入 `项目部署环境要求.md` §2.12) |
|
||||
| 2026-10-02 | 初版规划:盘点 BAT/Python/PowerShell/Gitea workflow 中机器相关工具路径;定义 `DTU_*` 变量清单、优先级、用户级与 SYSTEM runner 配置边界;规划 4 阶段迁移及离线/实机验收范围。未改工具调用代码 |
|
||||
|
||||
+12
-12
@@ -67,9 +67,9 @@ Graphviz / Cppcheck 的安装位置差异一律用 `DTU_*` 环境变量表达(
|
||||
|---|---|---|---|
|
||||
| `DTU_KEIL_UV4` | **必需**(MCU 构建机) | UV4.exe 完整路径 | `keil_build.py`(`@build`) |
|
||||
| `DTU_STLINK_CLI` | **必需**(用 ST-LINK CLI 的机器;已在 PATH 可不设) | ST-LINK_CLI.exe 完整路径 | `stlink_flash.py`(烧录/复位/回读) |
|
||||
| `DTU_PRECOMMIT_PYTHON` | 可选(pre-commit 不在 `.venv`/当前解释器时设;CI runner 建议必设) | 装有 pre-commit 的 python.exe 完整路径 | `scripts/ci_local.py`、CI L0/L0.5 |
|
||||
| `DTU_CPPCHECK` | 可选(开发机入 PATH 即可;无 PATH 的 CI runner 建议设) | cppcheck.exe 完整路径 | pre-commit cppcheck 钩子、CI L0 |
|
||||
| `DTU_MSVC_CL` | 可选(VS 标准位置时自动探测) | x64 `cl.exe` 完整路径 | 主机侧单测、CI L0.5 |
|
||||
| `DTU_PRECOMMIT_PYTHON` | 可选(pre-commit 不在 `.venv`/当前解释器时设;CI runner 建议必设) | 装有 pre-commit 的 python.exe 完整路径 | `scripts/ci_local.py`、CI 静态门禁 / 主机侧纯逻辑单测 |
|
||||
| `DTU_CPPCHECK` | 可选(开发机入 PATH 即可;无 PATH 的 CI runner 建议设) | cppcheck.exe 完整路径 | pre-commit cppcheck 钩子、CI 静态门禁 |
|
||||
| `DTU_MSVC_CL` | 可选(VS 标准位置时自动探测) | x64 `cl.exe` 完整路径 | 主机侧纯逻辑单测(本地与 CI) |
|
||||
| `DTU_WINDOWS_KITS_ROOT` | 可选(SDK 标准位置时自动探测) | `Windows Kits\10` 根目录 | 主机侧单测 |
|
||||
| `DTU_VSWHERE` | 可选(VS 标准位置时由 `%ProgramFiles(x86)%` 自动推导) | vswhere.exe 完整路径 | `simulator/build_sim.ps1` |
|
||||
| `DTU_GRAPHVIZ_DOT` | 可选(PATH 可发现时不设) | dot.exe 完整路径 | `tools/refgraph` 渲染 |
|
||||
@@ -157,7 +157,7 @@ Graphviz / Cppcheck 的安装位置差异一律用 `DTU_*` 环境变量表达(
|
||||
|---|---|---|
|
||||
| 统一入口 | **仓库 `.venv\Scripts\python.exe`** | 一切依赖第三方库的脚本(字体生成、截图转 PNG、RemoDispBus 回归)都用它 |
|
||||
| 解释器版本 | Python ≥ 3.10(任意发行版) | 种子解释器仅用于建 venv 一次;本机为 3.12.4 |
|
||||
| 依赖 | `fonttools==4.51.0`、`pillow==10.3.0`、`pyserial==3.5`、`pre-commit==4.6.2` | 清单 = 仓库根 `requirements.txt`(**必须保持纯 ASCII**:Windows 下 pip 按 GBK 代码页解码,中文注释会 UnicodeDecodeError);`pre-commit` 入 `.venv` 后,重建 venv 即可直接跑 L0 门禁(`scripts/ci_local.py` 自动选用) |
|
||||
| 依赖 | `fonttools==4.51.0`、`pillow==10.3.0`、`pyserial==3.5`、`pre-commit==4.6.2` | 清单 = 仓库根 `requirements.txt`(**必须保持纯 ASCII**:Windows 下 pip 按 GBK 代码页解码,中文注释会 UnicodeDecodeError);`pre-commit` 入 `.venv` 后,重建 venv 即可直接跑静态门禁(`scripts/ci_local.py` 自动选用) |
|
||||
| 入库情况 | `.venv/` 已 gitignore | **换机/首次必须重建** |
|
||||
|
||||
```powershell
|
||||
@@ -207,7 +207,7 @@ python tools/font_gen/extract_cn.py # 任意 python3:
|
||||
> 检查范围仅项目自有代码;`Lib/`、`Middlewares/`、`Drivers/BSP/UI`、`lftpd`、`dhara`、`test/` 等被排除。
|
||||
> 全库扫描:`pre-commit run --all-files`。详见 `docs/代码规范/pre-commit使用说明.md`、`docs/代码规范/cppcheck使用说明.md`。
|
||||
>
|
||||
> ⚠ **新增 L0.5 单测钩子(2026-10-01)后,"装了 VS"从"仅模拟器需要"升级为"提交门禁需要"**:
|
||||
> ⚠ **新增主机侧纯逻辑单测钩子(2026-10-01)后,"装了 VS"从"仅模拟器需要"升级为"提交门禁需要"**:
|
||||
> 只要装了 VS(§2.3)就自动具备,无需额外安装;但**完全未装 VS 的机器在改动看门狗相关文件时会被钩子拦截**。
|
||||
|
||||
### 2.8 引用关系图渲染(Graphviz,可选)
|
||||
@@ -410,7 +410,7 @@ pre-commit run --all-files
|
||||
|
||||
### 2.11 CI runner 环境(Gitea Actions,2026-10-01 起)
|
||||
|
||||
CI 跑 **L0 静态门禁 + L0.5 主机侧单测**(**不含 L1/L2/L3**——需真机 + ST-LINK,只在本地)。
|
||||
CI 跑 **静态门禁 + 主机侧纯逻辑单测**(**不含板端测试套件 / 主机侧回归 / 人工目检**——需真机 + ST-LINK,只在本地)。
|
||||
workflow 见 `.gitea/workflows/ci.yml`;覆盖边界与判定细节见 [`../测试说明/测试开发标准流程.md`](../测试说明/测试开发标准流程.md) §9。
|
||||
|
||||
| 项 | 要求 | 说明 |
|
||||
@@ -418,14 +418,14 @@ workflow 见 `.gitea/workflows/ci.yml`;覆盖边界与判定细节见 [`../测
|
||||
| Gitea 版本 | ≥ 1.21(实测远程 **1.25.4**) | workflow 置于 `.gitea/workflows/`,兼容 GitHub Actions 语法 |
|
||||
| Actions 开关 | 实例 `[actions] ENABLED = true` | site admin 配置;未开启则 push 不触发任何 job |
|
||||
| runner OS | **Windows**(`runs-on: windows`) | labels 须含 `windows`;与开发机同平台,避免脚本语法分叉 |
|
||||
| runner 软件 | Python 3.x、cppcheck、pre-commit、**MSVC**(VS C++ 工具链 + Windows SDK) | 前三个缺 → `l0-gates` **硬失败**(`::error::`);MSVC 缺 → `l05-host-unit-tests` **降级跳过并告警**(不硬失败,但等于 L0.5 裸奔)。runner 以 SYSTEM 服务运行,**用户级 PATH/变量不可见**:自动发现失败时用**机器级** `DTU_PRECOMMIT_PYTHON` / `DTU_CPPCHECK` / `DTU_MSVC_CL` 注入(见 `docs/CI/远程CI完整指南.md` §4.1.3) |
|
||||
| runner 软件 | Python 3.x、cppcheck、pre-commit、**MSVC**(VS C++ 工具链 + Windows SDK) | 前三个缺 → `static-gates` **硬失败**(`::error::`);MSVC 缺 → `host-unit-tests` **降级跳过并告警**(不硬失败,但等于主机侧纯逻辑单测裸奔)。runner 以 SYSTEM 服务运行,**用户级 PATH/变量不可见**:自动发现失败时用**机器级** `DTU_PRECOMMIT_PYTHON` / `DTU_CPPCHECK` / `DTU_MSVC_CL` 注入(见 `docs/CI/远程CI完整指南.md` §4.1.3) |
|
||||
| Keil | **不需要** | 刻意不跑 `@build`(需授权);`.map` 缺失时 `mem-numbers` 钩子自动跳过 |
|
||||
| `mem-numbers` | 有 `.map` 就跑,无则 `SKIP` | Keil 构建产物 `.map` 已 gitignore;自建带 Keil 的 runner 会照常跑该钩子 |
|
||||
|
||||
**runner 就绪前的过渡**:CI 只是"多一道网",**不能替代本地门禁**。提交前仍须在本地跑:
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py # 与 CI 同一套判定(L0 + L0.5)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py # 与 CI 同一套判定(静态门禁 + 主机侧纯逻辑单测)
|
||||
```
|
||||
|
||||
### 2.12 跨机验证(第二台机器 / 换机验收清单)
|
||||
@@ -453,7 +453,7 @@ Test-Path $env:DTU_STLINK_CLI # True
|
||||
|---|---|---|---|
|
||||
| ① | `& <任一 python> -m venv .venv` + `& '.venv\Scripts\python.exe' -m pip install -r requirements.txt` | 安装完成且含 pre-commit | pip 报 GBK 解码错 = `requirements.txt` 被污染(该文件必须纯 ASCII,不该发生) |
|
||||
| ② | `& '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py` | `BUILD_OK: 0 errors, 0 warnings`,exit 0 | 报错行**直接指出变量名**(如 `BUILD_ENV_ERROR: $DTU_KEIL_UV4 is set but not a file: ...`)——照报错修值;新机器首次启动 UV4 需 license(90 天评估),缺 Device Pack 则下载失败(构建不受影响) |
|
||||
| ③ | `& '.venv\Scripts\python.exe' scripts/ci_local.py` | L0 全钩子 Passed + L0.5 单测断言全部通过(当前 278 条),exit 0 | "找不到 MSVC" = VS 未装或装到非标准位置(设 `DTU_MSVC_CL` 可验证覆盖通道) |
|
||||
| ③ | `& '.venv\Scripts\python.exe' scripts/ci_local.py` | 静态门禁全钩子 Passed + 主机侧纯逻辑单测断言全部通过(当前 278 条),exit 0 | "找不到 MSVC" = VS 未装或装到非标准位置(设 `DTU_MSVC_CL` 可验证覆盖通道) |
|
||||
| ④ | `& 'tools\refgraph\gen_refgraph.bat'` → `git status` | 重生成后 `docs/images/` **无 diff**(图内容只依赖源码,与本机工具链无关) | 出现 diff = 异常,需查 |
|
||||
| ⑤ | `& "simulator\build_sim.bat"` | 自动探测 VS → 生成器匹配 → 构建成功(2026-10-02 起模拟器构建可用:配置引擎重构的文件迁移已同步进 `simulator/CMakeLists.txt`) | 报 `Cannot find source file` / `C1083` = CMakeLists 与源码目录再次失步(查文件迁移记录),与 `DTU_*` 变量无关;"未探测到 VS" 见 §2.3 |
|
||||
| ⑥ | (有 ST-LINK 硬件才执行)`& '.venv\Scripts\python.exe' .dsh\skills\stlink-flash\scripts\stlink_flash.py list` | 列出探针 SN | 只跑 `list`,**不烧录不复位**;无硬件跳过不扣分 |
|
||||
@@ -640,7 +640,7 @@ $env:DTU_KEIL_UV4 = 'D:\NotExist\UV4.exe' # 仅当前会话,不动用户级
|
||||
| 远程显示协议 | `docs/UI设计/RemoDispBus协议与接收设计说明.md` |
|
||||
| LVGL 模拟器部署 | `docs/UI设计/LVGL模拟器部署与使用说明.md`、`simulator/README.md` |
|
||||
| 串口调试 / 自动化测试 | `docs/测试说明/串口助手使用指南.md`、`docs/测试说明/串口助手设计说明.md`、`docs/测试说明/自动化测试流程.md` |
|
||||
| **测试分层 / L0.5 主机侧单测** | `docs/测试说明/测试开发标准流程.md`(分层与门禁)· `docs/测试说明/看门狗单元测试说明.md`(L0.5 范式:MSVC 离线编译 + 平台桩) |
|
||||
| **测试分层 / 主机侧纯逻辑单测** | `docs/测试说明/测试开发标准流程.md`(分层与门禁)· `docs/测试说明/看门狗单元测试说明.md`(主机侧纯逻辑单测范式:MSVC 离线编译 + 平台桩) |
|
||||
| **CI(Gitea Actions)** | `docs/CI/README.md`(**远程 CI 唯一入口**:服务端 / Runner 部署与保活 / workflow / 排障,**仅本机一台 runner,日常开发不必读**)· `docs/测试说明/测试开发标准流程.md` §9(覆盖边界) |
|
||||
| pre-commit / cppcheck | `docs/代码规范/pre-commit使用说明.md`、`docs/代码规范/cppcheck使用说明.md` |
|
||||
| 引用关系图(文件引用) | `docs/架构设计/引用关系图说明.md`(生成工具 `tools/refgraph`;渲染依赖 Graphviz,§2.8) |
|
||||
@@ -660,11 +660,11 @@ $env:DTU_KEIL_UV4 = 'D:\NotExist\UV4.exe' # 仅当前会话,不动用户级
|
||||
| 2026-10-02 | **模拟器构建修复**(配置引擎重构遗留):`simulator/CMakeLists.txt` 的配置文件引擎引用同步到迁移后位置——包含目录 `App/`→`App/cfg/`(`cfg_file.h`/`cfg_file_store.h`)、源文件 `App/cfg_file.c`→`App/cfg/cfg_file.c`、包含目录 `test/`→`test/board/`(`test_config.h`/`net_latency_test.h`);全量 include 离线核查无其他断链,模拟器全量构建恢复(`--cfgtest` 四场景 PASS,rc=0)。§2.12 验证链 ⑤ 行同步更新 |
|
||||
| 2026-10-02 | 更正 Device Pack 必装范围:`Keil::CMSIS` pack 不再列为必装项——本工程不依赖它(核心头文件由仓库 `Drivers/CMSIS/` 提供,`.uvprojx` 只引用 DFP pack;2026-10-02 本机实测未装 CMSIS pack 构建 0/0 通过),且其 keil.com 直链(5.7.0~6.1.0)均已下架 404;必装的只有 `Keil::STM32F4xx_DFP`(3.1.1,与 `.uvprojx` PackID 一致)。§2.2 / 第 2 步 / 搭建清单 / §2.10 同步更正 |
|
||||
| 2026-10-02 | 新增 §2.12「跨机验证(第二台机器 / 换机验收清单)」:变量配置 + sanity check → 六步验证链(含期望结果与失败诊断,含模拟器既有 CMake 问题标注)→ 反向 fail-fast 测试 → 通过判定,作为方案跨机验收的标准操作 |
|
||||
| 2026-10-02 | **本地工具路径环境变量方案落地**(`本地工具路径环境变量方案.md`):仓库脚本不再含本机绝对工具路径——10 处调用点(`build/download/reset.bat`、`_build.cmd` 退役、`ci_local.py`、`ci.yml`、`gen_refgraph.py`、`unit_test_harness.py`、`build_sim.ps1`、`make_font.py`)统一改为"`DTU_*` 变量优先 + 自动发现回退"(显式设置但无效 = 硬报错,不静默回退);新增 §2.1.1 变量清单/设置方法/解析语义;Keil 安装路径、Graphviz、cppcheck、VS/SDK 自动探测、Python 重建示例与速查表同步改为变量口径;`requirements.txt` 增 `pre-commit`(重建 `.venv` 即可直接跑 L0 门禁);本机已按 §2.1.1 配置用户级 `DTU_KEIL_UV4`/`DTU_STLINK_CLI` 并全链路验证(@build 0/0、ci_local L0+L0.5 全绿、refgraph 重生成、reset 探针识别) |
|
||||
| 2026-10-02 | **本地工具路径环境变量方案落地**(`本地工具路径环境变量方案.md`):仓库脚本不再含本机绝对工具路径——10 处调用点(`build/download/reset.bat`、`_build.cmd` 退役、`ci_local.py`、`ci.yml`、`gen_refgraph.py`、`unit_test_harness.py`、`build_sim.ps1`、`make_font.py`)统一改为"`DTU_*` 变量优先 + 自动发现回退"(显式设置但无效 = 硬报错,不静默回退);新增 §2.1.1 变量清单/设置方法/解析语义;Keil 安装路径、Graphviz、cppcheck、VS/SDK 自动探测、Python 重建示例与速查表同步改为变量口径;`requirements.txt` 增 `pre-commit`(重建 `.venv` 即可直接跑静态门禁);本机已按 §2.1.1 配置用户级 `DTU_KEIL_UV4`/`DTU_STLINK_CLI` 并全链路验证(@build 0/0、ci_local 静态门禁+主机侧纯逻辑单测全绿、refgraph 重生成、reset 探针识别) |
|
||||
| 2026-10-02 | 新增 **`Gitea_Runner部署指南.md`**:针对**本机 Gitea 原生部署**(`D:\Docker\Gitea\gitea.exe` + Windows 服务 + `127.0.0.1:3001`)定制的 Runner 端全流程——下载 / `register`(**`--instance` 用回环、`--labels "windows:host"` 必须精确匹配 `runs-on`**)/ `config.yaml` / `daemon install` 常驻 / 软件与 PATH 坑 / MSVC 路径约束 / 内网 action 零依赖改法 / 排障表 / `check_env.ps1` 自检脚本;§七相关文档登记 |
|
||||
| 2026-10-01 | 新增 **`CI配置指南.md`**(Gitea Actions 配置全流程:服务端 `[actions] ENABLED` / Windows runner 注册(`--labels "windows:host"`)/ runner 软件环境与 PATH / workflow 位置与触发 / 内网拉不到 `actions/checkout` 的三种处置 / 端到端验证清单 / 故障排查表 / 日常维护);§七相关文档登记 |
|
||||
| 2026-10-01 | **新增 §2.11 CI runner 环境(Gitea Actions,T-6)**:Gitea ≥1.21(实测 1.25.4)+ `[actions] ENABLED` + Windows runner(装 Python/cppcheck/pre-commit/MSVC);**不需要 Keil**(刻意不跑 `@build`,无 `.map` 时 `mem-numbers` 自动跳过);补"CI 不能替代本地门禁"与 `scripts/ci_local.py` 过渡命令 |
|
||||
| 2026-10-01 | **补主机侧单元测试(L0.5)环境要求**:§2.3 由"PC 模拟器环境"扩为"PC 侧编译环境(模拟器 + 主机侧单元测试)"——明确**同一套 VS 的 `cl.exe` 同时服务模拟器与单测**,MSVC 从"仅模拟器需要"升级为"提交门禁需要";§2.7 pre-commit 表补 `watchdog-unit-test` 钩子及"未装 VS 会被拦截";第 3 步 / 第 10 步冒烟 / 搭建清单 / §2.10 下载表(补 VS2022/2026 行)/ 速查表(补 `cl.exe` 缺失与 `C1083 stdio.h` 两条)/ 相关文档同步;总览拓扑图加 `MSVC cl.exe` 节点 |
|
||||
| 2026-10-01 | **补主机侧单元测试环境要求**:§2.3 由"PC 模拟器环境"扩为"PC 侧编译环境(模拟器 + 主机侧单元测试)"——明确**同一套 VS 的 `cl.exe` 同时服务模拟器与单测**,MSVC 从"仅模拟器需要"升级为"提交门禁需要";§2.7 pre-commit 表补 `watchdog-unit-test` 钩子及"未装 VS 会被拦截";第 3 步 / 第 10 步冒烟 / 搭建清单 / §2.10 下载表(补 VS2022/2026 行)/ 速查表(补 `cl.exe` 缺失与 `C1083 stdio.h` 两条)/ 相关文档同步;总览拓扑图加 `MSVC cl.exe` 节点 |
|
||||
| 2026-09-21 | 更正 SDL2 描述:`simulator/SDL2/` 并不入库(gitignore,fresh clone 缺失),原"**随仓库提供**/不用装"表述有误;第 3 步补全官方预编译包下载(直链 + 内网中转)、解压、`include/SDL2/` 头文件复制、5 文件校验步骤;§2.3 表格与速查表同步修正 |
|
||||
| 2026-09-21 | 模拟器构建改为自适应:新增 `simulator/build_sim.bat/.ps1`(vswhere 自动探测本机 VS 大版本 → 生成器 16=2019 / 17=2022 / 18=2026;`build/` 生成器不符自动删除重建,删除带重试抗文件锁);VS 版本不再固定,2019/2022/2026 兼容:总览图、§2.3、第 3 步、第 10 步冒烟、搭建清单、速查表同步更新 |
|
||||
| 2026-09-16 | 补 Graphviz(引用关系图渲染,可选):新增 §2.8,原 §2.8/§2.9 顺延为 §2.9/§2.10(章节内步骤号不变);总览图、搭建清单、§2.10 下载表、相关文档各补一项 |
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
| **出口与记录与信号** | 跳闸/告警出口、信号灯 | `TripKO` / KO | `App/protect/` + BSP 出口驱动 |
|
||||
| **动作留痕记录与上送** | SOE/动作记录、UI 显示、遥测上送 | 记录模块 + 通信 | `App/log/` + `Drivers/BSP/UI/` + `App/protocol/` |
|
||||
|
||||
> **分层原则**:语义取数与有效性只做"数"(码值→工程量),全周傅氏相量只做"相量"(工程量→复相量),保护判据只做"判据"(相量 + 定值→布尔),出口与记录/动作留痕只做"动作与留痕"。**算法与 I/O 分离**,使 全周傅氏相量/保护判据成为主机侧单元测试(L0.5)的天然适格对象(落实 `docs/任务规划/测试与架构专项.md`「采集链路测试可验证性约束」)。
|
||||
> **分层原则**:语义取数与有效性只做"数"(码值→工程量),全周傅氏相量只做"相量"(工程量→复相量),保护判据只做"判据"(相量 + 定值→布尔),出口与记录/动作留痕只做"动作与留痕"。**算法与 I/O 分离**,使 全周傅氏相量/保护判据成为主机侧纯逻辑单测的天然适格对象(落实 `docs/任务规划/测试与架构专项.md`「采集链路测试可验证性约束」)。
|
||||
|
||||
### 2.2 任务与中断模型(映射到 STM32F4 + FreeRTOS)
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
|
||||
| 阶段 | 交付物 | 验收判据 |
|
||||
| --- | --- | --- |
|
||||
| P0 | `adcTask`/`algo` 模块(采集触发 + 语义映射 + 标定 + 全周傅氏 + 双区交换)+ 设计文档 + L0.5 单测 | TPAFE 三路(`UA`/`UN`/`IA`)标准源读数误差 ≤ 目标;相量每 1 ms 刷新;`@test` 通过 |
|
||||
| P0 | `adcTask`/`algo` 模块(采集触发 + 语义映射 + 标定 + 全周傅氏 + 双区交换)+ 设计文档 + 主机侧纯逻辑单测 | TPAFE 三路(`UA`/`UN`/`IA`)标准源读数误差 ≤ 目标;相量每 1 ms 刷新;`@test` 通过 |
|
||||
| P1 | A 相过流 + A 相过负荷元件 + 定值模型 | 注入 1.2×/0.9× 定值,A 相正确动作/返回;返回系数正确 |
|
||||
| P2 | 过压/欠压 + 过频/低频 + 零序过压元件 | 越限动作时间符合定值延时 |
|
||||
| P3 | 自检闭锁 + 采样通道自检;三相类元件建接口 | 自检失败闭锁;未使能元件不误动 |
|
||||
@@ -511,8 +511,8 @@ B0 产品定义 ✅(2026-10-10 已确认,见 §1.5)
|
||||
|
||||
| 层 | 对象 | 方式 |
|
||||
| --- | --- | --- |
|
||||
| L0 静态 | 代码规范、文档链接、体积 | pre-commit 钩子(`cppcheck` / `doc-links` / `mem-numbers`) |
|
||||
| L0.5 主机单测 | 傅氏相量、定值解析、保护判据(**算法与 I/O 分离**后天然适格) | `test/host/<模块>/run_unit_test.py`(MSVC 离线编译真实源文件,参考 `test/host/cfg_file/run_unit_test.py`) |
|
||||
| 静态门禁 | 代码规范、文档链接、体积 | pre-commit 钩子(`cppcheck` / `doc-links` / `mem-numbers`) |
|
||||
| 主机侧纯逻辑单测 | 傅氏相量、定值解析、保护判据(**算法与 I/O 分离**后天然适格) | `test/host/<模块>/run_unit_test.py`(MSVC 离线编译真实源文件,参考 `test/host/cfg_file/run_unit_test.py`) |
|
||||
| 语义取数与有效性板端套件 | 采样/相量/保护元件 | `test/run_all_suites.py` 板端套件 |
|
||||
| 全周傅氏相量/保护判据台架 | 标准源注入、动作出口、时序 | 台架闭环(COM6 @115200) |
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
| 登记簿 `N-x` / `S-x` / `T-x`、专项 `A-x`、已删报告的 UI 簇 `U1~U5`(2026-10-26 方案 B) | 语义短语——**登记簿表首格本身就是短语**,如「双转换器无共同采样触发」「CI 远端闭环未验证」 | `scripts/archive/id_gate/id_aliases.json` 标 `retired-to-phrase` |
|
||||
| 网络设计条目 `F-NET/F-DRV/F-MS-<nn>`(12 个,2026-10-27 批次一) | 「`编号治理历史.md` §7 + 条目名」短语,如 `(`编号治理历史.md` §7「全局读即清搁浅事件」)`;条目名的权威清单 = `docs/网络/网络自愈架构设计.md` `编号治理历史.md` §7「条目名」列;那个 DRV 段孤儿号按本节末句**删号留事实**、未补定义 | `scripts/archive/id_gate/id_aliases.json` 标 `retired-to-phrase`(13 条)+属主文档 `编号治理历史.md` §7 编号列仅作历史对照 |
|
||||
| 「数据可信」任务卡号(中文前缀「卡」+ 数字,形态见 `编号治理历史.md` §9.3;2026-10-27 批次五) | §5 表的**标识列直接写任务名**(「端子/子板变比确认」「两点标定与精度验收」),代码注释与文档写语义短语(「标定折算落地」「未标定闭锁」「遥测快照发布」…);**逐号换算表见本文 `编号治理历史.md` §9.3** | ⚠ **机器面空白**:中文前缀不进 `scripts/archive/id_gate/check_ids.py` 语料(族 pattern 全是拉丁形态)→ 门禁咬不到,今天只有本表 + `编号治理历史.md` §9.3 + 评审三条兜;`id_aliases.json` 刻意不登记(登记了机器也看不见,只多一张没人核的表) |
|
||||
| 测试验证层级号(`L0`、`L0.5`、`L1~L3` 这一族,**注意与 UI 设计令牌层 L0、硬件位号 L1~L3、采集链旧层名等同形族无关**;2026-10-28 批次六) | 文档与 CI 步骤名写语义名(静态门禁 / 主机侧纯逻辑单测 / 板端测试套件 / 主机侧回归 / 人工目检);ASCII 短码(static / preflight / commitmsg / size / host-unit / coverage)**只进**日志标签、命令行旗标、CI job id、日志文件名 | ⚠ **同形族警示**:逐号换算表与判族表见 `scripts/archive/id_gate/编号治理历史.md` §10 与 §10.1(撞族六个,动手前先判族);本族虽是拉丁形态但**门禁咬不到**——`id-check` 同日退役、现行 `id-check-new` 只看新增行里的「字母+数字」形态,停发只靠本表 + 归档换算表 + 评审 |
|
||||
| `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` 等) | 同上,写事实 | 无定义,本就不该存在 |
|
||||
|
||||
@@ -218,4 +219,5 @@
|
||||
|
||||
| 日期 | 变更 | 说明 |
|
||||
|---|---|---|
|
||||
| 2026-10-28 | **§4.1.1 加一行「测试验证层级号」停发指针**(本族当日整族清退成语义名;事实本体在 `scripts/archive/id_gate/编号治理历史.md` §10,活文档只留指针、不复述换算表) | 用户裁定 A 方案:代码 + CI + 文档一次改到底 |
|
||||
| 2026-10-28 | **门禁三步退役 + 本文瘦身** | ① 两根护栏(可判定比例 / 不可判定绝对数)转报告制,不再拦提交;② 全库语料审计换成「只查新增行」的 `id-check-new`(零语料);③ 99 族语料与脚本移入 `scripts/archive/id_gate/`,钩子 52 → 51。本文同步从 36 节 / 58 KB 瘦到只留现行口径(约 150 行),过程记录与换算表进归档。 |
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
`[FAIL] cn-font tools/font_gen/make_font.py 退出码 1:ERROR: No module named fontTools`,
|
||||
解释器行打印的是 C:/Python314/python.EXE。纯标准库的 `menu-gen` / `cn-chars` 照旧绿——这是一根
|
||||
**只在字库链上假红**的闸,换任何一台没把 `.venv` 排在 PATH 前面的机器或 runner 都会重犯(同族第二次
|
||||
发作见 `scripts/ci_local.py` 文件头 2026-10-11 记的「L0 全灭」)。修法**不是**把 entry 写死成 `.venv`
|
||||
发作见 `scripts/ci_local.py` 文件头 2026-10-11 记的「静态门禁全灭」)。修法**不是**把 entry 写死成 `.venv`
|
||||
里的路径(那正是 2026-10-11 那次把候选收窄、等于把门禁绑死在一台机器上的错),而是脚本自己按优先级
|
||||
挑解释器并**逐个探针校验**:显式 `DTU_GEN_PYTHON` > 当前解释器 > 仓库 `.venv` > PATH 上探到的机器级,
|
||||
且必须真能 `import fontTools` 才算数(装了 pre-commit 不等于装了 fontTools;Microsoft Store 那个
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
> 它是"回归最快的两层之一"(静态层 + 逻辑层),但**不等于真机正确**。
|
||||
|
||||
- 22 个钩子各有一个 `files:` 白名单(改动被测模块或测试目录才跑)、`pass_filenames: false`;
|
||||
远端 CI 只跑 静态门禁 + 主机侧纯逻辑单测主机侧单测(见 `docs/CI/README.md`)。
|
||||
远端 CI 只跑 静态门禁 + 主机侧纯逻辑单测(见 `docs/CI/README.md`)。
|
||||
|
||||
## 1. 它在查什么(22 个实例)
|
||||
|
||||
|
||||
@@ -73,9 +73,9 @@
|
||||
3. **`[示例]`/`[计划]` 整行跳过**:想绕过本门禁,写个 `[示例]` 就行。这靠评审纪律,不靠机器。
|
||||
4. **未跟踪文件不在扫描面**(`git diff` 语义):手工在 `git add` 之前跑 `--worktree` 看不到新文件;
|
||||
提交时必已进暂存区,所以门禁场景不受影响。
|
||||
推论:**在 `pre-commit run --all-files`(`ci_local.py` 的 L0)下它是空转的**——
|
||||
推论:**在 `pre-commit run --all-files`(`ci_local.py` 的静态门禁)下它是空转的**——
|
||||
那种跑法把文件当"全量"传进来,而本脚本只读暂存区 → 暂存区空 → 打印通过。
|
||||
它只在**真实提交**时才有牙;别拿 L0 全绿当"没有新编号"的证明。
|
||||
它只在**真实提交**时才有牙;别拿静态门禁全绿当"没有新编号"的证明。
|
||||
5. **算术白名单是启发式**:`SIZE-2`/`LEN-1` 这类被排除;但若某处写着一个「不在白名单的前缀 +
|
||||
连字符 + 数字」,就会误报一次——把前缀加进白名单即可,不构成误拦风险。
|
||||
6. **不校验语义**:白名单里的前缀可以拿来写任何东西(`TIM3` 合法,`TIM99` 也放行)。
|
||||
|
||||
@@ -76,7 +76,7 @@ RAM 预算门禁 FAIL:RW_CCM 占用 92.3% > 92%(阈值 92%)
|
||||
本门与 `mem-numbers` 可能因受管快照与 `.map` 漂移而红,那不代表预算超标(处置见 §7 的 `mem-numbers` 篇)。
|
||||
8. **同码凭证在「多人同树」下必然不成立**(2026-10-27 实测):`check_size_gate.py --local` 的代码指纹取
|
||||
**git 索引里的 blob**,所以「工作区有别人在飞、尚未入索引的改动」时指纹一定对不上,报「这份源码没有被
|
||||
真实链接测量过」——**这不是预算问题,也不拦提交**(该脚本只挂在 `scripts/ci_local.py` 的 L0c,不在 51 条
|
||||
真实链接测量过」——**这不是预算问题,也不拦提交**(该脚本只挂在 `scripts/ci_local.py` 的体积门禁,不在 51 条
|
||||
pre-commit 钩子内)。判据:报错里若同时列出「未入索引的文件」且那些文件正被别的任务改,就等它们入库后
|
||||
统一 `@build` → `ram_baseline.py --update` 补凭证,**别为了把它调绿去 `git add` 别人在飞的改动**。本条写作时
|
||||
(2026-10-27 09:4x)凭证**仍是红的**:`App/protocol/iec104/iec104_comm.{c,h}` 正被通讯道改、未入索引;
|
||||
@@ -97,5 +97,5 @@ RAM 预算门禁 FAIL:RW_CCM 占用 92.3% > 92%(阈值 92%)
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-27 | §6 补第 8 条:同码凭证按 **git 索引 blob** 生成,工作区有在飞改动时 `--local` 必红;该检查只在 `ci_local` 的 L0c,**不是 pre-commit 钩子**,不拦提交(成因由道G 报出、Lead 复核) |
|
||||
| 2026-10-27 | §6 补第 8 条:同码凭证按 **git 索引 blob** 生成,工作区有在飞改动时 `--local` 必红;该检查只在 `ci_local` 的体积门禁,**不是 pre-commit 钩子**,不拦提交(成因由道G 报出、Lead 复核) |
|
||||
| 2026-10-23 | 首篇逐项文档(实跑:`RW_IRAM1 57.4%` / `RW_CCM 87.9%`,阈值 92%) |
|
||||
|
||||
Reference in New Issue
Block a user