Commit Graph

233 Commits

Author SHA1 Message Date
super_agent_bot fff961d803 feat(net/udp): UDP 数据面并入 netMsgQueue 串行执行(#73)
CI / 静态门禁 (pre-commit) (push) Failing after 49s
CI / 主机侧单测 (MSVC) (push) Failing after 30s
CI / 静态门禁 (pre-commit) (pull_request) Failing after 45s
CI / 主机侧单测 (MSVC) (pull_request) Failing after 44s
前置来自 #57 SNTP 调研:旧 net_sendto / net_recvfrom 由调用方直调
SPI 且无限自旋收包,既违约「netTask 是芯片唯一访问者」,又把 IWDG
预算吃在自旋里。

改造面
- 新增 Drivers/BSP/NET/net_udp.c/.h(真机专属,模拟器不编译):
  bind/set_peer/sendto/recvfrom 四条执行体 + 挂起收包表 + tick;
  net_msg_type_t 加四型,公开 API 一律「入队 → 睡在通知上」。
- 源地址出参不搭 net_msg_t(按值拷贝,AGENTS 禁加回传字段):改住
  net_udp.c 的 per-fd 槽,netTask 就地写回调用方内存,通知值只带
  结果码;每条出口都清槽,泄漏 = 该 fd 永久 BUSY。
- 发送复用 net_send_locked,不写第二份分块逻辑;「会话是否已死」
  收成一条判据 net_send_state_down()(TCP 看 ESTABLISHED、UDP 看
  UDP_OPEN),原先 4 处各写一遍。
- 顺带修掉真缺陷(新条 TRAP-NET-22):旧 sendto 把 DesIP 写成具体
  IP,芯片静默切 UDP Client,而旧 recvfrom 无条件剥 8 字节 → SNTP
  这类 48 B 应答丢载荷前 8 字节并交出垃圾源地址。加 udp_mode(塞进
  原有 padding,sizeof 不变)、bind 恒 SERVER、显式对端 sendto 切
  CLIENT、set_peer(fd, NULL) 轻量回 SERVER,仅 SERVER 且 avail >= 8
  才剥头。
- 截断定案:一次调用交付一个数据报,buf 装不下的尾巴用 64 B 栈上
  scratch 排空(不用 clear_recv_buf——它连后面已到达的包一起丢)。
- 取消点接齐:net_init 重建 cancel_all、net_socket 分配 fd clear、
  net_close settle_close + clear、重 bind 前先结算等包者,
  net_process_messages 第 5 个 tick 位挂 net_udp_tick。
- 收紧两处契约(原实现无生产调用者):UDP 未 bind 一律 -5;
  net_recv / _nb / _to 拒 DGRAM(绕过剥头就是静默吃 8 字节);
  NET_ERR_NOT_IN_NETTASK(-12) 退役为历史常量并写明原因。

验证
- 主机单测 test/host/net_socket 全套 616/616 绿(改造前 470):新
  分片 ns_s7_udp.c 20 组,改写 440 / 555 / 560 组;覆盖率
  net_udp.c 234/236(99.2%)、net_socket.c 956/969(98.7%),
  scripts/test_coverage_baseline.json 随本提交登记新文件。
- 测试抓到 4 个被测码真 bug:自等守卫误用调用侧谓词、芯片长度被读
  两次、槽指针取在 fd 合法性检查之前、重 bind 静默作废等待者。
- Keil ARMCC 0 Error(s) 0 Warning(s);net_udp.o Code 1,358 / ZI 224
  (8 fd × 28 B,其余模块零增长),全仓 RW_IRAM1 72.2%;内存台账
  受管区按门禁提示用 --write / --update 机器回填,未手改数字。
- pre-commit 全量钩子(含 doc-links、coding-standard、mem-budget、
  generated-fresh、bsp-err、registration、nested-comment)全绿。

限度
- 本轮无板子:真机回归 5 条(两端互通、set_peer(NULL) 能否恢复收
  广播、截断排空后下一包完整性、挂起 recv 期间 IWDG 余量、bind 写
  DesIP 与 open 先后的真芯片效果)已写进覆盖率口径「台架必验」。
- scripts/stack_report.py 退出码 1 是 10-26 起挂在账上的 iec104Task
  栈债(margin 1,016 < 1,024,见 docs/代码规范/pre-commit使用说明.md:
  122);本轮只让 net_bind 的 UDP 分支多一层静态帧(1,544 → 1,600),
  netTask margin 1,888 与 timeSyncTask 1,152 不受影响。抬栈要过
  mem-budget 台账,不在本分支自行处理。
- 登记簿销项按仓库约定留给主工作树,本分支不动 docs/任务规划/**。
2026-11-03 18:44:01 +08:00
super_agent_bot 493fa6ba0e feat(ui/remo): 7003 总线新增 STATUS 身份查询与 GOTO 直达页(#69)
STATUS(命令 4)一帧回答"现在在哪个画面":kind(MENU/PAGE/STANDBY)
+ 菜单路径/选中 + 页 id,布局契约与字节序见 ui_net_sink.h。由 uiTask
轮询驱动,同上下文读静态导航状态:零画面副作用、免锁、不发日志。
新增只读接口 ui_menu_get_nav_state / ui_content_page_current_desc。

GOTO(命令 5)按页 id 查生成表直开内容页,以 VIEW 权限进入(与
"已过查看密码"同档,不代输任何密码)。编译期门控
DTU_BENCH || UI_REMO_ALLOW_GOTO:产品构建恒拒(status=4),模拟器
构建默认开启便于无板验证;执行类页(clear/reset)固件侧同样拒绝
(status=2,与主机 EXEC_PAGE_IDS 同一事实源);待机中拒绝(status=5),
由主机按键唤醒后再来。成功应答带回目标叶节点下标便于对账。

ui_sink 主机单测补导航侧链接假件 + 最小 lvgl 桩(仅本测试目录)。
产品构建 Keil 0 Error 0 Warning;ui_sink 单测 17/17。
2026-11-02 22:19:58 +08:00
super_agent_bot 046097918d fix(ui/wave): 回放页切通道/切段后不重绘——key_cb 接管键后补失效
回放页 v1.0 建的键处理返回 true(接管全部键),但从未调
ui_content_page_invalidate():框架只在 key_cb 返回 false 时才替页面
失效,于是「键送达、状态已切、屏上纹丝不动」。该病被枚举缺陷完全
遮住(整页恒空、没有可切的画面),随本波枚举修复首次上板才暴露:
真机按 ←/→/↑/↓ 帧字节完全不变(同帧哈希),布局与状态机无问题。

- 上/下切通道、左/右切段成功路径、空态再探路径,各补一次
  ui_content_page_invalidate()(与 ui_page_clock / ui_page_password
  等已接管键的页面同纪律);
- 切段失败仍保持「按了没动」的诚实语义(不重绘、不把忙态清成空);
- 修改记录 v1.1。

/keil-build 0 错误 0 警告(Code 247,288 B,+4 B),RAM 不变;
台账 / 同码凭证同批刷新。

Refs #38
2026-11-02 18:21:34 +08:00
wandering1 7efa58d9a1 network: 台架 IP 迁到 192.168.31.100,并把重复写死的值收进单一真源
CI / 静态门禁 (pre-commit) (push) Failing after 25s
CI / 主机侧单测 (MSVC) (push) Successful in 48s
分两段(用户裁定 2026-11-02):先做地址迁移,再把同类字面量收敛成
单一真源,以后再改地址只动一处。

一、IP 迁移 192.168.1.100 → 192.168.31.100(gw 同步 → .31.1)
- 工厂默认:Drivers/BSP/NETCFG/net_cfg.c 的 NET_CFG_DEF_IP/_GW
- 注释/文档示例、主机与板级测试、台架 py 脚本、两份 sim_config.ini
- 字节序教学点同步换算 hex:0x6401A8C0 → 0x641FA8C0(内存
  C0 A8 1F 64),旧版示例引文与历史 Trap 记录按冻结纪律不动
- UI 黄金基准:网口设置 4 态 + shot_info 共 5 张 .bin 由
  check_content_pages.py --update 重刷(渲染值 IP 变了,属预期变更)
- 保留判定:客户点表引用(IEC104/继电保护规划)、芯片手册摘录、
  日期冻结的变更记录行、已退役台架档案 MDK-ARM/retracted_bench_*

二、单一真源收口(A+B+C+D,用户批准全做)
- 新建 test/host/bench_defaults.py:台架 py 默认值唯一定义点
  (DEVICE_IP/DEVICE_GW/PC_IP/NET_BCAST),22 个脚本改为引用;
  MDK-ARM 两探针与 scripts/board_ui_nav.py 经 sys.path shim 引入
- test/board/net_test_task.h 新增 TEST_PC_PEER_IP,板测三处
  PHASE*_REMOTE_IP 改引该宏(保留 -D 覆盖);Phase1 断言改
  memcmp 数组比较,数组各只出现一次
- simulator/tests/check_content_pages.py 从 net_cfg.c 正则解析
  NET_CFG_DEF_* 推导期望值(解析失败即报错、不静默兜底),
  脚本内台架 IP 字面量清零
- 装饰性注释去具体值(net_cfg.h/net_socket.h/net_socket.c/
  net_socket_sim.c);字节序教学 hex 对刻意保留为唯一写值点
- 刻意不动:主机单测夹具 IP(逐文件钉住的任意值是正确测试
  实践)、文档正文 IP(那是文档内容本身)

顺带:scripts/gen_task_table.py 规范串清掉「,B5」——编号清退提交
72b4b094 改了受管区没重跑生成器,task-table 门禁因此一直红

验证:产品 @build 0/0;CH395F 套件开关构建 0/0 后还原产品态;
6 套主机单测 rc=0;content_pages(含宏推导路径)/菜单 8 态/
推流位比对全 PASS;bom-drift 99 无漂移;doc-links/coding-standard/
cppcheck 绿;py_compile 23 文件 + --help 冒烟(含 shim 绝对路径)
2026-11-02 14:46:42 +08:00
super_agent_bot 72b4b0943c docs: clear task and layer shorthand IDs
Replace task/batch shorthand IDs and residual layer labels in
human-facing docs with semantic phrases. Add the new cleanup
policy to docs/编号规范.md and update the entry docs.

Machine keys (TC/CODE-RULE/MENU-STATE/TRAP/FR), hardware
designators/pins/MAC, protocol/version numbers and frozen
historical evidence are intentionally kept.

Docs only; no code behavior change. Verified with
check_doc_links.py --all and git diff --check.
2026-11-02 03:33:51 +08:00
wandering1 4a4ad6bc5f chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名,
不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体
run_boot_test.py 的真实调用。

- AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改
  「编译(/keil-build)」「串口回归(/serial-boot-test)」
- keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree
  方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围)
- 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入
  等 41 行历史证据保留旧名
- gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑
- 顺手补并行方案同行漏网计数 51→52 钩子

验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all
rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52
钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
2026-10-30 13:03:12 +08:00
wandering1 7708f12c10 fix(ui/screenlink): 按键应答扣到按键后画面,反馈典型 250-450 ms 降至 70-135 ms
按键召唤答的是按键前的旧帧:uiTask 还没消化键答案就发走了,新画面
只能等下一次周期召唤(280 ms/轮)才带得出——真机反馈 250-450 ms 的
主构成(根因账本 docs/子系统设计/屏板链路设计说明.md §7.1)。

改动(用户批准范围 A+B+C;屏板固件未动,决策三不变):
- sb_link:按键召唤改为只入队不起发;uiTask 同拍重绘并组帧成功后交
  凭据(sb_link_key_rendered),TIM3 1 kHz 的 sb_link_ms_tick 以 ≤1 ms
  放行新帧。140 ms 截止(SB_KEY_RESP_MAX_MS,+59.6 ms 线上 <205 ms
  屏板放弃线)保底发旧帧,"一召一答"契约不破;线忙时同旧丢单、
  不留扣件。起发两中断源(USART1 IDLE/TIM3)共用 sb_tx_kick,
  "判忙→占位"在 PRIMASK 临界区原子完成。
- ui_task:排空注键挪到组帧之前,注键→即派即绘→组帧同拍闭环;
  组帧返回 BSP_OK 才交重绘凭据(BUSY 拍帧还没反映按键)。
- ui_port:注键单槽改 8 深环(连按不再互相覆盖,满丢最旧+计数);
  新增 ui_port_service_keys:同拍 lv_indev_read + lv_refr_now,
  省掉 indev ≤20 ms + 刷新 ≤20 ms 两段等待。
- ui_menu_view:菜单移动选中只失效新旧两行带(黄金参考零像素变化),
  滚动/异常回退整框失效。
- simulator:窗口循环 sleep 扣除本轮已耗时(原先无条件睡满
  lv_timer_handler 返回值,叠加 Windows Sleep 15.6 ms 粒度放大观感)。
- 串口 TX 上报新增 dfok/dfold(凭据放行新帧数/超时保底旧帧数):
  dfold 连涨 = uiTask 被饿 ≥140 ms 的直接现场证据。

验证:test/host/sb_link 164/164(新增 18 条:不立刻起发/凭据放行/
140 ms 截止保底/忙时不留扣件/陈旧凭据不放行等边界);菜单 8 状态 +
内容页 66 张黄金像素 PASS;Keil @build 0 错误 0 警告(两次);
BOM 99 文件无漂移、代码规范、doc-links 本次文件零问题、cppcheck PASS。
文档:屏板规划文档已被主干收编进设计说明,延迟账本落
docs/子系统设计/屏板链路设计说明.md §7.1,并同步 §4 口径、§8 接口、
§9.1 单测台账 146→164、§10 坑账本补条。

RAM 台账(分支不跑 --write,并行分支提交约定):本笔盖戳行
RW_IRAM1 94,336 B (72.0%) / RW_CCM 57,376 B (87.9%),较上一台账行
+4,400 B——为距上次台账构建以来分支累计差(含批 3 状态灯等未盖戳
提交),非全部本笔引入;本次自身增量按对象计为 ui_port.o +12 B
(8 深键环+丢计数+indev 指针)与 sb_link/o 数十字节级。
gate: only mem-budget/mem-numbers pending lead ledger backfill
(合并落主干后 Lead 统一 --write 回填受管区)。
2026-10-30 09:50:31 +08:00
wandering1 add61ca2af Merge branch 'v2-on-ui' into task/screenlink-plan-to-design
# Conflicts:
#	docs/子系统设计/控制台单写者日志设计说明.md
2026-10-30 09:25:35 +08:00
wandering1 8744c833e5 docs(屏板链路): 规划文档收编为设计说明事实源,全仓引用与端口口径同步
- 重写 docs/子系统设计/屏板链路设计说明.md:链路角色、串口与端口配置、
  物理接线、协议实现口径、键值与状态灯映射、资源与耗时、对外接口、
  已验证/未闭环、坑账本——只写现状事实,演进过程查 git 历史
- 删除 docs/任务规划/屏板链路.md(内容已全部沉淀进上述设计说明,
  登记簿「完成即销项」;docs/任务规划/README.md 相应两行一并移除)
- 代码与主机侧测试的文档指针改指设计说明对应章节:
  sb_link.h/.c、sb_proto.h/.c、sb_led_map.h、console_log.c、ui_task.c、
  test/host 的 sb_link/sb_proto/sb_led_map 三套与两个台架脚本
- docs/README.md:设计文档索引行与设计说明描述更新,任务规划目录 7→6 文件
- docs/产品需求/产品需求设计文档.md:串口通道现状口径同步——第 11/12/13 条
  已交付(4208 B 应答/状态灯掩码/键上行/双出口并发)、LOGO 下发出局不实现、
  帧长实测固化、调试日志口 UART5/RS232#1(§4.2/§5.16/§6.6/§9.12/§9.13)
- docs/子系统设计/控制台单写者日志设计说明.md v1.8:全文端口口径由
  USART1 同步为 UART5/RS232#1(CON_UART* 单点宏),ORE 机理标注跨端口
  同因,时间线补端口迁移行;头部修订指针改为实际章节名

说明:本任务经用户显式授权在任务树内编辑 docs/任务规划/ 与产品需求基线
(铁律「单写者」豁免一次);主树合并后统一跑全量 check_doc_links --all。
2026-10-30 09:22:44 +08:00
wandering1 f754d59218 docs(测试说明): 按用途分六个子目录并同步全库引用
分层:流程与规范/ 单元测试/ 板级测试/ 专项验证/ 测试工具/ 测试数据/,
顶层只留新增的目录导航 README.md(归层判据 + 硬约束 + 本次处置记录)。

- 搬迁 28 个路径:测试数据三个目录 58 个文件为纯改名(R100,内容逐字节不变)
- 全库引用同步 124 个文件:AGENTS / docs 索引 / skills / 门禁注释 / scripts
- 门禁面:scripts/check_test_traceability.py 的 glob 改 rglob
  (分层后不递归扫描=追溯门禁直接瞎掉)
- scripts/rename_ref.py:收尾的 ℹ 提示在 GBK 代码页抛 UnicodeEncodeError,
  让进程以退出码 1 收场而改写其实已落盘(RC 与真实结果相反);改为把
  stdout/stderr reconfigure 成 UTF-8,与 check_doc_links.py 等同法
- docs/文档规范.md §1.1 补「子目录文件过多时再分一层」;§3.6 补 rename_ref
  搬整目录的两个实测坑:① 会改写目录内部文件的内容 ② 内部文件的 ../ 出链
  不补层级(本次 23 个被误改的证据文件已按 HEAD 原始字节逐字节还原)
- docs/README.md §6 改为与六个子目录一一对应,并补登原先漏登记的
  「启动日志原子性复验流程」一行
- .cubemx_survival_baseline.json:App/util/task_watchdog.c 的注释路径也随分层更新,
  该文件是存活审计的 watchdog-module 漂移锚点,按脚本自己的提示跑 --write 重建基线
  (实测重建后仅 watchdog-module 一条哈希变化)
- 铁律 4 例外(本次在任务树里改了单写者文件):AGENTS.md 与
  .pre-commit-config.yaml 只被 rename_ref 改写路径字面量(后者全在注释行),
  任务表块与钩子声明一字未动

验证:check_doc_links.py --sections-strict 覆盖全部 88 个改动 .md 全绿;
check_test_traceability.py 输出与主树逐行一致;196 个文本文件无 BOM、无新增
CR、无 U+FFFD、无行尾空白;陈旧引用面由主树 25 处降到 6 处(无新增断链)。
2026-10-30 07:16:07 +08:00
wandering1 b4588108ed merge: v2-on-ui into task/screenlink-bench-serial (wave + screenlink) 2026-10-30 01:08:37 +08:00
wandering1 0b84c43d2e feat(屏板): 状态灯位映射 sb_led_map + 104 RUN 观测口(批 3);附 4-1 并发压测脚本
批 3(LOGO 按用户裁定出局,不实现、sb_proto 的 LOGO 组帧挂待删标注):
- 新增 App/algo/sb_led_map.c/h:业务状态 → 屏板 LED 位掩码的唯一映射表
  (纯逻辑,主机单测可跑)。ID1 运行心跳 500ms 半周期闪(与主板 LED 同拍)、
  ID2 保护动作、ID3 104 链路 RUN、ID4 采样电平故障(不选 sticky——灯要能灭)、
  ID5 保护启动 picked;bit0 与 ID 6~19 恒 0。这张表参考源码里没有,属自定,
  出处 docs/产品需求/产品需求设计文档.md §5.16.3 ①。
- iec104_serv 新增 iec104_serv_link_run()(透传 link 状态机,不另存镜像量)。
- ui_task.c 帧组装块每拍现采四路状态源(均为任意上下文可读观测口)折算后
  传 sb_link_frame_update——替换批 2 的恒 0 占位;掩码变了帧就重组,
  复用 busy/dirty 机制,无新通路。
- 主机单测 test/host/sb_led_map 17/17(位语义/恒零位/闪烁相位/tick 回绕/
  NULL 契约);注册豁免走 SIM_EXEMPT(模拟器无屏板串口)。

批 4 的 4-1 双通道并发回归(真屏板在线实测):
- 新增 test/host/remolcd_pull_test.py:RemoDispBus 拉帧压测客户端
  (INIT→LCDMEM 10 Hz、逐帧 XOR+长度校验、往返延迟统计、--dump PBM)。
- 实测:网络侧两轮 2200 拉帧零错零超时(min 35/avg 63/max 131 ms);
  屏板侧 60 s 隔离窗口 rx +233 全收、drop 零新增、bad=0、off=0、无
  OFFLINE——两侧互不拖。观察项 len=7(断帧尺寸≠6,累计 7 次)出现窗口
  无法归因压测、隔离窗口零新增、有效帧零损失,已挂设计说明 §5.7 待查。

附带:sb_link.h frame_update 注释回填实测耗时(509/688 µs 取代旧估算)。
Keil 0 错误 0 警告;ci_local 全绿(SKIP=nested-comment,主树脚本另查 0 新发);
Code=239,064 B / RW_IRAM1 65.5 % / RW_CCM 87.9 %;覆盖率 90.1 % / 44 套件。
2026-10-30 00:39:57 +08:00
wandering1 dab0d8ba17 feat(屏板): 帧 B 组帧 + 中断起发 TX DMA + 按键队列 + 离线判定(批 2)
批 2 五项全部落地(docs/任务规划/屏板链路.md §3):
- 2-1 sb_link_frame_update():uiTask 渲染后按需组帧,不留 3200 B 影子
  副本,直接拿活显存与帧内点阵段 memcmp;组完自校验,过了才置 ready。
  静态 4208 B 帧缓冲实测落 RW_IRAM1(0x20006d98),不在 CCM。
- 2-2 空闲中断校完 6 B 召唤帧直接点 TX DMA(sb_tx_kick),不排队不攒批;
  busy 期间再来的召唤丢帧并计数,绝不叠发(屏板收帧一次 IDLE 一包)。
- 2-3 按键走环形队列(中断入、任务出),uiTask 按 §4.3 表映射 UI_KEY_*
  后调 ui_port_queue_key;9/10(RESET/FACTORY)按决策四先丢弃并留痕。
- 2-4 HAL_UART_TxCpltCallback 在 rs485.c 加第三行转发(三个全局回调
  落点收一处,防日后重复定义);busy 冻结帧缓冲,错误中断按 gState
  收拾被打断的 TX,防残留 busy 把链路永久锁死。
- 2-5 连续 >=10 s 无有效召唤帧才判离线(不按 500 ms——屏板约 5000 s
  自复位有 1 s 空窗),恢复即清标志;CRC 错帧静默丢弃并计数。

实测:台架 40 s(PC 顶替屏板,--key-fixed 2 持续改画面)rx=143 ok=143
keys=143 crc=0 / tx=143 done=143 drop=0 abor=0 fb=144 bad=0;脚本一停
10 s 准点出 OFFLINE。组帧耗时 509 µs 典型 / 709 µs 峰值——原 0.3 ms
判据系估算,已按实测在规划文档改口为 1 ms 并给出压降路径(未做)。

附带:rs485_on_rx 补 CODE-RULE-09 文档块(本轮改 rs485.c 触发整文件
重扫被拦);CRC 循环计数改 32 位(消掉 ARMCC 每轮 UXTH);bench 脚本
加 --reply/--key-fixed/--dump,并写明 CH340 收不了 700 kbaud 连续流、
--reply 收不全 4208 B 不是固件问题。

主机单测 146/146 PASSED(+65);Keil 0 错误 0 警告;ci_local 全绿
(SKIP=nested-comment,worktree 限制,主树脚本另查 0 新发)。
Code=238,936 B / RW_IRAM1 65.5 % / RW_CCM 87.9 %。
2026-10-29 20:59:36 +08:00
wandering1 7c8669e52a feat(屏板): USART1 700 kbaud + 收中断传输层 sb_link(1-2/1-3)
批 1 的 1-2 / 1-3:把屏板(DTU-LCD)发来的 6 B 帧 A 在 USART1 上稳定收下来。

1-2 波特率:.ioc 不保存任何 UART 的波特率(只有
USART1.IPParameters=VirtualMode),GUI 里改也不落盘,所以只能在
Src/usart.c 的 USER CODE BEGIN USART1_Init 2 区覆盖 huart1.Init.BaudRate
后重调一次 HAL_UART_Init(第二次调用时 gState 已非 RESET,HAL 只重算
BRR、不再跑 MspInit,GPIO/DMA/NVIC 不会被重配)。开机日志实测
BRR=0x0078,与理论值 84 MHz/(16x700 kHz)=7.5 逐位相符,误差 0%。

1-3 收中断:新建 Drivers/BSP/ScreenLink/sb_link.c/h——16 B 缓冲 + IDLE
中断断帧、帧 A 校验、错误中断里按 RM0090 清 ORE/NE/FE/PE、周期统计上报。
HAL_UARTEx_RxEventCallback / HAL_UART_ErrorCallback 是全局单例(唯一实现
在 rs485.c),故只在那里各加一行转发,按句柄过滤放在 sb_link 内。

台架实测(PC + USB 转 TTL 顶替屏板,COM6@700000)撞出两个真问题:
- 错误回调里无条件重挂 -> 出现 err=160 / rearm=160 同步增长,看着像每帧
  都重挂失败。真因是 HAL 把 NE/FE/PE 归为非阻塞型(接收照跑、RxState 仍是
  BUSY_RX),此时 ReceiveToIdle_IT 只会返回 HAL_BUSY。改为仅当
  RxState == HAL_UART_STATE_READY(ORE 那类阻塞型)时才重挂。
- 日志字面量里带中文 -> ARMCC #870-D invalid multibyte character
  sequence。字面量一律纯 ASCII,中文只写注释。

验证:主机单测 81/81 PASSED(test/host/sb_link/,其中按 RM0090 的清除语义
把"必须 SR->DR 顺序才清得掉 ORE"钉成断言);Keil 0 错误 0 警告;台架
rx == 发送帧数、ok == rx、crc=0、len=0、size=6、keys 随按键帧递增;先用
错波特率灌 64 B 垃圾(len=31、bits=0x06)后自动恢复且 rearm=0。
新增 test/host/screenlink_bench.py 作为可复跑的屏板激励(--key / --glitch-baud)。
2026-10-29 20:13:18 +08:00
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
wandering1 b25d70e962 feat(屏板): 协议层 sb_proto(帧 A 解析 / 帧 B 组帧 / CRC-16)
批 1 的 1-1:新建 Drivers/BSP/ScreenLink/sb_proto.c/h,纯逻辑、无 HAL 依赖,
可主机侧单测。屏板侧的协议事实源是 DTU-LCD 的 CRC16.c / MODBUS.c / rs485.h。

- CRC-16/MODBUS 用一张 16 位合并表(512 B,与屏板两张 8 位表同量)。可逐项
  验证的关系:s_crc_table[i] == (auchCRCLo[i] << 8) | auchCRCHi[i]——屏板把
  低字节表叫 Hi、高字节表叫 Lo,这正是下面那个字节序陷阱的根源。
- 帧 A 解析:先校 CRC 再看字段。地址/功能码/命令类型不符报 BSP_ERR_PARAM,
  CRC 不符报 BSP_ERR_CRC(调用方据此决定"计数"还是"静默丢")。RT_CMD 带
  非 0 key 不报错、按 0 收——为这一项丢一帧等于丢一次刷新,代价不对等。
- 帧 B 组帧:普通图(掩码小端)+ LOGO(宽高大端 + 魔数 0x89 0x45),整帧
  4208 B 重写(998 B 填充固定 0x00 且参与 CRC),组完即自校验。
  LOGO 容量按字节数判(((w+7)/8)*h <= 3200)而不是像素数:161x159 只有
  25599 像素,却要 3339 B 才装得下。
- 主机单测 59/59 PASSED(test/host/sb_proto/),覆盖率 74/74 行、6/6 函数。
  其中两条向量是屏板自己算出来的线上字节(01 03 01 00 F0 48 与
  01 03 00 09 31 DE),并按屏板 Check_CRC 的口径反查"它会认这一帧"。
- 双端注册:uvprojx 加 File + IncludePath;模拟器侧走 check_registration.py
  的 MCU-only 豁免(纯逻辑,模拟器无消费路径)。
- Keil 构建 0 错误 0 警告;Code 不变——尚无调用者,链接器按未用节丢弃。

文档侧的 CRC 字节序订正见下一个提交。
2026-10-29 19:17:28 +08:00
wandering1 94cce8b615 docs+scripts: 删除测试与架构专项文档并把引用改成就地口径
这是并行的另一路在飞的改动(本会话按用户「全部提交」指示代为落盘):
移除 docs/任务规划/测试与架构专项.md,并把指向它的引用改成就地口径
(app_assert.h / net_socket.c / ch395f_test.h 头注释改指本文件注释或
对应文档,check_size_gate.py 与 ram_baseline.py 的出处改指 CI 文档,
RAM预算台账、暂缓-RS485、采集与算法层设计说明、cubemx-survival、
id-check、两份 README 同步改引)。

如要拆回:git reset --soft HEAD~1 即可把这一笔退回工作区。
合树后 @build 0 错 0 警(Code=236456 RO=28124 RW=1876 ZI=137084)。
2026-10-28 22:00:34 +08:00
wandering1 6572591181 refactor(编号): 链路时延段号 L1~L11 → 权威 ASCII 段名(含 C 宏名与 peer 变量)
批次六余账之一。批次 3b 当时只改了 net_latency_test.c 的日志串与注释,本笔收掉剩下
的:标识符、peer 脚本、其余四个 board 文件。

- 段名映射(权威表在 scripts/archive/id_gate/编号治理历史.md §10):SPI_CMD /
  SPI_DMA / CHUNK_WAIT / TO_PC / QUEUE_RT / ECHO_RT / FRAME_RT / PROD_CYCLE /
  RELISTEN / LOGTAX / UI_LOOP。C 侧纯 ASCII,文档侧沿用中文段名。
- C 标识符 29 条规则 86 token:LAT_P13_OP_L5_RECV → LAT_P13_OP_QUEUE_RT_RECV、
  P13_ST_L3 → P13_ST_CHUNK_WAIT、LAT_P13_L3_SAMPLES → LAT_P13_CHUNK_WAIT_SAMPLES,
  并把文件内既有的 CW / QRT / LT 三个缩写前缀一并展开成同一套词,不留两套词汇。
- REQ2FIRST / FULLFRAME **不并入** FRAME_RT:它们是整帧往返段内两个不同测量
  (报告标签本就叫 req2first_mcu / fullframe_mcu),并入会撞名并丢掉区分度。
- peer(test/host/latency_peer.py)42 条标识符 + 7 个小写局部量 + 操作表整表重排。
  **JSON 输出键 l5_recv / seg_l6 / frames_l7 等 48 token 刻意不改**——它们是
  docs/测试说明/m2_data/ 五份冻结实测数据的 schema,改名等于让代码与归档证据
  漂移;边界与理由写进归档 §10.3,是**有记录的例外**不是漏网。
- 注释 23 处;顺带修一处上一批路径残留:peer 被写成 test/latency_peer.py,实际
  在 test/host/ 下,三处注释改对。
- 站点:test/board 111 + Drivers/BSP/CH395F/ch395f.h 1 + peer 46(无词边界口径)。
- 零代码生成变化:@build 改前/改后各一次,均 0 错 0 警且 Program Size 完全相同
  (Code=236456 RO=28124 RW=1876 ZI=137084)。被改名的标识符全落在
  ENABLE_PHASE13_TESTS 区间内 → 另开 TEST_SUITE_CH395F + PHASE13 组合编一次
  (0 错 0 警),套件开关事后无条件还原、sha256 与还原前一致,再重建产品构建。
- 等价证明:剥注释 + 套登记表后与 HEAD 逐字节相同(not_equivalent=0)。
- 禁改一字未动:console_log 的 "L1\r\n"(测试数据)、lftpd 与
  ftp_capture 的 "215 UNIX Type: L8"(FTP 应答断言)。
2026-10-28 18:49:45 +08:00
wandering1 04997e48aa refactor(编号): test/host 与 App/Drivers 注释去测试层级号(73 站点 / 64 文件)
同族第二道:代码注释面。41 个 run_unit_test.py 文件头与引用行、common harness 与两份
README、4 个 *_test.c 文件头、App/** 14 处(含 BOM 文件 App/cfg/cfg_file.c)、
GD5F nand_ftl.c 1 处,共 73 站点 / 64 文件;措辞取「主机侧纯逻辑单元测试运行器
(纯标准库)」避免同行「主机侧」重复。

零行为改动(机器证据,不是口头):
- 17 个 .c/.h 剥掉注释后与 HEAD **逐字节相同**;
- 42 个 .py 走 ast.dump 比较、仅归一 docstring(真实字符串字面量参与比较,故旗标或
  输出文本被动过必然报红)→ 全部相等;behaviour_changed=0。
- py_compile 42/42;抽跑 watchdog 47/47、freq 46/46(动手前先取基线,改后同数);
  CR 字节 0、EF BF BD 0、BOM 与 HEAD 逐一一致、check_bom_drift rc=0。

刻意未动:6 处字符串字面量(FTP 应答 "215 UNIX Type: L8"、lftpd_test.c SYST 断言、
console_log_test.c 的 "L1\r\n"、phasor_test.c 的 "L2-in ring" 文案与 printf 标题)——
改了就是改测试输出/行为;UI 设计令牌层 L0(ui_tokens.h、ui_content_page.c)与
test/host/README.md 的算法层号是**另外的族**。

两条 SyntaxWarning(ftp_cfg / net_cfg 文件头 \S 转义)**HEAD 同样存在**,已对 HEAD 副本
复现证明非本次引入,未顺手改。
2026-10-28 17:52:55 +08:00
wandering1 7e15f49ff3 refactor(编号): UI 菜单层级 L0~L3 → 主界面/一级/二级/三级菜单(批次 3a)
这族是菜单深度号:L0 根、L1 一级、L2 二级、L3 三级。用户 2026-10-28:「编号看不懂」,
且与采集链分层的 L1~L5 撞形(同一份文档里两套 L 号,读的人得先猜是哪套)。

改法(生成物走生成器,不手改):
- `tools/menu_gen/gen_menu.py`:加 MENU_LEVEL_NAMES 常量,生成 ui_menu_tab.c 的注释从
  `/* 12 L2 */` 变成 `/* 12 二级菜单 */`;重跑生成器,生成物 42 行注释随之更新。
- 文档 4 篇 113 行:菜单页面设计(94)、内容页架构(10)、01_主界面_说明(8)、
  中文字体生成与配置(1)。映射:L0 → 主界面(根)、L1/L2/L3 → 一级/二级/三级菜单;
  `4 级(L0~L3)` → `4 级(主界面 → 三级菜单)`;括号里的 `(L1)` → `(一级菜单)`。

范围纪律:只动 UI 菜单这一族。同名的测试层级族(L0/L0.5/L1/L2/L3,见测试开发标准流程)
与链路延迟分段 L1~L8 不动——那两个属批次 3 的另两族。

验证:@build 0 错 0 警;check_menu_rows 8 状态 PASS(5368 B 池不变);生成器重跑幂等
(rc=0,生成物只剩注释差异 42/42 行);doc-links 通过(195 文档);mojibake / bom-guard /
新编号门禁 / git diff --check 全绿。
2026-10-28 13:12:06 +08:00
wandering1 94b0d79026 feat(ui)+chore(docs): UI 补齐清单清空;数据可信销项与「卡」号清退
本笔含两组收尾工作(同一工作区内交错在 6 个共享文档/门禁配置里,
拆开会产生"文档声称的页数与树里黄金数不一致"的中间提交,故合为一笔):

一、UI 补齐(清单清空)
- 新建 6 个内容页模块:ui_page_accident / oper / alarm / start / yk
  复用 ui_w_records 共享组件(分页、空态"无记录"、按实例读盘缓存),
  记录层与五个写入点已由道F task-10 / 道G task-12 落地,本批只补页面;
  ui_page_runreport 为 13 行 2 页只读统计,四个数逐个挂范围标签
  (自电池周期 / 本次开机),boot_ready=0 显 "--" 不显 0。
- 数据面:ui_data_info_t 尾部追加 9 字段,ui_data_feed 用
  boot_stats_get() + iec104_serv_get_stats() 纯搬运;模拟器填确定值假数据
  (差异登记 docs/测试说明/模拟器差距清单.md §2.3 两行)。
- 接线:菜单树 6 叶绑 #page=,绑定叶 22→28、UI_PAGE_MAP_COUNT=28;
  字库 185→218 字 / 280→313 字形;双端注册 uvprojx +30、CMakeLists +6。
- 回归:内容页黄金 53→60(含 runreport_p2 真拍第 2 页),文本断言
  425 正向 / 36 反向;存量 53 张逐字节零漂移;记录 5 页各用独立空目录
  (模拟器把操作留痕写进 cwd 的 sim_oper.log,共用 tmp 会让空态拍不到)。

二、数据可信销项 + 「数据可信」任务卡号整族清退
- 删除已闭环的"已知电压注入验证"行与闭环结论块(证据在
  docs/硬件平台/ADC通道映射说明.md §8.1 与
  docs/测试说明/ADC前端注入验证流程.md §5~§7),§5 标识列改语义短语。
- 卡 0/0b/1~12 共 14 个号全仓 288 站点 / 56 文件清退成语义短语,
  代码注释、测试、门禁配置与文档一并改;换算表入 docs/编号规范.md §9.3。
  这一族是中文前缀,不进 check_ids 语料,刻意不入 id_aliases.json。

验证(实测)
- @build 0 错 0 警:Code=236456 RO=28124 RW=1876 ZI=137084;
  RAM +464 B(RW +304 / ZI +160),RW_IRAM1 62.2% / RW_CCM 87.9%,
  体积同码凭证与 RAM 台账已回填。
- check_menu_rows 8 状态 rc=0(池 5368/5448 常量未动);
  check_content_pages 60 页 rc=0;check_font_charset / registration /
  coding-standard / bom-drift / mojibake / doc-links --all / layer-deps /
  nested-comment 全 rc=0;check_ids --strict 无错误;主机单测
  ui_data 295 / boot_stats 21 / sys_log_mcu 141 / soe 32 / soe_store 27。

未做(不是遗漏)
- 六页真机判据(真记录翻页、掉电保持、清除后归零、boot_count 自增、
  拔 RTC 电池落 RESTARTED、IWDG 复位落 rst_cause、主站连一次 conn_up+1)
  已登记 docs/任务规划/登记簿.md §7;模拟器结构上照不到。
- docs/产品需求/产品需求设计文档.md 属另一条在飞改动,本笔不含;
  其中我已同步 6 处现状数字(28 叶 / 60 页 / 218 字),随该笔入库。
2026-10-28 07:23:16 +08:00
wandering1 728d60675f feat(UI/采集/104): 通讯设置页 + 开机账本 + adc_cal 可诊断化——三车道同树一次构建入库
三条道在同一棵树上交付,故合并成**一次权威构建**(@build 0 错 0 警,Code=235236 RO=26308 RW=1572
ZI=136924)后同笔入库;台账已按该构建回填(`ram_baseline.py --update` + `--write`、`chec
k_mem_numbers.py
 --write`),凭证与实际 .map 同源。归属如下:

· **通讯设置页**(道G task-9):新增 `Drivers/BSP/UI/ui_page_comm.{c,h}`(通道六注入,与死
区页同骨架同纪
  律),`#page=comm` 绑定使 bound **21 → 22**、内容页黄金 **47 → 53**(旧 47 页字节零变化,
新增六态含
  WAIT/OK 两张);`iec104_comm` 脏位 + store_save(**无事件位**)挂 defaultTask 同点位
