94b0d79026
本笔含两组收尾工作(同一工作区内交错在 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 字),随该笔入库。
134 lines
8.9 KiB
C
134 lines
8.9 KiB
C
/*
|
||
* 模块名称:Boot Stats(开机账本:上电次数 + 本次开机复位原因)
|
||
* 模块功能:给"运行报告"这一页提供两个**没有现成正源**的量——① 上电次数(掉电保持,
|
||
* 落 SD2506 RTC 用户 SRAM,零 NAND 磨损、零卷锁、零配置段);② 本次开机的
|
||
* 复位原因(RCC_CSR 标志在 Src/main.c 里读后即清,故本模块在开机时把
|
||
* 锁存值翻成枚举**驻留 RAM**,否则第二次读就什么都没有了)。
|
||
* 适用平台:STM32F407ZGTx(仅 MCU 端:依赖 SD2506 I2C 与 RCC_CSR;主机单测用桩 SD2506
|
||
* 与桩 main.h,见 test/host/boot_stats/;模拟器不编译本模块)
|
||
* 作者:道G log-maint-writer
|
||
* 创建日期:2026-10-27
|
||
* 修改记录:
|
||
* v1.0 2026-10-27 道G 创建(task-12「运行报告计数器」的数据面;UI 页面归 task-13 → 2026-10-28 已落地 ui_page_runreport.c)
|
||
*
|
||
* ┌── 四个数的分类台账(页面文案必须逐项挂类别,"不得含糊"是 UI 补齐清单的原文判据)──┐
|
||
* │ 上电次数 = **掉电保持**:本模块,落 RTC 用户 SRAM;口径见下面 BOOT_STATS_SEQ_* 一段 │
|
||
* │ 运行时长 = **本次开机以来**:现成值 `ui_data_info_t.uptime_s`(写侧 │
|
||
* │ App/app/ui_data_feed.c),**本模块不重复提供**——重复就是第二真源。 │
|
||
* │ 用户裁定 2026-10-27:累计时长不做(装置无正常关机流程,任何"累计"都只能 │
|
||
* │ 周期写近似值)。⚠ 连续开机 49.7 天后 tick 回绕归零,所以那一栏必须写 │
|
||
* │ 「本次开机以来」,不能让人读成累计值。 │
|
||
* │ 复位原因 = **本次开机**:本模块 `rst_cause`(跨启动的"上一次结束原因"本卡**不做**, │
|
||
* │ 它需要额外一次落盘写点,且口径未裁,别在页面上口头承诺) │
|
||
* │ 104 统计 = **本次开机以来**(跨重连不清零):计数在 App/task/iec104_serv.c, │
|
||
* │ getter 在 App/task/iec104_serv.h——账本归 104,不并进本模块(分层) │
|
||
* └──────────────────────────────────────────────────────────────────────────────┘
|
||
*
|
||
* ⚠ 「上电次数」这一格**不许写成终身次数**:保持介质是 RTC 芯片的电池域 SRAM,
|
||
* 换电池 / 电池欠压都会把它清掉。可读出来的只有"自 RTC 电池周期起"这一件事,
|
||
* 且必须能把「本计数器首次启用」和「电池域掉过电」分开——见 `seq_state`:
|
||
* UNKNOWN = SRAM 读失败(I2C 超时/芯片故障):既不知道计数,也不知道是否延续;
|
||
* FRESH = 记录无效但 RTC 没报欠压(BLF=0)= **本固件第一次写它**;
|
||
* CONTINUED = 记录有效 = 接着上一轮数;
|
||
* RESTARTED = 记录无效且 BLF=1 = 电池域掉过电,计数是从重开的。
|
||
* 把 FRESH 与 RESTARTED 混成一件事 = 拿"第一次上电"冒充"RTC 掉电"(或反过来);
|
||
* 把 UNKNOWN 当成 FRESH = I2C 故障那天,屏幕上的次数悄悄归 1。
|
||
*/
|
||
|
||
#ifndef BOOT_STATS_H
|
||
#define BOOT_STATS_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#include <stdint.h>
|
||
|
||
/* 复位原因编码。判定优先级(IWDG > WWDG > 软件 > POR > PIN > BOR > 未知)的
|
||
* **唯一出处**是 boot_stats_cause_from_csr():App/app/app_main.c 的开机打印改成调用
|
||
* 它取串,所以这里不再另列一遍顺序,两处也不许各写一条链(两处各写就会漂)。 */
|
||
#define BOOT_STATS_CAUSE_UNKNOWN 0u /* 六个标志都没置位(锁存值为 0 等) */
|
||
#define BOOT_STATS_CAUSE_IWDG 1u /* 独立看门狗:上一轮有任务卡死,自动恢复事件 */
|
||
#define BOOT_STATS_CAUSE_WWDG 2u /* 窗口看门狗 */
|
||
#define BOOT_STATS_CAUSE_SFT 3u /* 软件复位(崩溃钩子兜底的 NVIC_SystemReset) */
|
||
#define BOOT_STATS_CAUSE_POR 4u /* 上电复位 */
|
||
#define BOOT_STATS_CAUSE_PIN 5u /* 外部复位引脚 NRST */
|
||
#define BOOT_STATS_CAUSE_BOR 6u /* 掉电(brown-out) */
|
||
|
||
/* SRAM 记录的可信度状态,见文件头那一段。 */
|
||
#define BOOT_STATS_SEQ_UNKNOWN 0u
|
||
#define BOOT_STATS_SEQ_FRESH 1u
|
||
#define BOOT_STATS_SEQ_CONTINUED 2u
|
||
#define BOOT_STATS_SEQ_RESTARTED 3u
|
||
|
||
typedef struct {
|
||
uint32_t boot_count; /* 上电次数;seq_state == UNKNOWN 时**不可信**(此时为 0) */
|
||
uint8_t rst_cause; /* BOOT_STATS_CAUSE_*:本次开机 */
|
||
uint8_t seq_state; /* BOOT_STATS_SEQ_*:上电次数这一格的可信度 */
|
||
uint8_t write_failed; /* 1 = 本次自增没能写回 SRAM:这次只在 RAM 里数上了,
|
||
* 下次开机读到的还是旧值 ⇒ 账本少 1(只会少不会重) */
|
||
uint8_t rtc_blf; /* RTC 电池欠压标志原值(0/1);读不到时按 0,不参与判定 */
|
||
uint8_t ready; /* 0 = boot_stats_init() 还没跑:整表都是初值,别当数据用 */
|
||
} boot_stats_t;
|
||
|
||
/*
|
||
* 函数功能:把 RCC_CSR 锁存值翻成复位原因编码(优先级链的唯一出处)
|
||
* 入口参数:csr - RCC_CSR 的锁存值(Src/main.c 在 HAL_Init() 后采并清标志)uint32_t
|
||
* 返 回 值:BOOT_STATS_CAUSE_* 之一 uint8_t
|
||
* 限定条件:任意上下文(纯位判定,无副作用、不读外设、不取锁)
|
||
* 函数说明:IWDG/WWDG 排在软件复位之前是刻意的:看门狗复位才是我们要看的自动恢复
|
||
* 事件,而崩溃钩子常以 NVIC_SystemReset 收尾——两类标志同时置位时,报
|
||
* 看门狗更有价值(理由与 App/app/app_main.c 原注释一致,此处只搬家不改编)。
|
||
*/
|
||
uint8_t boot_stats_cause_from_csr(uint32_t csr);
|
||
|
||
/*
|
||
* 函数功能:取复位原因的可读串(纯 ASCII,供开机日志与页面共用)
|
||
* 入口参数:cause - BOOT_STATS_CAUSE_* 之一 uint8_t
|
||
* 返 回 值:静态常量串指针(永不 NULL;未知编码回落 "unknown") const char *
|
||
* 限定条件:任意上下文(只读 .const,不写缓冲、不需要调用方提供空间)
|
||
*/
|
||
const char *boot_stats_cause_str(uint8_t cause);
|
||
|
||
/*
|
||
* 函数功能:开机快照 + 上电次数自增(一次上电调一次,幂等)
|
||
* 入口参数:无 void
|
||
* 返 回 值:无
|
||
* 限定条件:**唯一调用点 = App/app/app_main.c 紧接 sd2506_init 之后**(内核启动前:
|
||
* 单线程、非 ISR、调度器未跑;本函数不调任何创建类内核 API)。之所以不放在
|
||
* defaultTask 的存储 bring-up:保持介质是 RTC 用户 SRAM 而不是文件系统,
|
||
* 不必等 STORAGE_READY,也就把 I2C 彻底挪出了喂狗点与卷锁之外。
|
||
* 本函数做 1 次 I2C 读 + 至多 1 次 I2C 写:I2C1 与 RTC 同总线,严禁再把它
|
||
* 搬进 iec104/adc 任何关键链或 ISR。重复调用直接返回,不重复计数。
|
||
* 函数说明:① 全程**不打日志**:I2C 失败/电池换过都是可观测性事件,不是装置故障;
|
||
* `[ERR]` 在本仓是"设备出故障"的唯一信号通道(@test 的 Forbidden 依赖它),
|
||
* 把可观测性事件塞进去就变成第二个信号源——与"日志让路不计 [ERR]"、
|
||
* 卷锁 fail-open 只投自检记录是同一条口径。状态一律留在结构体里。
|
||
* ② 落盘只写 8 字节到 RTC 用户 SRAM:无 FTL 写放大、不取卷锁、不进 /log;
|
||
* 一次上电一笔 I2C 写,失败**不做周期补偿**(否则"零写放大"方案会被做成
|
||
* 周期 I2C 轮询),后果就是 write_failed 那一条。
|
||
*/
|
||
void boot_stats_init(void);
|
||
|
||
/*
|
||
* 函数功能:读开机账本快照(纯拷字段)
|
||
* 入口参数:p_out - 输出缓冲;NULL 时无害返回 boot_stats_t *
|
||
* 返 回 值:无 void
|
||
* 限定条件:**任意上下文可读**——不取锁、不读时刻、不碰 I2C、不阻塞(照
|
||
* adc_cal_get_stats() / vol_lock_get_stats() 的形态:观测口一旦要去抢
|
||
* 总线或等锁,观测就成了故障的一部分)
|
||
* 函数说明:① 各字段都是 32 位对齐量或单字节,Cortex-M4 上单字/单字节读写原子;
|
||
* 跨字段**不保证同一瞬间**——本表里没有任何"两个字段合起来才成立"的判据,
|
||
* 所以混窗对显示无害。seq_state 与 boot_count 要一起看时,按"是否可信"
|
||
* 读,不要拿差值当账。
|
||
* ② ready == 0 表示 boot_stats_init() 还没跑(开机极早期或 SD2506 未就绪),
|
||
* 此时整表为 0,页面应显示占位而不是 0 次。
|
||
*/
|
||
void boot_stats_get(boot_stats_t *p_out);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* BOOT_STATS_H */
|