Files
STM32F4-Base/docs/测试说明/模拟器差距清单.md
T
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

13 KiB
Raw Blame History

模拟器差距清单(PC 模拟器 vs 真机)

用途:把散落在各子系统文档里的"模拟器无法建模 X"汇成单一事实源,回答一个关键问题—— 模拟器回归通过 ≠ 真机通过,那么差在哪?差多少?哪些必须真机补测? 性质:登记册(living document)。新增模拟器后端差异时必须在此登记一行,不得只写在别处。 上游依据:docs/网络/Net层阻塞模型架构设计.md §10(net 层 pending 化现状)。 相关:UI设计/LVGL模拟器部署与使用说明.md(部署与使用)、simulator/README.md(目录与架构)、 测试说明/测试开发标准流程.md(L3 门禁在测试分层中的位置)。


0. 一句话结论

模拟器覆盖的是 「纯逻辑 + 平台抽象之上」的那一层:UI 场景/渲染/导航、配置引擎、日志记录格式、 net_* BSD API 之上的协议状态机。它不覆盖「硬件时序 / 中断 / 真实复位 / CH395F 与 NAND 芯片行为」。 因此 L3(模拟器回归)与 L1/L2(板端套件 / @test 闭环)互补而非替代——见 §3 的"什么时候必须回真机"。


1. 模拟器覆盖范围(基线)

覆盖 说明
UI 场景层 Drivers/BSP/UI/ui_menu.c 等同一份源码,MCU 与 PC 共用(simulator/README.md §2.3)
渲染链路 LVGL I1 渲染 → SDL 窗口(MCU 端为帧缓冲 → RemoDispBus)
配置文件引擎 App/cfg/cfg_file.c 共享核心 + cfg_file_store_sim.c(宿主文件 sim_config.ini)
net_* BSD API 之上的协议 ui_net_sink.c(RemoDispBus 服务端,TCP 7003)与 net_socket_sim.c(Winsock 后端)
日志记录格式 sys_log 定长记录编解码 + 记录删除 + 七类记录(run / selftest / accid / alarm / start / oper / yk 各一份宿主文件,道F task-10;口径 = App/log/README.md「记录类型 = 日志 ID」)。sim_syslog.c 写/删宿主文件;删除与投递的时序与真机不同,见 §2.2

不覆盖:CH395F 真实网络栈、GD5F NAND、SD2506 RTC、TPAFE5160 ADC、RS-485、IWDG、中断/时序相关行为。


2. 差距清单

列含义:模拟器行为 / 真机行为 描述差异本身;影响 说明它会让什么 bug 逃过模拟器回归; 回归补测 给出"模拟器过 ≠ 真机过"时应补的真机手段(@test / 板端套件 / 目检)。

2.1 网络(net_socket_sim.c vs net_socket.c)

差距 模拟器行为 真机行为 影响 回归补测
无 pending 机制 net_accept/net_send 直接失败或阻塞于宿主 socket;无"挂起队列"概念 异步 TCP 状态机,accept/send 可处于 pending,由 net_poll 推进 accept 轮询计时失真、lftpd 控制通道 errno 分类、timeSync errno 分级等在模拟器上无法复现 @test + FTP 套件;ACCEPT 决定不 pending 化(2026-10-15),不再专门建模
代次号恒为 0 net_get_generation() 恒返 0(无"网络重建"概念) 每次 net_stack_bring_up / 重配置递增,各 TCP 服务据此重建监听 "网络重建后服务是否重建监听" 的逻辑在模拟器上恒走同一分支 @test 网络重配置用例(UI 改 IP 免重启生效)
消息泵缺失:net_recv_to 缺失(已消除:sim 以 GetTickCount64 + Sleep(1) 轮询兑现预算,含 -2 返回码与 timeout_ms=0 拒 INVAL);net_process_messages 仍缺失 net_recv_to 已实现(包装层模拟);消息泵未实现(MCU 专属:CH395F 消息队列驱动) 由 netMsgQueue 串行化 SPI 事务、支持毫秒级超时接收 依赖"消息泵"的上层逻辑在模拟器上不可编译/不可运行;带超时接收的外部语义已可双端共测,但预算兑现精度为 Sleep(1) 粒度 板端 CH395F / NET 套件
Socket 失效语义不同 以 in_use 判活(net_socket_sim.c 设计要点 4) 监听 Socket 随客户端一同失效,由 net 层 auto_relisten + 对账形态③④自动重建(无手动 net_relisten) "客户端断线 → 监听失效 → 重连" 的黏滞状态在模拟器上不出现 check_remo_sink.py 覆盖重连;真机 @test
单连接 backlog NET_SIM_BACKLOG = 1 CH395F 多 socket 并发 多客户端并发场景(FTP + 推流 + timeSync 同时)不可在模拟器验证 板端 NET 套件 + 台架并发场景
非阻塞语义模拟 创建即 FIONBIO(Winsock 句柄恒非阻塞,UI 主循环安全底线);阻塞/非阻塞按控制块 blocking 属性在包装层模拟(net_recv 无限轮询等待 / net_recv_nb / NET_MSG_DONTWAIT 立即 WOULDBLOCK,net_setblocking 双端同名) 中断驱动 + 状态机 + 挂起 recv(等待睡任务通知,netTask 零占用) 外部语义对齐(登记以说明"为何模拟器能测非阻塞路径与显式预算路径");内部挂起模型(无 pending 机制)仍不建模 ——