轮询;`port_104` 由
  宏改走 getter、`IEC104_SERV_PORT` 删除;字体 +听/监(185 字全覆)。**实机链已闭环**:FTP 外改
[comm]
  的 ca/port → 复位 → 串口出现 `section 'comm' (2 keys)` 与 `i104: listening o
n 9004` → 还原 2404,全程无
  FAIL ⇒「重启即生效 + 端口真值」不是推断是实测。上限核对 6/8 段、2/16 键,超限现象由 TC-CONFIG-056/057
  钉死(注册 -1 + `comm init: FAIL` 锚 + getter 回 ROM),**不抬宏**。
· **开机账本**(道F task-12):新增 `App/util/boot_stats.{c,h}`——上电次数落 SD2506 用户
 SRAM(magic +
  小端计数 + 版本 + 异或校验),一次上电 = 1 读 + ≤1 写,**零 NAND、零卷锁、零配置段**;四态互斥
  UNKNOWN / FRESH / CONTINUED / RESTARTED(BLF=1 才算电池域掉过电),**读失败绝不回写**;
文案口径「自 RTC
  电池周期起」,**禁写终身次数**。复位原因优先级链从 `app_main.c` 的 if-else 收进一份真源(串口与屏幕同
  源);104 四个只增不清计数 + `iec104_serv_get_stats()` 留在 serv、未塞进 link(会被 open
/close 洗掉)。
  运行时长按用户裁定 = **只报本次开机以来**,零新增代码。新套件 `test/host/boot_stats` 21/21,五处注入
  **全部有牙**(含「漂一字节被校验和挡下而不是报 256 倍错账」)。
