Files
STM32F4-Base/docs/门禁说明/nested-comment.md
T
wandering1 d64bbdc57f feat(wave): 故障录波数据面、接线与单通道回放页(软件闭环交付)
数据面(App/algo,纯逻辑,双端编译)
- wave_ring:8 路 × 320 点列优先环(1 kHz,预触发 160 + 后触发 160),adcTask
  唯一写者;触发/冻结/封存/冷却四态,冻结持续到落盘回执才解冻,滞留 2 s 自弃
  兜底——下一次真故障无波可录比丢一段更贵
- wave_store:段头编解码 + 拉模式打包游标 + 自带无表位运算 CRC-16/MODBUS(带
  锚点自检)+ 段名与整段长度等式;格式不依赖平台类型宽度
- 段头只存原始码值与封存瞬间生效系数,工程量只当单位标签、不进任何算式
- 三套主机单测(环 113 / 编解码 170 / 触发状态机 110),全部无桩、生产源原样
  纳入;新文件补 stddef 是 ARMCC 与主机差异,主机侧照不出这条分叉
- test/host/wave_read.py 是按 §6.2 的独立第二实现(dump/fft/shape/plot),92 条
  自洽断言;三份合成段互读双校验全过、哈希钉死,格式面双向闭合
- 三套新单测不加专属钩子,由覆盖率总闸全量真跑(沿用既有裁定)

接线(任务层)
- adc_cal:新增生效系数的 32 位定点原值入口(与既有浮点入口共用生效视图),
  段头按最低有效位精确记账,不经浮点回算
- adcTask:每拍压 8 路语义原始码值并推进状态机,封存那一拍同拍交棒封存瞬间事实;
  台架定周期自抓走双层编译门,产品镜像里零存在;串口新增一条 wave: 周期行
- defaultTask:卷空闲后一次心跳整段搬完(try 锁 + 256 字节分片 + 一次完整开档到
  关档),成败都回执;失败先删半成品再计错,段号只在成功时推进;持锁超 50 ms
  补一条告警并记历史峰值(喂狗余量可核)
- Lead 复核补第四道并发陷阱:采集任务优先级高于落盘道,搬段中途它可能正好把滞留
  超时的本段自弃、甚至封出下一段——数据区校验和是对「实际读到的字节」算的,
  半新半旧的段照样自洽,故关档后复检环侧身份判废:删文件、不补账、段号不推进
- Lead 复核抓到触发挂号不是原子操作(旧实现分三个字节写,消费者可在窗口里读到
  上一段的触发源与元件号):已把载荷压进一个字节,越界当场拒且不记任何计数;
  段文件字节布局零改动,三份合成段哈希逐字节不变
- protectTask:保护启动上升沿同点请求录波,一拍一次、元件号取最低位;判据本体
  零改动

人机(160×160 单色屏)
- 菜单树节点 26「手动录波」绑页面 + 记录查询组末新增「录波回放」叶,重跑生成器
  得 43 节点 / 34 叶 / 30 已绑,页面映射 28 → 30
- 回放页一屏一路、触发点前后各一周波共 40 点、每点 3 像素阶梯线(只用水平与
  垂直两种线段——单色屏斜线会被阈值裁掉);零点虚线、触发光标、窗口峰值、
  单位徽标与形状受限徽标;空态与坏段各一句显式文案,绝不用半截波形冒充数据
- ui_data 新增录波注入通道(六件操作表、三态返回);执行侧胶水带段头缓存、
  手动请求后连缓存一起作废、逐点走全仓唯一的码值换算入口
- 模拟器接真实环与编解码合成两段(含坏段与空态旗标),6 张新黄金参考,既有
  60 张逐字节零漂移未用更新模式;字库 218 → 224 字 / 319 字形
- 两份图形配置里图表组件仍为关;零堆分配;页面池水位不变
- 回放读路径的 uiTask 阻塞上界按实码写清:一次渲染 = 段表刷新 + 未缓存段头最坏
  22 次小读;已验段只剩一次开窗读 + 40 点解码;无实测数不编,实测归台架、
  仪器用已在跑的单轮工作耗时探针,不新加埋点

