Files
STM32F4-Base/test/host/wave_store/wave_store_test.c
T
wandering1 d64bbdc57f 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 条仍全过
2026-10-29 19:23:15 +08:00

773 lines
41 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.
/*
* 模块名称:录波段格式层(段文件字节布局编解码 + 打包游标 + 段名轮转)主机侧单元测试
* 被测对象:真实源文件(#include 原样纳入,测生产代码而非副本):App/algo/wave_store.c
* 该源是纯逻辑(无 HAL / CMSIS / RTOS / FatFs / 日志)→ **无需任何桩目录**。
*
* 为什么要主机侧(而不是只等台架):段文件是**证据**,一旦写进 NAND 就再也说不清
* 「当时想表达什么」。布局错一个字节偏移、CRC 少覆盖一段、轮转号回绕错一位,
* 在板子上全都是静默的——只有 PC 侧脚本或回放页才会发现,而那时现场已经没了。
* 所以字节级判据(整段长度等式、CRC 锚点、编解码往返、拒绝清单)必须钉在主机侧。
* 需要板子的部分(真 FAT 落盘、FTP 拉回、注入对表)见 docs/任务规划/故障录波.md 卡 1。
*
* 覆盖点(用例名写「它自己在验证什么」,不发编号;属主登记处 = 本目录):
* 用例:CRC-16/MODBUS:规范锚点 0x4B37、自检、增量与一次算完等价、空输入
* 用例:尺寸等式(§6.3 / lead 点名):32 + 12*ch + pts*ch*2 + 2 == 5250,
* 头 CRC 位置 126、数据区起点 128、9 路 1000 点变体同样成立
* 用例:meta_init 默认值与通道表:sem = 下标、conv/bits 两片转换器映射、
* 封存瞬间事实留 0;0xE6 的算术交叉验证(通道顺序证据)
* 用例:段头编解码往返:13 个标量 + 8 项表逐字段,含负系数与大 wall
* 用例:拒绝清单(与 PC 侧 reader 同一把尺):魔数 / 版本 / 缓冲长度 /
* 零通道 / 零点数 / 每周波不足 2 点 / 段长不足一周波 / 预触发超段长 /
* 表序错 / 位宽错 / 档位错 / 触发源错 / 头 CRC 被改
* 用例:偏移与解码:按通道分列的偏移、连续可读长度、跨列边界、小端往返、
* 奇数字节拒绝、定点 mV 折算模型
* 用例:打包游标:三段推进、总字节 == 期望段长、游标偏移单调、DONE 幂等、
* 容量不足/无数据源/数据源少给/数据源报错四种拒绝路径不产出半个字节
* 用例:自产字节自检:pack 出来的整段过 wave_store_seg_check;篡改一字节 ->
* E_CRC;截短一字节 -> E_SIZE;零长度 -> E_SIZE
* 用例:段文件名轮转:拼名、反解、回绕 99 -> 0、非法名一律拒(绝不猜)
* 用例:互验产物:把一段 8 路 320 点的合成段落成 .bin 供 PC 侧脚本只读校验
* (不参与判定,写失败只打印)
*
* 通过:全部 CHECK 通过(main 返回 0)。
* 创建日期:2026-10-29(故障录波 task-1)
*/
/* 主机测试专用:本文件的互验产物要用 stdio/stdlib/string 的
* 传统形态(MSVC 会对 strcpy/fopen 报 C4996,测试代码不受生产侧禁用清单约束) */
#define _CRT_SECURE_NO_WARNINGS 1
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ===================== 断言 ===================== */
static int s_total = 0, s_passed = 0, s_failed = 0;
#define CHECK(cond, name) do { \
s_total++; \
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
} while (0)
/* ===================== 被测代码(生产源原样纳入) ===================== */
#include "wave_store.c"
/* ===================== 公共夹具 ===================== */
#define IMG_CAP (WAVE_ST_SEG_SZ_CUR + 256u)
static uint8_t s_img[IMG_CAP];
static wave_seg_meta_t s_meta;
static wave_seg_meta_t s_back;
/*
* 段头单位标签与逐路有效位图的**平台派生式**(本体在 App/app/ui_data_feed.c 的 feed_eng(),
* 段头填写在 App/task/adc_task.c 的 532 至 553 行):电气 8 路全 VALID 才升二次值档位,
* 否则停在引脚域。本层不参与这条派生(wave_store 不链 adc_cal),放这里只为钉一件事:
* **夹具必须与平台派生式自洽**。一份真机写不出来的字节,验的是两个读者对不可能状态的
* 行为,正好掩盖真正的分歧(lead 裁定 2026-10-29,本套上一版就把 eng 留成引脚域而
* flags_seq 全置位,那份构造段没资格当互验夹具)。
*/
static uint8_t expect_eng(uint8_t a_flags_seq) {
return (a_flags_seq == 0xFFu) ? (uint8_t)WAVE_ENG_SEC : (uint8_t)WAVE_ENG_PIN;
}
static int eng_selfconsistent(const wave_seg_meta_t *p_m) {
return (p_m->eng == expect_eng(p_m->flags_seq)) ? 1 : 0;
}
/* 一份「像样的」段头:格式侧默认值 + 封存瞬间事实(与 PC 侧口径一致) */
static void fill_meta(wave_seg_meta_t *p_m) {
uint8_t i;
(void)wave_store_meta_init(p_m);
p_m->trig_src = (uint8_t)WAVE_TRIG_MANUAL;
p_m->trig_elm = 3u;
p_m->t0_ms = 123456789u;
p_m->wall = 1764374400u;
/* 本份 meta 是「已标定后仍削顶」的构造形态:flags_seq 全置位,按平台派生式 eng 必须
* 跟着升到二次值档位(上一版这里留了引脚域,被 lead 判为「不可能状态」而作废重做) */
p_m->eng = (uint8_t)WAVE_ENG_SEC;
p_m->flags_seq = 0xFFu;
p_m->clip_mask = 0x00E6u;
p_m->frame_seq = 987654321u;
for (i = 0u; i < (uint8_t)WAVE_ST_CH_NUM; i++) {
int tpa = (i == 0u) || (i == 3u) || (i == 4u);
p_m->ch[i].k_q16 = tpa ? 10000000 : 52800000;
p_m->ch[i].b_codes = tpa ? -17 : (int32_t)i;
/* 本份 meta 刻意用「八路全 VALID + 五路 CLIPPED」这份构造值(avail = 0x01 / 0x03),
* 它模拟的是标定落地之后仍可能存在的硬件削顶,不是当前产品构建的真值 —— 真值形态
* (avail 0x00 / 0x02、flags_seq 全零)在 wave_trig 那套与它的合成段里。
* 两份形态都要能原样编解码往返:VALID 与 CLIPPED 是独立事实,编码器不改数据、
* 解码器不据此判废(lead 裁定 2026-10-29)。削顶位与 clip_mask 的对应关系仍要保持
* 自洽(bit1 = 削顶,聚合起来正好 0xE6),那是数据自身的一致性,不是格式不变式。 */
p_m->ch[i].avail = (uint8_t)(tpa ? 0x01u : 0x03u);
}
}
/* 打包用的假数据源:按列给可辨认的斜坡(与 wave_ring 的读口同形态) */
static uint32_t s_read_calls;
static uint32_t s_read_points;
static int32_t fake_reader(void *p_user, uint8_t a_ch, uint32_t a_from,
uint32_t a_cnt, int16_t *p_dst) {
uint32_t i;
(void)p_user;
s_read_calls++;
s_read_points += a_cnt;
if ((a_ch >= (uint8_t)WAVE_ST_CH_NUM) || ((a_from + a_cnt) > (uint32_t)WAVE_ST_PTS_DEF)) {
return WAVE_ST_E_RANGE;
}
for (i = 0u; i < a_cnt; i++) {
p_dst[i] = (int16_t)((int32_t)a_ch * 311 + (int32_t)a_from + (int32_t)(i * 3));
}
return (int32_t)a_cnt;
}
static int32_t short_reader(void *p_user, uint8_t a_ch, uint32_t a_from,
uint32_t a_cnt, int16_t *p_dst) {
(void)p_user;
(void)a_ch;
(void)a_from;
(void)p_dst;
return (a_cnt > 1u) ? ((int32_t)(a_cnt - 1u)) : 0; /* 少给一个点 */
}
static int32_t err_reader(void *p_user, uint8_t a_ch, uint32_t a_from,
uint32_t a_cnt, int16_t *p_dst) {
(void)p_user;
(void)a_ch;
(void)a_from;
(void)a_cnt;
(void)p_dst;
return WAVE_ST_E_RANGE;
}
/* 把游标跑到 DONE,返回 -1 表示中途报错;镜像与长度回填 */
static int pack_whole(uint32_t a_cap, uint32_t *p_len, int *p_last_rc) {
wave_pack_ctx_t ctx;
uint32_t off = 0u;
uint32_t len;
int rc;
*p_len = 0u;
rc = wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL);
if (rc != WAVE_ST_OK) {
*p_last_rc = rc;
return -1;
}
for (;;) {
uint32_t n = 0u;
uint32_t cap = a_cap;
if ((off + cap) > IMG_CAP) {
cap = IMG_CAP - off;
}
rc = wave_store_pack_next(&ctx, &s_img[off], cap, &n);
if ((rc < 0) || (n > cap)) {
*p_last_rc = rc;
return -1;
}
off += n;
if (rc == WAVE_ST_DONE) {
break;
}
if (off >= IMG_CAP) {
*p_last_rc = WAVE_ST_E_SIZE;
return -1;
}
}
len = off;
*p_len = len;
*p_last_rc = rc;
/* 游标报的总长与实际产出必须相等(接线道拿这条比对 f_write 写回字节数) */
if (wave_store_pack_total(&ctx) != len) {
return -1;
}
if (wave_store_pack_offset(&ctx) != len) {
return -1;
}
return 0;
}
static int meta_same(const wave_seg_meta_t *p_a, const wave_seg_meta_t *p_b) {
uint8_t i;
if ((p_a->fs_hz != p_b->fs_hz) || (p_a->pts_per_cycle != p_b->pts_per_cycle) ||
(p_a->ch_count != p_b->ch_count) || (p_a->pts_per_ch != p_b->pts_per_ch) ||
(p_a->pre_pts != p_b->pre_pts) || (p_a->trig_src != p_b->trig_src) ||
(p_a->trig_elm != p_b->trig_elm) || (p_a->t0_ms != p_b->t0_ms) ||
(p_a->wall != p_b->wall) || (p_a->eng != p_b->eng) ||
(p_a->flags_seq != p_b->flags_seq) || (p_a->clip_mask != p_b->clip_mask) ||
(p_a->frame_seq != p_b->frame_seq)) {
return 0;
}
for (i = 0u; i < p_a->ch_count; i++) {
if ((p_a->ch[i].sem != p_b->ch[i].sem) || (p_a->ch[i].conv != p_b->ch[i].conv) ||
(p_a->ch[i].bits != p_b->ch[i].bits) || (p_a->ch[i].avail != p_b->ch[i].avail) ||
(p_a->ch[i].k_q16 != p_b->ch[i].k_q16) || (p_a->ch[i].b_codes != p_b->ch[i].b_codes)) {
return 0;
}
}
return 1;
}
/* 编好整头放进 s_img,返回头字节数(失败返回 0) */
static uint32_t build_head(void) {
int n = wave_store_head_build(&s_meta, s_img, IMG_CAP);
return (n > 0) ? (uint32_t)n : 0u;
}
/* ===================== 主测 ===================== */
int main(void) {
printf("=== Wave Store Tests:\n");
/* ---------- 用例:CRC-16/MODBUS ---------- */
printf("-- 用例:CRC-16/MODBUS --\n");
CHECK(wave_store_crc_selftest() == WAVE_ST_OK, "自检通过(锚点 123456789 = 0x4B37)");
{
static const uint8_t s_kat[9] = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
uint16_t one = wave_store_crc(s_kat, 9u);
uint16_t inc = wave_store_crc_update(WAVE_ST_CRC_INIT, s_kat, 4u);
inc = wave_store_crc_update(inc, &s_kat[4], 5u);
CHECK(one == 0x4B37u, "规范锚点值本身(不是自算自验)");
CHECK(inc == one, "增量与一次算完等价(落盘按 256 字节分片必须同值)");
CHECK(wave_store_crc(s_kat, 0u) == WAVE_ST_CRC_INIT, "零长度 = 初值(判废归调用方)");
CHECK(wave_store_crc_update(WAVE_ST_CRC_INIT, NULL, 8u) == WAVE_ST_CRC_INIT, "空指针不当数据");
CHECK(WAVE_ST_CRC_POLY == 0xA001u, "反射多项式 0xA001(不是 0x8005 正序)");
CHECK(WAVE_ST_CRC_XOROUT == 0u, "异或输出恒 0");
}
/* ---------- 用例:尺寸等式 ---------- */
printf("-- 用例:尺寸等式 --\n");
CHECK(WAVE_ST_HEAD_CRC_POS(8u) == 126u, "头 CRC 位置 = 30 + 12*8");
CHECK(WAVE_ST_HEAD_BYTES(8u) == 128u, "整头 = 128 字节");
CHECK(WAVE_ST_HEAD_SZ_CUR == WAVE_ST_HEAD_BYTES((uint8_t)WAVE_ST_CH_NUM), "当前值宏与参数化宏同源");
CHECK(WAVE_ST_DATA_BYTES(8u, 320u) == 5120u, "数据区 = 5120 字节");
CHECK(WAVE_ST_SEG_TOTAL(8u, 320u) == 5250u, "整段 = 5250 字节");
CHECK(WAVE_ST_SEG_SZ_CUR == WAVE_ST_SEG_TOTAL((uint8_t)WAVE_ST_CH_NUM, (uint16_t)WAVE_ST_PTS_DEF),
"整段当前值宏与参数化宏同源");
CHECK((32u + (12u * 8u) + (320u * 8u * 2u) + 2u) == 5250u,
"等式 32 + 12*ch + pts*ch*2 + 2 == 5250(接线道拿它比对 f_write 写回数)");
CHECK(WAVE_ST_SEG_TOTAL(9u, 1000u) == (32u + 108u + 18000u + 2u), "9 路 1000 点变体同式(每周波点数是数据)");
fill_meta(&s_meta);
CHECK(wave_store_seg_size(&s_meta) == 5250u, "seg_size 按头字段算出 5250");
CHECK(wave_store_head_bytes(8u) == 128u, "head_bytes(8)");
CHECK((wave_store_head_bytes(0u) == 0u) && (wave_store_head_bytes(10u) == 0u), "非法通道数返回 0");
CHECK(wave_store_data_off(8u, 320u, 0u, 0u) == 128u, "第 0 列点 0 从整头之后开始");
CHECK(wave_store_data_off(8u, 320u, 1u, 0u) == (128u + 640u), "按通道分列:第 1 列 = 128 + 320*2");
CHECK(wave_store_data_off(8u, 320u, 7u, 319u) == (5250u - 2u - 2u), "最后一列最后一点贴数据 CRC");
CHECK(wave_store_data_off(0u, 320u, 0u, 0u) == 0xFFFFFFFFu, "通道数非法 -> 哨兵");
CHECK(wave_store_data_off(8u, 320u, 8u, 0u) == 0xFFFFFFFFu, "列号越界 -> 哨兵");
CHECK(wave_store_data_off(8u, 320u, 0u, 320u) == 0xFFFFFFFFu, "点越界 -> 哨兵");
CHECK(wave_store_data_bytes_avail(320u, 300u, 40u) == 40u, "连续可读长度夹到列尾(20 点 = 40 字节)");
CHECK(wave_store_data_bytes_avail(320u, 0u, 1000u) == 640u, "整列 320 点 = 640 字节");
CHECK(wave_store_data_bytes_avail(320u, 320u, 1u) == 0u, "列尾之后没有字节可读");
/* ---------- 用例:meta_init 与 0xE6 交叉验证 ---------- */
printf("-- 用例:meta_init 与通道顺序 --\n");
CHECK(wave_store_meta_init(NULL) == WAVE_ST_E_PARAM, "meta_init 空指针");
CHECK(wave_store_meta_init(&s_back) == WAVE_ST_OK, "meta_init OK");
CHECK((s_back.fs_hz == 1000u) && (s_back.pts_per_cycle == 20u) && (s_back.ch_count == 8u) &&
(s_back.pts_per_ch == 320u) && (s_back.pre_pts == 160u), "默认值 = §6.2 的 1 kHz / 20 / 8 / 320 / 160");
CHECK(s_back.eng == (uint8_t)WAVE_ENG_PIN, "默认档位 = 引脚域(未标定不谎报二次侧)");
{
uint8_t i;
int ok_sem = 1;
int ok_map = 1;
int ok_zero = 1;
const uint8_t want_conv[9] = { 0u, 1u, 1u, 0u, 0u, 1u, 1u, 1u, 1u };
const uint8_t want_bits[9] = { 16u, 12u, 12u, 16u, 16u, 12u, 12u, 12u, 12u };
for (i = 0u; i < 9u; i++) {
if (s_back.ch[i].sem != i) {
ok_sem = 0;
}
if ((s_back.ch[i].conv != want_conv[i]) || (s_back.ch[i].bits != want_bits[i])) {
ok_map = 0;
}
if ((s_back.ch[i].avail != 0u) || (s_back.ch[i].k_q16 != 0) || (s_back.ch[i].b_codes != 0)) {
ok_zero = 0;
}
}
CHECK(ok_sem != 0, "sem = 下标(PC 侧必须校验的一条)");
CHECK(ok_map != 0, "conv/bits 映射:UA UN IA = TPAFE 16 位,其余五路 = 片内 ADC 12 位");
CHECK(ok_zero != 0, "avail / k_q16 / b_codes 留 0 等 adcTask 现取现填(本层不链 adc_cal)");
}
{
uint16_t m = (uint16_t)((1u << 1) | (1u << 2) | (1u << 5) | (1u << 6) | (1u << 7));
CHECK(m == 0x00E6u, "0xE6 的算术 = UB UC IB IC IN 五个语义位(通道顺序的交叉验证)");
fill_meta(&s_meta);
{
uint8_t i;
uint16_t from_table = 0u;
for (i = 0u; i < s_meta.ch_count; i++) {
if ((s_meta.ch[i].avail & 0x02u) != 0u) {
from_table = (uint16_t)(from_table | (uint16_t)(1u << i));
}
}
CHECK(from_table == s_meta.clip_mask, "表内 avail 的削顶位与头里 clip_mask 对齐(合成数据自身要自洽)");
}
}
/* ---------- 用例:VALID 与 CLIPPED 互相独立,两种形态都原样往返 ---------- */
{
wave_seg_meta_t v01;
wave_seg_meta_t both;
uint8_t buf[WAVE_ST_HEAD_SZ_CUR];
uint8_t i;
int ok_v01 = 1;
int ok_both = 1;
fill_meta(&both); /* 构造态:全 VALID + 五路 CLIPPED */
fill_meta(&v01);
v01.flags_seq = 0x00u; /* 本波真值:未标定 → 逐路有效位图全零 */
v01.eng = (uint8_t)WAVE_ENG_PIN; /* 派生式跟着落档:未升二次值 */
for (i = 0u; i < v01.ch_count; i++) {
/* 三路 TPAFE 既不VALID也不削顶;五路片内 ADC 削顶但未标定 */
v01.ch[i].avail = (uint8_t)(((i == 0u) || (i == 3u) || (i == 4u)) ? 0x00u : 0x02u);
}
/* 注意:head_build 成功时返回的是写出的字节数(不是 WAVE_ST_OK)——
* 与 wave_ring_read_ch 同一类「返回实际长度」的契约 */
if (wave_store_head_build(&v01, buf, (uint32_t)sizeof(buf)) != (int)WAVE_ST_HEAD_SZ_CUR) {
ok_v01 = 0;
}
if (wave_store_head_parse(buf, (uint32_t)sizeof(buf), &s_back) != WAVE_ST_OK) {
ok_v01 = 0;
} else {
if (s_back.flags_seq != 0x00u) {
ok_v01 = 0;
}
for (i = 0u; i < s_back.ch_count; i++) {
if (s_back.ch[i].avail != v01.ch[i].avail) {
ok_v01 = 0;
}
}
}
/* 注意:head_build 成功时返回的是写出的字节数(不是 WAVE_ST_OK)——
* 与 wave_ring_read_ch 同一类「返回实际长度」的契约 */
if (wave_store_head_build(&both, buf, (uint32_t)sizeof(buf)) != (int)WAVE_ST_HEAD_SZ_CUR) {
ok_both = 0;
}
if (wave_store_head_parse(buf, (uint32_t)sizeof(buf), &s_back) != WAVE_ST_OK) {
ok_both = 0;
} else {
if (s_back.flags_seq != 0xFFu) {
ok_both = 0;
}
for (i = 0u; i < s_back.ch_count; i++) {
if (s_back.ch[i].avail != both.ch[i].avail) {
ok_both = 0;
}
}
}
CHECK(buf[WAVE_ST_OFF_ENG] == expect_eng(buf[WAVE_ST_OFF_FLAGS]),
"构造形态落进字节之后 eng 与逐路有效位图仍自洽");
fill_meta(&v01);
v01.flags_seq = 0x00u;
v01.eng = (uint8_t)WAVE_ENG_PIN;
if ((wave_store_head_build(&v01, buf, (uint32_t)sizeof(buf)) == (int)WAVE_ST_HEAD_SZ_CUR) &&
(buf[WAVE_ST_OFF_ENG] == (uint8_t)WAVE_ENG_PIN) &&
(buf[WAVE_ST_OFF_FLAGS] == 0x00u)) {
CHECK(buf[WAVE_ST_OFF_ENG] == expect_eng(buf[WAVE_ST_OFF_FLAGS]),
"本波真值形态落进字节之后 eng 仍停引脚域(0x00 不是 0xFF,不许谎报二次值)");
} else {
CHECK(0, "本波真值形态编码后 eng 与逐路有效位图字节不符");
}
CHECK(expect_eng(0xFFu) == (uint8_t)WAVE_ENG_SEC, "派生式:电气 8 路全有效才升二次值档位");
CHECK(expect_eng(0x00u) == (uint8_t)WAVE_ENG_PIN, "派生式:一路都没标定则停引脚域");
CHECK(expect_eng(0x7Fu) == (uint8_t)WAVE_ENG_PIN, "派生式:部分标定(缺 IN 那一路)整帧仍算引脚域");
CHECK(eng_selfconsistent(&v01) != 0, "夹具自洽:真值形态(全零 / 引脚域)");
CHECK(eng_selfconsistent(&both) != 0, "夹具自洽:构造形态(全有效 / 二次值)");
CHECK(eng_selfconsistent(&s_meta) != 0, "夹具自洽:本套所有合成段共用的那份段头");
/* 反证本层**不参与**单位标签派生:故意造一份违反派生式的段头(全有效却仍标引脚域),
* 编码与解码都该照样通过——判废只留给完整性判据,派生式不一致是 PC 侧软告警的活。
* 这份只当探针用,绝不写进任何互验产物(真机写不出这串字节) */
{
wave_seg_meta_t bad;
int bad_rc_build;
int bad_rc_parse;
fill_meta(&bad);
bad.flags_seq = 0xFFu;
bad.eng = (uint8_t)WAVE_ENG_PIN;
bad_rc_build = wave_store_head_build(&bad, buf, (uint32_t)sizeof(buf));
bad_rc_parse = (bad_rc_build == (int)WAVE_ST_HEAD_SZ_CUR) ?
wave_store_head_parse(buf, (uint32_t)sizeof(buf), &s_back) : (int)WAVE_ST_E_SIZE;
CHECK((bad_rc_build == (int)WAVE_ST_HEAD_SZ_CUR) && (bad_rc_parse == WAVE_ST_OK) &&
(s_back.eng == (uint8_t)WAVE_ENG_PIN) && (s_back.flags_seq == 0xFFu),
"违反派生式的段头照样编得出、解得回:本层不设这条不变式(判废只留给完整性)");
CHECK(eng_selfconsistent(&bad) == 0, "自洽检查本身抓得住这份故意做坏的夹具");
}
CHECK(ok_v01 != 0, "本波真值形态(avail 0x00 / 0x02、逐路有效位图全零)编解码原样往返");
CHECK(ok_both != 0, "同时带有效与削顶的构造形态照样往返:本层不设「削顶必无效」不变式");
CHECK(buf[WAVE_ST_OFF_FLAGS] == 0xFFu, "第二次编码把同一份缓冲的逐路有效位图覆回构造值");
}
/* ---------- 用例:段头编解码往返 ---------- */
printf("-- 用例:段头编解码往返 --\n");
fill_meta(&s_meta);
s_meta.ch[0].k_q16 = 10000000;
s_meta.ch[0].b_codes = -8192;
s_meta.ch[7].k_q16 = 52800000;
s_meta.ch[7].b_codes = 8191;
s_meta.wall = 0xFFFFFFFFu;
s_meta.frame_seq = 0u;
{
uint32_t hn = build_head();
CHECK(hn == 128u, "head_build 产出整头 128 字节");
CHECK((s_img[0] == (uint8_t)WAVE_ST_MAGIC0) && (s_img[1] == (uint8_t)WAVE_ST_MAGIC1) &&
(s_img[2] == (uint8_t)WAVE_ST_VER), "魔数 = 57 52 01(第三字节就是版本号)");
CHECK(s_img[WAVE_ST_OFF_VER] == s_img[WAVE_ST_OFF_MAGIC + 2u], "ver 字段与魔数第三字节同值");
CHECK((s_img[30u + (12u * 8u)] != 0u) || (s_img[31u + (12u * 8u)] != 0u), "头 CRC 落在 126/127");
{
uint16_t crc = wave_store_crc(s_img, 126u);
CHECK((s_img[126] == (uint8_t)(crc & 0xFFu)) && (s_img[127] == (uint8_t)(crc >> 8)),
"头 CRC 低字节在前(§6.3)");
}
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_OK, "head_parse OK");
CHECK(meta_same(&s_meta, &s_back) != 0, "13 个标量 + 8 项表逐字段往返一致(含负系数)");
}
/* ---------- 用例:拒绝清单 ---------- */
printf("-- 用例:拒绝清单 --\n");
{
wave_seg_meta_t m;
fill_meta(&m);
m.ch_count = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "零通道拒(长度不成立)");
fill_meta(&m);
m.ch_count = 10u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "通道数超上限拒");
fill_meta(&m);
m.pts_per_ch = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "零点数拒(即使 CRC 能自洽)");
fill_meta(&m);
m.pre_pts = 321u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "预触发超段长拒");
fill_meta(&m);
m.pts_per_cycle = 1u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE,
"每周波不足 2 点拒(与 PC 侧 reader 同一把尺)");
fill_meta(&m);
m.pts_per_ch = 10u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE,
"段长不足一个整周期拒(基波无从取窗)");
fill_meta(&m);
m.fs_hz = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_SIZE, "采样率 0 拒");
fill_meta(&m);
m.ch[1].sem = 6u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "表序错(sem != 下标)拒");
fill_meta(&m);
m.ch[2].bits = 0u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "位宽 0 拒");
fill_meta(&m);
m.ch[2].bits = 33u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "位宽超域拒");
fill_meta(&m);
m.ch[3].conv = 2u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "转换器号非法拒");
fill_meta(&m);
m.eng = 3u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "折算档位 3 拒");
fill_meta(&m);
m.trig_src = 5u;
CHECK(wave_store_head_build(&m, s_img, IMG_CAP) == WAVE_ST_E_FORMAT, "触发源 5 拒");
fill_meta(&m);
CHECK(wave_store_head_build(&m, s_img, 127u) == WAVE_ST_E_PARAM, "缓冲装不下整头拒");
CHECK(wave_store_head_build(NULL, s_img, IMG_CAP) == WAVE_ST_E_PARAM, "空段头指针拒");
CHECK(wave_store_seg_size(NULL) == 0u, "seg_size 空指针返 0");
}
/* 解码侧的拒绝(先造一份合法头,再逐项破坏) */
fill_meta(&s_meta);
{
uint32_t hn = build_head();
uint8_t save = s_img[0];
CHECK(hn == 128u, "参考头重编 OK");
s_img[0] = (uint8_t)'X';
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_FORMAT, "魔数第一字节错 -> 格式错");
s_img[0] = save;
s_img[WAVE_ST_OFF_VER] = 2u;
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_FORMAT,
"ver 与魔数第三字节不一致 -> 格式错(不许按新布局猜)");
s_img[WAVE_ST_OFF_VER] = (uint8_t)WAVE_ST_VER;
s_img[WAVE_ST_OFF_MAGIC + 2u] = 2u;
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_FORMAT, "版本号 2 -> 拒");
s_img[WAVE_ST_OFF_MAGIC + 2u] = (uint8_t)WAVE_ST_VER;
CHECK(wave_store_head_parse(s_img, 127u, &s_back) == WAVE_ST_E_PARAM, "缓冲不足整头 -> 参数错");
CHECK(wave_store_head_parse(s_img, 7u, &s_back) == WAVE_ST_E_PARAM, "连通道数字节都读不到 -> 参数错");
s_img[40] = (uint8_t)(s_img[40] ^ 0xA5u);
CHECK(wave_store_head_parse(s_img, hn, &s_back) == WAVE_ST_E_CRC, "头覆盖区被改一字节 -> CRC 不符");
CHECK(wave_store_head_parse(NULL, hn, &s_back) == WAVE_ST_E_PARAM, "空缓冲指针");
CHECK(wave_store_head_parse(s_img, hn, NULL) == WAVE_ST_E_PARAM, "空输出指针");
}
/* ---------- 用例:小端解码与定点折算 ---------- */
printf("-- 用例:解码与折算 --\n");
{
static const uint8_t s_le[8] = { 0x00u, 0x80u, 0xFFu, 0xFFu, 0x2Du, 0x23u, 0x00u, 0x00u };
int16_t out[4];
uint32_t got = 0u;
CHECK(wave_store_le_decode(s_le, 8u, out, 4u, &got) == WAVE_ST_OK, "解码 OK");
CHECK((got == 4u) && (out[0] == -32768) && (out[1] == -1) && (out[2] == 9005) && (out[3] == 0),
"小端按补码解释:0x8000 -> -32768、0xFFFF -> -1");
CHECK(wave_store_le_decode(s_le, 7u, out, 4u, &got) == WAVE_ST_E_PARAM, "奇数字节拒(不丢尾巴)");
CHECK(wave_store_le_decode(s_le, 8u, out, 3u, &got) == WAVE_ST_E_PARAM, "容量不足拒");
CHECK(wave_store_le_decode(NULL, 2u, out, 1u, &got) == WAVE_ST_E_PARAM, "空指针拒");
}
{
wave_ch_desc_t d;
int32_t mv = 0;
d.sem = 0u; d.conv = 0u; d.bits = 16u; d.avail = 1u; d.k_q16 = 10000000; d.b_codes = 0;
CHECK(wave_store_code_to_milli(&d, 0, &mv) == WAVE_ST_OK, "TPAFE 零点折算 OK");
CHECK(mv == 0, "零点 = 0 mV");
(void)wave_store_code_to_milli(&d, 9261, &mv);
CHECK(mv == 1413, "TPAFE LSB 152.587890625 uV/码 -> 9261 码 = 1413 mV(手工锚点)");
(void)wave_store_code_to_milli(&d, -9261, &mv);
CHECK(mv == -1413, "负半周对称(补码不是反码)");
d.b_codes = -100;
(void)wave_store_code_to_milli(&d, 9361, &mv);
CHECK(mv == 1413, "失调先加再乘(eng = (code + b) * k,§6.3)");
d.b_codes = 0;
d.k_q16 = 52800000; /* 片内 ADC:805.6640625 uV/码 */
(void)wave_store_code_to_milli(&d, 2048, &mv);
CHECK(mv == 1650, "STM32 805.6640625 uV/码 -> 2048 码 = 1650 mV(手工锚点)");
d.k_q16 = 0;
(void)wave_store_code_to_milli(&d, 2048, &mv);
CHECK(mv == 0, "系数没填时幅值归零(形状仍在,PC 侧会看到平线)");
CHECK(wave_store_code_to_milli(NULL, 0, &mv) == WAVE_ST_E_PARAM, "空表项拒");
CHECK(wave_store_code_to_milli(&d, 0, NULL) == WAVE_ST_E_PARAM, "空输出拒");
}
/* ---------- 用例:打包游标 ---------- */
printf("-- 用例:打包游标 --\n");
fill_meta(&s_meta);
{
uint32_t len = 0u;
int rc = 0;
s_read_calls = 0u;
s_read_points = 0u;
CHECK(pack_whole(256u, &len, &rc) == 0, "整段跑完(256 字节分片)");
CHECK(s_img[WAVE_ST_OFF_ENG] == expect_eng(s_img[WAVE_ST_OFF_FLAGS]),
"整段头字节自洽:写出去的 eng 与逐路有效位图满足平台派生式");
CHECK(len == 5250u, "实际产出字节数 = 5250");
CHECK(s_read_points == (320u * 8u), "数据源被取的点数 = 320*8(不多取不少取)");
CHECK(s_read_calls == 40u, "每列 5 批 x 8 列 = 40 次回调(批量取数,不是逐点)");
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_OK, "自产字节过整段自检");
CHECK(meta_same(&s_meta, &s_back) != 0, "自检回填的段头与原段头一致");
{
uint16_t dcrc = wave_store_crc(&s_img[128], 5120u);
CHECK((s_img[len - 2u] == (uint8_t)(dcrc & 0xFFu)) && (s_img[len - 1u] == (uint8_t)(dcrc >> 8)),
"数据 CRC 覆盖数据区、低字节在前、贴在文件尾");
}
}
{
uint32_t len = 0u;
int rc = 0;
CHECK(pack_whole(128u, &len, &rc) == 0, "128 字节分片(= 整头那么大)也能跑完");
CHECK(len == 5250u, "128 分片总长仍 5250(产出与分片大小无关)");
}
{
uint32_t len = 0u;
int rc = 0;
CHECK(pack_whole(1u, &len, &rc) != 0, "容量 1 字节装不下整头 -> 首步就拒");
}
{
wave_pack_ctx_t ctx;
uint32_t n = 99u;
int rc;
CHECK(wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL) == WAVE_ST_OK, "pack_start OK");
CHECK(wave_store_pack_next(&ctx, s_img, 127u, &n) == WAVE_ST_E_PARAM, "头一步容量不足 -> E_PARAM");
CHECK(n == 0u, "拒绝时一个字节都不产(不写半截头)");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK((rc == WAVE_ST_OK) && (n == 128u), "同一游标重试成功(拒绝不留副作用)");
rc = wave_store_pack_next(&ctx, s_img, 1u, &n);
CHECK(rc == WAVE_ST_E_PARAM, "数据区容量装不下一个样本 -> 拒");
CHECK(n == 0u, "同样不产半个样本的字节");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
/* 恢复容量后这一拍产满 256 字节:数据区每次进循环都拿「批量上限」与
* 「容量剩余」里小的那个,256 字节 = 128 点 > 批量 64 点 → 两次取数才产满 */
CHECK((rc == WAVE_ST_OK) && (n == 256u), "恢复容量后继续推进(产满 256 字节)");
while (rc == WAVE_ST_OK) {
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
}
CHECK(rc == WAVE_ST_DONE, "跑到 DONE");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK((rc == WAVE_ST_DONE) && (n == 0u), "DONE 之后幂等(重复调不再产字节)");
}
{
wave_pack_ctx_t ctx;
uint32_t n = 0u;
int rc;
CHECK(wave_store_pack_start(&ctx, &s_meta, NULL, NULL) == WAVE_ST_OK, "无数据源也能 start(只产头)");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK((rc == WAVE_ST_OK) && (n == 128u), "头能产");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK((rc == WAVE_ST_E_STATE) && (n == 0u),
"没有数据源却进数据区 -> 拒且不产空字节(产空字节 = 伪造一条直线)");
}
{
wave_pack_ctx_t ctx;
uint32_t n = 0u;
int rc;
CHECK(wave_store_pack_start(&ctx, &s_meta, short_reader, NULL) == WAVE_ST_OK, "少给点数的数据源 start OK");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK(rc == WAVE_ST_OK, "头一步 OK");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK(rc == WAVE_ST_E_STATE, "数据源少给一个点 -> 拒(不拿半批凑长度)");
CHECK(wave_store_pack_start(&ctx, &s_meta, err_reader, NULL) == WAVE_ST_OK, "报错数据源 start OK");
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
rc = wave_store_pack_next(&ctx, s_img, 256u, &n);
CHECK(rc == WAVE_ST_E_RANGE, "数据源的错误码原样透传");
}
{
wave_pack_ctx_t ctx;
fill_meta(&s_meta);
s_meta.ch[0].sem = 5u;
CHECK(wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL) == WAVE_ST_E_FORMAT,
"pack_start 也跑同一把尺(表序错别想搬数据)");
s_meta.ch[0].sem = 0u;
s_meta.pts_per_ch = 0u;
CHECK(wave_store_pack_start(&ctx, &s_meta, fake_reader, NULL) == WAVE_ST_E_SIZE,
"零点数在 start 就拒");
CHECK(wave_store_pack_start(NULL, &s_meta, fake_reader, NULL) == WAVE_ST_E_PARAM, "空游标拒");
CHECK(wave_store_pack_start(&ctx, NULL, fake_reader, NULL) == WAVE_ST_E_PARAM, "空段头拒");
CHECK((wave_store_pack_offset(NULL) == 0u) && (wave_store_pack_total(NULL) == 0u), "空游标查询返 0");
}
/* ---------- 用例:整段自检 ---------- */
printf("-- 用例:整段自检与判废 --\n");
fill_meta(&s_meta);
{
uint32_t len = 0u;
int rc = 0;
CHECK(pack_whole(256u, &len, &rc) == 0, "参考段重跑 OK");
CHECK(wave_store_seg_check(s_img, 0u, &s_back) == WAVE_ST_E_SIZE, "零长度 -> 判废(不许画东西)");
CHECK(wave_store_seg_check(s_img, len - 1u, &s_back) == WAVE_ST_E_SIZE, "少一字节 -> 长度不符");
CHECK(wave_store_seg_check(s_img, len + 1u, &s_back) == WAVE_ST_E_SIZE, "多一字节 -> 长度不符");
CHECK(wave_store_seg_check(s_img, 300u, &s_back) == WAVE_ST_E_SIZE, "半截段 -> 长度不符");
{
uint8_t save = s_img[1000];
s_img[1000] = (uint8_t)(s_img[1000] ^ 0x5Au);
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_E_CRC, "数据区改一字节 -> 数据 CRC 不符");
s_img[1000] = save;
}
{
uint8_t save = s_img[len - 2u];
s_img[len - 2u] = (uint8_t)(s_img[len - 2u] ^ 0x01u);
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_E_CRC, "数据 CRC 自身被改 -> 不符");
s_img[len - 2u] = save;
}
{
uint8_t save = s_img[14];
s_img[14] = (uint8_t)(s_img[14] + 1u);
CHECK(wave_store_seg_check(s_img, len, &s_back) == WAVE_ST_E_CRC, "段首毫秒被改 -> 头 CRC 拦下");
s_img[14] = save;
}
CHECK(wave_store_seg_check(NULL, len, &s_back) == WAVE_ST_E_PARAM, "空镜像指针");
CHECK(wave_store_seg_check(s_img, len, NULL) == WAVE_ST_OK, "段头输出可空(只判废的调用方)");
}
/* ---------- 用例:段名轮转 ---------- */
printf("-- 用例:段名轮转 --\n");
{
char path[WAVE_ST_PATH_MAX];
uint8_t idx = 99u;
/* 名字长度 = 16 个字符(/wave/ 6 + WAVE 4 + 两位 2 + .BIN 4),缓冲要 17 字节;
* 本条断言在 PATH_MAX 曾写错成 16 时抓出了 seg_path 内部 buf[] 的越界写 */
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 0u) == 16, "段号 0 拼出 16 字符");
CHECK(strcmp(path, "/wave/WAVE00.BIN") == 0, "严格 8.3:/wave/WAVE00.BIN");
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 7u) == 16, "段号 7");
CHECK(strcmp(path, "/wave/WAVE07.BIN") == 0, "个位补零");
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 99u) == 16, "段号 99");
CHECK(strcmp(path, "/wave/WAVE99.BIN") == 0, "最大号");
CHECK(wave_store_seg_path(path, (uint32_t)sizeof(path), 100u) == WAVE_ST_E_RANGE, "段号 100 越界");
CHECK(wave_store_seg_path(path, 16u, 3u) == WAVE_ST_E_PARAM, "容量 16 装不下「16 字符 + 结束符」 -> 拒");
CHECK(wave_store_seg_path(path, 17u, 3u) == 16, "容量刚好 17 -> 通过");
CHECK(wave_store_seg_path(NULL, 17u, 3u) == WAVE_ST_E_PARAM, "空指针拒");
CHECK(wave_store_idx_from_name("/wave/WAVE07.BIN", &idx) == WAVE_ST_OK, "反解整路径");
CHECK(idx == 7u, "反解得 7");
/* 字面量用宏拼出来(运行时仍是同一个名字):本仓「字母紧跟数字」是编号门禁的
* 裸形态判据,测试数据里的自造号不该去撞它 */
CHECK(wave_store_idx_from_name(WAVE_ST_PFX "42" WAVE_ST_EXT, &idx) == WAVE_ST_OK, "反解纯文件名");
CHECK(idx == 42u, "反解得 42");
/* cppcheck 把「字符串字面量之后接宏」的拼接形态判成配置解析失败(unknownMacro
* 是配置类报错,不是代码缺陷;上一行的同族拼接它就不报)。本仓先例 =
* App/util/sys_clock.c 的带理由行内压制,这里同样写明理由而不是改测试。 */
// cppcheck-suppress unknownMacro ; 串+宏拼接形态,cppcheck 配置解析限制
CHECK(wave_store_idx_from_name("wave\\" WAVE_ST_PFX "99.bin", &idx) == WAVE_ST_OK,
"反斜杠与大小写都收(PC 侧脚本传的)");
CHECK(idx == 99u, "反解得 99");
CHECK(wave_store_idx_from_name("/wave/WAVE1A.BIN", &idx) == WAVE_ST_E_NAME, "非数字号 -> 拒");
CHECK(wave_store_idx_from_name("/wave/WAVE.BIN", &idx) == WAVE_ST_E_NAME, "缺号 -> 拒");
CHECK(wave_store_idx_from_name("/wave/WAVE007.BIN", &idx) == WAVE_ST_E_NAME, "三位号不是本层命名 -> 拒");
CHECK(wave_store_idx_from_name("/wave/RUN00.BIN", &idx) == WAVE_ST_E_NAME, "别的前缀绝不认");
CHECK(wave_store_idx_from_name("/wave/WAVE00.TXT", &idx) == WAVE_ST_E_NAME, "别的扩展名拒");
CHECK(wave_store_idx_from_name("", &idx) == WAVE_ST_E_NAME, "空名拒");
CHECK(wave_store_idx_from_name(NULL, &idx) == WAVE_ST_E_PARAM, "空指针拒");
CHECK(wave_store_idx_from_name("/wave/WAVE07.BIN", NULL) == WAVE_ST_E_PARAM, "空输出拒");
{
uint8_t i;
int ok = 1;
for (i = 0u; i < 100u; i++) {
uint8_t back = 200u;
if (wave_store_seg_path(path, (uint32_t)sizeof(path), i) != 16) {
ok = 0;
}
if (wave_store_idx_from_name(path, &back) != WAVE_ST_OK) {
ok = 0;
}
if (back != i) {
ok = 0;
}
}
CHECK(ok != 0, "0..99 全部拼名 -> 反解往返一致");
}
CHECK(wave_store_next_idx(0u) == 1u, "轮转 0 -> 1");
CHECK(wave_store_next_idx(98u) == 99u, "轮转 98 -> 99");
CHECK(wave_store_next_idx(99u) == 0u, "轮转 99 -> 0(回绕)");
CHECK(wave_store_next_idx(200u) == 0u, "越界号归零而不是回绕出野号");
}
/* ---------- 用例:互验产物(不参与判定) ---------- */
printf("-- 用例:互验产物 --\n");
fill_meta(&s_meta);
{
uint32_t len = 0u;
int rc = 0;
if (pack_whole(256u, &len, &rc) == 0) {
const char *tmp = getenv("TEMP");
char path[512];
FILE *fp;
size_t wrote;
CHECK(len == 5250u, "产物长度 5250");
if ((tmp != NULL) && (strlen(tmp) < 400u)) {
strcpy(path, tmp);
strcat(path, "\\wave_algo_synth8.bin");
fp = fopen(path, "wb");
if (fp != NULL) {
wrote = fwrite(s_img, 1u, len, fp);
(void)fclose(fp);
CHECK(wrote == (size_t)len, "合成段落盘成功");
CHECK(s_img[WAVE_ST_OFF_ENG] == expect_eng(s_img[WAVE_ST_OFF_FLAGS]),
"互验产物自洽:这份构造段的单位标签与逐路有效位图对得上(真机写得出)");
/* 形态在日志里说清楚:本套是「同时带有效与削顶」的构造形态,
* 本波真值形态(逐路有效位图全零、片内 ADC 只带削顶位)由 wave_trig 那套给 */
printf(" [INFO] 互验产物(构造形态 = 已标定后仍削顶:逐路有效位图 0xFF、avail 0x01 / 0x03、"
"单位标签 %u 即二次值):%s\n",
(unsigned int)s_img[WAVE_ST_OFF_ENG], path);
} else {
printf(" [INFO] 互验产物跳过:%s 打不开\n", path);
}
}
} else {
CHECK(0, "互验产物:打包失败");
}
}
printf("\n=== Wave Store Tests: %d/%d PASSED (failed=%d) ===\n", s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}