Files
STM32F4-Base/Drivers/BSP/UI/ui_page_clear.c
T
wandering1 ae09c5737a docs: 删任务规划两份已完成文档,数据可信内容迁入算法层/硬件平台/登记簿
任务规划目录从 5 个文件收敛到 3 个:只留导航(README)、待办(登记簿)、
暂缓专项(暂缓-RS485)。

删除的两个文件都不再有现行读者:
- UI补齐.md 执行清单 2026-10-28 清空、上板判据 2026-11-06 台架轮已复核;
  叶数口径迁 docs/UI设计/菜单页面设计.md §3.9,三条内容页硬规矩迁
  docs/UI设计/内容页架构.md §7.1,两条未闭环待办迁登记簿 §7/§8。
- 数据可信.md 不是完成记录,而是被约 130 处代码头注释引用的设计约束,
  因此按内容分三处落位后删除,不整篇丢弃。

数据可信内容的新归属(旧节号 → 新家):
- §2/§4 设计口径 → docs/算法/采集与算法层设计说明.md 新增 §13(13 小节),
  §7 前置决策表 → §13.12 已定案的前置决策。
- §3 台阶与波次退出判据 → docs/硬件平台/ADC通道映射说明.md 新增 §9。
- §5 任务卡 → docs/任务规划/登记簿.md §7(拆成变比确认 / 两点标定两行)。

引用改写 34 文件 77 处路径引用 + 14 文件 24 处昵称引用 + 双重节号 35 处,
覆盖代码头注释、测试、生成器与文档;归档区历史路径改纯文本提及。
四个 SVG 生成器文案同步并重跑,产物随脚本同批提交。

同批处理两件事:
- 9 个 UTF-16 LE 证据文件转 UTF-8。end-of-file-fixer 按字节判末尾换行,
  UTF-16 的换行是 0A 00、末字节 00,钩子会追加裸 0x0a 使文件变奇数长度,
  8 个已实际损坏。转码后逐文件比对 HEAD 原文验证正文零丢失。
- 修回 App/cfg/cfg_file.c 被剥掉的 UTF-8 BOM(补 3 字节,未整文件重写)。

验证:/keil-build 0 错误 0 警告(Code=255160);check_doc_links --all
--sections-strict 221 篇全通过、无法判定 0;pre-commit 全量全 Passed。
2026-11-09 16:13:57 +08:00

