Files
STM32F4-Base/Drivers/BSP/UI/ui_page_runreport.c
T
wandering1 a98d4e06e8 UI 其余五个实时页接入框架自动失效(按内容变化节拍分档取值)
- 描述符各加一行(机制 task-2 已建,本轮零框架代码改动):dc 300ms(电气量与
  ac 同账);yx 500ms(遥信变位按 1s 内上屏到位,比 300 档少 40% 稳态重绘);
  clock 1000ms(快变项只有墙钟秒尾);device_info / runreport 1000ms 并把新鲜度
  源换到通道二 .auto_seq = ui_data_info_seq(info 与 rt 同拍 200ms 发布)
  ——各页取值账目在该页描述符注释
- docs/UI设计:供给说明 §5.5 改六页全接终态(用户裁定 2026-11-13);内容页架构
  字段表/内存表/变更记录同步;自动化测试流程补 rtlive 覆盖行;页面扩充方案
  追加 74 张现势注(带日期史实行冻结不动)
- .dsh/skills/lvgl-simulator:开关表加 --rtlive 行、黄金参考计数 66→74 同步

验证:模拟器 0错0警;菜单 8 状态 + 内容页 74 张 + rtlive PASS(lead 独立复跑
RC=0,池水位 5368/5448B 均不漂移——timer 开页创建关页销毁、分配归还);
Keil 0错0警且 Code/RO/RW/ZI 与 task-2 基线逐字节一致(只改常量值);BOM 全补回。
2026-11-13 14:51:49 +08:00