2.2 日志与配置

差距 模拟器行为 真机行为 影响 回归补测
控制台单写者无对应实现 simulator/dbg_log.h 把 DBG_* 映射到 printf;不编译 console_log.c 单写者环形缓冲 + defaultTask 唯一发送 console_log 的环形缓冲溢出/丢弃计数逻辑模拟器测不到;共享代码若误调 console_log.h 声明,模拟器链接失败(已被此约束捕获) @test + 板端串口观测;docs/子系统设计/控制台单写者日志设计说明.md §4.1
mtime 粒度不同 宿主文件 mtime(秒级) FAT 打包时间(fdate<<16|ftime,分钟级) cfg_file_poll 的 (size, mtime) 变更令牌在两端触发时机不同;分钟级粒度下的"同分钟内两次改动"在真机可能被判为无变化 板端 STORAGE 套件;配置文件设计说明.md §变更检测
无应用回调(空操作) 模拟器不注册段回调,cfg_file_poll 重载后回调为空 各段回调真正执行(如网络重配置) "配置变更 → 触发回调 → 副作用"链路在模拟器上只验证到写盘,回调逻辑不覆盖 @test(配置保存 → UI 生效)
「清除记录」两端时序不同 syslog_request_clear() 在调用内同步删除宿主文件(sim_syslog.c 单线程,没有卷锁、没有队列),返回后状态立刻是 OK/FAIL,SYSLOG_CLEAR_WAIT 永不出现 非写者上下文只置标志,真正的 f_unlink 排在 defaultTask 的 syslog_drain() 尾部(卷锁 try:拿不到锁就顺延,见 App/log/sys_log_mcu.c) 「按了按钮 → 下一拍才见结果」这条异步链、卷占用顺延、以及删除与写者重建的交错,在模拟器上结构上不存在;UI 页面若把 WAIT 当失败,模拟器上看不出来 主机单测 test/host/sys_log_mcu/(TC-SYSLOG-110..122:请求侧零盘触点 / 落地只在 drain / 卷占用顺延 / 幂等 / 不误删;TC-SYSLOG-133/133b:清除成功投一条操作记录、清 oper 自己时不投)+ 台架装置维护页目检
两条投递口在宿主端合一 syslog_post_quiet() = syslog_post() 的别名(直写宿主文件,没有队列/待发数组),syslog_post_dropped() 恒返 0 两个口分开且判据不同:quiet 的调用链上不许内联日志行缓冲(protectTask / iec104Task 的静态最坏链硬线),丢弃只累加计数、由 defaultTask 在 syslog_drain() 里按差值补留痕 五类记录(事故 / 告警 / 启动 / 操作 / 遥控)在模拟器上永不丢弃,"队列满丢记录"、"丢了几条"、"投递侧不许打日志"这三件事在模拟器上结构上不可见;UI 若把"投了"当成"落盘了",模拟器看不出来 主机单测 test/host/sys_log_mcu/(TC-SYSLOG-132 / 134 / 135:队列侧与启动前数组侧各自的计数与零留痕)+ scripts/stack_report.py 静态最坏链(真机侧唯一手段)
堆后端不同 cfg_store_alloc = 宿主 malloc(堆余量以 MB 计) FreeRTOS 堆(configTOTAL_HEAP_SIZE 48 KB,实测最低水位 ~4.8 KB) 分配失败路径在模拟器上永不触发——"KB 级分配必然返回 NULL"这类缺陷模拟器照不出来(AGENTS.md 已列为铁律) 内存预算门禁 + 真机 [HEAP] 水位告警(S11)

2.3 硬件专属(模拟器完全无对应)