台账与文档
- 内存台账:录波环与落盘/回放静态逐条入册,三档对照 81,536 → 88,296 → 89,936
  字节(62.2% → 68.6%),累计 +8,400;预估 5.9 KB 与实测 +6,760 的差额点名;
  散列文件头注释同步;任务栈表核对后无需抬栈;体积趋势行与同码凭证由权威构建
  重打(0 错误 0 警告,四项体积与上一轮相同——本轮改动只有注释与声明位置)
- 设计说明:§5 补第四条并发陷阱;§6.3 定字段口径(两份独立实现的共同依据);
  §7 回填实测;§9 周期行写实;§10 台架判据改两段式且不比绝对相位;§12 补读路径
  风险行;抬头状态改成「代码面已交付、未做完的只有板级那一轮」
- 卡片:软件闭环一栏列证据;台架三条按用户裁定保持未完成,不做虚假销项
- 口径同步:数据可信、UI 补齐、页面补齐规划、产品需求、采集短板评测、任务规划
  索引与两份 README;陈旧现状句清扫 16 处(带日期戳的历史取数行一律不动)
- 门禁说明:nested-comment 记仓库根探测必须兼容 linked worktree 的 gitdir 文件
  形态(修前该钩子在任务树里恒报环境错、静默不可用);coding-standard 记
  「函数头注释超过 24 行就把函数功能挤出回看窗口」这条机制与本次现场
- 覆盖率基线登记两个新覆盖模块(套件 41 → 44 套);环单测重打后该文件有效行
  227 → 226、覆盖行 226 → 225,比率仍 99.6%,是分母变小不是覆盖率回退

为什么是单一提交:文档链接、双端注册、生成器幂等(菜单与字库)、内存数字与
体积事实源、编号门禁这五道整仓门禁要求代码、页面、菜单树、字库、文档与台账
同处一个提交才自洽,按数据面/接线/人机/台账拆成四笔必然红其中至少四道

编号门禁新增行 14 处命中已按语义改写:魔数常量按字符命名、段名样例改由格式串
生成、频谱表头改 f / 2x / 5x 并加一行图例;改写后自洽断言 92 条仍全过
2026-10-29 19:23:15 +08:00

