feat(wave): 故障录波数据面、接线与单通道回放页(软件闭环交付)
数据面(App/algo,纯逻辑,双端编译) - wave_ring:8 路 × 320 点列优先环(1 kHz,预触发 160 + 后触发 160),adcTask 唯一写者;触发/冻结/封存/冷却四态,冻结持续到落盘回执才解冻,滞留 2 s 自弃 兜底——下一次真故障无波可录比丢一段更贵 - wave_store:段头编解码 + 拉模式打包游标 + 自带无表位运算 CRC-16/MODBUS(带 锚点自检)+ 段名与整段长度等式;格式不依赖平台类型宽度 - 段头只存原始码值与封存瞬间生效系数,工程量只当单位标签、不进任何算式 - 三套主机单测(环 113 / 编解码 170 / 触发状态机 110),全部无桩、生产源原样 纳入;新文件补 stddef 是 ARMCC 与主机差异,主机侧照不出这条分叉 - test/host/wave_read.py 是按 §6.2 的独立第二实现(dump/fft/shape/plot),92 条 自洽断言;三份合成段互读双校验全过、哈希钉死,格式面双向闭合 - 三套新单测不加专属钩子,由覆盖率总闸全量真跑(沿用既有裁定) 接线(任务层) - adc_cal:新增生效系数的 32 位定点原值入口(与既有浮点入口共用生效视图), 段头按最低有效位精确记账,不经浮点回算 - adcTask:每拍压 8 路语义原始码值并推进状态机,封存那一拍同拍交棒封存瞬间事实; 台架定周期自抓走双层编译门,产品镜像里零存在;串口新增一条 wave: 周期行 - defaultTask:卷空闲后一次心跳整段搬完(try 锁 + 256 字节分片 + 一次完整开档到 关档),成败都回执;失败先删半成品再计错,段号只在成功时推进;持锁超 50 ms 补一条告警并记历史峰值(喂狗余量可核) - Lead 复核补第四道并发陷阱:采集任务优先级高于落盘道,搬段中途它可能正好把滞留 超时的本段自弃、甚至封出下一段——数据区校验和是对「实际读到的字节」算的, 半新半旧的段照样自洽,故关档后复检环侧身份判废:删文件、不补账、段号不推进 - Lead 复核抓到触发挂号不是原子操作(旧实现分三个字节写,消费者可在窗口里读到 上一段的触发源与元件号):已把载荷压进一个字节,越界当场拒且不记任何计数; 段文件字节布局零改动,三份合成段哈希逐字节不变 - protectTask:保护启动上升沿同点请求录波,一拍一次、元件号取最低位;判据本体 零改动 人机(160×160 单色屏) - 菜单树节点 26「手动录波」绑页面 + 记录查询组末新增「录波回放」叶,重跑生成器 得 43 节点 / 34 叶 / 30 已绑,页面映射 28 → 30 - 回放页一屏一路、触发点前后各一周波共 40 点、每点 3 像素阶梯线(只用水平与 垂直两种线段——单色屏斜线会被阈值裁掉);零点虚线、触发光标、窗口峰值、 单位徽标与形状受限徽标;空态与坏段各一句显式文案,绝不用半截波形冒充数据 - ui_data 新增录波注入通道(六件操作表、三态返回);执行侧胶水带段头缓存、 手动请求后连缓存一起作废、逐点走全仓唯一的码值换算入口 - 模拟器接真实环与编解码合成两段(含坏段与空态旗标),6 张新黄金参考,既有 60 张逐字节零漂移未用更新模式;字库 218 → 224 字 / 319 字形 - 两份图形配置里图表组件仍为关;零堆分配;页面池水位不变 - 回放读路径的 uiTask 阻塞上界按实码写清:一次渲染 = 段表刷新 + 未缓存段头最坏 22 次小读;已验段只剩一次开窗读 + 40 点解码;无实测数不编,实测归台架、 仪器用已在跑的单轮工作耗时探针,不新加埋点 台账与文档 - 内存台账:录波环与落盘/回放静态逐条入册,三档对照 81,536 → 88,296 → 89,936 字节(62.2% → 68.6%),累计 +8,400;预估 5.9 KB 与实测 +6,760 的差额点名; 散列文件头注释同步;任务栈表核对后无需抬栈;体积趋势行与同码凭证由权威构建 重打(0 错误 0 警告,四项体积与上一轮相同——本轮改动只有注释与声明位置) - 设计说明:§5 补第四条并发陷阱;§6.3 定字段口径(两份独立实现的共同依据); §7 回填实测;§9 周期行写实;§10 台架判据改两段式且不比绝对相位;§12 补读路径 风险行;抬头状态改成「代码面已交付、未做完的只有板级那一轮」 - 卡片:软件闭环一栏列证据;台架三条按用户裁定保持未完成,不做虚假销项 - 口径同步:数据可信、UI 补齐、页面补齐规划、产品需求、采集短板评测、任务规划 索引与两份 README;陈旧现状句清扫 16 处(带日期戳的历史取数行一律不动) - 门禁说明:nested-comment 记仓库根探测必须兼容 linked worktree 的 gitdir 文件 形态(修前该钩子在任务树里恒报环境错、静默不可用);coding-standard 记 「函数头注释超过 24 行就把函数功能挤出回看窗口」这条机制与本次现场 - 覆盖率基线登记两个新覆盖模块(套件 41 → 44 套);环单测重打后该文件有效行 227 → 226、覆盖行 226 → 225,比率仍 99.6%,是分母变小不是覆盖率回退 为什么是单一提交:文档链接、双端注册、生成器幂等(菜单与字库)、内存数字与 体积事实源、编号门禁这五道整仓门禁要求代码、页面、菜单树、字库、文档与台账 同处一个提交才自洽,按数据面/接线/人机/台账拆成四笔必然红其中至少四道 编号门禁新增行 14 处命中已按语义改写:魔数常量按字符命名、段名样例改由格式串 生成、频谱表头改 f / 2x / 5x 并加一行图例;改写后自洽断言 92 条仍全过
This commit is contained in:
@@ -106,6 +106,15 @@
|
||||
* 不并进 cal: 行是因为 9 个码值会把该行推到 168 字符、
|
||||
* 撞 DBG_CON_LINE_MAX(192) 的静默截断边界(见该行注释)。
|
||||
* 无新增跨任务状态、无新增外设访问,栈 +36 B(局部码值数组)
|
||||
* v3.4 2026-10-29 DTU 项目组 故障录波数据面接线(task-2):新增 2c——每拍把 8 路
|
||||
* 语义原码压进 wave_ring 并 tick 推进;EVT_SEALED 那一拍现取
|
||||
* 折算/有效性/墙钟事实组 wave_seal_cal_t 并 latch_meta 恰好
|
||||
* 一次(握手次序照 App/algo/wave_ring.h 头注唯一合法次序);
|
||||
* 心跳末尾加 §9 唯一周期行 wave:(字段逐字,取数
|
||||
* wave_ring_get_stat);新增 2d——bench 定周期自抓,只在
|
||||
* DTU_BENCH 且 WAVE_BENCH_AUTOCAP 非 0 的双层编译门内存在,
|
||||
* 默认关闭 + 5 分钟时长上限 + 启动一次性告警(§10 红线);
|
||||
* adc_task_init 内接 wave_ring_init(init 禁日志约束不破)
|
||||
*/
|
||||
|
||||
#include "adc_task.h"
|
||||
@@ -123,6 +132,8 @@
|
||||
#include "console_log.h"
|
||||
#include "sys_log.h" /* SYSLOG_SELFTEST:采样故障留痕(仅故障才写) */
|
||||
#include "task_prof.h" /* 常开耗时探针:单拍耗时 */
|
||||
#include "wave_ring.h" /* 故障录波环(task-2 数据面接线):push/tick/latch_meta/get_stat */
|
||||
#include "sys_clock.h" /* 封存交棒的 wall 字段:sys_clock_get() epoch 秒(UINT32_MAX=未对时,写 0) */
|
||||
|
||||
#define DBG_TAG "[ADC_TASK]"
|
||||
#include "dbg_log.h"
|
||||
@@ -132,6 +143,17 @@
|
||||
* 消费节拍改由 TIM3 ISR 置位 ADC_WAKE_FLAG 提供,与生产同源,不再自定周期) */
|
||||
#define ADC_TASK_REPORT_MS 10000U
|
||||
|
||||
/* bench 定周期自抓(§10 红线「默认关闭 + 有时长上限 + 启动打一次告警」的落点):
|
||||
* WAVE_BENCH_AUTOCAP 默认 0——产品档连外层 DTU_BENCH 都没有,双层编译门保证
|
||||
* 产品镜像里 2d 段整段不存在;台架确实要定周期刷段时才本地改 1 重编译,
|
||||
* 用完立刻回 0(每 2 s 一段 = 每 5 分钟一轮 NAND 写,长挂白烧写次数,dhara
|
||||
* 坏块记账还是持久代价)。宏形态照 App/algo/adc_cal.c 的 adc_cal_prot_blocked():
|
||||
* 联合条件放 #if,未定义宏恒为假,不把 cppcheck 引到旁路上。
|
||||
* 周期/上限取自 §10 的算式:2 s/段、300 s 自动停。 */
|
||||
#define WAVE_BENCH_AUTOCAP 0
|
||||
#define WAVE_BENCH_PERIOD_MS 2000u
|
||||
#define WAVE_BENCH_MAX_MS 300000u
|
||||
|
||||
/* 统计:已消费帧数 / 无新帧次数(首拍必为无新帧,之后应稳定为 0 增量)。
|
||||
* 原始码值缓冲由 语义取数与有效性层 App/algo/adc_input.c 持有(消费侧按语义取数,不写死下标) */
|
||||
static uint32_t s_frames_consumed;
|
||||
@@ -157,6 +179,20 @@ static uint8_t s_rail_syslogged;
|
||||
* 200 ms(与 v2.6 修"重复采样污染 DFT 窗"同族误差,勿"优化"成拍计数) */
|
||||
static uint32_t s_meas_ms;
|
||||
|
||||
/* 故障录波环接线状态(task-2;状态机与计数全在 App/algo/wave_ring.c,这里只搬运):
|
||||
* s_wave_codes - 本拍 8 路语义原码(文件级 static,不上单拍的栈——本任务静态
|
||||
* 最坏链余量按 1024 B ISR 底线核)
|
||||
* s_wave_cal - EVT_SEALED 拍的交棒结构(同理不放局部:整块 ~160 B) */
|
||||
static int16_t s_wave_codes[WAVE_RING_CH];
|
||||
static wave_seal_cal_t s_wave_cal;
|
||||
#if defined(DTU_BENCH) && (WAVE_BENCH_AUTOCAP != 0)
|
||||
/* bench 自抓编排:on = 已过写者就绪门开始跑;off = 到时自动停(init 重置) */
|
||||
static uint8_t s_wave_bench_on;
|
||||
static uint8_t s_wave_bench_off;
|
||||
static uint32_t s_wave_bench_t0;
|
||||
static uint32_t s_wave_bench_next;
|
||||
#endif
|
||||
|
||||
|
||||
/* 消费者就绪门:由 adcTask 自己在进入等待前置 1;单写者 = 本任务,ISR 只读。
|
||||
* ⚠ 为什么必须有这道门:TIM3 在 app_main_init()(Src/main.c:140)里就
|
||||
@@ -288,6 +324,13 @@ int adc_task_init(void) {
|
||||
s_meas_ms = 0u;
|
||||
adc_health_init(); /* 内含 采样通道自检 贴轨观察器复位(rail_init) */
|
||||
(void)meas_init(); /* 遥测快照双区/序号复位(幂等;发布状态属本任务,init 无日志) */
|
||||
wave_ring_init(); /* 录波环纯清 RAM(无内核依赖;本函数在开闸前跑,init 禁日志不破) */
|
||||
#if defined(DTU_BENCH) && (WAVE_BENCH_AUTOCAP != 0)
|
||||
s_wave_bench_on = 0u;
|
||||
s_wave_bench_off = 0u;
|
||||
s_wave_bench_t0 = 0u;
|
||||
s_wave_bench_next = 0u;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -454,6 +497,103 @@ void adc_task_func(void *arg) {
|
||||
adc_health_report(&h_st);
|
||||
}
|
||||
|
||||
/* 2c) 故障录波环泵送(task-2 数据面接线;状态机与计数全在 App/algo/wave_ring.c,
|
||||
* 本段只做搬运)。每拍取 8 路语义**原码**压环 + tick 推进;触发那拍的样本
|
||||
* 落在段内点 WAVE_RING_PRE-1(点序 lead 钉死)。只存原码不存折算工程量——
|
||||
* §6.3 裁定:样本域 = 原始码,折算系数进段头,回放/PC 侧统一换算。
|
||||
* 某路取不到(开机头几拍)保持上值(与 adc: 心跳行同一兜底口径)。
|
||||
* tick 返 EVT_SEALED 的那拍必须**同拍** latch meta(头注唯一合法次序),
|
||||
* 漏了 = 2 s 后环自弃计 err,defaultTask 根本不会见到这一段。 */
|
||||
{
|
||||
uint8_t w;
|
||||
int32_t wev;
|
||||
|
||||
for (w = 0u; w < WAVE_RING_CH; w++) {
|
||||
int32_t wc = 0;
|
||||
|
||||
if (adc_input_raw((adc_semantic_t)w, &wc) == 0) {
|
||||
s_wave_codes[w] = (int16_t)wc;
|
||||
}
|
||||
}
|
||||
(void)wave_ring_push(s_wave_codes, WAVE_RING_CH);
|
||||
wev = wave_ring_tick(now, s_frames_consumed);
|
||||
|
||||
if (wev == WAVE_RING_EVT_SEALED) {
|
||||
uint8_t c;
|
||||
|
||||
/* 封存瞬间事实(交棒结构 s_wave_cal 为文件级 static,不上栈):
|
||||
* eng / VALID 位图 / CLIPPED 位图 / 墙钟 / 逐路 conv+bits+avail+k+b。
|
||||
* eng 口径 = adc_cal.h 注③ 的 8 路量纲标签(判据源
|
||||
* App/app/ui_data_feed.c feed_eng(),与 prot_blocked 的三路口径**不是**
|
||||
* 一件事):8 路电气量全 VALID 才是二次值;V0.1 无 CT 变比,一次值不可达。
|
||||
* wall 未对时写 0(sys_clock_get 失败 = UINT32_MAX,不许拿上电毫秒冒充)。
|
||||
* k 取 adc_cal_k_q16() int32 原值;b 的 float 回乘在本域无损
|
||||
* (|b| 限幅 8192 << 2^24,float32 精确表示整数;禁用浮点只针对 k)。 */
|
||||
s_wave_cal.flags_seq = 0u;
|
||||
s_wave_cal.clip_mask = 0u;
|
||||
for (c = 0u; c < WAVE_RING_CH; c++) {
|
||||
const uint32_t f = adc_cal_flags((adc_semantic_t)c);
|
||||
const adc_channel_desc_t *d = adc_input_desc((adc_semantic_t)c);
|
||||
|
||||
if ((f & ADC_CAL_F_VALID) != 0u) {
|
||||
s_wave_cal.flags_seq |= (uint8_t)(1u << c);
|
||||
}
|
||||
if ((f & ADC_CAL_F_CLIPPED) != 0u) {
|
||||
s_wave_cal.clip_mask |= (uint16_t)(1u << c);
|
||||
}
|
||||
s_wave_cal.ch[c].conv = (d != NULL) ? (uint8_t)d->source
|
||||
: (uint8_t)ADC_SRC_STM32;
|
||||
s_wave_cal.ch[c].bits = (s_wave_cal.ch[c].conv == (uint8_t)ADC_SRC_TPAFE)
|
||||
? 16u : 12u;
|
||||
s_wave_cal.ch[c].avail = (uint8_t)f;
|
||||
s_wave_cal.ch[c].k_q16 = adc_cal_k_q16((adc_semantic_t)c);
|
||||
s_wave_cal.ch[c].b_codes = (int32_t)adc_cal_b_codes((adc_semantic_t)c);
|
||||
}
|
||||
s_wave_cal.eng = (s_wave_cal.flags_seq == 0xFFu)
|
||||
? (uint8_t)WAVE_ENG_SEC : (uint8_t)WAVE_ENG_PIN;
|
||||
{
|
||||
uint32_t wall = sys_clock_get();
|
||||
|
||||
s_wave_cal.wall = (wall == UINT32_MAX) ? 0u : wall;
|
||||
}
|
||||
/* 只在 EVT_SEALED 拍调一次;返错不补救——环 2 s 超时自弃兜底,
|
||||
* 这里补第二行日志就违反 §9「不在已留痕处重复锚点」 */
|
||||
(void)wave_ring_latch_meta(&s_wave_cal);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2d) bench 定周期自抓(§10 红线落点:默认关闭 + 时长上限 + 启动一次性告警)。
|
||||
* 编译门 = DTU_BENCH 且 WAVE_BENCH_AUTOCAP 非 0,产品镜像无本段。
|
||||
* 节拍:每 WAVE_BENCH_PERIOD_MS 投一次 bench 请求(环侧冷却/去重自管,
|
||||
* 被丢的计入 ovf——这是 bench 的预期观测面,不是故障)。
|
||||
* 计时起点等写者就绪门(v2.1 理由:就绪前直发会与 defaultTask 存储
|
||||
* 日志字节级交错);到时自动停 + 一次性 STOP 行,之后不再投任何请求。
|
||||
* ⚠ 这两条 WARN/STOP 是一次性生命周期日志,不是周期行,§9 一条线约束不破 */
|
||||
#if defined(DTU_BENCH) && (WAVE_BENCH_AUTOCAP != 0)
|
||||
if (s_wave_bench_off == 0u) {
|
||||
if (s_wave_bench_on == 0u) {
|
||||
if (dbg_console_writer_ready() != 0u) {
|
||||
s_wave_bench_on = 1u;
|
||||
s_wave_bench_t0 = now;
|
||||
s_wave_bench_next = now + WAVE_BENCH_PERIOD_MS;
|
||||
/* [WARN] 进正文(本仓无 WARN 级;用 ERR 级会带 [ERR] 误触 @test 禁用标记) */
|
||||
DBG_INFO("[WARN] wave bench autocap ON: 1 seg / %u ms, auto-stop %u s (BENCH BUILD ONLY)",
|
||||
(unsigned int)WAVE_BENCH_PERIOD_MS,
|
||||
(unsigned int)(WAVE_BENCH_MAX_MS / 1000u));
|
||||
}
|
||||
} else if ((uint32_t)(now - s_wave_bench_t0) >= WAVE_BENCH_MAX_MS) {
|
||||
s_wave_bench_off = 1u;
|
||||
DBG_INFO("wave bench autocap STOP after %u s",
|
||||
(unsigned int)(WAVE_BENCH_MAX_MS / 1000u));
|
||||
} else if ((uint32_t)(now - s_wave_bench_next) >= WAVE_BENCH_PERIOD_MS) {
|
||||
s_wave_bench_next = now + WAVE_BENCH_PERIOD_MS;
|
||||
(void)wave_ring_request((uint8_t)WAVE_TRIG_BENCH, 0u);
|
||||
} else {
|
||||
/* 本拍未到周期点:无事可做(比较用无符号差值,回绕安全) */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 3) 首帧码值上报:**写者就绪后**才落日志。写者未注册时 con_append 走
|
||||
* 无锁直发路径,与 defaultTask 的存储初始化日志会字节级交错
|
||||
* (2026-10-10 台架实测);此时挂起上报,就绪后补报最近一帧。
|
||||
@@ -610,6 +750,46 @@ void adc_task_func(void *arg) {
|
||||
(long)(ms.freq_hz * 100.0f),
|
||||
(unsigned)ms.freq_valid);
|
||||
}
|
||||
|
||||
/* 录波环周期行(docs/子系统设计/故障录波设计说明.md §9 逐字模板,
|
||||
* 字段名与顺序**不许动**——PC/台架脚本按行名 grep)。全链路只加这一条
|
||||
* 周期行(每条 DBG ≈90 B Flash + 216 B 栈,§9 明令不加第二条):
|
||||
* seg = 累计成功 ACK 的段数(wave_ring_stat.seg_total,不是轮转号)
|
||||
* armed= 1 表示此刻有段在飞(FREEZE/SEALED;IDLE/COOL 为 0——
|
||||
* COOL 只挡新触发不录新段,画进 armed 会让台架误读)
|
||||
* src = 触发源 M 手动 / P 保护 / T bench 定时,- = 本 boot 未触发过
|
||||
* err = 落盘失败 + SEALED 超时自弃(环内 err_cnt 收口,落盘道不数
|
||||
* 第二份账);ovf = ACK 前重复触发与冷却期丢弃数;
|
||||
* drop = 冻结窗口丢弃的录波点数(单位=点)
|
||||
* 判读:bench 自抓一轮后 seg_total 应增长;err 涨而 seg 不涨 =
|
||||
* 落盘道在丢段,查 defaultTask 侧 [WAVE] 失败行 */
|
||||
{
|
||||
wave_ring_stat_t wst;
|
||||
|
||||
(void)wave_ring_get_stat(&wst);
|
||||
{
|
||||
const char *sc;
|
||||
|
||||
if (wst.trig_src == (uint8_t)WAVE_TRIG_MANUAL) {
|
||||
sc = "M";
|
||||
} else if (wst.trig_src == (uint8_t)WAVE_TRIG_PROTECT) {
|
||||
sc = "P";
|
||||
} else if (wst.trig_src == (uint8_t)WAVE_TRIG_BENCH) {
|
||||
sc = "T";
|
||||
} else {
|
||||
sc = "-";
|
||||
}
|
||||
DBG_INFO("wave: seg=%lu armed=%u src=%s pts=%u err=%lu ovf=%lu drop=%lu",
|
||||
(unsigned long)wst.seg_total,
|
||||
(unsigned)((wst.state == (uint8_t)WAVE_RING_ST_FREEZE) ||
|
||||
(wst.state == (uint8_t)WAVE_RING_ST_SEALED)) ? 1u : 0u,
|
||||
sc,
|
||||
(unsigned int)WAVE_RING_PTS,
|
||||
(unsigned long)wst.err_cnt,
|
||||
(unsigned long)wst.ovf_cnt,
|
||||
(unsigned long)wst.drop_cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
report_at = now + ADC_TASK_REPORT_MS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user