差距 模拟器行为 真机行为 影响 回归补测
IWDG 硬件行为 不编译看门狗模块(simulator/ 无 task_watchdog.c,也无 IWDG 桩) 独立看门狗,LSI 时钟驱动,超时真复位 真实复位行为、LSI 漂移、RCC_CSR 标志均不可建模;位图/判定逻辑也不在模拟器内跑——改由主机侧单测覆盖 真机 @test(复位原因日志 + 故障注入 V2/V4)+ 看门狗主机侧单测(L0.5,39 断言)
看门狗设计说明.md §4.7 / §7
CH395F 芯片 无(Winsock 代其 BSD API) SPI 寄存器/命令/中断、PHY 协商、真实以太网帧 寄存器时序、命令回复、中断风暴、PHY 100M 全双工强制 板端 CH395F 套件(52 断言点)+ @test
GD5F NAND 无 SPI NAND:BBT 扫描、ECC、坏块管理、dhara FTL 坏块/ECC/写入失败等路径完全不可测 板端 GD5F 套件(76 断言点)+ @test
SD2506 RTC 无 I2C RTC:时间读写、闹钟、温度、电池电压 时间源相关(sys_clock_init 降级路径)在模拟器上不可触发 板端 RTC 套件(18 断言点)+ sys_clock L0.5(已覆盖算术面)
TPAFE5160 ADC 无 SPI ADC + BUSY EXTI 中断 中断驱动采集链路 待功能实现(上游 B1)
RS-485 半双工 无 UART4 + DE 方向控制,DWT 微秒延时 DE 收发切换时序(特有陷阱) 待功能实现(上游 B2)
启动段硬件初始化 只跑纯软件部分 I2C/SPI 外设初始化、BBT 扫描耗时、FatFS 挂载 启动段死机(无看门狗保护窗口)只能在真机复现 真机开机日志判读(项目启动顺序管理.md §6 已落地)

2.4 哪些差异是"有意的对齐"(非缺陷)

登记以免后人误判为差距:

项 说明
lv_conf.h 独立 模拟器 simulator/lv_conf.h 与 MCU Lib/lv_conf.h 相互独立(SDL/主题差异),有意不统一
DBG_* → printf 模拟器 stub 覆盖 MCU 版头(simulator/README.md 文件职责表),有意
NET_NO_OS 剥离 FreeRTOS net_socket_sim.c 构建时定义,剥离 RTOS 依赖,有意
无 SDL_PollEvent LVGL SDL 驱动内部统一轮询,主循环轮询会吞键盘事件(simulator/README.md 已知坑 7)

3. 什么时候必须回真机(决策规则)

模拟器回归(L3)通过后,满足以下任一条件即必须走真机(@test / 板端套件):

  1. 改动触及硬件访问层:Drivers/BSP/ 下任何驱动的寄存器/时序/中断代码
  2. 改动触及 §2.3 硬件专属差距的任一子系统的初始化或运行时行为
  3. 改动触及 §2.2 的 mtime 粒度 / 无应用回调 / 堆后端三条:变更检测令牌、段回调副作用、内存分配失败路径
  4. 涉及网络重建 / 多连接并发(代次号恒为 0、单连接 backlog)
  5. 涉及启动段(osKernelStart 之前)(启动段硬件初始化)
  6. 涉及内存预算:新增任务/缓冲/栈(堆后端不同;模拟器堆不可作依据)

反之,纯 UI 场景/渲染/导航、配置解析算术、日志记录编解码格式的改动,模拟器 + L0.5 可给出足够信心。


4. 登记约定

  • 何时登记:新增/修改 simulator/sim/ 下任何后端、或发现"某逻辑模拟器走了不同分支"时,必须在 §2 加一行。
  • 格式:沿用本表列(差距 / 模拟器行为 / 真机行为 / 影响 / 回归补测)。
  • 联动:若该差距影响 L3 门禁的可信度,须同步在 UI设计/UI_自动化测试流程.md 引用本清单。
  • 收敛:当某差距被后续工作消除时,不删行——改标 删除线 + 注明消除日期与提交,保留"曾经差在哪"的历史。

5. 变更记录

日期 变更
2026-10-13 自愈收敛阶段 1.5 recv 语义主流化:net_socket_sim.c 镜像 net_recv_nb / net_recv_to / net_setblocking,阻塞语义改按控制块 blocking 属性在包装层模拟(Winsock 句柄仍恒 FIONBIO)——N-3 的 net_recv_to 缺失部分已消除(划线留档),N-6 改写为"模式属性 + 包装层模拟";内部挂起模型(N-1)不变
2026-10-02 初版(S13):收录 6 条网络差距(N-1N-6)、4 条日志/配置差距(L-1L-4)、7 条硬件专属差距(H-1~H-7)、4 条"有意对齐"项。证据来源:net_socket_sim.c 文件头设计要点 + 双端导出函数逐一对比(MCU 有而 sim 缺 9 个)+ 各子系统文档既有记载汇总。§3 给出 6 条"必须回真机"决策规则