· **adc_cal 可诊断化**(道E task-43):三个 volatile 计数 + `adc_cal_get_stats()`
/ `adc_cal_note_persisted()`
  (水位**盖写非自增**,因为一次 save 覆盖整档,数 save 会留假未落盘);「差额 × 脏位」三态语义唯一落在
  `adc_cal.h`,其中「差额 > 0 且脏位恒 0」= 绕过语义 setter 的静默丢失;TC-ADCCAL-019~023。

三条**必须留档的自纠**(都是执行道自己报的,我不替他们润色):
① stdint 那条断链是**宿主套件抓到、通讯道先修**,采集道承认自己是重复添加者并删掉自己那份;
② 「exit 2 不是证据,断言变红才是」——采集道第一版反证注入了未声明标识符导致编译错,差点当成功;
③ 套件汇总行原写 `s_total/s_total`,失败时会打印「21/21 PASSED (failed=1)」这种自相矛盾的行,已改。

另修:`check_registration.py` 的 `SIM_EXEMPT` 加一行豁免 `App/util/boot_stats.c
`(依赖 I2C 与 RCC 位掩码,
MCU 专属;核过 `sys_clock.c`/`sha256.c` 同形态先例),注册反向校验 exit 1 → 0。发号 TC-IEC1
04-034、
TC-UIDATA-029 已实抬游标(`TC-IEC104-033→034`、`TC-UIDATA-026→029`)。

门禁:id-check --strict 与 plain 均 rc=0、doc-links --all rc=0、注册校验 0、cppche
ck / coding-standard /
bom-drift 0;iec104 179/179、ui_data 295/295、boot_stats 21/21、adc_cal 11
7/117、run_par 67/67;菜单 8 状态
池 5368 不变、内容页 53/53。**已知遗留**:内容页数 47 → 53 的全仓文本同步尚未做(另卡跟踪),本笔只保
证代码与黄金一致;面板注键真机那一腿缺 CLI,交台架道补。
2026-10-27 11:29:23 +08:00
wandering1 0054ae629a refactor(编号): F-* 网络设计条目号转语义说明——批次一收尾,代码注释 43 处 + 文档 9 处
批次一(不碰门禁即可清)的最后半批,原派道中途失败且**未留任何改动**(Lead 核实那批文件零 diff 后重派
setpage 执行,14 只文件 / 46 处),测试侧与一张生成器共 9 处由 Lead 收尾。

做法:**逐处先读属主文档 §7 速查表里那条的语义**,再改写成「一句话结论 + §指针」,同号跨文件同一措辞,
无一处机械删号(例:`F-NET-7` → 「唯一读者红线(docs/网络/网络自愈架构设计.md §7 I7)」)。属主登记处
**12 条定义行原样保留**、不删登记族(删了历史行会报未登记前缀),只在该章加停发句。

顺带清掉 **3 处裸形**(`T6` → 「台架实测」、`M4-lite`、`Q1` 语义化)——裸形正是 id-check 校验 2 抓的形态,
属净减。测试侧收尾时两条纪律:主机侧**断言字符串必须保持 ASCII**(`ns_s3_msg.c` 等三处的号在 CHECK 文本
里,改成 `sole reader of the global register` 而不是中文);板端注释可用中文。

生成器 gen_net_instability_svg.py 文案改动 → 同批重跑产物 `docs/images/网络不稳根因总览.svg`:只有那一
行的**框宽随文字自适应**(526 → 551,右缘 1411 未越界),画布 1560x1468 不变,无溢出告警。

⚠ 本笔**只含代码注释面**:该半批的文档面(BSD 函数调用关系 9 处、属主文档 §7 停发句、
生成器与产物那张网络不稳图)已被上一笔 `git add docs tools` 一并收走,两笔合起来才是完整的 F-* 批次。
自证:@build **0 错 0 警**且 Program Size 与改前**逐字节同数**(Code=233900 RO=26104 RW=1
536 ZI=136808,
注释无误伤的机器证据);net_socket 457/457、iec104_serv / net_pending 两套 exit 0;cppchec
k 钩子参数零
发现、coding-standard 0(lftpd_inet.c 那 4 条是存量且该目录本在钩子 exclude 内,用 HEAD 副本对照
甄别过)。
2026-10-27 09:05:27 +08:00
wandering1 48a8ef0564 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 上下文」已被装配层裁定覆盖)归算法层下一笔。
2026-10-27 05:07:18 +08:00
wandering1 b623a406ad fix(栈·存储·UI 收尾): 断掉临界区↔断言幻影环回到 1,464,标定门控单点化,卷锁五态可观测
这四笔在主树里已经互相纠缠(同一次 @build 编译、同一份 .htm 度量),拆成四笔就不
存在任何一个被真编过的中间态,所以合一笔入库;下面按模块分段陈述,作者与出证归属
逐段写明。

—— 一、崩溃证据路径的整行独占收尾(log-maint-writer,task-26)——
上午那笔 1d4b6a17 之后,stack_report 报 iec104Task 静态最坏链 1,544 / margin 1,016,
低于 1,024 的 ISR 底线。真因不是新增局部缓冲,而是本模块与内核临界区之间新长出
一条环:taskEXIT_CRITICAL ⇒ vPortExitCritical ⇒ configASSERT ⇒ vApplicationAssertFailed
⇒ dbg_console_write_raw ⇒ 回到本模块。armlink 把这一整坨计入**每一个含临界区的
任务链**(vApplicationAssertFailed 的 Max Depth 由约 40 B 涨到 128 B + In Cycle),
所以九条链同涨约 80 B——同涨本身就排除了任何单个调用方。
改法两处:① 本模块三处窗口(取凭证 / 还凭证 / 整行入环)改用 CON_CRIT_ENTER/EXIT
(PRIMASK 保存恢复到局部变量;注释写全为什么不能直接 __enable_irq()——嵌套时会
替别人打开任务级临界区——以及屏蔽面变宽是有意为之);② 异常/终点钩子里那对
自证空转的 begin/end 摘掉(异常态 begin 放行但不持有、end 是幂等空操作,调它只有
成本没有收益),任务态 STALL 行保留 begin/end,这个区分写进两处注释与文档 §4.4.1。
实测四棵树逐个 .htm 解析(身份钉 tree + Linker run 时刻 + 产物来源):A=77779d49
链 1,456;B=1d4b6a17 链 1,464 / margin 1,096 PASS;C=含未提交钩子接线的工作树
1,544 / 1,016 FAIL;D=本轮修复后(Linker run 22:52)回到 **1,464 / 1,096 PASS**,
九条链齐降 80 B。断环图证:vPortExitCritical 由 136 B + In Cycle 变 8 B 且不再
In Cycle,con_tx_release 子树 152→8;Error_Handler 回到 280 + In Cycle = A 树同值
(既有发送环无人拆动)。顺带删掉 Src/freertos.c 崩溃路径里最后一个 HAL_Delay(200)
(该文件真实调用现为 0)。
测试:console_log 152→**153** 条(只增不减);换钉不删防——015 现在钉「PRIMASK
窗口 2 次、深度 1、内核临界区 0 次」,新钉 015c 绝不等待,160 注入点搬到
con_pm_hook,176 强化到未调度路径连 PRIMASK 窗口都不开;红线水位
con_pm_at_tx_max 与 con_uart_crit_at_tx_max 双 0;判据⑦(延后的行与当场直发逐
字节相同)170b/171/178 仍全绿。作者自报并纠正一处:换钉时曾把 TC-CONLOG-015b 改
编号,而 015b 在生产注释与文档里都被引用——已恢复「编号不退役、语义搬过去」。
覆盖率 App/log/console_log.c 199/200 → 256/257 行、21/21 函数(只增)。

—— 二、未标定闭锁判据收成单点访问器(ui-param-writer,task-16)——
「UA/UN/IA 三相 VALID 全置位才允许跑判据」这条门控原先在两处各自镜像实现
(protect_task.c 的 s_cal_locked 与 ui_data_feed.c 的 feed_cal_block),改一处另一处
静默失效且无门禁可照。现新增 adc_cal_prot_blocked() 落在 App/algo/adc_cal,DTU_BENCH
特例收进一处;两个消费者同批改调、本地副本删除。作者还查实并纠正了我先前一句
错判:全仓 App/app→App/task 的 include 实为 0 条,故访问器不能落在 protect_task。
新增 TC-ADCCAL-018 八条(含冷启动即闭锁),adc_cal 套件 99/99。

—— 三、共享卷锁五态可观测性(ui-param-writer,task-17)——
原状比登记簿写的更糟:五个可区分态(未调用 / try 超时 / 分片超时 / fail-open /
未持锁却 release)生产侧零留痕,且 vol_lock.c 的返回值注释与代码相反——
fail-open(锁没建成、互斥已失效却在读写盘)被返 1 **谎报成取到锁**,而那正是
vol_lock.h 记录的台架实证故障(并发写坏 FTL → 喂狗点饥饿 → IWDG 误复位)的前置。
锁层加四个 volatile 计数(16 B 静态,零新局部、零新 callee、零日志、零 FatFs)与
不取锁的 vol_lock_get_stats();13 处 try 调用点一字未动。**刻意不在锁层记时刻**:
记时刻要读 tick,等于给 defaultTask 取锁链添帧——诊断不值那几个字节;消费方若
自盖时间戳,那只能是被观测到的时刻而非发生时刻,.h 里已写明不得写成首次/末次。
四套本地假件各加 s_last_wait_to(字段名逐字一致),把「喂狗点取锁一律 timeout=0」
与「ftpTask 分片一律 100 ms」从注释纪律升级成会 FAIL 的断言;红-绿反证:临时把
sys_log_mcu.c 的 0u 改 50u → TC-SYSLOG-010/012 双红,把 lftpd_io.c 的 100u 改 0u →
TC-LFTPIO-034 红,两处按原串改回并用 SHA-256 证明逐字节还原。TC-VOLLOCK-015~019
共 45/45(无新桩)。

—— 四、UI 死接口清理(ui-maint-writer,task-6 收尾)——
删掉 ui_data_maint_ready():全仓零消费者、只有它自己的用例在读。**更值得记的是
它为什么能活过一整轮评审**:头注释写的是「页面用它决定这页能不能操作」,而页面
从来没用过它——注释陈述的是意图,评审读的是意图,门禁读的是可达路径,两者都不
反驳「意图未实现」。作者对「连用例一起删」做了显式抗命:TC-UIDATA-025 的 13 条里
只有 4 条读那个死口,另 9 条钉的是真防线(六口未注入安全返回 / 残表整表拒绝 /
被拒不覆盖已生效的好表),故删冗余的 025a、把 025i/025k/025m 换成用 reset_req()
的可观察返回值判定,性质不丢。ui_data 套件 244 → 243(净减 1),黄金 40 页与
池 5448 B 未变(该刀零像素影响,参考图无 diff)。

—— 门禁与未跑项(本笔如实留账)——
cubemx-survival 基线已 --write(漂移 6 项全部是上述意图内区域:断言/栈溢出钩子
实现、看门狗模块行等),基线文件同笔入库,随后 --check 通过。
本笔未跑的钩子只有两条:mem-numbers 与 mem-budget——磁盘 .map 出自 22:52 那次
构建、含尚未入库的 UI 与工具改动,与 HEAD 台账必然不同码;按仓库既有约定允许
--no-verify + 正文留证,回填在紧随的台账一笔统一做(含 ram_baseline --update 与
覆盖率基线两条 [NOTE]:ui_data.c 65→316、iec104_comm.c 36/36)。
两条已挂着的账:① id-check 不可判定引用 1,162 / 上限 1,166,**余量只剩 4**,
主要吃进者是我今天推进的 UI 那笔(68 条文本断言的注释引用),账在 Lead;
② 崩溃注入腿(故意复位)台架复验未跑,需先由代码道提交触发器,且判据只判原子性
不判 [ERR]=0——IPSR 路径的主张归主机侧套件,硬件照不出来。

产物身份(按 D 表要求钉死,避免数字与树脱钩):本笔包含的固件树就是那张 D 表对应的
那一棵——MDK-ARM/.../STM32F407-Demo.htm 的 mtime = 2026-10-26 22:52:12(.htm/.map 按
约定不入库,故把时刻写进正文)。提交前在本树复跑 stack_report.py:exit 0,
iec104Task 1,464 / margin 1,096(42%)PASS、defaultTask 3,040 / 2,080(40%)PASS,
与 D 表逐项一致;本次运行不含任何未提交固件源码之外的新改动。
2026-10-26 23:31:05 +08:00
wandering1 52e28885cb feat(UI): 装置维护 3 页落地(时钟 / 强制复归 / 清除记录),内容页黄金 27→40
三个薄页面模块 ui_page_clock / ui_page_reset / ui_page_clear,页面注册由生成器
自动写双端(裁定 A-1:「三叶各绑一页」与「零新增 .c」不可兼得,选薄页面加生成器,
不写手写 node switch——那种分发在增删叶子时会静默换功能,且没有任何门禁照得到)。

数据侧是注入式函数表:UI 不 include App 层的头。两条硬理由都实测过——分层方向
(layer-deps)之外,模拟器根本不链 sys_clock,ALLOW 豁免挡得住 layer-deps,
挡不住模拟器链接期。面板对时的唯一真判据是回读 RTC:sys_clock_set() 恒返 0
(RTC 写失败只打日志),所以真机侧写 1 后回读 1,最坏约 200 ms 落在一次「保存」
按键上,IWDG 800 ms 窗有余;上界与依据都写进注释。

「无时钟」徽标位置有两个像素级实测坑:放标题带右侧会被 4 字标题连成「时钟设置
无时钟」一句话;放页脚带左侧 x=7 会压住页脚——页脚从 x=6 起排,不是居中。
最终落在清单末行与页脚之间的空带 y=124。clear_done 那张顺带给出像素证据:
清 run.log 之后「操作记录」那一行从 2 变 3,页面上没缓存、没撒谎。

