ae09c5737a
任务规划目录从 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。
255 lines
11 KiB
C
255 lines
11 KiB
C
/*
|
||
* 模块名称: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,
|
||
};
|