Files
STM32F4-Base/test/host/run_par/run_par_test.c
T
wandering1 3675b89e8e feat(adc): 新增变比维 r 三维折算,录波段格式升 v2
采样板注入 Ua=5V/Ia=0.5A 读数偏小,根因是固件只输出引脚电压、
缺「端子→引脚」变比。折算改三维:

工程量 = (code + b) × k[µV/码] × 1e-6 × r

k 保持 ROM 引脚 LSB,b 为零点,r 为端子变比(int32 Q16)。
变比不能并进 k:UA 端子域 k 达 1.5e5 µV/码、Q16 溢出 int32;
r 单独存 Q16 不溢出。两点标定改标 r,k 仍是板级事实。

- 新开单段 [adc_cal_sec](8 键 r_ua..r_in),cfg 段数达 8/8 上限
- 一次变比复用 prot_set 四项,不新开段;r=0 表示本路不折算
- ROM 默认带台架实测变比(电压族 1000、电流族 29.4118),
  烧进固件即显二次值;VALID 仍须两点标定,两者互不派生
- eng 判据改「每路要么已配变比、要么削顶不显示」,不再看 VALID
- 104 地址 0x8050~0x8057 写 r,run_par_cal_r_set 写值与置脏同函数
- 录波 v2:通道表加 int32 r_q16,头 128→160、整段 5250→5282;
  code_to_milli 用余数分解换等价单次圆整,20 万组大整数对拍 0 不符
- 文档同步:采集与算法层/通道映射/故障录波/网络配置/配置文件/
  产品需求/内存台账,历史带日期实测数字按冻结口径不改
- 单测 13 套全绿,/keil-build 0 错误 0 警告(256024/31760/
  2080/152368),RAM 实测回填内存内容分配策略 §6.1
2026-11-11 11:31:34 +08:00

