feat(UI): 遥测死区与系数 2 页——注入表第四张、三态用脏位、WAIT 与 OK 各出一张黄金

这两页是「UI补齐」里唯一带**持久化语义**的一档,所以整笔的设计重心不在画面上,而在**页面如何诚实表达
「已受理」与「已落盘」**——这一点由 setpage-scout 在开工前的只读查实钉下来:cfg_file_save() 全仓唯一调用
者是 run_par_store_save(),而 default_task.c:246 **每轮无条件**调它、函数首行 s_dirty==0 就直接返回
⇒
落盘动力本来就在 defaultTask,不在 104 会话、也不在事件位。因此本笔走 A2(UI 不当授权写者),不需要
新事件位、不需要新同步口。

页面三态判据 = run_par_dirty_peek():ENTER / WAIT(脏位仍在 = 已受理未落盘)/ OK(脏位归零 = 整档已外科
保存成功)/ 写被拒 = ERR。两条限制写在页面注释里没藏:脏位是**整档全局**的(一趟落 [run_par] 9 键 +
[adc_cal] 两段,归零只证明「这一档落盘了」,不证明「我这一项单独落盘」);他人随后再改会重新置脏 ⇒ 沿用
clear/clock 页的结果锁存,把「我这次动作的结果」与「档位此刻的状态」分开。

**为什么黄金必须出两张**(这条是本轮最有价值的防假证据规则):模拟器假件**保留脏位**、全仓唯一的
s_yt_dirty=0 写点是 sim_yt_pump()(setter 只置不清),于是 WAIT 与 OK 各出一帧——两组同键序、唯一差别是
EXTRA 里的 --yt-nopump。若假件同步清脏位,真机的 WAIT 态永远不会被像素回归覆盖,黄金就成了「只钉住最
好看那一帧」。TC-UIDATA-028o 另钉住 ui_data 层零缓存。

形状:新增 ui_data 第四张注入表(八字段整表校验、拒残表),实现落在装配层 App/app/ui_data_feed.c,
页面零 App include(分层方向 + 模拟器不链 run_par 两条老理由都成立);共用视图模型 ui_w_ytview 承载两页。
合法域一律由执行侧出口给(run_par_db_range / RUN_PAR_K_MAX_UV),页面不抄第二真源——系数页 v1 只用粗守
并在注释写明两层守卫互不掩盖,µV/码 ×[0.5,2.0] 那条真带的出口器归 task-43,**没有**在页面里抄常量。

取舍与遗留(如实):系数行 d=0 整 µV(1e9 装不下两位小数,注释写了「要小数改哪、不收紧假上限」);b 覆盖
无口未做;徽标只画系数页;行名因「率」缺字改为 Hz死区(font-charset 机器先抓到的,不是我目测判的)。

实测:@build 0 错误 0 警告(Code=233,900 / RW-data=1,536);ui_data 单测 277/277、run_par
67/67 零回归;
内容页黄金 47 页全过、**存量 40 张逐字节零漂移**(git 里只有 7 张新增);menu_rows / 池水位 / font-charset
/
bom / mojibake / nested-comment / cppcheck / coding-standard / layer-deps /
bsp-err 全 0 新增;编号五项式
refs 8,778 / dec 7,622 / und **1,156**(上限 1,166)/ 比值 86.83%(基线 86.0% 之上)/ 错误差值
0,注释零裸形
字母+数字。generated-fresh 与 registration-check 本地 PASS;新增 6 只源文件按规矩**双端注册同笔**(叶子
+
registry 19→21 + uvprojx + CMakeLists),uvprojx 这次属「真的增删源文件」才入库。