ui_w_records 的 UI_W_REC_LOG_* 顺序必须等于 syslog_id_t 顺序(7 类,只准尾追加),
已加编译期顺序守卫;执行侧 7 条映射表同样带编译期守卫。

测试与真值:内容页黄金 27 → 40 页(+13),文本断言 +68、反向断言 ABSENT +19;
27 张旧参考 git diff 为空 = 逐字节未变。往返池 5448 B 与菜单常驻堆 5368 B 均未变;
字库未扩(源码在用 171 并 KEEP_EXTRA 39 = 清单 183),font-charset 与 cn-chars、
cn-font 字节一致。@build 0 错误 0 警告;uiTask 静态最坏链 1,336 B / 16,384
(余 91%)与基线相同,页面钩子没进最坏链。

一条测试学结论单独留痕:反向断言不能当「位置不重叠」的证据——本脚本的字形匹配
把 y 带判得比字面宽(徽标在 125..136、页脚在 144..155,仍被 (140,160) 判命中)。
该防线改由正向断言「页脚未被徽标压住」承担(压字那一版实测过不了它)。

两条自我更正一并记档(作者自报,按真值而非原口径):
① 先前报「文案零新增汉字」是错的——观测行拟用「动作元件」,「元」不在子集,
cn-chars 与 font-charset 如实红;最终改「保护动作 N」才让「零新增」成为事实。
顺序是:先按机器红,再按真值写结论。
② 先前报「6 个文件 BOM 逐个核过」被 bom-guard 证伪:丢 BOM 发生在上一次编译之后,
正是 AGENTS 那句「历史 0 警告不代表安全」的活例;此后一律以退出码为准。

未覆盖项不假装:台架「N>0 → N=0」在产品构建的未标定闭锁期拿不到(闭锁期 N 恒 0),
判据显式退化为「页面受理 + 落一条操作记录」,写在 rs_watch 注释里。

生成物入库例外照用:uvprojx 只加 3 个 ui_page_ 的 FilePath(真增删源文件),
CMakeLists 同批;两处都由 gen_menu.py 自己写,没手写。

本笔先走了一次真钩子:20 条钩子实跑通过(ui-golden 菜单 8 状态加内容页 40 页、
generated-fresh、registration-check、font-charset、ui-data 单测、cppcheck、
coding-standard、bom、mojibake、id-check、doc-links 等),只有 mem-numbers 与
mem-budget 两条红——磁盘上的 .map 出自 20:39 那次构建、含三条在飞道的代码,
与 HEAD 台账必然不同码。按仓库既有约定(这两条等台账回填时允许 --no-verify 并
正文留证)落这一笔;回填由我统一在收尾一笔做。

三条在飞道(task-26 崩溃证据路径、task-16 与 task-17 谓词收敛和卷锁观测、
task-36 台架工具)的未暂存改动被 autostash 暂时收走,钩子看到的是 HEAD 加本笔;
过程中曾把 comm 道未跟踪的 iec104_comm.c 临时移出(它不在 stash 覆盖面内),
那次移出反而让 ui-golden 撞见 CMake 缓存里的悬空引用而红——已改用「先落 comm
那笔、再落本笔」的顺序消掉这个中间态。ui_data_maint_ready() 待删(只有测试在读
的自检口),排在本笔之后单独一笔。
2026-10-26 22:43:54 +08:00
wandering1 77779d49d6 feat(ui+记录): 整定写路径与四类页落地,五类记录写入方补齐
UI 面:ui_data 增加深度 4 参数写队列(请求 168 B、队列 .bss 672 B)与状态机
OK/ERR_RANGE/ERR_LOCKED/ERR_BUSY,ui_data_feed 出队后按注入 ops 走「闭锁 → 整批
预检 → 逐项 prot_set_set_sealed」,只提交改动位图;par 帧尾追加 cal_block
(口径 = UA/UN/IA 三路 ADC_CAL_F_VALID,与 protect_task 判据同构,不用 rt.eng
反推,V0.1 下 eng 恒 0)、UI_DATA_VER 升到 2。参数/定值/控制字/软压板四叶绑定
内容页(UI_PAGE_MAP_COUNT 12→16,复用四个查看模块,零新增 .c),编辑态按
is_editable 分派,LOCK(写不进去)与「未标定」(仍可改值)两源两通道两词。

记录面:syslog_request_clear/state/apply 三段式删除 API(UI 只发请求,落地在
defaultTask 的 drain 尾部且必须后置,卷锁一律 try,幂等只认 FR_NO_FILE);
事故/告警/启动/操作/遥控五类各有一个真实写入点,记录类型 = 日志 ID 本身
(一类一个 /log 严格 8.3 文件;64B 定长体没有类型字段位,塞字段会砸掉既有读者)。
为守 protectTask 872 B 静态最坏链另拆 syslog_post_quiet()(无 DBG 的投递核,
丢弃留痕挪到 drain 侧差值补发),syslog_post 对外语义逐字不变。

为什么两道合成一笔而不是各一笔(实测,不是省事):simulator/main.c 同时消费
两道新增 API(整定 pump 与 logtest 清除用例),按道切文件会让中间提交编不过;
RAM 受管快照与体积同码凭证描述的是合并后的 .map,拆开必落在「台账与 .map 不同
码」这条红线上。pre-commit 跑钩子前会 stash 未 staged 部分,这类耦合没法绕。

实测:@build 0 错误 0 警告;Code 228,260 / RO 24,960 / RW 1,440 / ZI 135,976 B;
RW_IRAM1 79,992 B = 61.0%、RW_CCM 87.9%(阈值 92%);九个任务静态最坏链全 PASS,
protectTask 872 B / 57% 改前后逐字节不变(记录投递没垫栈)。主机单测 ui_data 82→
164、sys_log_mcu 106→137、prot_elm 47→60、iec104 141→154、iec104_serv 28→34,
既有断言零删改;48 个钩子 --all-files 全绿;编号门禁可判定 86.34%(基线 86.0%)、
不可判定 1,153 处低于上限 1,166;doc-links 无法判定 0;UI 黄金 18 页扩到 27 页
连跑两次逐字节一致;字库 diff 空。

顺手修掉三处既有缺陷(都是本轮第一个消费者才暴露):sim_syslog.c 的 s_paths 行宽
16 而 "sim_selftest.log" 正好 16 字符没有 NUL 位(stat/remove 读越过数组尾,此前
靠后面 .rodata 碰巧为 0);ui_w_setlist 二态行 ENTER 不回绕(控制字与软压板出厂值
全在最后一项,按了像没反应而叠层里已是越界值);生成物 ui_menu_tab.h 与 gen_menu.py
模板漂移(上一笔改了模板没重跑生成器),现随生成器输出对齐。

未收口(不算完成):保存后重启值保持与主站 C_RD 读回两条只能上板,台架窗口执行;
LOCK 屏显与越界反显的页面侧在模拟器造不出来(模拟器不跑定值自检、无 NAND),
已由生成侧单测与代码注释点名差距;同类页数与钩子数陈述残留另立待办。
2026-10-26 15:51:12 +08:00
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号)
一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、
采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。

门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏
undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8
reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF;
id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。

test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字,
是本批唯一改变构建产物的文件集合。

为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged
部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、
不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。

实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01%
(基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 /
告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。

基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处
引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使
识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。

⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二
根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有),
今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。

固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0;
App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889
断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿;
check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带
BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。

.cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法:
Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。

同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%,
RW_CCM 57,376 B / 87.9%,阈值 92%。
2026-10-26 12:28:14 +08:00
wandering1 f6e9106f0d fix(UI): 推流往返返回点喂位——多个慢 RPC 之和不再串成 UI 停摆(大窗判读 F3)
道F task-24。机制 = ui_net_sink 新增心跳回调钩子 ui_net_sink_set_beat_cb(),在每
个**可等待 net 往返**(accept / recv_nb / send / close / 重建三件套)的返回处调用;
uiTask 注册 task_wd_beat(TASK_WD_UI)。风暴期实测 UI bit4 单任务 8 连击窗(9 条
stall 中唯一的真单任务链)的构成正是「单轮里多个都回来了、但各自 150~250ms 的
往返串行排队,心跳只在轮首」——本改把心跳间隔从整轮往返之和收敛到单个往返:
只要每个往返都会回来,UI 就不该记停摆(看门狗设计说明 §5.3 按位判据抓真挂死)。

【边界(lead 裁定口径)】往返语义零改动:不加超时、不动回执协议(§11.6 否决的
「net_* 裸加有界等待」不重演,请求序号方案与事件驱动 accept 均**不做**,上界
收口归登记簿 E-27/E-28 批 3)。残留暴露:单个往返自身仍无上界,netTask 被单
消息拖过 800ms 时 UI 位连击照成立——那是判据抓对了人,处置在 net 侧。

【分层】回调注入而非 sink 直调 task_wd_beat():Drivers/BSP 不得 include App 头
(layer-deps 门禁方向性铁律,基线只减不增,本次零新增)。

【零行为变化面】钩子未注册(模拟器/黄金回归形态、ui_task 注册之前)= 与改前
逐字一致;协议/帧格式/门控/兜底节拍全不动。ui-golden(菜单 8 状态 + 内容页
11 页)通过。

【测试】新增主机套件 test/host/ui_sink/(stub 假件驱动生产 ui_net_sink.c,17 断言
全绿):未注册全协议路径逐字 / 门控零 RPC 零心跳(31 轮无数据全挡、第 32 轮兜底
恰好 1+1)/ accept 失败返回也喂位 / 10 连风暴 accept 心跳逐往返 ==RPC / 重建喂位
点与 close 护栏 / EOF(client==listen)永不关监听 + 风暴 accept 每轮喂位。
suite 由 test-coverage rglob 自动发现(无需改台账)。

【文档】UI_设计架构.md 新增 §5.8(推流心跳边界:现象/处置/边界/分层/残留)+
变更记录行;ui_net_sink.h 钩子契约注释;ui_task.c v1.4 记录与 :271 注释更新。

【门禁】@build 0/0(Code=224860,vs F2 后:Code +92B / ER_IROM1 →249500 /
RW+4 / ZI+4 / RAM 79008)。--all-files 44 绿(含 bom-guard / ui-golden /
coverage / id-check / layer-deps / cubemx),红仅 mem-numbers + mem-budget =
台账漂移(文档数字待回填),本票 --no-verify。
gate: only mem-budget/mem-numbers pending lead ledger backfill

【台架(待台架实测,等你排)】两轮各自独立 ≥30min:① 单独 relisten_stress :7003
② 探针轮。v1.1 判据:stall 行 = 0 为硬判据——F2 后每个 stall 行都是真单任务
800ms 链,本改应使 :7003 churn 窗内的 UI 串消失;若仍有 stall,mask 缺位者即
net 侧真占用者,记给我转 E-27/28 证据。MISS start 为诊断行不计违规。
2026-10-25 18:42:14 +08:00
wandering1 498cc60b9d fix(UI): ui_page_yx.c 补 string.h——memset 隐式声明 #223-D 两条警告
pages-ui 合入(merge dea7591)后 done 门禁在主树全量重编暴露;
道E 树内增量构建吃缓存未触发重编,故其提交时"0 警"未假报但漏检此条
("只有重编该文件才暴露"的已知形态,教训进下方回填说明)。
体积不变(Code=224688 RO=24640 RW=1328 ZI=135088);BOM 已核 EF BB BF。
随笔同步内存台账受管区(pages 12 笔合入的 --no-verify 欠账)。
2026-10-25 15:45:29 +08:00
wandering1 3dc7f4a088 docs(UI方案): 返回系数小数位按可分辨精度修正 + 遥信量落地注
两件事都落在这份方案文档里,合一笔提交(同一文件的两处改动按文件切分提交不干净,故合并说明)。

一、四个返回系数:口径从"我偏离文档"改成"文档原口径错了"(Lead 裁定 2026-10-25)
- §3.2.2 表里 4 行小数位 3 → 2 并加 ⚠;落地注改写;同步去掉 ui_w_setview.c 注释与
  tools/setview_check/check_setview_limits.py 文档串里的"偏离"措辞。
- 原口径 3 位来自"把 Q10 当成十进制存储"这个误会:Q10 存不下 0.95(0.95×1024 截成 972,真存
  0.94921875),按 3 位显示等于多打一位根本不存在的数字;整定单写 0.95 的人看到屏上 0.949 会
  怀疑装置、甚至反复重输(重输还是 0.949)。2 位才是这份存储在自己可分辨精度上的正确读数。
- 不改行为,只改文档与注释:屏上显示与行表换算逻辑一笔未动。

二、§3.1.3 落地注(遥信量页落地后回填,含两条命名取舍与一条通用陷阱)
- 命名:idx0~3 屏上写「位置1~4」不写「YK1~4 位置」;idx30 写「保护闭锁」不写「闭锁1」;点表未
  定义名的行用点号占位,不编造功能名。
- 值映射次序(先无效 → 再单双点 → 后位值)与双点支路"当前无自动化证据"的诚实登记。
- 通用陷阱:把 *_PUT(dst, 字面量) 宏用在 const char *const 表项上,sizeof(源) 变成指针宽度,
  CJK 名被截而短 ASCII 名恰好完好——只有像素文本断言会报"名字匹配不到而值在原位"。

跳过的门禁(三条同族):mem-numbers / mem-budget / size-gate(L0c),根因是台账与体积凭证待回填。
2026-10-25 15:33:44 +08:00
wandering1 4332142680 feat(ui-pages): 遥信量 33 行固定名表页接实时快照(新增 yx_dp)
批 4 主体(票 task-15;规格 docs/UI设计/页面扩充详细方案.md §3.1.3 方案 A)。V0.1 只有 6 点有
硬件来源,其余 27 点在屏上就是 "--"——这是真实状态,不是页面没做完。

页面与数据
- 33 行固定名表(Flash const)+ 动态分页(8 行/页 = 5 页)。两处命名不照抄文档简写:idx0~3
  屏上写「位置1~4」而非「YK1~4 位置」(遥信页出现 YK 前缀会被读成遥控对象,对应关系留在注释);
  idx30 写「保护闭锁」(写侧位号注释的语义即此,用编号反而丢信息)。idx4~13、idx31~32 点表未
  定义功能名,如实写点号占位(YX5…、YX32),不编造功能名。
- 值映射次序:先判无效 → 再判单/双点 → 最后看位值。无效位上的位值不代表任何事实,次序颠倒就会
  把垃圾位画成「复归」。
- 快照新增 yx_dp 双点位图(+8 B)。不教页面"前 4 点是双点":单双点是 104 点表事实(YX_DP_N /
  iec104_pts_yx_is_dp),抄进表现层就是第二真相——点表把双点段挪位或改起始下标,屏上不报错,
  只会用错词形容同一个点。写侧逐点问 is_dp() 拼位图。