476 lines
20 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.
/*
* 模块名称:运行参数段(run_par)主机侧单元测试
* 模块功能:离线钉死「数据可信」运行参数段的三层契约——① [run_par] cfg 段:
* 注册/工厂默认/落盘往返/文件改值生效(真实 cfg 引擎 + 内存虚拟盘,
* 同 adc_cal 套件先例);② 104 地址映射:命中集/空隙/两段式/分档
* 量程守卫;③ k/b 覆盖直通 adc_cal_set(真实标定层):合法带拒绝、
* 单侧 RMW 保另一侧、回显 = 生效值视图。
* 被测对象:真实源文件(#include 纳入):run_par.c + cfg_file.c + adc_cal.c;
* dbg 计数桩(stub/dbg_log.h)+ cfg_store_* 内存虚拟盘 + adc_input_desc
* V0.1 权威映射桩。**路由顺序证据(run_par 表先 / prot_set 后)在
* test/host/iec104 TC-IEC104-029 钉(那边才有全协议栈)**。
*
* 覆盖点(TC-RUNPAR-xxx;属主登记处 = 本目录,族 TC-HOST-RUNPAR):
* TC-RUNPAR-001 init 工厂默认 = 工程量重定后的额定档 + idx 守卫
* TC-RUNPAR-002 死区写往返 + 分档上限(V/A/Hz)+ NaN/负值拒 + 定点量化
* TC-RUNPAR-003 落盘链:set→脏→store_save 成功清脏 + 文件含 9 键 +
* load 回读往返 + 外部改文件值生效 + 无脏 save 空操作
* TC-RUNPAR-004 104 读映射:0x8030~0x8038 命中 / 空隙与邻段 -1 / k·b·r 回显
* TC-RUNPAR-005 104 两段式:选择段不提交不置脏,执行段提交置脏
* TC-RUNPAR-006 k/b 直通:合法带接受与拒绝 / 非正 k 拒 / b 单侧 RMW 保 k /
* 非法带值经 load 注入后回显 = 生效值(哨兵视图)
* TC-RUNPAR-007 init 幂等:重复调用复位默认 + 清脏;重注册被引擎拒(留痕)
* TC-RUNPAR-008 引擎副本 = 生效值(无回调):load 原地改副本即时可见
* TC-RUNPAR-009 变比 r 的 104 读写:0x8050~0x8057 写 2000.0 回读一致 / 写 0
* 接受且回读 0 / 0.5 与 -1 与 40000 拒 / 0x8058 非本段 / 选择段
* 粗守只挡 NaN·Inf(0.5 在选择段返 0,执行段才拒——两段可不同)
*
* 通过:全部 TC PASS(main 返回 0)。
* 创建日期:2026-10-25(道D 运行参数段)
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 先桩头后生产源码(顺序敏感,同 adc_cal 套件) */
#include "dbg_log.h"
/* ===================== 内存虚拟文件系统(cfg_store_* 强符号) ===================== */
#include "cfg_file_store.h"
#define VFS_CAP 8192u
static uint8_t s_vfs[VFS_CAP];
static size_t s_vfs_len = 0u;
static uint32_t s_vfs_mtime = 100u;
static int s_vfs_exists = 0;
static int s_vfs_writes = 0;
void *cfg_store_alloc(size_t size) { return malloc(size); }
void cfg_store_free(void *p_buf) { free(p_buf); }
uint32_t cfg_store_heap_free(void) { return 0u; }
int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nread) {
size_t n;
(void)p_path;
if (!s_vfs_exists) {
return -1;
}
n = s_vfs_len;
if (n > cap) {
n = cap;
}
(void)memcpy(p_buf, s_vfs, n);
*p_nread = n;
return 0;
}
int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len) {
(void)p_path;
if (len > VFS_CAP) {
return -1;
}
(void)memcpy(s_vfs, p_buf, len);
s_vfs_len = len;
s_vfs_exists = 1;
s_vfs_mtime++;
s_vfs_writes++;
return 0;
}
int cfg_store_stat(const char *p_path, cfg_file_stat_t *p_st) {
(void)p_path;
if (!s_vfs_exists) {
return -1;
}
p_st->size = (uint32_t)s_vfs_len;
p_st->mtime = s_vfs_mtime;
return 0;
}
int cfg_store_remove(const char *p_path) {
(void)p_path;
s_vfs_exists = 0;
s_vfs_len = 0u;
return 0;
}
const char *cfg_store_config_path(void) { return "/config.ini"; }
const char *cfg_store_tmp_path(void) { return "/config.tmp"; }
/* ---- sys_log 桩 ---- */
#include "sys_log.h"
static int s_syslog_count = 0;
void syslog_post(syslog_id_t id, const char *desc) {
(void)id;
(void)desc;
s_syslog_count++;
}
/* ---- adc_input_desc 桩:V0.1 权威映射(docs/硬件平台/ADC通道映射说明.md) ---- */
#include "adc_input.h"
static const adc_channel_desc_t s_desc_stub[ADC_SEM_COUNT] = {
{ ADC_SRC_TPAFE, 6u }, /* UA = buf[6] */
{ ADC_SRC_STM32, 14u }, /* UB = CH14(PC4) */
{ ADC_SRC_STM32, 3u }, /* UC = CH3 */
{ ADC_SRC_TPAFE, 5u }, /* UN = buf[5] */
{ ADC_SRC_TPAFE, 2u }, /* IA = buf[2] */
{ ADC_SRC_STM32, 6u }, /* IB = CH6 */
{ ADC_SRC_STM32, 1u }, /* IC = CH1 */
{ ADC_SRC_STM32, 2u }, /* IN = CH2 */
{ ADC_SRC_STM32, 5u }, /* TEMP = CH5 */
};
const adc_channel_desc_t *adc_input_desc(adc_semantic_t ch) {
if ((int)ch < 0 || (unsigned)ch >= (unsigned)ADC_SEM_COUNT) {
return NULL;
}
return &s_desc_stub[(unsigned)ch];
}
/* dbg 计数定义(stub/dbg_log.h extern 声明) */
int g_rptest_err_count = 0;
int g_rptest_info_count = 0;
int g_rptest_debug_count = 0;
/* ===================== 测试框架(极简,同 adc_cal 套件) ===================== */
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)
#define ASSERT_NEAR(a, b, eps, name) CHECK(fabsf((float)(a) - (float)(b)) <= (eps), name)
/* ===================== 被测代码 ===================== */
#include "cfg_file.c"
#include "adc_cal.c"
#include "run_par.c"
static void vfs_reset(void) {
s_vfs_len = 0u;
s_vfs_mtime = 100u;
s_vfs_exists = 0;
}
/* 全复位:引擎段复位 + run_par 复位 + 标定复位(重注册被拒 = 预期,清计数) */
static void reset_all_rp(void) {
run_par_init();
adc_cal_init();
g_rptest_err_count = 0;
}
/* ===================== 测试用例 ===================== */
static void tc_defaults(void)
{
uint8_t i;
vfs_reset();
reset_all_rp();
for (i = 0u; i < 4u; i++) {
CHECK(fabsf(run_par_deadband(i) - 0.5f) <= 1e-6f, "TC-RUNPAR-001 voltage ch factory 0.5 V");
}
for (i = 4u; i < 8u; i++) {
CHECK(fabsf(run_par_deadband(i) - 0.005f) <= 1e-9f, "TC-RUNPAR-001 current ch factory 0.005 A");
}
CHECK(fabsf(run_par_deadband(8u) - 0.5f) <= 1e-6f, "TC-RUNPAR-001 freq factory 0.5 Hz");
CHECK(run_par_deadband(9u) == 0.0f, "TC-RUNPAR-001 idx guard returns zero");
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-001 init leaves clean");
}
static void tc_set_range(void)
{
volatile float fnan;
vfs_reset();
reset_all_rp();
CHECK((run_par_deadband_set(0u, 0.7f) == 0)
&& (fabsf(run_par_deadband(0u) - 0.7f) <= 1e-6f),
"TC-RUNPAR-002 write round trip voltage domain");
CHECK((run_par_deadband_set(4u, 0.02f) == 0)
&& (fabsf(run_par_deadband(4u) - 0.02f) <= 1e-6f),
"TC-RUNPAR-002 write round trip current domain");
CHECK(run_par_deadband_set(0u, 50.0f) == 0, "TC-RUNPAR-002 voltage cap endpoint inclusive");
CHECK((run_par_deadband_set(0u, 50.001f) == -1)
&& (fabsf(run_par_deadband(0u) - 50.0f) <= 1e-6f),
"TC-RUNPAR-002 over voltage cap rejected");
CHECK(run_par_deadband_set(4u, 1.001f) == -1, "TC-RUNPAR-002 current cap 1 A enforced");
CHECK(run_par_deadband_set(8u, 5.001f) == -1, "TC-RUNPAR-002 freq cap 5 Hz enforced");
CHECK(run_par_deadband_set(0u, -0.001f) == -1, "TC-RUNPAR-002 negative rejected");
fnan = 0.0f;
fnan = fnan / fnan;
CHECK(run_par_deadband_set(0u, fnan) == -1, "TC-RUNPAR-002 NaN rejected");
CHECK(run_par_deadband_set(9u, 1.0f) == -1, "TC-RUNPAR-002 idx guard rejected");
CHECK((run_par_deadband_set(2u, 0.1234f) == 0)
&& (fabsf(run_par_deadband(2u) - 0.123f) <= 1e-6f),
"TC-RUNPAR-002 milli quantization rounds to nearest");
}
static void tc_persist(void)
{
char probe[24];
vfs_reset();
reset_all_rp();
CHECK(run_par_store_save() == 0, "TC-RUNPAR-003 clean save is no-op ok");
CHECK(run_par_deadband_set(2u, 0.9f) == 0, "TC-RUNPAR-003 seed write ok");
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-003 write marks dirty");
CHECK((run_par_store_save() == 0) && (run_par_dirty_peek() == 0u),
"TC-RUNPAR-003 save clears dirty on success");
(void)memset(probe, 0, sizeof(probe));
(void)memcpy(probe, "db_uc=", 6u);
CHECK((s_vfs_exists == 1) && (strstr((const char *)s_vfs, "db_uc=900") != NULL),
"TC-RUNPAR-003 file carries milli value");
CHECK(strstr((const char *)s_vfs, "db_freq=500") != NULL,
"TC-RUNPAR-003 all nine keys present (freq spot check)");
/* 往返:改内存 → 落盘 → 引擎 load 回读 */
CHECK(run_par_deadband_set(4u, 0.02f) == 0, "TC-RUNPAR-003 second seed");
(void)run_par_store_save();
run_par_deadband_set(4u, 0.5f); /* 覆盖内存态 */
CHECK(cfg_file_load() == 0, "TC-RUNPAR-003 engine reload ok");
CHECK(fabsf(run_par_deadband(4u) - 0.02f) <= 1e-9f,
"TC-RUNPAR-003 reload restores last saved value");
/* set(4,0.5) 是一次未落盘的本地整定 → 脏必须活过 reload(reload 不是
* 清脏事件;只有 save 成功才清) */
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-003 unsaved local edit survives reload");
/* 外部改档(FTP 手改 config.ini 的现场形态):load 后副本即生效值 */
{
static const char s_edited[] =
"[run_par]\ndb_ua=250\ndb_ub=500\ndb_uc=500\ndb_un=500\n"
"db_ia=10\ndb_ib=5\ndb_ic=5\ndb_in=5\ndb_freq=800\n";
(void)memcpy(s_vfs, s_edited, sizeof(s_edited) - 1u);
s_vfs_len = sizeof(s_edited) - 1u;
s_vfs_exists = 1;
s_vfs_mtime++;
}
CHECK(cfg_file_load() == 0, "TC-RUNPAR-003 edited file accepted");
CHECK((fabsf(run_par_deadband(0u) - 0.25f) <= 1e-6f)
&& (fabsf(run_par_deadband(4u) - 0.01f) <= 1e-9f)
&& (fabsf(run_par_deadband(8u) - 0.8f) <= 1e-6f),
"TC-RUNPAR-003 edited values live in copy immediately");
/* 越界文件值:引擎照收(int32 域),104/COV 读到超程值 —— 守卫在**写路径**,
* 文件通路的防线是"人工编辑 + 沉降去抖 + 可再改回";与 adc_cal 合法带不同,
* 死区超程不毁量纲(只是上送变钝),此处钉住"引擎不吞值"即可 */
CHECK(fabsf(run_par_deadband(0u) - 0.25f) <= 1e-6f, "TC-RUNPAR-003 value stable re-read");
}
static void tc_104_read(void)
{
float v;
vfs_reset();
reset_all_rp();
(void)run_par_deadband_set(0u, 0.6f);
CHECK((run_par_104_read(0x8030u, &v) == 0) && (fabsf(v - 0.6f) <= 1e-6f),
"TC-RUNPAR-004 db first slot hit");
CHECK((run_par_104_read(0x8038u, &v) == 0) && (fabsf(v - 0.5f) <= 1e-6f),
"TC-RUNPAR-004 db last slot (freq) hit");
CHECK(run_par_104_read(0x8039u, &v) == -1, "TC-RUNPAR-004 gap address not owned");
CHECK(run_par_104_read(0x8027u, &v) == -1, "TC-RUNPAR-004 prot_set alarm addr untouched");
CHECK(run_par_104_read(0x802Fu, &v) == -1, "TC-RUNPAR-004 below base not claimed");
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 152.587890625f) <= 1e-4f),
"TC-RUNPAR-004 k echo = adc_cal effective UA");
CHECK((run_par_104_read(0x8047u, &v) == 0) && (fabsf(v - 805.6640625f) <= 1e-4f),
"TC-RUNPAR-004 k echo last channel (IN, STM32 ROM)");
CHECK((run_par_104_read(0x8048u, &v) == 0) && (v == 0.0f),
"TC-RUNPAR-004 b echo factory zero");
CHECK((run_par_104_read(0x8050u, &v) == 0) && (fabsf(v - 1000.0f) <= 1e-3f),
"TC-RUNPAR-004 r echo first slot = ROM voltage family 1000.0");
CHECK((run_par_104_read(0x8054u, &v) == 0) && (fabsf(v - 29.4118f) <= 1e-3f),
"TC-RUNPAR-004 r echo current family = 29.4118");
CHECK(run_par_104_read(0x8058u, &v) == -1, "TC-RUNPAR-004 above r block not owned");
}
static void tc_104_two_step(void)
{
float v;
vfs_reset();
reset_all_rp();
CHECK((run_par_104_write(0x8031u, 0.8f, 1u) == 0)
&& (fabsf(run_par_deadband(1u) - 0.5f) <= 1e-6f)
&& (run_par_dirty_peek() == 0u),
"TC-RUNPAR-005 select validates without commit or dirty");
CHECK((run_par_104_write(0x8031u, 0.8f, 0u) == 0)
&& (fabsf(run_par_deadband(1u) - 0.8f) <= 1e-6f)
&& (run_par_dirty_peek() == 1u),
"TC-RUNPAR-005 execute commits and marks dirty");
CHECK(run_par_104_write(0x8031u, 60.0f, 1u) == -1, "TC-RUNPAR-005 range reject in select");
CHECK(run_par_104_write(0x8039u, 1.0f, 0u) == -2, "TC-RUNPAR-005 foreign addr passes through");
CHECK(run_par_104_write(0x8027u, 1.0f, 0u) == -2, "TC-RUNPAR-005 alarm addr passes through");
CHECK((run_par_104_read(0x8031u, &v) == 0) && (fabsf(v - 0.8f) <= 1e-6f),
"TC-RUNPAR-005 committed value readable at same addr");
}
static void tc_104_cal_proxy(void)
{
float v;
vfs_reset();
reset_all_rp();
CHECK(run_par_104_write(0x8040u, 300.0f, 0u) == 0, "TC-RUNPAR-006 k in-band accepted");
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 300.0f) <= 0.01f),
"TC-RUNPAR-006 k echo follows adc_cal_set");
CHECK((run_par_104_write(0x8040u, 400.0f, 0u) == -1)
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
"TC-RUNPAR-006 k over band rejected by adc_cal, RAM intact");
CHECK((run_par_104_write(0x8040u, 70.0f, 0u) == -1),
"TC-RUNPAR-006 k under band rejected (0.5x ROM)");
CHECK(run_par_104_write(0x8040u, -1.0f, 0u) == -1, "TC-RUNPAR-006 non-positive k rejected");
CHECK((run_par_104_write(0x8048u, -5.0f, 0u) == 0)
&& (fabsf(adc_cal_b_codes(ADC_SEM_UA) + 5.0f) <= 1e-3f)
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
"TC-RUNPAR-006 b single-side RMW keeps k");
CHECK(run_par_104_write(0x8048u, -9000.0f, 0u) == -1, "TC-RUNPAR-006 b over +-8192 rejected");
CHECK((run_par_104_write(0x8040u, 200.0f, 1u) == 0)
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
"TC-RUNPAR-006 k select does not touch adc_cal");
/* 生效值视图回显:文件塞越带 k → 哨兵回退 ROM → 104 回显 ROM */
{
static const char s_badk[] =
"[adc_cal]\nk_ua=99011483\nvalid=1\n";
(void)memcpy(s_vfs, s_badk, sizeof(s_badk) - 1u);
s_vfs_len = sizeof(s_badk) - 1u;
s_vfs_exists = 1;
s_vfs_mtime++;
}
CHECK(cfg_file_load() == 0, "TC-RUNPAR-006 poisoned file loaded by engine");
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 152.587890625f) <= 1e-4f),
"TC-RUNPAR-006 echo = effective (ROM fallback), not raw file value");
reset_all_rp();
CHECK(cfg_file_save() == 0, "TC-RUNPAR-006 reset file rewritten");
}
static void tc_init_idempotent(void)
{
int err_before;
vfs_reset();
reset_all_rp();
(void)run_par_deadband_set(1u, 0.9f);
(void)run_par_104_write(0x8040u, 200.0f, 0u);
err_before = g_rptest_err_count;
run_par_init();
CHECK(fabsf(run_par_deadband(1u) - 0.5f) <= 1e-6f, "TC-RUNPAR-007 re-init restores defaults");
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-007 re-init clears dirty");
CHECK(g_rptest_err_count > err_before, "TC-RUNPAR-007 dup register leaves trace");
CHECK(fabsf(run_par_deadband(0u) - 0.5f) <= 1e-6f, "TC-RUNPAR-007 values intact after dup reg");
}
static void tc_copy_is_effective(void)
{
static const char s_file[] =
"[run_par]\ndb_ua=650\ndb_ub=500\ndb_uc=500\ndb_un=500\n"
"db_ia=5\ndb_ib=5\ndb_ic=5\ndb_in=5\ndb_freq=500\n";
vfs_reset();
reset_all_rp();
(void)memcpy(s_vfs, s_file, sizeof(s_file) - 1u);
s_vfs_len = sizeof(s_file) - 1u;
s_vfs_exists = 1;
s_vfs_mtime++;
CHECK(cfg_file_load() == 0, "TC-RUNPAR-008 load ok");
CHECK(fabsf(run_par_deadband(0u) - 0.65f) <= 1e-6f,
"TC-RUNPAR-008 no cache layer: copy read is effective value");
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-008 load itself is not a local edit");
}
/* TC-RUNPAR-009:变比 r 的 104 读写(0x8050~0x8057)与两段式粗守面 */
static void tc_104_ratio(void)
{
float v;
vfs_reset();
reset_all_rp();
/* 写 2000.0:电压族量级(2 倍于 ROM 默认 1000),回读 = 生效值 */
CHECK(run_par_104_write(0x8050u, 2000.0f, 0u) == 0, "TC-RUNPAR-009 r 2000.0 accepted");
CHECK((run_par_104_read(0x8050u, &v) == 0) && (fabsf(v - 2000.0f) <= 0.01f),
"TC-RUNPAR-009 r readback = 2000.0");
CHECK((adc_cal_ratio(ADC_SEM_UA) > 1999.99f) && (adc_cal_ratio(ADC_SEM_UA) < 2000.01f),
"TC-RUNPAR-009 r reached adc_cal (write-through)");
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-009 r execute marks dirty");
/* 写 0 = 显式关掉本路折算(成功路径,不是拒绝) */
CHECK(run_par_104_write(0x8050u, 0.0f, 0u) == 0, "TC-RUNPAR-009 r 0 accepted (explicit off)");
CHECK((run_par_104_read(0x8050u, &v) == 0) && (v == 0.0f), "TC-RUNPAR-009 r readback = 0");
CHECK(adc_cal_ratio_configured(ADC_SEM_UA) == 0u,
"TC-RUNPAR-009 r 0 -> channel reports not configured");
/* 拒绝面:0 与 >=1.0 之外、以及非有限值 */
CHECK(run_par_104_write(0x8050u, 0.5f, 0u) == -1, "TC-RUNPAR-009 r 0.5 rejected");
CHECK(run_par_104_write(0x8050u, -1.0f, 0u) == -1, "TC-RUNPAR-009 r -1 rejected");
CHECK(run_par_104_write(0x8050u, 40000.0f, 0u) == -1, "TC-RUNPAR-009 r 40000 rejected");
{
volatile float fnan = 0.0f;
fnan = fnan / fnan;
CHECK(run_par_104_write(0x8050u, fnan, 0u) == -1, "TC-RUNPAR-009 r NaN rejected");
CHECK(run_par_104_write(0x8050u, fnan, 1u) == -1, "TC-RUNPAR-009 r NaN rejected in select too");
}
/* 上限端点(32767.99)在带内,接受 */
CHECK(run_par_104_write(0x8050u, 32767.99f, 0u) == 0, "TC-RUNPAR-009 r upper band endpoint accepted");
/* 选择段:只粗守(NaN/Inf),0.5 这类"执行段会拒"的值在选择段返 0 */
CHECK(run_par_104_write(0x8051u, 0.5f, 1u) == 0,
"TC-RUNPAR-009 select only guards finiteness (0.5 passes)");
CHECK((run_par_104_read(0x8051u, &v) == 0) && (fabsf(v - 1000.0f) <= 1e-3f),
"TC-RUNPAR-009 select did not touch the ratio");
CHECK(run_par_104_write(0x8051u, 0.5f, 0u) == -1, "TC-RUNPAR-009 execute rejects the same 0.5");
CHECK((run_par_104_read(0x8051u, &v) == 0) && (fabsf(v - 1000.0f) <= 1e-3f),
"TC-RUNPAR-009 rejected execute left the ratio intact");
/* 段边界:0x8057 是 r 块末地址,0x8058 起非本段 */
CHECK(run_par_104_write(0x8057u, 29.4118f, 0u) == 0, "TC-RUNPAR-009 last r slot writable");
CHECK(run_par_104_write(0x8058u, 2.0f, 0u) == -2, "TC-RUNPAR-009 0x8058 passes through to prot_set");
CHECK(run_par_104_read(0x8058u, &v) == -1, "TC-RUNPAR-009 0x8058 not owned on read");
/* 落盘往返:脏 → save → 文件带 r_ua 键 → load 回读生效 */
CHECK(run_par_104_write(0x8050u, 2000.0f, 0u) == 0, "TC-RUNPAR-009 seed 2000.0 before save");
CHECK(run_par_store_save() == 0, "TC-RUNPAR-009 save ok");
CHECK((run_par_dirty_peek() == 0u) && (s_vfs_exists == 1),
"TC-RUNPAR-009 save cleared dirty and wrote file");
CHECK(strstr((const char *)s_vfs, "r_ua=131072000") != NULL,
"TC-RUNPAR-009 file carries r_ua=131072000 (= 2000 x 65536)");
(void)run_par_104_write(0x8050u, 1000.0f, 0u); /* 覆盖内存态 */
CHECK(cfg_file_load() == 0, "TC-RUNPAR-009 engine reload ok");
CHECK((run_par_104_read(0x8050u, &v) == 0) && (fabsf(v - 2000.0f) <= 0.01f),
"TC-RUNPAR-009 reload restores saved ratio");
/* 回显 = 生效值视图:文件塞越带 r(< 1.0)→ 哨兵按 0 报,不回显原始坏值 */
{
static const char s_badr[] =
"[adc_cal_sec]\r\nr_ua=1\r\n";
(void)memcpy(s_vfs, s_badr, sizeof(s_badr) - 1u);
s_vfs_len = sizeof(s_badr) - 1u;
s_vfs_exists = 1;
s_vfs_mtime++;
}
CHECK(cfg_file_load() == 0, "TC-RUNPAR-009 poisoned ratio file loaded");
CHECK((run_par_104_read(0x8050u, &v) == 0) && (v == 0.0f),
"TC-RUNPAR-009 echo = effective (out-of-band reported as 0)");
}
int main(void)
{
printf("=== RunPar Layer Host Tests ===\n");
tc_defaults();
tc_set_range();
tc_persist();
tc_104_read();
tc_104_two_step();
tc_104_cal_proxy();
tc_init_idempotent();
tc_copy_is_effective();
tc_104_ratio();
printf("=== RunPar Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}