255 lines
11 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 clear (清除记录内容页)
* 模块功能:装置维护 → 清除记录 叶子(node 27,#P 受密码保护)的内容页——
* 七行清单(运行 / 自检 / 事故 / 保护告警 / 保护启动 / 操作 / 遥控记录),
* 值列 = 该类记录当前条数(0 条显"无记录"),逐行独立清除;
* 行内两段式确认:第一次 ENTER 该行值列变"确认?",第二次 ENTER 才投递,
* 之后该行按清除状态显 WAIT / OK / ERR,直到光标移开才收回;
* 条数只走 ui_w_records 的**同一枚注入 reader**(裸计数入口
* ui_w_records_count_of),删除只发请求(执行侧 = syslog_request_clear),
* 真反馈 = 状态机 + 下一帧条数归零,不是"按了就算成功";
* 只读进入(查看密码)整页不投递;
* 本文件零 App include、零文件系统触点(既不调 MCU 专属的
* syslog_clear_apply(),也绝不自己 f_unlink / 碰卷锁——defaultTask 是
* 记录唯一写者,uiTask 在 IWDG required_mask 内,给它一条最坏数百 ms
* 的存储路径就是拿误复位当代价)
* 为什么不做"一键清全部":七类记录分属不同事件面(运行/自检 vs 事故/告警/启动 vs
* 操作/遥控审计),一次点击清光七类 = 一次误操作毁掉全部审计面;
* 逐行清已完全覆盖判据(删后重建、计数归零、不存在时幂等)
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:DTU 项目组
* 创建日期:2026-10-26
* 修改记录:
* v1.0 2026-10-26 DTU 项目组 创建(道F task-3;判据 docs/UI设计/
* 内容页架构.md §7.1 第 2 条「清除记录…不允许只做没有实际效果的按钮」;
* 删除 API 与请求/落地分离 = task-4,口径真源
* App/log/README.md 与 App/log/sys_log.h)
*/
/* 头文件包含区 */
#include <string.h>
#include "ui_page_clear.h"
#include "ui_content_page.h"
#include "ui_data.h"
#include "ui_tokens.h"
#include "ui_w_records.h"
/* 私有宏定义区 */
#define CL_ROW_N UI_W_REC_LOG_N /* 行数 = 记录源个数(两端映射表长度同源) */
#define CL_NONE 0xFFu /* "无行"哨兵(行号上限 7,绝不撞) */
#define CL_TXT_NONE "无记录" /* 0 条(用字已在子集,与两条记录页同一词) */
#define CL_TXT_CONFIRM "确认?" /* 第一段确认('?' 是 ASCII) */
/* 结果词沿用全 ASCII 一排:WAIT 是正常态(MCU 端删除在 defaultTask 的
* syslog_drain() 尾部落地),模拟器端同步删除永不出现 WAIT —— 两端都要能显示 */
#define CL_TXT_WAIT "WAIT"
#define CL_TXT_OK "OK"
#define CL_TXT_ERR "ERR"
/* 私有函数声明区 */
static const char *cl_name_of(uint16_t a_row);
static uint8_t cl_id_of(uint16_t a_row);
/* 静态变量定义区 */
/* 行名表(Flash);顺序 = UI_W_REC_LOG_* 编号序,与两端映射表同一套下标 */
static const char *const k_cl_name[CL_ROW_N] = {
"运行记录", "自检记录", "事故记录", "保护告警", "保护启动", "操作记录", "遥控记录"
};
static const uint8_t k_cl_id[CL_ROW_N] = {
UI_W_REC_LOG_RUN, UI_W_REC_LOG_SELFTEST, UI_W_REC_LOG_ACCIDENT, UI_W_REC_LOG_ALARM,
UI_W_REC_LOG_START, UI_W_REC_LOG_OPER, UI_W_REC_LOG_YK
};
static uint8_t s_confirm; /* 待确认行(第一次 ENTER 已按下),CL_NONE = 无 */
static uint8_t s_note; /* 已投递、要显示清除结果的行,CL_NONE = 无 */
static uint8_t s_err; /* 投递被拒的行,CL_NONE = 无 */
static uint16_t s_sel_seen; /* 上一次绘制时的选中行(移开光标即收回三个锁存) */
/* 函数定义区 */
/*
* 函数功能:取行名
* 入口参数:row - 行号 uint16_t
* 返回值:名(Flash 字面量;越界回空串,不越表读) const char *
* 限定条件:无
*/
static const char *cl_name_of(uint16_t a_row) {
if (a_row >= CL_ROW_N) {
return "";
}
return k_cl_name[a_row];
}
/*
* 函数功能:取行对应的记录源编号
* 入口参数:row - 行号 uint16_t
* 返回值:UI_W_REC_LOG_*;越界回 0(首号,调用方已在越界时不调本函数) uint8_t
* 限定条件:无
* 函数说明:编号不透明——本页不知道也不该知道"3 号是 alarm.log",映射在两端
* 适配层(App/task/ui_task.c 与 simulator/main.c),加一类记录时
* 本表加一行、两端各加一条映射,屏上自动多一行
*/
static uint8_t cl_id_of(uint16_t a_row) {
if (a_row >= CL_ROW_N) {
return (uint8_t)UI_W_REC_LOG_RUN;
}
return k_cl_id[a_row];
}
/*
* 函数功能:开页复位(框架 init_cb)
* 入口参数:无
* 返回值:无
* 限定条件:框架开页链内(此时本次授予权限已装载)
* 函数说明:三个锁存全部归无——清除状态是**按 id 的全局单值**(syslog 侧
* 上电以来一直是 OK),开页直读会把"昨天清过操作记录"显示成"这次
* 清成功了"(契约 §6.3 第 3 条同一件事:状态是全局的,页面必须锁存)
*/
static void pg_init(void) {
s_confirm = CL_NONE;
s_note = CL_NONE;
s_err = CL_NONE;
s_sel_seen = ui_content_page_selected_row();
}
/*
* 函数功能:逐行取数(框架 refresh;值列按"结果 > 确认 > 条数"优先级)
* 入口参数:a_row / a_name / a_value - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
* 函数说明:已投递那一行的词来自 ui_data_maint_clear_state()(执行侧直通,
* FAIL 也照实显 ERR:原文件一个字节没少,可以再发一次请求);
* 没投递的行每帧现算条数——清 run.log 之后 sys_log 会往**操作记录**
* 补一条留痕,所以"操作记录"那行会从 0 变成 1,这是真的,页面不缓存
* "我以为归零了"(清 oper.log 自己时刻意不补,App/log/README.md 已载)
*/
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
uint8_t id;
uint16_t n;
(void)memset(a_value, 0, UI_BUF_STRLEN);
id = cl_id_of(a_row);
(void)memcpy(a_name, cl_name_of(a_row), strlen(cl_name_of(a_row)) + 1u);
if ((uint8_t)a_row == s_note) {
int st = ui_data_maint_clear_state(id);
if (st == UI_DATA_CLEAR_ST_OK) {
(void)memcpy(a_value, CL_TXT_OK, sizeof(CL_TXT_OK));
} else if (st == UI_DATA_CLEAR_ST_WAIT) {
(void)memcpy(a_value, CL_TXT_WAIT, sizeof(CL_TXT_WAIT));
} else {
(void)memcpy(a_value, CL_TXT_ERR, sizeof(CL_TXT_ERR));
}
return;
}
if ((uint8_t)a_row == s_err) {
(void)memcpy(a_value, CL_TXT_ERR, sizeof(CL_TXT_ERR));
return;
}
if ((uint8_t)a_row == s_confirm) {
(void)memcpy(a_value, CL_TXT_CONFIRM, sizeof(CL_TXT_CONFIRM));
return;
}
n = ui_w_records_count_of(id); /* 现算:不留"我以为归零了"的缓存 */
if (n == 0u) {
(void)memcpy(a_value, CL_TXT_NONE, sizeof(CL_TXT_NONE));
return;
}
(void)lv_snprintf(a_value, UI_BUF_STRLEN, "%u", (unsigned int)n);
}
/*
* 函数功能:自绘(框架 draw_cb)——顺带承担"光标移开就收回锁存"的观测
* 入口参数:a_layer / a_clip - 契约见 ui_content_page.h
* 返回值:无
* 限定条件:框架绘制链内
* 函数说明:为什么在 draw 里看选中行:↑↓ 移选是框架内置的(本页 key_cb 对
* 非 ENTER 键一律交回框架,看不到它们),而每次移选框架都会
* invalidate 并重绘——draw 是唯一必然发生、且总在状态变化之后的观测点。
* 语义 = "用户已经看过程序结果 / 已经放弃这次确认",本页不再有
* "上一次的结果挂在另一行上"的显示
*/
static void pg_draw(lv_layer_t *a_layer, const lv_area_t *a_clip) {
uint16_t sel = ui_content_page_selected_row();
(void)a_layer;
(void)a_clip;
if (sel != s_sel_seen) {
s_sel_seen = sel;
s_confirm = CL_NONE;
s_note = CL_NONE;
s_err = CL_NONE;
}
}
/*
* 函数功能:按键处理(行内两段式确认)
* 入口参数:a_key - LVGL 键值 lv_key_t
* 返回值:true = 本页已消费;false = 交回框架(↑↓ 移选、←/ESC 关页 = 取消) bool
* 限定条件:框架按键链内;描述符 edit_capable=1
* 函数说明:只读进入一律交回框架(本页没有只读可做的动作,也没有保存行可显
* LOCK——清单页没有态标位,加了反而像"可以按");
* 第一次 ENTER 只是把该行标成待确认(不做任何投递),第二次才投;
* 投递被拒(含维护通道未注入)时置 s_err,光标一动就收回
*/
static bool pg_key(lv_key_t a_key) {
uint16_t sel;
if (a_key != LV_KEY_ENTER) {
return false;
}
if (ui_content_page_is_editable() == false) {
return false; /* 只读:清除是修改类动作,一条都不投 */
}
sel = ui_content_page_selected_row();
if (sel >= CL_ROW_N) {
return true; /* 越界防御:不投任何编号 */
}
if ((uint8_t)sel != s_confirm) {
s_confirm = (uint8_t)sel; /* 第一段:只标记,不投递 */
s_note = CL_NONE;
s_err = CL_NONE;
ui_content_page_invalidate();
return true;
}
s_confirm = CL_NONE;
s_err = CL_NONE;
if (ui_data_maint_clear_req(cl_id_of(sel)) == 0) {
s_note = (uint8_t)sel; /* 已受理:接下来按状态机显 WAIT/OK/ERR */
} else {
s_err = (uint8_t)sel;
}
ui_content_page_invalidate();
return true;
}
/*
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=clear 绑定引用)
* 七行 1 页(rows_per_page = UI_LIST_ROWS(8) > 7 ⇒ 不翻页);key_intercept=0:
* 本页没有"编辑到一半"的态要保,← 与 ESC 一律关页 = 取消
*/
const content_page_def_t ui_page_clear_desc = {
.id = "clear",
.title = "清除记录",
.selectable = 1u,
.edit_capable = 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,
.row_count = CL_ROW_N,
.init_cb = pg_init,
.refresh = pg_refresh,
.draw_cb = pg_draw,
.key_cb = pg_key,
};