124 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 块注释嵌套(nested-comment)— 注释体内不得再出现 /*
> 一句话:C 的块注释**不嵌套**。注释体内再出现一个 /* 时,ARMCC 报诊断 9-D
> 「nested comment is not allowed」。本仓的触发形态极其日常——**在注释里写作用域或通配路径**。
> 脚本 `scripts/check_nested_comment.py`(纯标准库、只读,2026-10-27 接线)。
## 1. 它在查什么
逐字节状态机走 代码 / 行注释 / 块注释 / 字符串 / 字符字面量 五个状态,
**只有在块注释体内**看到的 /* 才算问题,报「文件:行:列 + 上下文指纹 + 修法」。
| 判定 | 形态(本仓真实写法或自测用例) |
|---|---|
| **算** | 注释里写路径通配:`Drivers/BSP/UI/**`、`test/host/**`、`App/algo/*`(那串里的 /* 就是第二次注释的起点) |
| **算** | 块注释内部某行行首又开一个注释(复制粘贴手滑);块注释里用引号包住 /*;块注释里行注释符之后再来 /* |
| **不算** | 字符串字面量里的 /*(`const char *s = "/* 不是注释 */";`) |
| **不算** | 行注释里写 /*(编译器不报,本闸也不报) |
| **不算** | 本仓既有的尖括号写法 `<Drivers/BSP/UI/ui_w_soe.c>`(**推荐改法**) |
## 2. 为什么要它
- **只在重编译那个 TU 时才暴露**:改了某个头里的注释,只有编到它的包含者才看得见;
历史「0 错误 0 警告」不代表现在这份是干净的(与 `bom-guard` 完全同形的坑)。
- **按包含者数量刷屏**:一处嵌套写在被广泛 include 的头里,每个包含者各打一行。
本仓实测一例扩散到 **16 条警告**,把同批 10 条**真**警告挤到看不见——
等于把「能不能看清报错」这件事本身弄坏。
- 本闸**不需要构建**:提交时就报,不必等 @build,也不必去猜是谁的编译单元。
## 2.1 接线当日实测(都可复现)
| 测项 | 结果 |
|---|---|
| 全仓 `--all` 实扫 | **403 个 .c/.h:新发 0、存量 2 处**(都进基线,见 §7),退出码 0 |
| 形态电池(10 项,逐条对齐期望命中数) | **10/10**:5 个真形态全照出,5 个不该报的全安静 |
| `--selftest` 四项 | **4/4**:正例照得出 / 反例不误报 / 基线吞得下 / 只减不增有牙 |
| **真字节回放(空基线)** | 把 §7 那两份假件头当「今天新提交」跑 → 两条 [NEW]、退出码 **1** |
| 同一批真字节 + 已登记基线 | 两条 [BASE]、退出码 **0**(基线只决定「算不算新的」,不决定「有没有」) |
| 形态对照(重建当日那一处) | 尖括号写法 → 0 命中;同一段退回星号形态 → **1 命中,列 29** |
| 已提交历史回放 | 近 150 个提交里被改过的 .c/.h 共 **281 份旧字节:0 命中** ⇒ 这类问题此前从没被机器拦过,全靠人工在编译后清 |
> 编译器侧原文(**冻结引用**)——2026-10-27 02:22 那次 @build 的构建日志:
>
> ```
> ..\App\algo\run_par.c(267): warning: #9-D: nested comment is not allowed
> ```
>
> ⚠ 该日志每次 @build 都被覆盖、且不入库(02:37 已被下一次构建清掉,我写完这段再去查就没了),
> 所以把原文抄在这里存档;当日那三处同类**没有一处在已提交历史里留下字节**(上表最后一行),
> 本闸真正第一次拦住,会是「下一次有人在注释里写星号形态」。
## 3. 触发面
钩子用 `types: [c]`(即 .c/.h),第三方与生成物由配置顶层 exclude 挡掉。
脚本的 `--all` 与钩子喂进来的文件集**刻意同口径**(2026-10-27 实测 403 个文件)——
手跑面与提交面不一致,就会变成「两个数」,本仓已经为这种事返工过好几轮。
纯文档(.md)或纯 Python 的提交不会命中本闸——`types: [c]` 只喂 .c/.h 两类文件,想确认全仓干净就手跑本节的自查命令。
单独自查:`& .venv\Scripts\python.exe scripts/check_nested_comment.py --all`
## 4. 报错怎么读
```
[NEW ] App/x/y.c:45:17 块注释体内出现 /*(出现 1 次同形)
上下文:件才能测。 * * ⚠ 仅供 test/host/** 使用,不参与 MCU 构建。 * * 为
修法:注释里别写 /**(C 块注释不嵌套)——路径写全目录就好(Drivers/BSP/UI/),
或按本仓既有做法用尖括号包住(<Drivers/BSP/UI/ui_x.c>)
```
- `[NEW ]` = 基线之外的新命中 → **拦提交**(退出码 1)。
- `[BASE]`(要加 `--verbose` 才逐条打印)= 存量在册,不拦(见 §7)。
- `[TIGHTEN]` = 某条基线本轮实测已还清,该删了。
- 退出码:0 通过或只剩存量;1 有新命中;2 用法或环境错误。
## 5. 好处
- 秒级、**零第三方依赖**(只用标准库)。这点是刻意的:`generated-fresh` 那天栽在
「提交环境里裸 python 不是 .venv、装不到 fontTools」,本闸不碰第三方库,任何解释器
都跑得动——不会再造一根依赖某台机器环境的闸。
- 把「注释里写路径」这种看起来人畜无害的写法变成机器可判,而不是靠 reviewer 眼力。
## 6. 限制与盲区
1. **只管「块注释体内出现 /*」这一件事**。不判注释是否闭合(未闭合是编译错误,编译器自己
会拦,不需要一根只读闸替它操心);不判行注释里的 /*(编译器也不报)。
2. 不做预处理:若把 /* 藏进宏展开里(本仓没有这种写法),本闸照不到。
3. 基线指纹按**内容**不按行号——存量那几行改了行号仍算在册;反过来,同一文件里换一个新
形态会算新发(这条有专门的自测项)。
4. 只覆盖 .c/.h。Python 生成器模板将来若写出 /**,本闸照得到**产物**(产物是 .c/.h),
但不会指出模板——那段链由 `generated-fresh` 保证「脚本与产物同一笔提交」。
5. 编译器侧的真实警告仍以 `@build` 为准:本闸只拦「会让 ARMCC 报 9-D 的那一类」,
编译日志里其他诊断一概不管。
6. **仓库根探测必须兼容 linked worktree**:脚本原先用「`.git` 是不是目录」定位仓库根,而 `git worktree`
出来的任务工作树里 `.git` 是**一个文件**(写着 `gitdir: …`)不是目录 → 本闸在任何任务树里恒
退出码 2「仓库根定位失败」,**静默不可用**。这不是「放过了真问题」,是**根本没跑**——比放过更坏,
因为调用方看到红字会以为环境有问题、看到跳过会以为干净。2026-10-29 已把该判据从 `isdir` 改成
`exists`(`scripts/check_nested_comment.py:342`,修复归当日提交)。全仓 `scripts/` 只有它这么探根,
其余只读闸在任务树里一直正常。**后果要说清楚**:门禁修好之前那批录波改动没被它扫过,lead 手工对
改动面逐文件扫了一遍、嵌套命中 0,所以没有放过真问题——但那是**人工兜底,不是门禁功劳**。
通用教训:**门禁脚本按 `isdir(.git)` 探根 = 在 worktree 里自杀**;新增只读闸要么走
`git rev-parse --show-toplevel`,要么同时接受 `.git` 的文件形态。
## 7. 豁免与基线(只减不增)
接线当日全仓 `--all` 实扫 **403 个 .c/.h:新发 0、存量 2 处**,都在主机侧假件头里:
| 文件:行 | 形态 | 为什么今天不报 | 还法 |
|---|---|---|---|
| `test/host/common/ch395f_fake.h:7` | 注释里写作用域 `test/host/**` | 该假件不参与 MCU 构建(换 GCC/clang 的 -Wcomment 就会报) | 改成 test/host 目录 这类语义短语 |
| `test/host/common/fatfs_fake.h:9` | 同上形态 | 同上 | 同上 |
两处都属 `test/` 写域,接线时不由本道代改,按既有范式钉进脚本内 `BASELINE`,
**除这两处外零豁免**。一旦有人把这两份假件 include 进板端编译单元,就会按包含者数量刷出
9-D——这正是基线要显式挂着的原因:**不是「没问题」,是「已知待还」**。
还清一条就删那一行(`--prune` 打台账并点名已还清的)。
## 8. 改这套门禁本身
- 加判定形态必须同时加**正例与反例**:`--selftest` 四项(正例照得出 / 反例不误报 /
基线吞得下 / 只减不增有牙)之外,另有 10 项形态电池逐条对齐期望命中数。
- ⚠ **配置文件注释里不要复述声明行的字面量**:接线当天我在 `.pre-commit-config.yaml` 的
注释里写了一遍那个 grep 模式,文本 grep 的计数当场虚高 1(口径与自查命令见
`docs/门禁说明/README.md` §2 首条)。数数交给 YAML 解析,注释只描述。
- 加/删一条钩子要同批改三处:AGENTS.md 的钩子数、本目录 §2 标题、配置声明,外加索引行。