移交三条不混进本笔:① 全量 --selftest 现 exit 1 是 task-41 硬闸在模拟器上也拒 reset/clear(既有红,快检档
不受影响),归台架道定夺;② 内容页 40→47 的全仓文档计数改写由 Lead 派;③ run_par_deadband_set 的限定
条件语措辞(「iec104Task 上下文」已被装配层裁定覆盖)归算法层下一笔。
This commit is contained in:
2026-10-27 05:07:18 +08:00
parent 1d39571114
commit 48a8ef0564
27 changed files with 1682 additions and 11 deletions
@@ -47,10 +47,10 @@ dhara 失败类 +84;RO 另 +208,全是该失败类名表)→ `ER_IROM1`
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
| 区域 | 用量 | 容量 | 占比 |
|---|---|---|---|
| ER_IROM1 | 257,272 B (251.24 KB) | 1,048,576 B (1024 KB) | 24.5% |
| RW_IRAM1 | 80,312 B (78.43 KB) | 131,072 B (128 KB) | 61.3% |
| ER_IROM1 | 260,004 B (253.91 KB) | 1,048,576 B (1024 KB) | 24.8% |
| RW_IRAM1 | 80,920 B (79.02 KB) | 131,072 B (128 KB) | 61.7% |
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
| **Flash (ROM 合计)** | **258,792 B (252.7 KB)** | 1,048,576 B (1024 KB) | **24.7%** |
| **Flash (ROM 合计)** | **261,540 B (255.4 KB)** | 1,048,576 B (1024 KB) | **24.9%** |
<!-- MEM_SNAPSHOT:END -->
各区的容量与常量来源:
@@ -470,6 +470,57 @@ FreeRTOS 堆不变(`free=17248 / min_ever=9312`)。验证链:`@build` 0/0
**留作候选、本轮刻意未做**:① **共享静态行缓冲池**(`dbg_console_line_claim()/release()` 发还栈上数组,改为从 2 个静态槽领取)——可再削每条日志帧的全部 152 B(全链再省 ~1.3 KB),但把"日志缓冲在调用者栈上"改成"跨任务共享 + 抢占失败即丢行",**改变日志并发语义**,需配套主机侧单测才动;② `timeSyncTask` 静态余量已正好等于 1024 B ISR 底线,**该链上再加任何调用都必须先抬栈**(已在 `Src/freertos.c` 栈注释里写明)。
#### 4.0.7 configASSERT↔控制台幻影环:三树实测与记账口径(2026-10-26 道G task-38 出证)
**机制**:`configASSERT` 在 FreeRTOS port/tasks/queue 内部可达(map 中 `refers to
vApplicationAssertFailed` 共 103 行),而 `vApplicationAssertFailed` 钩子(`Src/freertos.c`,
2026-09-29 起)经 `dbg_console_write_raw` 打印 `[ERR] ASSERT FAILED`;同时控制台裸发/入环
路径一度也走 `taskENTER_CRITICAL()`(→ `vPortEnter/ExitCritical` → `configASSERT`)——
两个方向互可达 ⇒ 调用图成**幻影环**。armlink 对环的展开策略(截断,或 `In Cycle` 单趟
记账)**随环的形状变动**:三棵树里 port.o、FreeRTOSConfig.h 与 configASSERT 宏逐字节相同
(本节按树核对),静态记账却差出最高 80 B——这就是「幻影环只是记账」的完整证明。
**三树实测**(每行钉双指纹:产物路径 + htm 内 Linker run 时刻;量数一律用**该树自己的**
`scripts/stack_report.py` 从该树根跑起,重跑脚本不算新测量):
| 树(提交态) | 产物指纹 | `vPortExitCritical` 子树 D | 最坏链含断言族 | defaultTask / netTask / iec104Task |
|---|---|---|---|---|
| bench-21(77779d49) | `_wt\bench-21-77779d49\MDK-ARM\STM32F407-Demo\STM32F407-Demo.htm`,Linker run 16:27:18 | **32**(展开 `⇒vApplicationAssertFailed⇒dbg_console_write_raw⇒con_tx_bounded`,钩子 D24) | 138 个函数条目的最坏子树经过钩子(浅链 +16~24 B,例 `net_get_generation` D72) | 3032 / 1912 / 1456 |
| bench-23(1d4b6a17,task-23 整行入环) | `_wt\bench-23-1d4b6a17\…\STM32F407-Demo.htm`,Linker run 19:43:40 | 8(截断) | 0 | 3040 / 1920 / 1464(+8 = `dbg_console_drain` 局部变量,task-23 真账) |
| 主干在飞(task-26 钩子 begin/end 接线,未提交) | 主干 .htm,Linker run 20:39:43 ——**该产物已被 22:52 覆盖,本行数字是会话内逐帧存档**(差分表见 task-38 卡片与 45086cf5 提交正文) | 8,但 `con_tx_release` 子树 D152(`⇒vPortExitCritical⇒vApplicationAssertFailed⇒dbg_console_tx_end (Cycle)`) | 3 条任务链各 **+80 B**;iec104Task 1544 → margin 1016 < 1024 = **FAIL** | 3120 / 2000 / 1544 |
| 主干现状(b24b2b5f,PRIMASK 落地后) | `MDK-ARM\STM32F407-Demo\STM32F407-Demo.htm`,Linker run 22:52:12 | 8(钩子未在其下展开) | **9/9 条最坏链干净** | 3040 / 1920 / 1464(margin 2080/1920/1096,全 PASS) |
**① 现状点名(22:52 树)**:断言→控制台这条边**仍然活着**(map refers 103 行未减,钩子
`[Called By]` 仍挂着 port/tasks/queue 全家),只是 armlink 当前不在临界区函数下展开它——
状态是「**休眠**」不是「解耦」。模块级记账底数还在:**37 个函数、14 个模块**的最坏子树
经过 `vPortExitCritical`(每个吃 8 B 底账)——`net_socket`×12、`net_pending`×9、`tasks`×3、
`task_watchdog`×2、`port`×2、`adc_input_poll` / `tpafe5160_snapshot` / `sys_clock_get` /
`osEventFlagsGet` / `xEventGroupClearBits` / `prvIsQueueEmpty` / `vListInsertEnd` /
`fire_event` / `s_epoch_ms` 各 1。**不止 console_log 一家**:凡走内核临界区 API 的模块都在
这张账本上,环形状再变时先咬这些链。
**② 量化表("把环断掉"能回收多少)**:现状每链 **0 B**——断言已不在任何最坏链,没有可
回收的存量;历史兑现值即上表:浅链 +16~24 B(b21 时代)→ 全图截断(b23 起)→ 深链
+80 B×3(20:39 实证)。环形状再波动一次的潜在摆动带 ≈ **−80 ~ +144 B/链**(弹药深度 =
`dbg_console_write_raw` 现测 D128 + 钩子自身帧 0)。
⚠ **反向风险(钉死,原话引用为准):若将来 `configASSERT` 被定义成空宏或被裁剪,本表
每链会"回收"约 80~90 B——那是记账变化,不是固件变好;谁把它写成优化收益就是在骗人**
(真实代价是内核误用全部静默)。要"正式断环"而不牺牲取证,正确方向是让断言钩子的
发送口不再回流临界区(task-26 的 PRIMASK 窗口已是这个方向),**不是**把宏裁空。
**③ 口径:硬线 vs 实际余量**:静态最坏链是**保守建模**——`Error_Handler`(own 152 + 环
记账 208 = D360)与 `[ERR]→NVIC_SystemReset` 这类终点路径,真实运行不会把环走满;它
也不算 ISR 上下文保存(约 100 B)与间接调用(§4.0.6 盲区条同源)。所以静态链适合当
**硬线**(可复现、无噪声、机器可判:margin ≥ max(1024 B, cfg×30%)),不适合当实际余量;
实际余量看 §4.0.2 的串口 `[STACK]` 水位。台架判据「栈水位不劣于上一轮」必须加容差或
改拿本节的静态硬线(「栈水位判据加容差或改静态最坏链当硬线」卡 = task-32,与本节和
§4.0.2 互指)。
**取证纪律(本节方法的可复用模板)**:凡跨树/跨构建引用数字,必须同时钉**产物路径 +
Linker run 时刻**——commit hash 不是稳定身份(重写正文即换 hash),tree hash + 父提交
才是;测量脚本必须从目标树根用该树自己的 `scripts/`(`REPO_ROOT` 跟随 `__file__`,
拿主树脚本进别的树量 = 量的还是主树产物,2026-10-26 实测踩实)。
#### 阶段 0 验收
- 串口可见 `[STACK]` 报告:每任务 `peak_used/free_min` + 堆 `free/min_ever`
+4 -4
View File
@@ -15,18 +15,18 @@
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|---|---|---|---|---|
| ER_IROM1 (Flash) | 257,272 B | 1,048,576 B | 24.5% | 参考 |
| ER_IROM1 (Flash) | 260,004 B | 1,048,576 B | 24.8% | 参考 |
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
| RW_IRAM1 | 80,312 B | 131,072 B | 61.3% | PASS |
| RW_IRAM1 | 80,920 B | 131,072 B | 61.7% | PASS |
| 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 |
|---|---|---|---|
| 网络 | 13,702 B | 12 KB | ⚠ 超 1.4 KB |
| 存储 | 7,072 B | 12 KB | OK |
| UI | 33,533 B | 16 KB | ⚠ 超 16.7 KB |
| UI | 34,085 B | 16 KB | ⚠ 超 17.3 KB |
| RTOS 堆 | 57,376 B | 56 KB | 堆=配额 |
| 应用 | 15,485 B | 8 KB | ⚠ 超 7.1 KB |
| 应用 | 15,537 B | 8 KB | ⚠ 超 7.2 KB |
| 其它 | 10,523 B | — | 参考 |
<!-- MEM_BUDGET:END -->