287 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:UI Page RunReport(运行报告内容页)
* 模块功能:运行报告叶子节点(node 41,父节点「记录查询」)的内容页数据——13 行只读统计,
* 分 2 页(UI_LIST_ROWS=8 行/页 → 8 + 5);数据全部经 ui_data 装置信息帧下发
* (写侧 App/app/ui_data_feed.c 搬 boot_stats_get() 与 iec104_serv_get_stats(),
* 本文件零 App include,分层方向由 layer-deps 门禁守护)
* 口径纪律(判据 = 本页四个数逐个挂范围标签 + App/util/boot_stats.h 四数台账):
* ① **四个数逐个挂范围标签**,不许含糊——上电次数=自 RTC 电池周期(换电池即清零,
* **禁写终身次数**,固件里压根没有跨开机计数器)、复位原因/运行时长/104 统计
* =本次开机以来(104 四枚跨重连不清零,但**断电即归零**);
* ② boot_ready==0(账本未初始化)或 seq_state==0(UNKNOWN)时上电次数显示 "--"
* 而不是 0——把"没有数"洗成"0 次"是最坏的一种误导;
* ③ write_failed==1 单独占一行("未写回"):后果是账本**少计**,绝不会重复计数
* (掉电保持数 +1 后先写 SRAM 再发布,写失败仍按内存值显示);
* ④ 运行时长是 tick 回绕口径(32 位 ms ≈ 49.7 天、uptime_s ≈ 136 年),
* 显示按"天 + 时时:分分"退化(与装置信息页同一 ASCII 记法,见 di_fmt_uptime),
* **不做跨开机累计**(用户裁定 2026-10-27)
* 几何余量(2026-10-28 黄金目检):「自电池周期」5 字 x12px 从 x=101 起、右缘 161,**余量 0**——
* 值列再加字或换字号必切边;要扩文案必须先减字或改标签列宽度,别直接加字。
* 台架复核已完成(2026-11-06,Issue #53 台架轮、实机 boot 187–193,证据贴 Issue #53 评论):
* 上电一次 boot_count +1 真机复核——链条 187→193 六连击无跳号,SRAM 保持 +
* 写回正常(本机无市电开关,用 ST-LINK 系统复位代理完整上电,电池域存储语义
* 全覆盖);IWDG 复位后 rst_cause 落「独立看门狗」经 SWD 缩窗真溢出复核;
* 主站接入一次 conn_up +1 经静默窗 +1 精确归因复核;拔 RTC 电池落 RESTARTED
* 一项台架不可验(用户裁定 2026-11-05,销项不挂账)。模拟器无 boot_stats
* (依赖 SD2506 I2C 与 RCC_CSR)与 104 协议栈,黄金参考拍的是固定假数据
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:DTU 项目组
* 创建日期:2026-10-28
* 修改记录:
* v1.0 2026-10-28 DTU 项目组 创建(形态照 ui_page_device_info.c 的行表 + 值格式化,
* 分页与选中带照 ui_page_deadband.c 的列表页几何)
*/
/* 头文件包含区 */
#include <string.h>
#include "ui_page_runreport.h"
#include "ui_data.h"
#include "ui_tokens.h"
/* 私有宏定义区 */
#define RR_ROW_N 13u /* 行数(2 页:8 + 5) */
#define RR_INVALID "--"
#define RR_SEC_PER_DAY 86400u
/* 复位原因编码(与 App/util/boot_stats.h 的 BOOT_STATS_CAUSE_* 一一对应;
* 这里刻意写成数字常量并在注释里钉死映射——BSP 不得 include App 头,
* 编码变更由 test/host/ui_data 的搬运断言与 App/log/README 口径联动发现 */
#define RR_CAUSE_UNKNOWN 0u
#define RR_CAUSE_IWDG 1u
#define RR_CAUSE_WWDG 2u
#define RR_CAUSE_SFT 3u
#define RR_CAUSE_POR 4u
#define RR_CAUSE_PIN 5u
#define RR_CAUSE_BOR 6u
/* 上电次数可信度编码(BOOT_STATS_SEQ_*) */
#define RR_SEQ_UNKNOWN 0u
#define RR_SEQ_FRESH 1u
#define RR_SEQ_CONTINUED 2u
#define RR_SEQ_RESTARTED 3u
/* 静态变量定义区 */
/* 行名表(Flash;四数各自挂范围行,口径不许含糊) */
static const char *const s_names[RR_ROW_N] = {
"上电次数", "计数范围", "计数可信", "计数写回", "复位原因",
"复位范围", "运行时长", "时长范围", "主站接入",
"发送帧数", "发送失败", "接收字节", "统计范围",
};
/* 帧缓存:发布序号变化才整帧拷一次(同装置信息页口径) */
static ui_data_info_t s_info;
static uint32_t s_seq;
/* 函数定义区 */
/*
* 函数功能:取用最新装置信息帧(只在发布序号变化时整帧拷)
* 入口参数:无
* 返 回 值:1 - 有可用帧;0 - 首帧未到或本帧无效 uint8_t
* 限定条件:uiTask 绘制上下文
*/
static uint8_t rr_frame_ready(void) {
uint32_t now = ui_data_info_seq();
if (now != s_seq) {
s_seq = now;
if (ui_data_info(&s_info) != 0) {
(void)memset(&s_info, 0, sizeof(s_info));
return 0u;
}
}
return (s_info.valid != 0u) ? 1u : 0u;
}
/*
* 函数功能:开页复位缓存(强制下一次绘制重取最新帧,避免停在上一帧的计数上)
* 入口参数:无
* 返 回 值:无 void
* 限定条件:开页时由框架调用
*/
static void rr_init(void) {
s_seq = 0u;
(void)memset(&s_info, 0, sizeof(s_info));
}
/*
* 函数功能:复位原因编码 → 屏上中文(5 字以内,避免挤压值列)
* 入口参数:cause - 复位原因编码(RR_CAUSE_*) uint8_t
* 返 回 值:中文标签(Flash 字面量,恒非 NULL) const char *
* 限定条件:编码越界(含 SD2506 诊断混进来的位)一律落"未知",不猜
*/
static const char *rr_cause_str(uint8_t cause) {
const char *p;
switch (cause) {
case RR_CAUSE_IWDG: p = "独立看门狗"; break;
case RR_CAUSE_WWDG: p = "窗口看门狗"; break;
case RR_CAUSE_SFT: p = "软件复位"; break;
case RR_CAUSE_POR: p = "上电复位"; break;
case RR_CAUSE_PIN: p = "引脚复位"; break;
case RR_CAUSE_BOR: p = "掉电复位"; break;
default: p = "未知"; break;
}
return p;
}
/*
* 函数功能:上电次数可信度编码 → 屏上中文
* 入口参数:state - 可信度编码(RR_SEQ_*) uint8_t
* 返 回 值:中文标签(Flash 字面量,恒非 NULL) const char *
* 限定条件:编码越界一律"未知"
* 函数说明:RESTARTED 的屏上话是"掉电重开"——它指的是 RTC 电池域掉过电
* (计数值本身还在,但跨不掉电那段已经不可信),不是"整机重启过"
*/
static const char *rr_seq_str(uint8_t state) {
const char *p;
switch (state) {
case RR_SEQ_FRESH: p = "首次启用"; break;
case RR_SEQ_CONTINUED: p = "延续"; break;
case RR_SEQ_RESTARTED: p = "掉电重开"; break;
default: p = "未知"; break;
}
return p;
}
/*
* 函数功能:格式化运行时长(本次开机以来,秒)
* 入口参数:sec - 秒数 uint32_t
* a_buf - 输出缓冲 char *
* a_cap - 缓冲容量 uint32_t
* 返 回 值:无 void
* 限定条件:a_buf/a_cap 有效
* 函数说明:>= 1 天走 "%ud%02u:%02u"(ASCII 小写 d,避免为"天"字扩字模);
* < 1 天走 "%02u:%02u:%02u"。与装置信息页同一记法,两页不许各写一套
*/
static void rr_fmt_uptime(uint32_t sec, char *a_buf, uint32_t a_cap) {
uint32_t day = sec / RR_SEC_PER_DAY;
uint32_t rem = sec % RR_SEC_PER_DAY;
if (day > 0u) {
(void)lv_snprintf(a_buf, a_cap, "%luud%02lu:%02lu",
(unsigned long)day, (unsigned long)(rem / 3600u),
(unsigned long)((rem % 3600u) / 60u));
} else {
(void)lv_snprintf(a_buf, a_cap, "%02lu:%02lu:%02lu",
(unsigned long)(rem / 3600u),
(unsigned long)((rem % 3600u) / 60u),
(unsigned long)(rem % 60u));
}
}
/*
* 函数功能:总行数(固定 12 行,分 2 页)
* 入口参数:无
* 返 回 值:行数 uint16_t
* 限定条件:uiTask 绘制上下文(经框架回调)
*/
static uint16_t rr_count(void) {
return RR_ROW_N;
}
/*
* 函数功能:逐行取名/值(13 行,见 s_names 表)
* 入口参数:row - 行下标 uint16_t
* name - 名输出缓冲 char *
* value - 值输出缓冲 char *
* 返 回 值:无 void
* 限定条件:uiTask 绘制上下文;首帧未到时数值行一律 "--"(不显示 0 冒充有数)
*/
static void rr_refresh(uint16_t row, char *name, char *value) {
const uint32_t cap = (uint32_t)UI_BUF_STRLEN;
uint8_t ready;
if (row >= RR_ROW_N) {
value[0] = (char)0;
return;
}
(void)lv_snprintf(name, cap, "%s", s_names[row]);
ready = rr_frame_ready();
if (ready == 0u) {
(void)lv_snprintf(value, cap, "%s", RR_INVALID);
return;
}
switch (row) {
case 0u: /* 上电次数 */
if ((s_info.boot_ready == 0u) || (s_info.boot_seq_state == RR_SEQ_UNKNOWN)) {
(void)lv_snprintf(value, cap, "%s", RR_INVALID);
} else {
(void)lv_snprintf(value, cap, "%lu", (unsigned long)s_info.boot_count);
}
break;
case 1u: /* 计数范围 */
(void)lv_snprintf(value, cap, "%s", (s_info.boot_ready == 0u) ? "未知" : "自电池周期");
break;
case 2u: /* 计数可信(四态;未初始化落"未知") */
(void)lv_snprintf(value, cap, "%s", rr_seq_str(s_info.boot_seq_state));
break;
case 3u: /* 计数写回 */
(void)lv_snprintf(value, cap, "%s",
(s_info.boot_write_failed != 0u) ? "未写回" : "正常");
break;
case 4u: /* 复位原因 */
(void)lv_snprintf(value, cap, "%s", rr_cause_str(s_info.rst_cause));
break;
case 5u: /* 复位范围 */
(void)lv_snprintf(value, cap, "%s", "本次开机");
break;
case 6u: /* 运行时长 */
rr_fmt_uptime(s_info.uptime_s, value, cap);
break;
case 7u: /* 时长范围 */
(void)lv_snprintf(value, cap, "%s", "本次开机");
break;
case 8u:
(void)lv_snprintf(value, cap, "%lu", (unsigned long)s_info.iec104_conn_up);
break;
case 9u:
(void)lv_snprintf(value, cap, "%lu", (unsigned long)s_info.iec104_apdu_tx);
break;
case 10u:
(void)lv_snprintf(value, cap, "%lu", (unsigned long)s_info.iec104_tx_fail);
break;
case 11u:
(void)lv_snprintf(value, cap, "%lu", (unsigned long)s_info.iec104_rx_bytes);
break;
default: /* 统计范围 */
(void)lv_snprintf(value, cap, "%s", "本次开机");
break;
}
}
/*
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=runreport 绑定引用):
* 只读列表页——selectable=1 走真分页(13 行 / UI_LIST_ROWS 行 = 2 页,页脚真实页码);
* band_h=UI_ROW_BAND_H(选中行黑字须配白带);无写入口,故无保存态/无三态
*/
const content_page_def_t ui_page_runreport_desc = {
.id = "runreport",
.title = "运行报告",
.selectable = 1u,
.top_y = UI_LIST_TOP,
.pitch = UI_ROW_PITCH,
.band_h = UI_ROW_BAND_H,
.text_dy = 0,
.left_x = UI_LIST_LEFT_X,
.right_x = UI_LIST_RIGHT_X,
.rows_per_page = UI_LIST_ROWS,
.count_cb = rr_count,
.init_cb = rr_init,
.refresh = rr_refresh,
/* 自动失效轮询——通道二(.auto_seq = ui_data_info_seq;机制见 ui_content_page.h
* 的 auto_ms/auto_seq 注,口径见供给说明 §5.5):本页是上电计数、复位原因、
* 104 观测计数与记录条数的汇总,活跃链路(104 轮询、记录追加)下计数秒级
* 走动,1000ms 档既让读数"看起来在活",又不在稳态多刷(seq 恒随 info 帧
* 前进,周期即重绘频率;info 与 rt 同拍 200ms,见供给说明 §5.2) */
.auto_ms = 1000u,
.auto_seq = ui_data_info_seq,
};