fix(ui/wave): 钉死并修复回放页恒空根因——读侧用反 seg_path 返回契约

FR 埋点构建上板判读(本 boot 串口实录,临时诊断构建 = 上一提交):

    [UI_TASK] ui wave scan #1 done cnt=7 np=0 tt=0 ac=407
    [ERR][UI_TASK] ui wave head #1 path fail idx=6

- 目录枚举一直是好的(cnt=7 = 盘上七段全数入表,锁也拿到,tt=0):
  10-31「唯独 ui_task_wave_scan 拿不到目录条目」的定性被推翻。
- 真正坏的在 load_head / seg_win 的拼路径判错:契约
  (App/algo/wave_store.h:458)是「成功 = 写入字符数(本形态恒 16),
  失败才是负码」,读侧自建成功能起写成 != 0 判失败——每次成功都被
  判死,f_open 永远走不到,盘上有段也恒显「无录波段」。写侧
  default_task.c 同函数用 <= 0,一直是对的,所以「FTP 六段全见、
  写侧枚举正常、唯独回放恒空」的怪象全线得解。
- 模拟器走桩、不编译本文件读侧,黄金参考图与主机单测因此从未覆盖
  这条链,此病从建成存活至今(板端首张真机波形图见 PR #67)。
- 两处口径改 <= 0(与写侧一致);表列着的段 f_open 失败补带 FR 码
  的永久留痕——该失败态页面无视觉面,10-31 误定性的教训就是静默。
- 临时诊断(#N 序号 / try 闸日志 / 无条件 done 行)收敛删除,留
  最小永久集:scan opendir/readdir FR、零条目 INFO、head/win 开档
  失败 FR。try 闸恢复静默(看门狗设计说明 §4.3 口径)。
- /keil-build 0 错误 0 警告:Code 247,284 B(较临时诊断构建
  −368 B,较修复前 +236 B),RAM 不变;台账 / 同码凭证 / 数字回填
  同批刷新。

Refs #38
This commit is contained in:
2026-11-02 18:07:52 +08:00
parent 3e01e9d98e
commit d6253aa974
5 changed files with 58 additions and 58 deletions
+45 -45
View File
@@ -37,8 +37,19 @@
* ui_task_wave_scan 失败不再无条件钉 s_wave_scanned——
* opendir/readdir 失败本轮返 -1(卷忙语义),下一次进页/
* 按键自然重试;失败分支与「FR_OK 零条目」补带 FR 码留痕
* (DBG_TAG [UIWAVE])供板级远程定性根因。FR_NO_PATH
* (从未录过波)归 0 段并钉表的设计原口径不动。
* (正文「ui wave scan」前缀,tag 沿用本文件 [UI_TASK])
* 供板级远程定性根因。FR_NO_PATH(从未录过波)归 0 段
* 并钉表的设计原口径不动。
* v1.7 2026-11-02 DTU 项目组 根因修复:wave_store_seg_path 契约是「成功 =
* 写入字符数(恒 16),失败为负码」,读侧两处(load_head
* / seg_win)自建成起把返回值 != 0 当失败——每次成功拼
* 路径都被判死,回放页开档永远走不到 f_open,盘上有段
* 也恒显「无录波段」(本 boot FR 埋点钉死:scan done
* cnt=7 + head path fail;写侧同函数用 <= 0 一直是对的;
* 模拟器桩不走本文件,故黄金图从未暴露此病)。口径改
* <= 0;开档失败补带 FR 码的永久留痕(该失败态页面无
* 视觉面);第一幕临时诊断(序号/try 闸日志/无条件
* done 行)收敛删除。
*/
#include "ui_task.h"
@@ -423,8 +434,6 @@ static const ui_soe_reader_t s_soe_reader = { ui_task_soe_count, ui_task_soe_get
static uint8_t s_wave_idx[WAVE_ST_SEG_MAX]; /* 盘上现存段号升序表 */
static uint8_t s_wave_cnt; /* 表内条数 */
static uint8_t s_wave_scanned; /* 表是否有效(manual_req 后作废) */
static uint8_t s_wave_dbg_seq; /* 诊断留痕(第一幕第二组):本 boot
* 扫描尝试序号,供各行对齐 */
static uint8_t s_wave_cached_idx; /* 段头缓存属于哪一段 */
static uint8_t s_wave_cache_ok; /* 该段已验完整(头+数据 CRC) */
static wave_seg_meta_t s_wave_meta; /* 已验段的头(172B,唯一驻留) */
@@ -461,15 +470,10 @@ static int ui_task_wave_scan(void) {
uint8_t i;
uint8_t full; /* 1 = 本轮拿到完整事实,允许钉表 */
s_wave_dbg_seq++; /* 诊断留痕(第一幕第二组):每次尝试一个序号 */
if (vol_lock_wait(0u) == 0u) {
/* try 闸此前无任何留痕(看门狗设计说明 §4.3 只禁「锁层自身」打日志——
* 这里是消费方的一次性观测,计数走不取锁的 vol_lock_get_stats) */
vol_lock_stats_t vs;
vol_lock_get_stats(&vs);
DBG_ERROR("ui wave scan #%u try fail tt=%lu ac=%lu fo=%lu",
(unsigned)s_wave_dbg_seq, (unsigned long)vs.try_timeout,
(unsigned long)vs.acquired, (unsigned long)vs.failopen);
/* 卷忙本轮跳过 = 静默(看门狗设计说明 §4.3 口径:消费方在 try 失败
* 分支不留日志,锁层竞争看 vol_lock_get_stats 计数);下一次进页/
* 按键自然重试——本轮没拿到事实,绝不钉表 */
return -1;
}
s_wave_cnt = 0u;
@@ -497,29 +501,23 @@ static int ui_task_wave_scan(void) {
}
(void)f_closedir(&dir);
if (full == 0u) {
DBG_ERROR("ui wave scan #%u readdir fail fr=%d got=%u",
(unsigned)s_wave_dbg_seq, (int)rr, (unsigned)s_wave_cnt);
DBG_ERROR("ui wave scan readdir fail fr=%d got=%u", (int)rr,
(unsigned)s_wave_cnt);
}
} else if (fr == FR_NO_PATH) {
full = 1u; /* 从未录过波 = 0 段,完整事实 */
} else {
DBG_ERROR("ui wave scan #%u opendir fail fr=%d", (unsigned)s_wave_dbg_seq,
(int)fr);
DBG_ERROR("ui wave scan opendir fail fr=%d", (int)fr);
}
vol_lock_release();
if (full == 0u) {
s_wave_cnt = 0u; /* 半截计数不外泄:本轮按「无段」显示 */
return -1;
}
{
/* 判读表补全(诊断):每一次完成扫描都留一行(含非零条目——
* 「扫描成功但页面仍空」也要能被排除),随根因修复收敛删减 */
vol_lock_stats_t vs;
vol_lock_get_stats(&vs);
DBG_INFO("ui wave scan #%u done cnt=%u np=%d tt=%lu ac=%lu",
(unsigned)s_wave_dbg_seq, (unsigned)s_wave_cnt,
(fr == FR_NO_PATH) ? 1 : 0, (unsigned long)vs.try_timeout,
(unsigned long)vs.acquired);
if (s_wave_cnt == 0u) {
/* 判读表第三条(FR_OK 但零条目):钉盘级证据;有段不重复刷
* (DBG_* 每条调用点约 90B Flash,开页成功态由画面自证) */
DBG_INFO("ui wave scan done: dir ok, 0 seg entries");
}
/* 插入排序:条数上限 100,不引 qsort 的 stdlib 依赖 */
for (i = 1u; i < s_wave_cnt; i++) {
@@ -589,22 +587,23 @@ static int ui_task_wave_load_head(uint8_t a_idx) {
static FIL f; /* static:FIL = 528B,理由同 soe_store_read */
char path[WAVE_ST_PATH_MAX]; /* 容量 = WAVE_ST_PATH_MAX(17 字节口径,
* 自己声明路径缓冲不得写死 16) */
FRESULT fr = FR_OK; /* 诊断留痕可能未走到任何 f_read 就读它 */
FRESULT ofr = FR_OK; /* 诊断留痕(第一幕第二组):开档 FR 码 */
FRESULT fr;
FRESULT ofr; /* 开档 FR 码:开档失败留痕用 */
UINT br = 0u;
int rc;
if ((s_wave_cache_ok != 0u) && (s_wave_cached_idx == a_idx)) {
return UI_WAVE_OK;
}
if (wave_store_seg_path(path, (uint32_t)sizeof(path), a_idx) != 0) {
DBG_ERROR("ui wave head #%u path fail idx=%u", (unsigned)s_wave_dbg_seq,
(unsigned)a_idx);
if (wave_store_seg_path(path, (uint32_t)sizeof(path), a_idx) <= 0) {
/* 契约(wave_store.h:458):成功 = 写入字符数(恒 16),失败才是负码。
* 建成功能起这里写成 != 0,把每次成功都当失败 → 回放页开档永远走不到
* f_open,盘上明明有段也恒显「无录波段」(2026-11-02 板级 FR 埋点
* 钉死:scan done cnt=7 + head path fail;写侧 default_task.c 同函数
* 用 <= 0,一直是对的)。口径取和写侧一致的 <= 0。 */
return UI_WAVE_NONE;
}
if (vol_lock_wait(0u) == 0u) {
DBG_ERROR("ui wave head #%u try fail idx=%u", (unsigned)s_wave_dbg_seq,
(unsigned)a_idx);
return UI_WAVE_NONE; /* 卷忙 = 本轮按无段处理(不阻塞 uiTask) */
}
rc = UI_WAVE_NONE;
@@ -667,9 +666,12 @@ static int ui_task_wave_load_head(uint8_t a_idx) {
s_wave_cache_ok = 1u;
} else {
s_wave_cache_ok = 0u;
DBG_ERROR("ui wave head #%u rc=%d idx=%u ofr=%d fr=%d br=%u",
(unsigned)s_wave_dbg_seq, rc, (unsigned)a_idx, (int)ofr,
(int)fr, (unsigned)br);
if (ofr != FR_OK) {
/* 表上明明列着这个段、开档却失败:页面只会显示「无录波段」,
* 没有任何视觉面——必须留痕(10-31 半轮栽就栽在这类静默面上) */
DBG_ERROR("ui wave head idx=%u open fail fr=%d", (unsigned)a_idx,
(int)ofr);
}
}
return rc;
}
@@ -811,7 +813,7 @@ static int ui_task_wave_seg_win(uint8_t a_idx, uint8_t a_ch,
int16_t *a_codes, int32_t *a_peak_milli) {
static FIL f;
char path[WAVE_ST_PATH_MAX];
FRESULT fr = FR_OK; /* 诊断留痕(第一幕第二组):开档 FR 码 */
FRESULT fr; /* 开档 FR 码:开档失败留痕用 */
UINT br = 0u;
uint32_t off;
uint32_t want;
@@ -842,14 +844,12 @@ static int ui_task_wave_seg_win(uint8_t a_idx, uint8_t a_ch,
if (want != UI_WAVE_WIN_PTS * 2u) {
return UI_WAVE_BAD; /* 窗口贴不出完整 40 点:显式不可读 */
}
if (wave_store_seg_path(path, (uint32_t)sizeof(path), a_idx) != 0) {
DBG_ERROR("ui wave win #%u path fail idx=%u", (unsigned)s_wave_dbg_seq,
(unsigned)a_idx);
if (wave_store_seg_path(path, (uint32_t)sizeof(path), a_idx) <= 0) {
/* 同 ui_task_wave_load_head:契约成功 = 16,失败是负码;!= 0 的旧写法
* 把成功当失败,窗口永远贴不出(2026-11-02 根因修复,详见那里注释) */
return UI_WAVE_NONE;
}
if (vol_lock_wait(0u) == 0u) {
DBG_ERROR("ui wave win #%u try fail idx=%u ch=%u", (unsigned)s_wave_dbg_seq,
(unsigned)a_idx, (unsigned)a_ch);
return UI_WAVE_NONE;
}
rc = UI_WAVE_BAD;
@@ -895,10 +895,10 @@ static int ui_task_wave_seg_win(uint8_t a_idx, uint8_t a_ch,
}
}
*a_peak_milli = peak;
} else {
DBG_ERROR("ui wave win #%u rc=%d idx=%u ch=%u ofr=%d br=%u",
(unsigned)s_wave_dbg_seq, rc, (unsigned)a_idx, (unsigned)a_ch,
(int)fr, (unsigned)br);
} else if (fr != FR_OK) {
/* 理由同 load_head 的开档留痕:表列着的段开不出,页面无视觉面 */
DBG_ERROR("ui wave win idx=%u ch=%u open fail fr=%d", (unsigned)a_idx,
(unsigned)a_ch, (int)fr);
}
return rc;
}