- UI_DATA_YX_N(33) 与 iec104_pts_yx_count() 编译期比不了(那个 #define 在协议层 .c 内),故写侧
  留一条一次性 DBG_ERROR(约 90 B Flash,只在真漂移时打一次)。名表行数 = UI_DATA_YX_N 由数组
  宽度法编译期守卫钉住(ARMCC C99 无 _Static_assert,本仓惯用写法)。
- 补 include 一条实测坑:DBG_ERROR 是宏,漏 include "dbg_log.h" 时 ARMCC 把它当隐式声明函数放过
  编译期,错落在链接期(L6218E Undefined symbol DBG_ERROR)——报错点离根因隔了一整个链接步骤,
  已在 include 处写明「DBG_TAG 必须在 include 之前」。

一个只有像素文本断言能抓到的截断 bug
- 照抄批 1 的 *_PUT(dst, 源) 宏(长度取 sizeof(源)),但本表源是 const char *const 数组的
  **元素(指针)**,sizeof 得 4 → 「故障总」被截成「故」,而 "YX9" 恰好 4 字节看起来完全正常。
  编译器 / cppcheck / 编码钩子全不响。定位来自断言:第 2 页「过流Ⅰ段」匹配不到,同一行值列
  「复归」却在原位 = 名字被截、值没错位。修法:长度取目标缓冲容量 UI_BUF_STRLEN。同名宏、不同
  参数类别(数组 vs 指针),批 1 那处本身是对的,照抄才中招——教训写在宏定义上方。

模拟器与覆盖诚实性
- 固定帧发布 yx_dp = 0x0F,但刻意不把这几位置进 yx_valid:V0.1 无开入硬件,屏上就是 "--"。不用
  「让它显示合位/分位」的假数据填满画面,那等于让黄金参考声称本机有开关位置遥信。
- 代价如实登记:合位/分位这条词映射当前**没有任何自动化证据**(像素恒 IV;页面依赖 LVGL 进不了
  主机单测)。已写进方案 §3.1.3 落地注与契约落地进度·四,作为开入落地后的台架判据,不假装测过。

测试
- 主机单测 +2 条(TC-UIDATA-009c 双点位图原样透传、不被 valid 掩;009d 点数 33 的独立见证,注明
  绊红时要同步改哪四处)→ 85/85。
- 文本断言 +33 条(yx / yxp2 两态,含页码钉住 33/8 = 5 页的分页数学);两页像素基线单独一笔。
- font-charset 本批零新增字:名表用字全部命中既有 183 字子集——「置/故/障/总/外/部」正属批 1
  成组并入 KEEP_EXTRA 的那批字,第二次兑现(当时若按 extract 输出重写字符集,这些字早被洗掉,
  上屏是空白,而门禁只会说「字不在清单里」)。

体积(同一棵树、构建前后的实测增量)
- Code 224272→224688(+416);RO 24040→24640(+600);RW 1324→1328(+4);
  ZI 134844→135088(+244)→ Flash 约 +1020 B、RAM +248 B。
- RO 增量主要是 33 行名表与名称串,是"把 33 个真点名上屏"的代价,不是可省开销。

跳过的门禁(三条同族,同一根因):mem-numbers / mem-budget / size-gate(L0c)——台账与体积凭证
回填是 Lead 单写者,本分支不做静默处理,数值见上。
2026-10-25 15:33:04 +08:00
wandering1 45393be3d5 feat(ui-pages): 参数定值控制字软压板四个查看页接 par 快照
四个空叶子(参数定值 → 查看 → 参数/定值/控制字/软压板)接上 par 通道。页面薄、模型厚:
新建 Drivers/BSP/UI/ui_w_setview.{c,h} 收四张视图表 + Q10 换算 + 越界反显,四个页面各是
一份实例 + 四个跳板钩子;渲染与分页复用批 1 的 ui_w_setlist(查看态 commit=NULL:无保存行、
无编辑通路,§3.2.6 查看列口径)。

行表三项口径全部有真源,一项都不在这份文件里拟定
- id = App/algo/prot_set.h 的枚举序。BSP 不 include App 头(分层门禁),所以行表里是裸数字
  + 行尾注枚举名——这份对不上就没人能在 BSP 侧编译期发现,于是从外面钉:见下面的新脚本
- min/max = App/algo/prot_set.c 的 g_prot_set_meta Q10 上下限按该行小数位换算的结果。整定页
  下一批复用同一批表,界内界外一处一份,不会出现「查看看着合法、整定说越界」两套边界
- 显示名/小数位/单位 = docs/UI设计/页面扩充详细方案.md §3.2.2~§3.2.5 四张表

一处偏离文档(不是静默偏离,写进三处:代码注释 / 文档落地注 / 本正文)
- 四个返回系数按 2 位小数显示,文档写 3 位。理由是量值真实性不是排版:Q10 存不下 0.95
  (0.95×1024 截成 972 → 真存 0.94921875),按 3 位显示就多打一位根本不存在的数字;整定单
  上写 0.95 的人看到屏上 0.949 会怀疑装置、甚至反复重输(重输还是 0.949)。2 位才是这份存储
  在自身可分辨精度上的正确读数——文档的 3 位来自「把 Q10 当成十进制存储」这个误会
- 单位挂在值串尾(10.00A / 500ms)而不是塞进显示名:值列只有数字会让 60000 既是 ms 又是 A。
  为此给 ui_w_setlist_row_t 加了 unit 字段(该结构此前无任何消费者,扩展零成本);只挂在
  非编辑态——编辑中是逐位输入串,挂单位会让位光标算错列宽。A/V/ms/Hz 全 ASCII 不占字库

新增可重跑核验:tools/setview_check/check_setview_limits.py
- 从外面把三件事钉住:四张表的 id 恰好覆盖 0..39 各一次;数值行 min/max 等于 meta 上下限的
  换算;kind 与 meta.is_ctl 一致、dec 查表与行表小数位一致、单位只允许 ASCII。改 meta 不改
  行表就红。当前输出:行表 40 行 / dec 40 项 / meta 40 项 -> OK
- 写这个脚本时自己踩的两个坑也留在脚本注释里:① 正则用 \w+ 吃不下带引号的单位,会让整行
  **静默跳过**(少检一行比检查错更难发现)→ 改 \S+ 并加「行数必须等于 40」硬闸;② 脚本要
  打印中文,Windows 控制台按 CP936 写 stdout 会崩在半路(崩在半路 = 一条都没检查)→ 开头
  把 stdout/stderr 锁到 UTF-8

两个只有画面才会暴露的错(都不是静态检查抓到的,都记进了注释)
- ui_data_par() 的通道是 0=成功 / -1=尚无首帧,我按 bool 反义用了:真机与模拟器上都会「永远
  读不到」,整页值列恒 --,而页面看起来完全正常、编译与钩子全绿。看像素图才发现满屏横杠
- 肉眼把 CT 一次额定读成 1000A,字形匹配器证明值列只有 4 个字符位(命中 x=101/107/113/119):
  实际是 100A。断言因此按匹配器能证明的写。顺带一个自洽佐证:PT 10000/100 V 配 CT 100/5 A
  正是 10 kV 馈线终端的变比;控制字页默认值天然两两一组(Ⅰ/Ⅱ 段跳闸、Ⅲ 与过负荷段告警),
  行表若整体错位就不可能复现这个组合——这条已进像素断言,成为 id 对齐的机械证据

无数据时的显示口径
- 参数帧未就位:名字照打、值列打 --(不把 0 当定值显示,也不给整页空白让人以为菜单坏了)
- 越界反显只在 bad_valid=1 时参与:把「没数据」画成「这行有毛病」是假告警,比不画更糟
- Q10 换算不用浮点也不用 64 位除:整数段 + 小数段拆分四舍五入,满位进位(MicroLIB 软浮点与
  __aeabi_ldiv 一进镜像几百字节,模拟器(宿主 CRT)永远照不出这笔代价)

批 1 那个 KEEP_EXTRA 决策在这里第一次兑现
- 本批行表用到的「延时 / 电流 / 压力 / 总 / 配 / 重 / 间 / 未」等字,正是当时成组并入 KEEP_EXTRA
  的 35 个「未知冗余」字里的一批;若当时按 extract 输出重写字符集,这些字形已被洗掉,本批
  上屏就是白框,而 font-charset 只会报「字在清单里没有」——它报不出「字曾被删掉过」
- 本批 font-charset 直接 Passed,未扩字库、未重生成字体

验证
- @build 0 错误 0 警告;模拟器 0 警告;内容页 19 项 PASS(16 页像素 + 四页共 39 条文本断言),
  其余 12 页逐字节不变;菜单 8 状态 PASS;ui-data 83/83、soe 32/32
- 四页出图逐张肉眼确认:值列右缘不越屏(10000V 最宽)、名值不重叠、第 1 页行槽与页脚不撞;
  分页页码正确(定值 18 行 = 3 页、软压板 10 行 = 2 页)
- check_setview_limits.py OK;ui_menu_tab.h 等生成物全部由 gen_menu.py 重生成(手改生成物 = 一定
  会被覆盖,见批 2 那条),uvprojx/CMakeLists 注册由生成器写入,另手工登记 ui_w_setview.c 两处
- 全量钩子 --all-files:见下一行 gate;红项只有同族三条(台账与同码凭证回填归单写者)

体积(相对 b746a1f 台账 217204 / 21504 / 1256 / 132680)
- 实测 Code 221856 / RO 23508 / RW 1300 / ZI 134660
  → Code +4652、RO +2004、RW +44、ZI +1980
- Flash 合计 +6700 B、RAM +2024 B(三批累计)。本批 RO +2120 主要是 40 行行表与名称/单位串,
  ZI +388 是 par 帧缓存(184 B)+ 四个组件实例 + 注册表增量;这是把 40 项真定值上屏的代价,
  不是可省开销

待台架实测:装载真定值后的四页读数(含真机 bad_mask 非 0 时的反显行)、翻页节拍、
PT/CT 变比与 104 上位的定值读回(C_RD)一致性。

gate: only mem-budget/mem-numbers/size-gate pending lead ledger backfill
(同族三条,root cause 一条:台账与同码凭证回填归单写者)
像素基线单独提交(下一笔新增 4 个 shot_v*.bin):本笔单独 checkout 时 ui-golden 会报缺参考,
属预期拆分;上面的钩子结论取自「本笔 + 下一笔」的合并树。
自查:12 个新增/改动 UI 源文件 BOM 与 HEAD 对齐(本批 edit 又剥掉 4 次,逐次补回);CR = 0;
U+FFFD = 0;C 字面量纯 ASCII(中文只在带 BOM 文件里)。
2026-10-25 14:44:47 +08:00
wandering1 67c469fa40 feat(ui-pages): SOE 记录页接 soe_store 只读遍历(类型 7/8 首次上屏)
ui_w_soe 组件(task-14 落)此前两端都没绑 reader,只能显示"无记录"空态。本批补齐三件事:
App 侧只读遍历、两端 reader 绑定、SOE 记录页与菜单注册。

App/log/soe_store 只读 API(Lead 已放行,本批独占这两个文件)
- soe_store_count() / soe_store_read(idx, soe_rec_t*):与 syslog_count /
  syslog_get_record 同构——8 条一窗(512 B)整块读、命中窗口零 I/O、卷锁 try(0 超时)
  非阻塞;定长记录逐条 open 等于同一扇区开 4 次,SPI NAND 上这个差距是肉眼可见的卡顿
- 读者不得看到半成品记录,三道保障缺一不可(规则写进 soe_store.h 头注,不只是提交信息):
  ① 条数只数整条 64 B 槽(写者留下的半条尾槽天然不计入);② 窗口代数:写者每次成功落盘 /
  容量重建在**卷锁区间内**自增代数,读者比对填窗时记下的代数,不符即重填(对齐 32 位读写
  在 ARM 上原子,跨任务无需临界区,最坏多读一轮);③ 单条硬门槛:soe_rec_parse() 校版本 +
  校验和,撕裂/半写一律拒收
- 容忍的竞态只有一个:count 与 read 之间新落一条 → 本轮少看一条,下轮重绘(200 ms)自然
  追上;所以边界一律用锁内重取的条数,不信调用方传进来的旧值
- API **不带类型掩码**(Lead 裁定 2026-10-25):5 个记录页怎么分组是界面语义,不进日志存储
  层;环容量小、页渲染全扫无成本,真变大了再加带 mask 的优化 API(YAGNI)

⚠ 顺带修掉一颗定时炸弹:菜单生成器与生成物漂移
- 提交里的 Drivers/BSP/UI/ui_menu_tab.h 带着 5 个表查询 API 的规范注释块,而
  tools/menu_gen/gen_menu.py 只会输出"声明 + 行尾短注释"——说明历史上有人**手改生成物**
  补注释、没回灌生成器。我这次加 #page=soe 后重新生成,等于把那些注释抹掉,bsp-err 立刻
  报 5 条 return-doc
- 修法不是把生成物改回去(下次重新生成还会再抹一次),而是把注释块**回灌进生成器**;
  回灌后重跑 gen_menu.py 产出的 ui_menu_tab.h 与 HEAD **逐字节一致**(git diff 为空),
  漂移关闭。生成物里的手工优化 = 一定会被覆盖的优化,要么进生成器要么别改生成物

时标口径(跨模块陷阱,注释即防线)
- 页面时标取记录内的 CP56(事件自己的时标,不是"现在")。它与运行/自检记录页同口径,靠两条
  事实叠出来:sys_clock 的 epoch 域本身就是本地时间(App/util/sys_clock.h 的
  sys_clock_set_unix 注明"Unix epoch time (秒,本地时间)"),而 soe_rec.c 的 epoch→CP56
  分解不加任何时区偏移。**谁"顺手补 8 小时",SOE 页就会和文本记录页差 8 小时**;要引显式
  时区必须两侧一起改

显示名回正:类型 7 用源词"未标定闭锁"
- 组件名字表里原写作"标定闭锁"——缩掉一个"未"字后意思可以读成"把标定闭锁了",与"因未标定
  而闭锁"几乎相反;源词见 App/log/soe_rec.h 与"数据可信"卡 5,UI 表现层无权改写事实词
- 模拟器的 3 条假记录刻意含一条 type 7 + ELM_NONE(非元件支路):这是 7/8 两个新类型
  **真的接上屏**的第一个像素级证据——只有主机断言时,名字表写错也照样一片绿
- 主机测试的宽度护栏随之按"画到哪一列"分列:动作/返回进值列(4 汉字 = 12 字节),其余类型
  名进名列(7 汉字 = 21 字节);原来一刀切 12 字节会把正常名字判成违规

页面与注册
- 新建 Drivers/BSP/UI/ui_page_soe.{c,h}:薄跳板,一条记录两行共用组件内单条读盘缓存,
  实例独立(两页共用一份缓存会互相命中对方记录号,task-6 的教训)
- 菜单树 SOE记录 加 #page=soe;重生成 4 个生成物,uvprojx / CMakeLists 两处注册由生成器
  自动写入(本批 uvprojx 只含这一条新增,无 UV4 抖动)
- reader 绑定在两端适配层:真机 App/task/ui_task.c(CP56 → UI 视图 + 容量钳位),
  模拟器 simulator/main.c(固定 3 条、时标固定串,保证黄金参考可复现)

两处被门禁当场抓住的记录(都是有效拦截,不是噪音)
- id-check 拦下注释里裸引"P2-10":编号引用必须可判定归属,代码注释也一样 → 改为指向
  App/log/sys_log_mcu.c 与事实本身,不留裸编号
- 又一次 BOM 铁律现场复现:一次编辑剥掉 ui_w_soe.c 的 BOM,ARMCC 立刻报一串 #8/#7/#20
  假语法错(missing closing quote 之类),而 MSVC 侧 0 警告完全照不出来;补回 EF BB BF
  即 0/0。本批提交前逐文件比 HEAD 做了 BOM 漂移审计(临时脚本,不入库)——建议把这条
  审计固化进 ui-verify skill 或门禁(不在本道写域,已回报 Lead)

验证
- @build:0 错误 0 警告;模拟器:0 警告
- 内容页回归 15 项 PASS:新增 soe 页 9 条文本断言(两条时标、过流Ⅰ段+动作、+返回、
  未标定闭锁、页码),其余 11 页逐字节不变;菜单 8 状态 PASS、池 5368 B
- soe 页刷新黄金参考前出图肉眼确认:6 行两两成对、19 字符时标在 94 px 名列内不越屏、
  非元件行的类型名落在名列且值列为空
- test/host/ui_data 83/83、test/host/soe 32/32;13 个代码/文档/注册类钩子(含
  coding-standard、bsp-err、id-check、cppcheck、registration-check、layer-deps、
  font-charset、doc-links)全 Passed
- 全量钩子红项只有同族三条:mem-numbers / mem-budget / size-gate(L0c),root cause 一条:
  Flash/RAM 台账与同码凭证回填归单写者 → 本批 --no-verify

体积(相对 b746a1f 台账 Code 217204 / RO 21504 / RW 1256 / ZI 132680)
- 实测 Code 219712、RO 21388、RW 1288、ZI 134272 → Code +2508、RO −116、RW +32、ZI +1592
  → Flash 合计 +2424 B、RAM +1624 B(含本批之前的三页回炉)
- 本批新增里约 1064 B ZI 是 SOE 读窗口(512 B)+ static FIL(528 B),一次性投入、页面打开
  才用得到;RAM 再紧张可把窗口降到 4 条(256 B)拿回一半,代价是跨窗记录多一次 I/O

待台架实测:真机 /log/soe.log 有记录时的翻页与尾部行为、卷忙时该行退化显示、SOE 页与文本
记录页时标一致性(跨 8 小时口径的现场核对)、记录增长时窗口代数失效后的重读节拍。

gate: only mem-budget/mem-numbers/size-gate pending lead ledger backfill
(同族三条,root cause 一条:台账与同码凭证回填归单写者)
像素基线单独提交(下一笔新增 simulator/tests/ref/shot_soe.bin):本笔单独 checkout 时
ui-golden 会报"缺参考",属预期拆分;上述钩子结论取自"本笔 + 下一笔"的合并树。
自查:BOM 漂移审计 17 个改动文件全对齐 HEAD;CR = 0;U+FFFD = 0;字面量纯 ASCII。
2026-10-25 14:44:45 +08:00
wandering1 4380098cc1 feat(ui-pages): 装置信息/交流量/直流量三页接 ui_data 快照真值
三页原先的读数是 Flash 里的参考项目字面量(交流量 24 行、直流量 3 行、装置信息 6 行,含
"软件校验 4454""程序日期 2024.08.27""第 10 页"这类与本机无关的假值)。本笔把它们接到
task-14 落地的快照通道:页面只读快照、零 App include(分层方向基线仍 3 条,不增不减)。

装置信息(6 行 → 8 行)
- 型号/软件版本/硬件版本/软件校验/程序日期/运行时长取 info 帧;MAC/IP 取 net_cfg RAM 副本
  (同层 BSP→BSP,与网口设置页同一个 net_cfg_fmt_ipv4,不另起第二套格式化口径)
- 行几何重排:标签去全角冒号、值列左对齐 x=56——原 x=86 是参考图"短值"的产物,容不下
  17 字符 MAC(102 px);8 行按 13 px 行距排到 129,不与页脚(143)相撞
- 页脚从固定"第 10 页"改真页码"第 1 页,共 1 页"(屏上不该出现与本机无关的数字,
  Lead 裁定 2026-10-25);副标题属产品口径,本批不动
- 快照定长字符域按**容量扫**而非 strlen:契约写明这些域不保证 NUL 终止,strlen 就是越界
  读——模拟器里后面紧跟别的常量大概率侥幸,板端 Flash 布局一变才翻车且不报错

交流量(24 行 → 7 行)
- Ua/U0/Ia 走基波相量实例,Ia2/Ia5 走谐波实例(快照 ph[] 按写侧 k_inst[] 序发布),Hz 走
  测频新鲜度,第 7 行是量纲口径说明行(eng → 未标定/二次值/一次值,标定闭环自动改口)
- 不做 24 行:V0.1 只有 8 路电气量、5 路削顶无采样源、相量实例 5 个,照抄就是 17 行 "--"
  的假骨架(docs/UI设计/页面扩充详细方案.md §3.1.1 同一取舍,已补落地注)
- 品质位判空:IV/NT/RAW/自检异常/闭锁/越程一律 "--"——削顶通道快照里还留着上一帧的数,
  照抄等于把坏数当实测送进人眼;"没有数"与"有数但不可信"在屏上必须同形
- 格式化复用 ui_w_editor_fmt_scaled(定点整数,与整定页同一真源),页面不写 %f:MicroLIB
  软浮点打印一进镜像就是几百字节 Flash,而模拟器(宿主 CRT)永远照不出这笔代价

直流量(3 行 → 2 行)
- V0.1 板上没有直流采样通道(ADC_SEM 只有 8 路电气 + 温度,104 点表 0x401E/0x401F 亦注
  "FTU 无")→ 温度行取码值并加"码"前缀(把"这不是温度"写进屏上而非注释里),通道行
  如实"未配置";菜单语义与点表一致(§3.1.2 方案①)

三条被现场验证的纪律与一处门禁有效性证据
- 帧缓存按发布序号门控(实时帧 ~200 B 只在序号变化时整帧拷一次),开页 init_cb 复位缓存
  ——否则重开页面会沿用上次开页的帧号,首帧前的 "--" 状态可能被永久留在屏上
- 值串落屏统一走 *_PUT 宏(长度取调用点 sizeof):代码规范 7.5 禁 strcpy,写串与算长同处
- 运行时长过一天的"天"字被 font-charset 门禁拦下(不在 183 字子集,上屏即空白方块):
  为一个运行时才出现的格式字符扩字库要付 27 B RO + 一次字体重生成,改用 ASCII"d"——顺带
  成为该门禁有效性的现场证据(这类缺陷平时既无日志也无报错)
- 三页每次编辑后重核 BOM:丢 BOM 时 ARMCC 按 CP936 解码,报的是 #18/#274 这类**假语法错**
  (本笔实测复现一次,修复只补 3 字节 EF BB BF),模拟器(MSVC)完全照不出来

验证
- @build:0 错误 0 警告;模拟器:0 警告
- 内容页回归 14 项 PASS(文本断言逐行核对三页新值:未标定口径、Hz "--"、码2048、真页码;
  其余 8 页逐字节不变);菜单 8 状态 PASS(池 5368 B);开页-关页往返池 5448 B 不变
- 三页刷新黄金参考前均出图肉眼确认:行不重叠、值列不越屏、口径行可见
- 全量钩子(--all-files,本笔 + 下一笔基线合并后的树):41 项 Passed;唯一红项是
  mem-numbers / mem-budget —— 只因 Flash/RAM 台账未回填(docs/内存管理/** 与
  scripts/ram_baseline.tsv 是单写者禁区,归 Lead),故本笔 --no-verify

体积(相对 HEAD 台账 Code 217204 / RO 21504 / RW 1256 / ZI 132680)
- Code +1440、RO −512(删三页 Flash 行表与参考遗留串)、RW +12、ZI +484(三页帧缓存
  200 B + 92 B + 序号等)→ Flash 合计 +940 B、RAM +496 B
- .map 明细:ac_refresh 202 B、dc_refresh 164 B、di_refresh 290 B、di_fmt_uptime 92 B

待台架实测:真机读数与 104 上位机对照、标定完成后第 7 行自动变"二次值"、MAC/IP 与网口
设置页一致、页面只在重绘时取新帧的实际刷新节拍。

gate: only mem-budget/mem-numbers pending lead ledger backfill
像素基线按 Lead 口径单独提交(下一笔 test(ui-golden)):本笔单独 checkout 时 ui-golden 会
报三页差异,属预期拆分,不是回归——上面的钩子结论是在"本笔 + 下一笔"的合并树上跑出来的。
自查:三页 BOM = EF BB BF、CR = 0、U+FFFD = 0、字面量纯 ASCII(中文只在带 BOM 文件里)。
2026-10-25 14:44:44 +08:00
wandering1 969fc5c132 merge: task/n17-probe 合入 v2-on-ui
任务 n17(1 个提交)经 task-worktree 回收:
- fix(网络): 主动拆会话登记即返——drop 路径不再占用 netTask(登记簿 N-17 收口)
2026-10-25 10:32:25 +08:00
wandering1 a3c6e89378 fix(网络): 主动拆会话登记即返——drop 路径不再占用 netTask(登记簿 N-17 收口)
道F 批 2 / task-17。定位 + 单点收口,不开 net 层跨上下文大口子。

【定位(链路,全部代码可复核)】
探针挂起 30s → time_sync.c:149 预算耗尽 → :187 net_drop_conn()。NET_MSG_
DROP_CONN 处理器(net_drop_conn_locked)内同步跑 net_sock_reset_sync
(QUARANTINE)(2ms 节奏、400ms 底/600ms 顶,自家注释原记「等待期 netTask
有界占用 ~400-600ms」):netTask 的 1ms 主循环冻结在同一迭代 → NET 位
停摆;串行总线整窗停摆 → UI 推流/FTP/104 在途消息回执顺延;对时调用方
睡在 net_wait_result 等回执期间无分片心跳 → 五位同窗缺席 → 连续 8 个
100ms 抽样停喂 → 声明 [WD] task stall(V5 时代 req=0x37、快照只剩 bit0,
即日志 mask=0x00000001)。被动断连/EOF 走 net_listener_tick 异步沉降机
(D10 条令)故纯 FTP 负载 0 stall——只有主动 drop 留在同步原语里。

【修复】监听型 fd 的芯片拆除改为 net_listener_settle(QUARANTINE) +
net_listener_request(REQ_DROP) 登记即返,拆链/排渣/等 CLOSED/重建全部
交 net_listener_tick 30ms 节奏——与被动释放同一台机器(E16 台架实证该
自建序列 RST 10/10、FIN 5/5)。非监听 client fd 无监听可保,同步形态
原样保留(全仓零生产调用者)。返回值语义显式改为「拆除与重建已登记」
(net_socket.h 注释按 lead 要求写明,防未来调用方按旧语义写)。
APP_ASSERT(S12 试点)分派门与断言位置未动,登记时序下仍成立。

【评估否决留档(lead 要求论证入文档)】「缩短重建重试 5×2s」不采:
该退避在 time_sync_open_listener() bind/listen 失败路径、已被
time_sync_delay_fed() 分片喂保护、不在缺口链上;缩短反撞 TRAP-NET-09
沉降窗。全文见 docs/网络/网络自愈架构设计.md §4.6(已改写)。

【竞态与信源核对】① 主路径置 CLOSED 后迟到的 DISCONNECT/TIMEOUT 被
net_events 状态门拦截(只认活会话态),不会把刚登记的 QUARANTINE 降级;
② 零新增读清点——全局寄存器唯一读者仍 net_poll,隔离窗两处本 Socket
读清是 net_listener_tick 原位,§4.10 三条准入判据不触发;③ E13 物理
边界(对端不配合 FIN_WAIT_2 长尾)逐字不变,只移动等待发生的位置。

【测试】net_socket 主机套件 457/457:TC-NETSOCK-240f/321b/321b2/460e
重锚到登记式(settle+REQ_DROP、rst 机零启动、tick 预算 ≤20、无 [ERR]),
新增 321g/h/i + 460f 钉住 client fd 旧同步形态。全量 --all-files 40 绿。

【体积增量(vs scripts/ram_baseline.tsv 2026-10-25 行)】
ER_IROM1 237328→237344(+16 B);RW_IRAM1 75792(+0);ZI 0。
@build 0 错误 0 警告(Code=215948 RO=21396 RW=1232 ZI=131984)。

gate: only mem-budget/mem-numbers pending lead ledger backfill
test-coverage 本条**绿**(已覆盖行净增,未动用裁决③的覆盖率例外——
旧同步路径行经 client fd 新用例继续被覆盖,净差非负)。其余含
id-check/doc-links/全部主机单测绿。仅 mem-numbers/mem-budget 两红 =
台账 +16B 漂移待回填(lead 单写者禁区)。

【越界申报】① test/host/net_socket/{ns_s3_msg,ns_s4_state,ns_s5_io}.c
(批准③同类:只动本任务相关用例,未碰他人行);② docs/网络/
网络自愈架构设计.md §4.6 改写(lead 本任务指令明示)。登记簿 N-17
销项与 README 波次行归 lead;docs/内存管理 数字未碰(禁区)。

台架:V5 复跑判据(e02_probe + FTP ≥30min 零 stall / 零复位)属网络
对端类——待 lead 排窗,步骤清单随 done 消息发出。
2026-10-25 10:08:46 +08:00
wandering1 201d713226 feat(ui-data): ui_data 三通道快照 + ui_data_feed 装配 + SOE 显示名映射
按 docs/UI设计/UI_数据供给设计说明.md 的 D 方案把 App→UI 数据通道落地:
App 组帧、BSP 存帧、页面只读;UI 侧零 App include,薄包装只在两端适配层。
本笔是纯增层、不接任何页面 → 零像素变化(菜单 8 状态 + 内容页 11 页黄金
参考逐字节不变,simulator/tests/ref 无 diff,未刷基线)。

新增
- Drivers/BSP/UI/ui_data.{c,h}:实时量 rt / 参数 par / 装置信息 info 三通道存储
  + 发布序号盖章 + 只读访问。零 App include、零 LVGL include(分层叶子);
  读写同在 uiTask 上下文串行 → 无锁、无双缓冲、无 seqlock(契约 §5.1)。
- App/app/ui_data_feed.{c,h}:快照唯一填充者。rt/info 每 200 ms 一帧,par 只在
  prot_set_checksum() 变化时重发;首轮立即发布,免启动初期闪一下 "--"。
- Drivers/BSP/UI/ui_w_soe.{c,h}:SOE 二进制记录的显示名映射与注入式 reader
  契约。未标定闭锁=7 / 标定复归=8 已进表——编号按 App/log/soe_rec.h 与
  App/algo/prot_elm.h 现场数过(类型 1..8 + 哨兵 9、元件 0..8、ELM_NONE=0xFF、
  FLG_IV=0x80),不采信文档与消息转述。reader 备好但两端未绑定:落盘侧还
  没有只读 API(Lead 已裁该 API 归下一工作项),未注入按"无记录"空态渲染。

量纲诚实性(契约 §4.1 同步改成真值口径,不再抄旧码值表述)
- eng 不写"0 = 原始码值":标定层 ROM 系数是引脚侧 LSB,采样环折算出来的就是
  ADC 引脚电压(承诺边界 docs/任务规划/数据可信.md §2.6);电气 8 路
  adc_cal_flags() 的 VALID 全置位才升 eng=1 二次值——整帧共用一个 eng,只要
  还有一路是引脚电压就不能贴"二次值"标签,宁可整体保守。
- TEMP 不参与线性标定 → 恒带新增品质位 UI_DATA_Q_RAW=0x20,页面不挂单位。
- UB/UC/IB/IC/IN 无基波相量实例(V0.1 削顶)→ Q_IV|Q_OV;未满一周波 → Q_NT。
- 谐波另开 ph[] 相量实例槽带:yc 的语义通道下标容不下同通道的二次/五次谐波。

接线与双端注册
- App/task/ui_task.c:ui_menu_init 之前 ui_data_init + 首帧发布;主循环每轮
  ui_data_feed_tick() 排在 ui_port_step 之前(本轮渲染用本轮刚发布的帧)。
- simulator/main.c:发布固定合成帧(无头与窗口两个初始化点,先于建页);
  定值不手抄 40 个数,改为链入 App/algo/prot_set.c 取出厂默认(抄表等于造
  第二真相);build_date 用固定串而非 __DATE__,否则跨天重建会把装置信息页
  黄金参考刷掉,而黄金参考是判据本身。
- 注册:uvprojx 加 3 条(App 组 ui_data_feed.c + UI 组 ui_data.c/ui_w_soe.c)与
  simulator/CMakeLists.txt;ui_data_feed.c 不进模拟器 → check_registration.py 的
  SIM_EXEMPT 加一行(App/app/app_main.c 先例,已报备)。

主机单测与钩子
- test/host/ui_data:82 断言全绿。钉三类事:快照语义(首帧前读 -1、序号由本层
  盖章、三通道互不串台、NULL 防御、init 幂等复位);跨层不透明下标现场一致
  (yc 序 = adc_semantic_t、q10 序 = prot_set_id_t,同一 TU 里 include 两侧头);
  SOE 编号与名字表现场一致(八类事件都有名、采样/标定四个词互不相同、
  名长不超列宽、越界回占位)。
- .pre-commit-config.yaml 加 ui-data-unit-test 钩子(第 43 个,Lead 已批自加)。
- scripts/id_registry.json 建族 TC-HOST-UIDATA(属主登记处 = 单测目录,同
  TC-HOST-ADCCAL 先例;不建族则新号被编号门禁判"未登记前缀")。

体积(相对 HEAD 台账 Code 215744 / RO 21396 / RW 1224 / ZI 131984)
- Code +1272 B、RO +108 B(字库 4 字,见上一笔)、RW +24 B、ZI +696 B
  → ER_IROM1 238520 B(22.7%)、RW_IRAM1 76504 B(58.4%,台账 57.8%)。
- .map 逐项:ui_data.o 488 B(Code 184/RW 12/ZI 476)、ui_data_feed.o 221 B
  (1034/12/209);LVGL 池 0 增量;ui_w_soe 未接线不进镜像(.map 无其符号)。
- 台架未跑(本笔零像素、零接线):首帧时刻、200 ms 刷新、eng 升档与遥信
  位图真值全部留"待台架实测",接线完成后请 Lead 排窗。

遗留与风险(随回报进台账)
- 写路径(契约 §6 的 param_request/state)不落:无消费者就是永不执行的死码,
  随整定 4 页一起落;届时须先定"UI 整定 vs iec104Task 写 prot_set"的并发口径
  (prot_set_set_sealed 的影子区非重入)。
- 公共地址 CA 未下发:固件里 CA 逐帧从 ASDU 头解析,无集中常量可取。
- 遥信位图与 App/protocol/iec104/iec104_pts.c 板端源是两处镜像(该文件位号
  写在函数体内未导出,UI 不得 include 协议头),开入采集落地时应并成单源。
- 版本类字段是本文件内的宏(仓库无产品级版本登记表,值沿用页面既有显示
  值);软件校验位在拿到构建期 CRC 步骤前一律显示 "--",不抄参考遗留值。

gate: 全量钩子(--all-files)40 项通过;cppcheck/test-cppcheck/coding-standard/
layer-deps(基线仍 3 条)/registration/bsp-err(存量 38 条不变)/font-charset/
id-check(增量无新账)/doc-links/ui-golden/主机单测与覆盖率全绿;唯一红项是
mem-numbers 与 mem-budget——两者只因 Flash/RAM 台账未回填(禁区,归 Lead)。

gate: only mem-budget/mem-numbers pending lead ledger backfill

自查:BOM(Drivers/BSP/UI/* 与 App/task/ui_task.c 保持 EF BB BF,新建 UI 两件
同规范;App/app 与 test/host 无 BOM 与本目录一致);CR = 0 全 LF;U+FFFD = 0;
字符串字面量纯 ASCII(中文只进注释)。
2026-10-25 09:56:22 +08:00
wandering1 8cbc7bc6b6 feat(ui-font): 字库补 SOE 显示名用字 采/样/恢/效(179→183 字 / 274→278 字形)
单独提交:按字库独占规矩,缺字改动自成一笔,不与功能改动混笔。

用字来源(下一笔提交的 ui_w_soe 显示名映射):事件类型"采样闭锁/
采样恢复/标定复归"与时标行的"时钟无效"。缺字上屏是空白方块、无日志
无报错,只能靠像素回归与门禁照出来。

流程(font-regen 四步,未跳步):
1. extract_cn 扫出 144 字 < 现清单 179 字:35 字源码已不用但不属 KEEP_EXTRA
   那 4 字(异/常/片/芯)——它们是 2026-09 窗口固件写进 /log/selftest.log
   定长记录的历史用字,跨固件持久落盘,静态扫描照不出来。故按并集回填,
   本笔只加 4 字、不做瘦身:瘦身等于让历史审计记录永久渲染空白且不可逆,
   收益(约 35 字 × 27 B RO)与代价完全不对称(同门禁头注"刻意保留 ≠
   未知冗余"的同一判定逻辑)。这 35 字要不要入 KEEP_EXTRA 已回报 Lead
   裁决,不在本道写域。
2. make_font.py 重生成 ui_font_cn_12.c:coverage OK,183 字全覆盖;▶/Ⅰ/Ⅱ/Ⅲ
   补回字形的单码位 cmap 条目未被宽稀疏 range 吞掉(首命中守卫通过)。
3. 像素回归:菜单 8 状态 + 内容页 11 页黄金参考逐字节不变,
   simulator/tests/ref 无 diff → 本笔不产生基线变化,未用 --update 刷参考。
4. 构建:@build 0 错误 0 警告(RO-data 21396→21504,+108 B,1bpp 12px 每字
   约 27 B 含字形描述符);模拟器 0 警告。

计数字段同步(同一事实只改数字,不重复抄流程):
- docs/UI设计/UI_中文字体生成与配置.md:产物说明 + 体积档(含 4 字实测增量)
- docs/UI设计/UI_界面制作流程.md C3 行
- docs/UI设计/UI_设计架构.md 默认字体行
- simulator/README.md 已知坑第 4 条

自查:BOM/CR/U+FFFD/字面量 ASCII 全树核对通过;全量钩子 40 项通过,
仅台账两条待回填(数字随下一笔的 ui-data 提交一起由 Lead 回填)。

gate: only mem-budget/mem-numbers pending lead ledger backfill
2026-10-25 09:56:10 +08:00
wandering1 c4edf352fe refactor(UI): 记录页改注入式 reader,layer-deps 基线 6->3(道B task-6)
B-6 分层收紧(UI_数据供给设计说明.md §7.2 契约原样落地):
- ui_w_records:删 #include "sys_log.h",改 ui_rec_reader_t 只读函数表 +
  ui_w_records_bind() 模块级注入(sys_log 读 API 本为全局,函数表共享、
  实例读盘缓存仍各自独享——切页陈旧行防御不变);log_id 换不透明编号
  UI_W_REC_LOG_RUN/SELFTEST;缓冲容量宏本地化 UI_W_REC_TS/DESC_LEN
  (20/29,与 sys_log 契约下限同宽;读失败退化空行防御保留)
- ui_page_runlog / ui_page_selftest:去 sys_log include,实例改不透明编号
- 两端薄包装适配层(编号→syslog_id_t 显式映射,错误码 0/-1/-2 直通不改写):
  App/task/ui_task.c 与 simulator/main.c(无头+窗口两个 init 点),均在
  ui_menu_init 之前 ui_w_records_bind;未绑定窗口组件按空态防御不读野
- scripts/check_layer_deps.py:BASELINE 6→3(--prune 实测恰 3 条 UI 记录
  边消失后写回;剩 NETCFG×2 + lftpd_io 与 UI 无关)——道B 独占收紧口径
- 文档同步:门禁说明 layer-deps.md §1/§6/§9 与 README 计数 6→3;
  UI_数据供给设计说明.md §7.2 加落地注记、§14"计划削减"改"已兑现"
- 首提被 id-check 拦下:注释/文档里自造"批卡编号"(B-6/道B-3)命中
  "未登记前缀"增量拦截——已全数改回 task-6/道B 普通指称(编号不回收、
  禁自造,教训入本提交);id-check 复跑 RC=0(S10 告警为存量非本改动)

验证:
- 模拟器 0 警告;菜单 8 状态 + 内容页 11 页黄金参考逐字节 PASS
  (记录页数据流经 reader 后像素与基线一致,无基线更新提交)
- cppcheck/coding-standard/bsp-err/font-charset/no-mojibake 单跑全绿;
  layer-deps 复跑:6 处反向 include = 豁免 3 / 基线内 3 / 新增 0,
  --prune 显示"基线无需收紧(3 条存量全部仍成立)"
- doc-links --all 的 35 处告警全为本仓检出路径含空格误扫 SDL2 自带 md
  (环境问题、非本改动引入;提交门禁按暂存文件名过滤不受影响)
- @build 0 Error 0 Warning(终树复跑 BUILD_OK);uvprojx 无 UV4 抖动、
  无新文件不涉注册;其余 38 钩全绿(ui-golden/font-charset/coding-standard/
  bsp-err/id-check/cppcheck…单跑复验 RC=0)

增量(A/B 同树 .map,基线 = 合入 c1b425d 后 HEAD):
- Flash:Code +104 B、RO +8 B(两端薄包装代码 + reader 函数表 const)
- RAM:RW +4 B(s_reader 指针)/ ZI +4 B(两页实例 struct 枚举改 uint8
  本应 -8 B,链接段对齐后净 +4 B);RW+ZI 合计 +8 B

gate: only mem-budget/mem-numbers pending lead ledger backfill
- pre-commit run --all-files 实测仅这两条失败(台账/RAM 文档 vs .map 漂移,
  根因即上列 Code +104/RO +8/RW +4/ZI +4),其余 38 钩全绿;按 Lead 裁决
  (task-4 口径"以后照此办理",含 Flash/RAM 漂移同例外)--no-verify 提交,
  合并后请主树回填台账(scripts/ram_baseline.py --write 由 Lead 侧执行)
- 手工自查通过:5 个 BOM 文件 BOM=EF BB BF 齐全、docs/py/main.c 保持无 BOM,
  全部 10 文件 CR=0、U+FFFD=0
- 台账增量供回填:Flash Code +104 B、RO +8 B;RAM RW +4 B、ZI +4 B
2026-10-25 05:21:05 +08:00
wandering1 f711130b4c feat(UI组件): 新增数值编辑器/定值表/动作确认页三组件(道B task-5)
域5 新增 3 对文件(双端注册 uvprojx + CMakeLists),只做渲染与输入抽象、
不接真实数据源(数据绑定 = 批 2 道E ui_data/prot_set 经回调注入):
- ui_w_editor:定点整数(10^decimals)逐位编辑——上下步进(0..9 环绕无进位)、
  左右移数字光标(首末环绕)、ENTER 提交经 min/max 范围校验,越界拒绝并
  显示"越界"提示留在编辑态;光标反显盒与 ui_page_net_cfg 字节光标同画法
  (含"只画单字符防折行"实测坑注释);共享格式化 ui_w_editor_fmt_scaled()
  单一规则源(值列显示与编辑光标恒同串)
- ui_w_setlist:定值四视图(参数/定值/控制字/软压板)共用列表渲染 +
  整定输入——行表(名称<=7字/id/类型/小数位/范围/二态串表) + 回调注入
  (get/set/commit/state/bad);查看视图(commit=NULL)与整定视图只差绑定:
  整定视图末行固定"保存"行,值随交接态 ENTER/WAIT/OK/ERR(net_cfg 先例),
  整表一次提交(一次封存一次校验和,页面补齐规划 §3.2);二态行 ENTER
  循环切换;保存失败越界行经 bad 回调反显(页面扩充详细方案 §3.2.7)
- ui_page_action:可复用操作确认链——一次确认 -> 二次确认 -> ok_cb
  只投递不阻塞 -> done_text 回报(或成功即关页),拒收显示 ERR 可重试;
  ESC/LEFT 关页取消(实测取消后菜单与基线逐字节一致);单例描述符由
  开页装载(内容页框架全局单页前提),不定义 ui_page_action_desc
  (registration-check 页面模块判据不误拦,批 2 跳板各自出口)

组件文案用字全部落在上一分支预置字集内(font-charset WARN 60->52 印证
确/取/消/越/二/次/返/回 已转源码在用),无需再动字库。

BSP 公开 API 约定合规(首提被 cppcheck/coding-standard/bsp-err 三钩拦下,
按门禁口径修净后复验全绿):
- 键分发返回值取 uint8_t 1/0 不用 bool(bsp-err/err-type:bool 只许
  is_/has_ 谓词);ui_w_editor_value 直返 int32_t(出参形状无错误通道)
- ui_page_action_open 返回 bsp_err_t:BSP_ERR_PARAM 参数无效 /
  BSP_ERR_STATE 已有页打开 / BSP_ERR_NO_SPACE 框架开页失败(可判分支)
- get/state/bad 回调 typedef 各补"返回值:"注释块(bsp-err/return-doc)
- cppcheck variableScope 变量收scope 7 处;CODE-RULE-18 Yoda 1 处
  (sel+1u==total -> sel==count);提交内已不含 --wdemo/--wact harness

验证:
- 模拟器零警告;组件行为经临时 sim-only harness(--wdemo/--wact) 截图
  目检 7 态:基线视图/bad 反显/进编辑步进/越界拒绝/二态切换/保存整表
  提交 OK/确认链三段/取消回菜单;harness(main.c 4 处 + CMake 1 行 +
  ui_w_demo.c)已完整还原删除,提交内不含(最终树 main.c 无改动,
  CMakeLists diff 仅 3 行注册)
- 回归:菜单 8 状态 + 内容页 11 页黄金参考逐字节 PASS(ref 无变化,
  无基线更新提交);池基线 5368/往返 5448 不变
- 门禁:coding-standard PASS / font-charset PASS / layer-deps PASS
  (新文件零 App include,基线 6 不变——减基线归 task-6)
- @build 0 Error 0 Warning;Code +324 B、RO/RW/ZI 0(组件尚无引用方,
  ARM 链接器未剥离仅因新 TU;批 2 页面接线后实例 RAM 每页约 +40~60 B)

gate: 全钩子正常执行通过,无 --no-verify
- pre-commit run --all-files 全绿(含 mem-budget/mem-numbers:本分支基于
  回填后台账 d9ab955,RAM 0 变化、RO 不变,仅 Code +324 B 不涉受管区)
- 手工自查:6 新文件 BOM=EF BB BF 齐全、全 LF、U+FFFD=0、字面量仅
  UTF-8 中文(UI 带 BOM 先例口径:保存/越界/二次确认/告警/跳闸等,
  全部已在字体子集);ASCII 串仅 ENTER/WAIT/OK/ERR 状态字
2026-10-25 04:34:43 +08:00
wandering1 b802651a56 merge: task/UI-font-subset 合入 v2-on-ui
任务 UI-font(2 个提交)经 task-worktree 回收:
- docs(UI设计): 字体字符集计数回填 179字/274字形并登记cmap首命中事故
- feat(UI字体): 扩字60字为页面扩充预置, 修复生成器cmap插入顺序缺陷
2026-10-25 03:27:11 +08:00
wandering1 2e0f66a07e feat(UI字体): 扩字60字为页面扩充预置, 修复生成器cmap插入顺序缺陷
字库扩充(批2页面预置, task-4/道B):
- ui_cn_chars.txt 119→179 字(CJK 176 + 符号 ▶/,/:),含 ASCII 共 274 字形
- +60 字: Ⅰ Ⅱ Ⅲ 段 延 投 退 跳 闸 零 频 荷 负 低 过 电 一 二 次 位 公
  出 回 外 部 度 温 态 状 总 接 显 示 最 近 未 标 欠 闭 锁 长 间 障 配
  道 重 载 返 年 月 分 秒 合 已 请 求 确 取 消 越
  依据 = 页面补齐规划/页面扩充详细方案两篇规划中出现的页面显示串;
  待拍板的厂家设置支用字(厂/家/恢/调/试)不入
- Ⅰ/Ⅱ/Ⅲ 宋体有字形(simsun face0 cmap 实测 HAS)且正常入字体,罗马数字
  无需任何工具链兜底

make_font.py 修复(本次扩字暴露的真缺陷):
- LVGL get_glyph_dsc_id() 线性扫描 cmaps[]、首个包含码位的 range 即返回:
  Ⅰ(U+2160) 使稀疏 range 起点 0x4E09→0x2160,把 U+25B6 ▶ 单码位补回条目
  吞进宽 range → 模拟器箭头全变方框(菜单 658 px 差异);旧 verify_coverage
  按"进了任一 range 即覆盖"判定,全绿照不出解析路径倒置
- 修复: _insert_cmap_entry() 把补回条目插到包含它的 range 之前;
  _cmap_lookup_gid() 可达性守卫,可达即跳合并(全角逗号走稀疏列表,不再
  追加永远不可达的死条目); verify_coverage() 改按首命中语义

验证(全绿):
- font-regen 四步(extract→并回 KEEP_EXTRA→make_font→diff);仓库外沙箱
  dry-run + 确定性: 沙箱/仓库产物 sha256 一致(26e519db…)
- 逐码位结构对账: added=60 / removed=0 / 保留字形位图字节零差异 /
  度量一致(multiset 差 = 新字身)
- font-charset PASS(60 未知冗余 WARN = 预置字,批2接线后自然转在用)
- 模拟器菜单 8 状态 + 内容页 11 页黄金参考与旧字体逐字节一致(箭头恢复);
  ref/ 基线无变化,故无黄金参考更新提交
- @build 0 Error 0 Warning; A/B 同树构建: RO-data +1528 B, RW/ZI(RAM) 0

gate: only mem-budget/mem-numbers pending lead ledger backfill
- pre-commit --all-files 实测: 仅这两条失败(台账受管区/内存文档数字 vs
  .map 漂移; 根因是 RO-data +1528 B 的 Flash 增量, RAM 无变化);
  其余 38 钩子全绿(font-charset/ui-golden/cppcheck/编码规范/单测族均过)
- 修复手段 ram_baseline.py --write / check_mem_numbers.py --write 均写
  docs/内存管理/**(Lead 单写者域), 本分支按裁决不代跑, 请合并后回填
- 手工自查: 改动 8 文件 BOM/CR/U+FFFD 与各自原状一致(全 LF 无 BOM),
  字体产物纯 ASCII, make_font.py 中文仅在注释与打印, 无字面量改动
2026-10-25 03:23:52 +08:00
wandering1 56893709fe feat(S12): 模块级轻量断言宏 APP_ASSERT + net_socket 状态机不变量试点 2 处
规格:docs/任务规划/测试与架构专项.md §2.1(模块级轻量断言宏,必须零生产开销)。
本提交只做道C-1:新增断言宏 + 2 处试点,不开 net 层大口子、不新增 test/host 目录。

改动
- 新增 Drivers/BSP/app_assert.h:APP_ASSERT 两级裁剪。
  · 关闭态(默认,产品构建):编译期消失、零符号零代码;条件仍进 sizeof 的
    不求值上下文,写错变量名/比较类型会当场报错,不会出现"裁掉=从没被编译器看过"。
  · 打开态:失败交 configASSERT 用的同一个钩子 vApplicationAssertFailed()
    (裸控制台打 [ERR] ASSERT FAILED file:line 后软复位);宿主端(模拟器 /
    主机单测)落 stderr + abort(),非零退出码不会被一键回归静默吞掉。
  - 开关 APP_ASSERT_ENABLE 默认注释,策略与 ENABLE_DESTRUCTIVE_TESTS 同级。
  - 纯宏、无函数、无数据,不新增 .c → 不涉及双端注册(.h 靠 include 路径找到)。
- Drivers/BSP/NET/net_socket.c:2 处试点,都落在"函数头写了限定条件、函数体
  一项都没检查"的缺口上。
  · net_close_locked:控制块必须落在 s_net_socks 数组内且 in_use——此前给出
    越界或已释放指针会一路走到 memset(sizeof(net_sock_t)),直接踩脏 fd 表。
  · net_drop_conn_locked:进入时必须 in_use + TCP 流 + 会话处于 ESTABLISHED
    或 CONNECTING——函数体无条件置 CLOSED 并登记重建,将来绕过
    NET_MSG_DROP_CONN 分派门的新调用路径会拆掉一个健康监听器(症状与 N-30
    一族同源难查),断言把这条状态机不变量钉成机器可查。
  两处均 netTask 上下文、非中断、非高频;条件纯读无副作用;既有错误返回分支
  一条都没改(断言不替代错误码,见 app_assert.h 第四节纪律)。
- test/host/net_socket/run_unit_test.py:include_dirs 补 Drivers/BSP 一行,使该
  套单测编 net_socket.c 时用真 app_assert.h,不造桩——假件与真头漂移正是本
  runner「常量闸」要防的那类自洽假绿;stub 目录仍在最前,同名头命中不受影响。
- Drivers/BSP/README.md:根级文件表补 app_assert.h 一行。

两处有意偏离示意稿(理由已写进头注释)
1) 断言点不再额外发一条 DBG_ERROR:钩子本身已打印 file:line,而每条 DBG_*
   调用点约 90 B Flash + 216 B 栈(AGENTS「日志不是免费的」);断言点又常在
   「内核状态已不可信」的现场,走裸控制台比走日志行缓冲稳。
2) 头落 Drivers/BSP(与 dbg_log.h、bsp_err.h 同级)而非示意稿的 App/util:它是
   纯宏叶子能力,落 App/util 会让 BSP 新增一条反向 include App 层头的边,而
   layer-deps 存量基线只减不增、加不进去;落 Drivers/BSP 已在 MCU 与模拟器双端
   include 路径内 → 双端零工程文件改动(uvprojx / CMakeLists 一字未动)。

验证(工作树 _wt/S12-assert,分支 task/S12-assert)
- @build 产品态:BUILD_OK,0 错误 0 警告。
- 体积与 RAM:Code=213780 RO=18860 RW=1204 ZI=131852,与 scripts/ram_baseline.tsv
  的 2026-10-24 基线行完全相同 → Flash/RAM 增量 0 B;.map 中 app_assert 相关
  符号 0 条(规格完成判据"无新增符号")。gate 全绿,无需 mem-budget 例外。
- 打开态实测(临时取消注释编译,跑完已还原成注释态):0 错 0 警,
  Code=213916(2 处断言共 +136 B ≈ 68 B/处,无每调用点栈代价),RW/ZI 不变。
- 宿主 MSVC 三态实测:关闭态断言不求值但条件仍做类型检查(错误标识符报
  C2065);打开态真条件不触发、假条件 stderr 打 [ASSERT] FAILED 后 abort;
  net_socket 主机单测 452/452 通过(用真头,开关默认关)。
- 门禁实跑:cppcheck(warning/perf/portability/style)、no-mojibake、coding-standard、
  layer-deps、bsp-err、id-check、doc-links、registration-check、mem-budget 全 0。
- 台架 @test 未跑:bench 锁归 Lead;产品态本改动零行为差异,无需烧录回归。

遗留
- 打开态未在台架上真跑一轮(需 bench 锁 + 一个故意造假的注入点):待台架实测。
- 文档销项(测试与架构专项.md §2.1、并行分道方案.md 对应行、AGENTS 任务表)
  归主工作树统一处理,本分支未碰 docs/任务规划/**。
2026-10-25 03:04:07 +08:00
wandering1 7048e9cb9a docs(任务规划): 瘦身为只留待办,波次条目改用语义名
任务规划目录只保留将来要做的事:删掉已完成的证据链与已作废的历史快照,
并把波内条目的 C/D 短码全部换成语义名。

为什么删 C/D 短码:它们同字母不同义——C 组在 UI_界面制作流程.md 里是
UI 设计约束(屏幕/字符集/文本/编码),D 组在控制台单写者日志设计说明.md
与配置文件设计说明.md 里是决策记录。同一串短码在三处指三件事,读的人
只能猜,所以条目一律改用一行短语作标识。

- 排期详述.md 重写为「结构债与验证常态」,条目全语义名,出处列留冻结号
- README.md 删修订记录块(本文件自述不留变更记录),波次行改语义描述
- W1-采集保护收尾.md 67→30 行,只留 M3 送检与 B10 卡
- 基线与待确认.md 73→15 行,删已作废的旧栈表与旧余量数字
- 登记簿.md 逐行收缩到未闭环残余,N-29 的 .git 订正为实测 233 MB
- 测试与架构专项.md 删 S1-c(106 例已落地,且被覆盖率总闸覆盖)
- 并行分道方案.md 删批 0,修两处会返回 rc=3 的 new 命令形态
- 覆盖率口径:套件 16→33、未挂钩子 12→17、墙钟 25s→67s(实测)
- 同步 10 处跨文档引用,指向合并或删除后的新位置
2026-10-25 01:35:21 +08:00
wandering1 5a806d8a26 fix(log,net): 处置主机单测照出的 10 条现状,撤销 1 条误报
用户裁定「全部修」。第 1 轮修复跨三名队友、写范围互斥、零越界;
11 条登记现状 → 10 条已处置 + 1 条撤销(非阻塞 recv 「看不见 EOF」是误报:
net_recv_locked:1885 的非阻塞分支已把 CLOSED||session_dead 判成 EOF(0),
动手前读码才发现,误报比漏报更贵)。

存储与日志(App/log/sys_log_mcu.c、soe_store.c、cfg_file_store_mcu.c、sys_log.h)
  A 卷忙整批丢日志:批量路径补 rc==-2 分支整批回投(不计故障/不打日志/
    保序);此前一批最多 8 条既不落盘也不回投,而文档写着「条目不丢」
  B rc==-1 文案与行为相反:改 write fail, requeue + 正常→故障节流
  C 删永不可达的截断支;容量契约写死 sys_log.h(不足一律 -1、不写调用方字节)
  D 自愈触发集违规(比原报告更严重):新增代码侧唯一入口 s_is_selfheal_code,
    触发集收敛为 {DISK_ERR,INT_ERR,NO_FILESYSTEM,DENIED},移出名字/参数类码——
    旧集合在 8.3 同名同因下每轮删一次自己的历史日志且永不自愈,正是存储架构
    §10.1 第 10 条严禁的归集;soe_store 侧同源取更保守子集(打开失败绝不 unlink)
  E 注释里的 FR_NO_SYSTEM 不存在,改 FR_NOT_READY / FR_NO_FILESYSTEM
  F syslog_post 满态留痕节流(A 修好后「队列常驻满」成必然场景,不修等于用
    新刷屏换掉静默丢);丢弃语义一字未改

NET(net_socket.c/.h、net_types.h、net_socket_sim.c、lftpd.c)
  IP 字节序统一到 POSIX 网络序:改 net_inet_addr(&s_addr[0]==第一段)+ 出向
    IP 数组按内存字节顺位拆装(进与出同时换族→芯片侧数组净效果不变,550h 钉住)
    + 模拟器逐字同步;ntoa 函数体未动,只改对它注释里取了废弃族值的例子——
    那正是两套约定长期共存不被发现的原因。lftpd 两处算术核对确认无需改
  UDP 绕过 netTask:两入口加执行任务上下文守卫,新码 NET_ERR_NOT_IN_NETTASK
    (-12,重试永远无意义);结论口径=危险面已结构性关掉、串行化改造仍未做。
    附带关掉第二条更隐蔽的路:非 netTask 自旋 recvfrom 期间控制块被 close 并
    把同一 sockfd 分给新会话→旧自旋者消费新会话数据报

控制台(console_log.c/.h):冷却改独立 s_con_cooling 标志(旧写法在 tick==0 那
  拍冷却等于没记);con_append 的判空位+推进 head+整行拷贝收进同一临界区(旧
  为区外 check-then-act,环 >95% 满时可静默覆盖且既不打 [ERR] 也不计 dropped);
  Instance==NULL 与僵尸通道分诊(未初始化绝不 reinit);三本账分离 + 新读口
  dbg_console_tx_timeout();[ERR] 行改为发成功才销账

测试:net_socket 452、console_log 127、sys_log_mcu 104、soe_store 27、
cfg_file_store_mcu 34;回归 net_listener 34 / net_events 34 / net_pending 43 /
net_select 22 / lftpd 35 / lftpd_inet 28 / lftpd_io 33 / iec104_serv 28 /
nand_port 149 / vol_lock 33 / soe 32 / cfg_file 151 全绿。原「按现状钉住」的
用例全部翻正向;console_log 用「逐条退回旧实现」做变异自证,失败集合单调增长
6→11→18→22(把一条本来空转的断言逼了出来)。
游标抬升:TC-NETSOCK-545→560、TC-SYSLOG-100→107、TC-CONLOG-135→167。

文档:控制台设计说明 §4.1 口径与 §13-O2 销项 + v1.5;运行与自检记录 §4.4/§4.5
四条;存储架构 §10.1 第 10 条推论(指向 s_is_selfheal_code);TRAP-NET-21 后续
条目;BSD 指南新增 §9.0 s_addr 唯一约定;覆盖率口径 §9「第 1 轮修复处置」表 +
后续项 5 条 + 台架复验 7 条 + 新坑 8 条 + 变更记录。

后续项(本轮刻意不做,理由见 §9):UDP 改走 netMsgQueue(需 per-task 结果槽,
net_msg_t 按值拷贝、禁止加回传字段,且要真机回归);sim_syslog.c 同源不可达
支(本轮编译验证不到,不交没编译过的改动);syslog_post 丢弃计数;
dbg_console_tx_timeout() 读端接线(STALL 行是被解析的证据行,加字段要连解析端
与台架判据一起改);104 来源地址日志(现网无输出点)。

gate: @build 0 errors 0 warnings(Code=213720 RO=18860 RW=1204 ZI=131500)。
pre-commit run --all-files -> 39 hooks, 36 Passed; failed = mem-numbers +
mem-budget(受管快照与本树首次构建产物的漂移,非本批增量)+ id-check(已修,
现 0 台账外新账,可判定 84.63% > 基线 82.6%)。实测水位 RW_IRAM1 57.4%、
RW_CCM 87.9%,均低于 92% 红线;RAM/Flash 本批增量为数个 static 变量。
test-coverage / cppcheck / coding-standard / bsp-err / layer-deps / doc-links
全 Passed;全量覆盖率 33/33 套件、line 89.3%、func 92.3%、逐文件下降 0。
按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。
2026-10-24 19:43:56 +08:00
wandering1 5224f21622 fix(NET,lftpd): 处置覆盖率照出的三处死路径(改名 + 修 timeout=0 + 激活异常集)
三处都由本轮 P1 新增的主机单测照出,用户逐条裁定后实施。共同性质:都不是改动引入
的回归,而是**从没被执行到的分支**——生产路径上要么没有调用者、要么没走边界,所以
编译器(0 错 0 警)、cppcheck、真机日志对它都无话可说。

1) 缺陷一(选 a:改名,行为零变化)
   lftpd_string_trim 的尾部循环起点落在 strlen(p) 那一格 NUL,isspace(0) 为假 →
   循环体一次都不执行,"尾部空白从未被裁",而名字承诺两头都去。改名
   lftpd_string_ltrim + 删掉那条死循环 + 在 .h/.c 写明"只去前导"。不去尾才是对的:
   RFC 959 以命令终止符 CRLF 界定命令,参数(PASS 口令、含空格的文件名)的尾随
   空格属于参数本体,去掉等于静默改数据;CRLF 已由 lftpd_inet_read_line 截断。
   调用点 lftpd.c 同步改名。全库无遗留真引用(余 6 处旧名均为解释改名历史的散文)。

2) 缺陷二(按推荐修)
   net_select 的 timeout = {0,0} 换算出 timeout_ms = 0,而退出条件写着
   if ((timeout_ms > 0) && (已用时 >= timeout_ms)) → 超时永不生效,退化成死等;
   POSIX 语义是"只查一轮立即返回"。去掉 timeout_ms > 0 这个错误前置即可。就绪
   检查在本判断之前,故有就绪时仍立刻带计数返回;同文件 net_poll_events 的
   timeout >= 0 本来就对,现在两条对齐。风险面:拿它当"非阻塞查一遍"会让 netTask
   回不到主循环,而 netTask 在 IWDG required_mask 里(串行 socket 栈唯一执行者)。

3) 缺陷三(选 ①:按 POSIX 置位)
   exceptfds 是恒为空的死参数:循环开头 p_sock == NULL → continue 先跳过,而
   check_socket_error() 只有"表项为空 / fd 越界"才返回 1,越界的 i 又不在本循环
   范围内 → 调用方按 POSIX 直觉用它收异常,永远什么都收不到,且返回码不报错。
   把异常判定提到 continue 之前:表项不存在的 fd 计入 exceptfds 与返回计数,并更新
   函数注释。选 ① 会改变返回计数语义,故补一条"未请求异常集时不计数"的用例,
   确保只查可读的老调用方不被误伤(TC-NETSEL-012b)。

测试同步翻转:原 TC-NETSEL-008 / TC-NETSEL-012 是"现状桩、改了会响",现在改成正向
契约,并新增 TC-NETSEL-012c(异常位只落到被请求的那个集合);TC-LFTSTR-005 从
"尾部不裁"的现状登记改为正向契约(尾随空格必须保留)。net_select 22/22、
lftpd_io 33/33 全绿。

@build:0 错误 0 警告。实测体积 ER_IROM1 232,512 B(快照 232,536 → **−24 B**)、
Flash 233,704 B(−20 B)、RW_IRAM1 75,272 B(+8 B 是上一提交 net_cfg 那个字节,
本批零新增)、**RW_CCM 57,376 B 不变**;四档百分比 22.2/57.4/87.9/22.3 全不变。
Code 减少 36 B 即删掉的死循环。

覆盖率台账:门禁如实报了一次 lftpd_string.c 已覆盖行 8 → 7(**代码少了一行**,不是
覆盖退化),已 --update-baseline 重登;现 22/22 套件、line=3003/3446(87.1%)、
func=236/259(91.1%)、tested_files=26、untested_files=118。

文档:主机单测覆盖率口径 §9 新增"三处死路径的处置"小节(裁定、改法、影响、钉住
它的用例一一对应),并把两处"等裁定"的登记改为已处置。

自查:coding-standard / test-cppcheck / id-check --strict(无台账外新账)/ doc-links
(35 处 = 存量)全过;八个改动文件与 HEAD 逐项比对 BOM/CR/U+FFFD/行尾无变化
(这几个文件本就不带 BOM,未被动过)。pre-commit --all-files 仍只有三条失败且与
本分支无关:mem-numbers / mem-budget(.map 与 docs/内存管理 受管快照漂移,增量见
上,按并行分支约定不在分支里 --write,等 Lead 合并后统一回填)与 ui-golden
(simulator/tests/ref/*.bin 在 .gitignore 内,新建 worktree 天生没有黄金参考)。
2026-10-23 23:44:48 +08:00
wandering1 b1291f1308 test(NETCFG,CFG): P1①四个主机单测模块,并修 net_cfg 的 WAIT 契约与定长错记
按 docs/测试说明/主机单测覆盖率口径.md §9 的清单吃 P1 第①批「配置与工具」四块,
新增 4 个主机单测模块共 104 条断言,全部 MSVC 编译通过并 PASS:
- test/host/net_cfg/(45 条):出厂默认可用、注册幂等与内容、load 报 -1 仍返回 0
  (RAM 恒有效契约)、跨任务保存交接状态机 IDLE/WAIT/OK/FAIL、MAC 三态优先级、
  IPv4 定长格式化。
- test/host/ftp_cfg/(16 条):[ftp] 段注册(**p_cb 必须为 NULL**,口令下次登录才
  生效)、默认口令判定大小写不敏感、NULL 一律判不等(fail-closed)。
- test/host/prot_set_store/(16 条):blob 返回码分流 1 无档 / 0 成功 / **2 换代
  (只打 INFO)** / -1 损坏;写盘失败必须回置脏标志(否则定值静默丢失)。
- test/host/task_prof/(27 条):无效样本闸门(防 DWT 复位把 max 抬到 2^32)、
  未 enter 就 exit 不记账、首次 5s + 每 2s 节流、环形缓冲与裸发两通道、
  周期→微秒换算随 SystemCoreClock 变。

覆盖率收益(已 --update-baseline 登记):套件 16 → 20 全绿;files_tested 19 → 23;
未触及 125 → 121 个文件(.c 47 → 43、9,657 → 9,228 行);分母 2,893 → 3,163 插桩
行;line=2811/3163(88.9%)、func=225/240(93.8%)。§9 的恒等式同步更新且仍咬合:
43 = 9(P1 剩) + 5(P2) + 29(P3)、9,228 = 2,188 + 2,226 + 4,814。

新单测照出并修掉两处真问题(都在 net_cfg,不是测试写错):
1. 实现违反自己的文档契约:net_cfg.h 明写"属主任务写盘中显示 WAIT",但
   net_cfg_save_run 为了不丢新请求会先清 pending 再写盘 → 写盘那几百毫秒里 UI
   保存行退回上一轮的 OK/IDLE(第一次保存按 ENTER 后屏幕上还是 ENTER),用户只
   会再按一次。补 s_save_busy 标志,TC-NETCFG-028/026/030 钉死(含失败路径必须
   清 busy,否则 UI 永久卡 WAIT)。
2. "定长 11 字符"是错的:%03u.%03u.%03u.%03u = 4 段×3 位 + 3 个点 = **15**。这个数
   错了 4 处(net_cfg.h、net_cfg.c、网络配置说明.md 两处)——是我照抄它才撞上
   FAIL,错在文档不是代码。已全部更正,并确认 len ≥ 16 的要求与 UI_BUF_STRLEN=32
   本来就没问题(无截断风险)。

桩的口径(沿用既有模块,不另起一套):真实头优先——cfg_file.h / cfg_file_store.h /
console_log.h / prot_set.h 全用真头(接口一漂移就编译期报错),只有 main.h 必须桩
(真实 main.h 拉整棵 HAL 头树,PC 上编不动;桩里字段名与位掩码宏名与 CMSIS 一致)。
prot_set_store 连真实 prot_set.c 一起编:blob 的编解码与脏标志在它里面,只有拿真
serialize 产出的 blob 才能造出"换代"与"损坏"两种真实形态,手搓等于测自己的桩。
配置引擎本身(解析/写盘/沉降)归 test/host/cfg_file 的 103 条,本批不重复测。

编号门禁:按 2026-10-21 批 3 的 B 层口径补建 4 族 TC-HOST-*(NETCFG 游标 056 /
FTPCFG 014 / PROTSTOR 016 / TASKPROF 020,代码即属主登记处)。这一步不是走形式:
建族之前文档里引用 TC-NETCFG-028 被判「台账之外新账」直接卡提交,建族后才通过;
编号规范 §8.4 变更记录加一行说明族数 9 → 13。id_registry.json 只做增量插入(+88 行,
无重排)。

自查(提交时 pre-commit run --all-files,**仅 3 条失败且都与本分支改动无关**):
- mem-numbers / mem-budget:本 worktree 因需验证代码改动而 @build 过,新生成的 .map
  与 docs/内存管理 受管快照漂移。实测增量 ER_IROM1 +12 B、RW_IRAM1 +8 B、Flash
  +16 B、**CCM 0**,水位百分比全不变(改动 = 1 个静态字节 + 几条指令)。按并行分支
  约定**不在分支里 --write**,台账由 Lead 合并后统一回填。
- ui-golden:simulator/tests/ref/*.bin 写在 .gitignore 里(黄金参考是本地生成物),
  新建的 worktree 天生没有,报的是"缺黄金参考"而非像素差异;本分支零 UI 文件改动。
- 其余全部 Passed,含 @build 0 错误 0 警告、test-cppcheck(它抓到过我一条 p!=NULL
  后又解引用的写法,已改为断言+显式守卫)、coding-standard、doc-links(35 处=存量)、
  id-check --strict、mojibake 与 BOM/CR/U+FFFD/行尾逐项卫生。
- 自审修掉自己写的两条恒真断言(形如 x==0?1:1 的假绿)与一个重复编号标签。
- 4 个新模块**尚未挂 pre-commit 钩子**:.pre-commit-config.yaml 是单写文件,任务工作
  树内不改;覆盖率脚本按 test/host/**/run_unit_test.py 自动发现,已实跑 20/20。
- 未跑 @test:改动仅 1 个静态字节 + UI 保存行的显示时序,无硬件语义变化。
2026-10-23 23:44:47 +08:00
wandering1 b22779d244 docs(存储): 补上 N-15 页缓冲可合并性论证,判定三块不可合并
报告 N-15 一直写"与 R-3 同批评估",但评估从来没写过——nand_ftl.c:300
的注释直接引用了这份不存在的"报告 R-3"。本次补齐:

- 6,216 B = RW 16 + ZI 6,200,三块 2 KB 页缓冲 6,144 B 占 98.8%
- 不可合并:dhara_map_write 先 auto_gc(map.c:327)再落数据,GC 搬页
  与"待写的 s_cache_buf""攒了 16 页的 checkpoint 元数据 s_page_buf"
  三方同时存活,任两块合并都是静默错数据而非报错
- s_page_buf 也不能缩:元数据跨 0..1,999 偏移,编程的是整页
- 唯一可动的是 s_copy_buf:分块 512 B(-1,536 B,4 次 CPU 轮询)或
  内部搬页 copy-back(-2,048 B,须实证"奇偶属性相同"限制)
- 收益落点 RW_IRAM1(余 55,808 B),红线在 CCM(余 7,904 B)→ 判定不做

顺带订正 nand_ftl.c 两处注释:悬空引用改指本节;s_page_buf 原文写
"仅装 132 B 走轮询"不成立,实际按整页 2,048 B 编程、同样走 DMA。

@build 0 错 0 警告,镜像逐项不变(232,536 / 132,688 / 232,824)。
2026-10-23 12:03:08 +08:00
wandering1 2575177e81 refactor(gate): bsp-err 四项检查改用自描述标签,废掉 R1~R4 这组撞车代号
纯改名,检查行为一个字没动:规则逻辑、阈值、豁免形状全部原样。

改名原因(用户一句「R4 到底是什么」问出来的):R1~R4 是我在 check_bsp_err.py 注释里
自造的序号,而这个裸形在本仓另有含义——R1~R8 已在 scripts/id_registry.json 登记为
"专项风险"的文档内编号(保护规划与看门狗的风险表都在用),带连字符的那一族又是审查
报告的可靠性 / 内存优化项编号(编号规范 §1 的"连字符承载语义"一行正是这么写的)。
**同一个裸形四种意思**。最刺人的一点:编号门禁**不会报**这种撞法,因为编号规范 §4.3
已把这些裸形声明为"同号不同义"、走 collides_with 白名单,校验 4 不计事故——同一天里
我起的另一组自造号被当场拦下、这组代号却一路绿灯,差别不在我做对了什么,而在白名单
挡的是"事故"、不是"可读性"。

新名同时是基线键的第二字段(格式 文件|标签|名字):
· err-type        错误通道不外泄(对外接口不拿 HAL_StatusTypeDef / bool 当成败)
· timeout-name    超时宏不取负值
· return-doc      公开函数必须写「返回值:」
· consume-result  返回值不得裸丢弃(须接住或显式 (void))
老名对照:R1=err-type、R2=timeout-name、R3=return-doc、R4=consume-result。

同步面:脚本里的规则码与 38 条基线键的字段**一起换**(中途不跑 --prune,否则改名会被
读成"38 条全还清 + 38 条新账"),换完 --emit-baseline 复核仍是 38/38/0、RC=0。现状类
文本全部换新名:AGENTS.md、pre-commit使用说明.md §2.27(新增"检查项叫什么"一行含老新
对照与撞号说明)、bsp_err.h 边界纪律、报告 §0.4 / §9 / 附录 A,以及代码注释里的 5 处
(gd5f2gq5ue.c/.h、nand_ftl.h、ui_menu_tab.h)。已提交的变更记录行里的老名按"冻结历史
不回改"处理,以 §2.27 对照行为准。ui_menu_tab.h 带 BOM,Edit 又剥掉、已按原样补回。

写说明文字时又被编号门禁拦了一次:我为了举例"老名撞车",在那段话里写了两个编号字面,
被判"未紧邻文档名 / 未登记前缀"共 3 类新账——连批评自造编号的段落本身都在自造编号。
改法是不写号、只描述形态并指向权威条目。

验证:@build **0 错 0 警告**,Program Size 与上一笔**逐字段相同**(Code 213,676、RO
18,860、RW 1,188、ZI 131,500)→ 证明确为注释与文案,故不跑 @test(无指令变化);
pre-commit run 全绿 **17 项 Passed**;check_doc_links --all --sections-strict 147
  篇 0
问题(中途我自己写的 §4.3 没紧邻文档名,被判成本文自引用,已补全路径);9 个文件字节
卫生 0 异常(UI 头 BOM 仍在、无 CR、无 U+FFFD、均有尾换行)。
2026-10-23 09:36:01 +08:00