728d60675f
三条道在同一棵树上交付,故合并成**一次权威构建**(@build 0 错 0 警,Code=235236 RO=26308 RW=1572
ZI=136924)后同笔入库;台账已按该构建回填(`ram_baseline.py --update` + `--write`、`chec
k_mem_numbers.py
--write`),凭证与实际 .map 同源。归属如下:
· **通讯设置页**(道G task-9):新增 `Drivers/BSP/UI/ui_page_comm.{c,h}`(通道六注入,与死
区页同骨架同纪
律),`#page=comm` 绑定使 bound **21 → 22**、内容页黄金 **47 → 53**(旧 47 页字节零变化,
新增六态含
WAIT/OK 两张);`iec104_comm` 脏位 + store_save(**无事件位**)挂 defaultTask 同点位
轮询;`port_104` 由
宏改走 getter、`IEC104_SERV_PORT` 删除;字体 +听/监(185 字全覆)。**实机链已闭环**:FTP 外改
[comm]
的 ca/port → 复位 → 串口出现 `section 'comm' (2 keys)` 与 `i104: listening o
n 9004` → 还原 2404,全程无
FAIL ⇒「重启即生效 + 端口真值」不是推断是实测。上限核对 6/8 段、2/16 键,超限现象由 TC-CONFIG-056/057
钉死(注册 -1 + `comm init: FAIL` 锚 + getter 回 ROM),**不抬宏**。
· **开机账本**(道F task-12):新增 `App/util/boot_stats.{c,h}`——上电次数落 SD2506 用户
SRAM(magic +
小端计数 + 版本 + 异或校验),一次上电 = 1 读 + ≤1 写,**零 NAND、零卷锁、零配置段**;四态互斥
UNKNOWN / FRESH / CONTINUED / RESTARTED(BLF=1 才算电池域掉过电),**读失败绝不回写**;
文案口径「自 RTC
电池周期起」,**禁写终身次数**。复位原因优先级链从 `app_main.c` 的 if-else 收进一份真源(串口与屏幕同
源);104 四个只增不清计数 + `iec104_serv_get_stats()` 留在 serv、未塞进 link(会被 open
/close 洗掉)。
运行时长按用户裁定 = **只报本次开机以来**,零新增代码。新套件 `test/host/boot_stats` 21/21,五处注入
**全部有牙**(含「漂一字节被校验和挡下而不是报 256 倍错账」)。
· **adc_cal 可诊断化**(道E task-43):三个 volatile 计数 + `adc_cal_get_stats()`
/ `adc_cal_note_persisted()`
(水位**盖写非自增**,因为一次 save 覆盖整档,数 save 会留假未落盘);「差额 × 脏位」三态语义唯一落在
`adc_cal.h`,其中「差额 > 0 且脏位恒 0」= 绕过语义 setter 的静默丢失;TC-ADCCAL-019~023。
三条**必须留档的自纠**(都是执行道自己报的,我不替他们润色):
① stdint 那条断链是**宿主套件抓到、通讯道先修**,采集道承认自己是重复添加者并删掉自己那份;
② 「exit 2 不是证据,断言变红才是」——采集道第一版反证注入了未声明标识符导致编译错,差点当成功;
③ 套件汇总行原写 `s_total/s_total`,失败时会打印「21/21 PASSED (failed=1)」这种自相矛盾的行,已改。
另修:`check_registration.py` 的 `SIM_EXEMPT` 加一行豁免 `App/util/boot_stats.c
`(依赖 I2C 与 RCC 位掩码,
MCU 专属;核过 `sys_clock.c`/`sha256.c` 同形态先例),注册反向校验 exit 1 → 0。发号 TC-IEC1
04-034、
TC-UIDATA-029 已实抬游标(`TC-IEC104-033→034`、`TC-UIDATA-026→029`)。
门禁:id-check --strict 与 plain 均 rc=0、doc-links --all rc=0、注册校验 0、cppche
ck / coding-standard /
bom-drift 0;iec104 179/179、ui_data 295/295、boot_stats 21/21、adc_cal 11
7/117、run_par 67/67;菜单 8 状态
池 5368 不变、内容页 53/53。**已知遗留**:内容页数 47 → 53 的全仓文本同步尚未做(另卡跟踪),本笔只保
证代码与黄金一致;面板注键真机那一腿缺 CLI,交台架道补。
373 lines
15 KiB
C
373 lines
15 KiB
C
/*
|
||
* 模块名称:运行参数段(L2/L3 整定层,「数据可信」卡 10)——实现
|
||
* 模块功能:见 run_par.h。工厂默认 = 「数据可信」卡 6 额定 0.5 % 档(电压 500
|
||
* mV / 电流 5 mA / 频率 500 mHz,毫单位 int32 定点落盘);[run_par]
|
||
* 段 9 键(db_ua…db_freq,均 ≤15 字符)≤ CFG_FILE_MAX_KEYS 不拆段
|
||
* (通道系数覆盖**不落本段**——直通 adc_cal 的 [adc_cal]/[adc_cal_ofs],
|
||
* 系数唯一真源在那两段,lead 裁②a)。
|
||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||
* 作者:DTU 项目组
|
||
* 创建日期:2026-10-25(道D 卡 10)
|
||
* 修改记录:
|
||
* v1.0 2026-10-25 DTU 项目组 创建:[run_par] 段 + 104 映射 + 直通 adc_cal
|
||
* + B7 式脏标志落盘请求
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#include <math.h>
|
||
#include <stddef.h>
|
||
#include <string.h>
|
||
|
||
#include "run_par.h"
|
||
|
||
#include "adc_cal.h"
|
||
#include "cfg_file.h"
|
||
|
||
#define DBG_TAG "[PAR]"
|
||
#include "dbg_log.h"
|
||
|
||
/* 常量定义区 */
|
||
|
||
/* cfg 段名(≤ CFG_FILE_MAX_NAME_LEN);键名 6~7 字符小写 */
|
||
#define RUN_PAR_SEC "run_par"
|
||
|
||
/* 104 运行参数地址(复现规划 §5.1 类内落实;权威逐项表不在仓,送检点表到位
|
||
* 后以送检为准替换本常量段——lead 裁①。0x8027~0x802A = prot_set 告警段,永不让位) */
|
||
#define RUN_PAR_IOA_DB0 0x8030u /* 9 点:0x8030~0x8038 */
|
||
#define RUN_PAR_IOA_K0 0x8040u /* k 覆盖 8 路:0x8040~0x8047 */
|
||
#define RUN_PAR_IOA_B0 0x8048u /* b 覆盖 8 路:0x8048~0x804F */
|
||
|
||
/* 死区合法域上限(毫单位):电压 50 V / 电流 1 A / 频率 5 Hz——守卫挡误写,
|
||
* 不限工况;下限统一 0(负死区 = 配置错误) */
|
||
#define RUN_PAR_DB_MAX_U 50000L
|
||
#define RUN_PAR_DB_MAX_I 1000L
|
||
#define RUN_PAR_DB_MAX_F 5000L
|
||
|
||
/* 静态变量定义区 */
|
||
|
||
/* [run_par] 段 RAM 副本(引擎原地更新;属主契约与 µs 级混窗见「数据可信」§4.4,
|
||
* 同 adc_cal——本层不挂回调,副本即生效值,消费侧实时读) */
|
||
typedef struct {
|
||
int32_t db[RUN_PAR_DB_N];
|
||
} run_par_db_t;
|
||
|
||
static const run_par_db_t s_db_rom = {
|
||
{ 500L, 500L, 500L, 500L, /* UA/UB/UC/UN:0.5 V(卡 6 额定档) */
|
||
5L, 5L, 5L, 5L, /* IA/IB/IC/IN:0.005 A */
|
||
500L } /* 频率:0.5 Hz(绝对档,非额定百分比) */
|
||
};
|
||
static run_par_db_t s_db;
|
||
|
||
/* 键表 offset 与 run_par_db_t 字段一一对应(sizeof(int32_t) 步进) */
|
||
static const cfg_key_def_t s_db_keys[] = {
|
||
{ "db_ua", CFG_VAL_INT, 0u, 4u },
|
||
{ "db_ub", CFG_VAL_INT, 4u, 4u },
|
||
{ "db_uc", CFG_VAL_INT, 8u, 4u },
|
||
{ "db_un", CFG_VAL_INT, 12u, 4u },
|
||
{ "db_ia", CFG_VAL_INT, 16u, 4u },
|
||
{ "db_ib", CFG_VAL_INT, 20u, 4u },
|
||
{ "db_ic", CFG_VAL_INT, 24u, 4u },
|
||
{ "db_in", CFG_VAL_INT, 28u, 4u },
|
||
{ "db_freq", CFG_VAL_INT, 32u, 4u }
|
||
};
|
||
|
||
/* 落盘脏标志:104 写成功置位,run_par_store_save 成功清零(B7 prot_set 同款,
|
||
* 只置不清 = 写盘失败天然重试);单字节,跨任务 peek 无害 */
|
||
static uint8_t s_dirty;
|
||
|
||
/* 内部函数实现区 */
|
||
|
||
/*
|
||
* 函数功能:毫单位定点 → 工程量浮点(**本模块唯一换算点**)
|
||
* 入口参数:milli - 毫单位 内 int32_t
|
||
* 返 回 值:工程量 内 float
|
||
* 限定条件:仅本文件内使用(消费侧一律走 run_par_deadband()/run_par_db_range(),
|
||
* 不许各自再写一次 /1000——那才是"第二真源"最常见的样子)
|
||
*/
|
||
static float s_milli_to_eng(int32_t milli) {
|
||
return (float)milli / 1000.0f;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:死区点量程上限(毫单位)
|
||
* 入口参数:idx - 0..8 uint8_t
|
||
* 返 回 值:上限;idx 越界返 0 内 int32_t
|
||
*/
|
||
static int32_t s_db_cap_milli(uint8_t idx) {
|
||
if (idx < 4u) {
|
||
return RUN_PAR_DB_MAX_U;
|
||
}
|
||
if (idx < RUN_PAR_DB_N - 1u) {
|
||
return RUN_PAR_DB_MAX_I;
|
||
}
|
||
if (idx == (uint8_t)(RUN_PAR_DB_N - 1u)) {
|
||
return RUN_PAR_DB_MAX_F;
|
||
}
|
||
return 0L;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:工程量浮点 → 毫单位定点(只舍不入的截断由调用侧范围守卫兜底)
|
||
* 入口参数:v - 工程量(须已判有限) float
|
||
* 返 回 值:四舍五入最近毫单位 内 int32_t
|
||
*/
|
||
static int32_t s_milli_of(float v) {
|
||
return (int32_t)lrintf(v * 1000.0f);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:死区值校验(不提交),set/104 选择段共用同一守卫 = 两路语义同源
|
||
* 入口参数:idx - 0..8 uint8_t;db - 工程量 float
|
||
* 返 回 值:合法毫单位 ≥0;-1 非法(越界/NaN/负/越程) 内 int32_t
|
||
*/
|
||
static int32_t s_db_check(uint8_t idx, float db) {
|
||
int32_t milli;
|
||
|
||
if ((idx >= RUN_PAR_DB_N) || !isfinite(db) || !(db >= 0.0f)) {
|
||
return -1L;
|
||
}
|
||
milli = s_milli_of(db);
|
||
if ((milli < 0L) || ((long long)milli > (long long)s_db_cap_milli(idx))) {
|
||
return -1L;
|
||
}
|
||
return milli;
|
||
}
|
||
|
||
/* 公共函数实现区 */
|
||
|
||
/*
|
||
* 函数功能:初始化——副本置工厂默认 + 注册 [run_par] 段(幂等,内核前)
|
||
* 返 回 值:无
|
||
*/
|
||
void run_par_init(void) {
|
||
cfg_section_reg_t reg;
|
||
|
||
(void)memcpy(&s_db, &s_db_rom, sizeof(s_db));
|
||
s_dirty = 0u;
|
||
|
||
(void)memset(®, 0, sizeof(reg));
|
||
reg.name = RUN_PAR_SEC;
|
||
reg.p_data = &s_db;
|
||
reg.p_defaults = &s_db_rom;
|
||
reg.data_size = sizeof(s_db);
|
||
reg.p_keys = s_db_keys;
|
||
reg.key_count = (uint8_t)(sizeof(s_db_keys) / sizeof(s_db_keys[0]));
|
||
reg.p_cb = NULL; /* 不挂回调:RAM 副本即生效值(同 adc_cal) */
|
||
if (cfg_file_register(®) != 0) {
|
||
DBG_ERROR("register %s FAIL (dup/full?) - file values inactive",
|
||
RUN_PAR_SEC);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读第 idx 路死区(工程量)
|
||
* 返 回 值:V / A / Hz;越界返 0.0(消费侧按"无死区"保守处理) float
|
||
*/
|
||
float run_par_deadband(uint8_t idx) {
|
||
if (idx >= RUN_PAR_DB_N) {
|
||
return 0.0f;
|
||
}
|
||
return s_milli_to_eng(s_db.db[idx]); /* 毫→工程:唯一换算点 = s_milli_to_eng */
|
||
}
|
||
|
||
/*
|
||
* 函数功能:取第 idx 路死区的合法域(工程量)——**合法域的唯一出口**
|
||
* 入口参数:idx - 0..8 uint8_t;lo/hi - 输出下限/上限(工程量 V/A/Hz) float *
|
||
* 返回值:0 - 已写出;-1 - idx 越界或指针为空(调用方不得回退成自造默认域) int
|
||
* 限定条件:任意上下文(纯常量读,无锁、无 I/O)
|
||
* 函数说明:为什么公开这个口:UI 整定页要给数字编辑器设 min/max,而这些上限的
|
||
* 真源是本模块的 RUN_PAR_DB_MAX_*(电压 50V / 电流 1A / 频率 5Hz,
|
||
* "额定 × 二档裕量")。页面里抄一份就是第二真源——判据「不新增第二份
|
||
* 真源」最典型的复发形态是"顺手把常量写进界面代码"。因此本函数的
|
||
* 消费者是装配层 App/app/ui_data_feed.c 的注入表实现,不是页面本身。
|
||
* ⚠ 给的是**粗守域**(挡误写),不是工况域;真提交仍由 s_db_check
|
||
* 同源守卫裁(104 选择段与 UI 编辑器共用同一批常量,不会漂移)。
|
||
*/
|
||
int run_par_db_range(uint8_t idx, float *lo, float *hi) {
|
||
if ((idx >= RUN_PAR_DB_N) || (lo == NULL) || (hi == NULL)) {
|
||
return -1;
|
||
}
|
||
*lo = 0.0f;
|
||
*hi = s_milli_to_eng(s_db_cap_milli(idx));
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写第 idx 路死区(守卫通过才提交;成功置脏)
|
||
* 返 回 值:0 成功;-1 非法(副本不动) int
|
||
*/
|
||
int run_par_deadband_set(uint8_t idx, float db) {
|
||
int32_t milli = s_db_check(idx, db);
|
||
|
||
if (milli < 0L) {
|
||
return -1;
|
||
}
|
||
s_db.db[idx] = milli;
|
||
s_dirty = 1u;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:104 运行参数地址读(命中集精确匹配,见 run_par.h 地址表)
|
||
* 返 回 值:0 命中;-1 非本段(调用方落 prot_set) int
|
||
*/
|
||
int run_par_104_read(uint32_t addr, float *out) {
|
||
if (out == NULL) {
|
||
return -1;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_DB0) && (addr < RUN_PAR_IOA_DB0 + RUN_PAR_DB_N)) {
|
||
*out = run_par_deadband((uint8_t)(addr - RUN_PAR_IOA_DB0));
|
||
return 0;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_K0) && (addr < RUN_PAR_IOA_K0 + RUN_PAR_CH_N)) {
|
||
*out = adc_cal_k_uv((adc_semantic_t)(addr - RUN_PAR_IOA_K0));
|
||
return 0;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_B0) && (addr < RUN_PAR_IOA_B0 + RUN_PAR_CH_N)) {
|
||
*out = adc_cal_b_codes((adc_semantic_t)(addr - RUN_PAR_IOA_B0));
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写某路系数 k(µV/码)**并在同一函数内置脏**——整定的语义写入口
|
||
* 入口参数:sem - 电气语义通道 adc_semantic_t;k_uv - 系数(µV/码,>0) float
|
||
* 返回值:0 - RAM 副本已写 + 已置脏;-1 - 粗守或执行段拒(副本未动、不置脏) int
|
||
* 限定条件:整定路径上下文(iec104Task 的 104 执行段;UI 经 ui_data_yt_ops_t 由
|
||
* 装配层 App/app/ui_data_feed.c 转进来)。勿在 adcTask 调用
|
||
* 函数说明:**为什么"写值 + 置脏"必须长在一个函数里**(lead 裁②,2026-10-27):
|
||
* 置脏是持久化承诺,写值是 RAM 事实,两者拆开就会出现"屏上已改、盘上
|
||
* 永不落、且没有任何错误返回"这种无人报错的状态——本仓今天已实测到它的
|
||
* 既有形态(绕过本模块直调 adc_cal_set() 的写者不置脏,值永远不落盘,
|
||
* 另立 task-43 处置)。所以这里不提供单独的 mark_dirty 公开口:它一旦
|
||
* 存在就一定有人只调它(同"零消费者的状态查询口"那条教训)。
|
||
* 零点 b 走 RMW 取**当前生效值**(经合法带哨兵,天然满足再写校验)。
|
||
* 粗守(0 < k ≤ RUN_PAR_K_MAX_UV)只为挡误写,真带在 adc_cal 执行段裁:
|
||
* 两层各说各的,页面上"合法域内仍被拒(ERR)"就是执行段拒的,不伪装。
|
||
*/
|
||
int run_par_cal_k_set(adc_semantic_t sem, float k_uv) {
|
||
int32_t k_q16;
|
||
int32_t b_codes;
|
||
|
||
/* 通道号不在这里判:adc_cal_set 自带合法性与合法带裁决(越界一律 -1),
|
||
* 本层再判一次就是第二真源。粗守只挡误写:0 < k ≤ RUN_PAR_K_MAX_UV */
|
||
if (!isfinite(k_uv) || !(k_uv > 0.0f) || (k_uv > RUN_PAR_K_MAX_UV)) {
|
||
return -1;
|
||
}
|
||
k_q16 = (int32_t)lrintf(k_uv * 65536.0f);
|
||
b_codes = (int32_t)lrintf(adc_cal_b_codes(sem));
|
||
if (adc_cal_set(sem, k_q16, b_codes) != 0) {
|
||
return -1; /* 执行段拒:副本未动,脏位也不置 = 不承诺任何持久化 */
|
||
}
|
||
s_dirty = 1u;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写某路零点偏移 b(码值)并置脏(与 k 同款语义,粗守 ±1e6 码)
|
||
* 入口参数:sem - 电气语义通道 adc_semantic_t;b_codes - 零点(码值) int32_t
|
||
* 返回值:0 - 已写并置脏;-1 - 粗守或执行段拒(副本未动、不置脏) int
|
||
* 限定条件:同 run_par_cal_k_set()
|
||
* 函数说明:k 侧 RMW 用当前生效值;±8192 真带由 adc_cal 执行段裁(粗守目的是
|
||
* 挡误写不是限工况)。本函数的消费者 = 104 的 0x8048~ 分支(改造后),
|
||
* 不是"为将来预留"的口
|
||
*/
|
||
int run_par_cal_b_set(adc_semantic_t sem, int32_t b_codes) {
|
||
int32_t k_q16;
|
||
|
||
if (((float)b_codes < -1.0e6f) || ((float)b_codes > 1.0e6f)) {
|
||
return -1;
|
||
}
|
||
k_q16 = (int32_t)lrintf(adc_cal_k_uv(sem) * 65536.0f);
|
||
if (adc_cal_set(sem, k_q16, b_codes) != 0) {
|
||
return -1;
|
||
}
|
||
s_dirty = 1u;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:104 运行参数地址写(两段式;k/b 经本模块语义 setter——**run_par 不存
|
||
* 系数**,系数唯一真源 = [adc_cal]/[adc_cal_ofs],lead 裁②a)
|
||
* 返 回 值:0 接受 / -1 拒绝 / -2 非本段(调用方落 prot_set) int
|
||
* 函数说明:选择段 = 校验不提交(死区走与 set 同源守卫;k/b 的 adc_cal 合法带
|
||
* 由执行段裁决——IEC104 允许执行段 PN,两段结果可不一致,注释在此
|
||
* 明说防"选择过了怎么还 PN"的现场困惑)。k/b 两支自 2026-10-27 起一律
|
||
* 走 run_par_cal_k_set() / run_par_cal_b_set():写值与置脏在同一个函数
|
||
* 里发生,本函数不再自己碰 s_dirty(lead 裁②:消除两份置脏逻辑)。
|
||
*/
|
||
int run_par_104_write(uint32_t addr, float v, uint8_t select) {
|
||
uint8_t slot; /* 每个命中分支先赋值再使用(无未init读路径) */
|
||
|
||
if ((addr >= RUN_PAR_IOA_DB0) && (addr < RUN_PAR_IOA_DB0 + RUN_PAR_DB_N)) {
|
||
int32_t milli;
|
||
|
||
slot = (uint8_t)(addr - RUN_PAR_IOA_DB0);
|
||
milli = s_db_check(slot, v);
|
||
if (milli < 0L) {
|
||
return -1;
|
||
}
|
||
if (select == 0u) {
|
||
s_db.db[slot] = milli;
|
||
s_dirty = 1u;
|
||
}
|
||
return 0;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_K0) && (addr < RUN_PAR_IOA_K0 + RUN_PAR_CH_N)) {
|
||
slot = (uint8_t)(addr - RUN_PAR_IOA_K0);
|
||
if (!isfinite(v) || !(v > 0.0f) || (v > 1.0e9f)) {
|
||
return -1;
|
||
}
|
||
if (select != 0u) {
|
||
return 0; /* 粗守过了 ≠ 执行必过:合法带裁决在执行段(见函数头) */
|
||
}
|
||
/* 写值 + 置脏收进语义 setter(粗守 0<k≤上限 也在里面,与本支
|
||
* 原先那条逐字同值守卫一致,行为不变)*/
|
||
return run_par_cal_k_set((adc_semantic_t)slot, v);
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_B0) && (addr < RUN_PAR_IOA_B0 + RUN_PAR_CH_N)) {
|
||
slot = (uint8_t)(addr - RUN_PAR_IOA_B0);
|
||
if (!isfinite(v)) {
|
||
return -1; /* NaN/Inf 不得进 lrintf(该转换是 UB) */
|
||
}
|
||
if (select != 0u) {
|
||
return 0;
|
||
}
|
||
/* 同款语义 setter:粗守 ±1e6 码在 setter 内,±8192 真带由 adc_cal 执行段裁 */
|
||
return run_par_cal_b_set((adc_semantic_t)slot, (int32_t)lrintf(v));
|
||
}
|
||
return -2;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:落盘脏探查(非消费)
|
||
* 返 回 值:1 有未落盘整定;0 干净 uint8_t
|
||
*/
|
||
uint8_t run_par_dirty_peek(void) {
|
||
return s_dirty;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:执行落盘(整档外科保存成功后清脏;失败脏保持 = 下轮重试)
|
||
* 返 回 值:0 成功/无脏;-1 写盘失败 int
|
||
* 限定条件:defaultTask(配置引擎属主)上下文,SYS_EVT_RUN_PAR_SAVE 驱动
|
||
* 函数说明:一次 save 同时覆盖 [run_par] 9 键与刚被 adc_cal_set 改写的
|
||
* [adc_cal]/[adc_cal_ofs] 副本(lead 裁②:一条事件位一次 save)。
|
||
* 成功路径上给标定层盖一次落盘水位(adc_cal_note_persisted())——它是
|
||
* **观测回执**,不参与本函数任何判定;失败路径不盖,于是"save 一直失败"
|
||
* 在标定层侧表现为差额长期不归零(道G task-43 第②项)。
|
||
*/
|
||
int run_par_store_save(void) {
|
||
if (s_dirty == 0u) {
|
||
return 0;
|
||
}
|
||
if (cfg_file_save() != 0) {
|
||
DBG_ERROR("cfg save FAIL - keep dirty, retry next round");
|
||
return -1;
|
||
}
|
||
s_dirty = 0u;
|
||
adc_cal_note_persisted(); /* 观测回执:水位追平,差额归零(不是判定输入) */
|
||
return 0;
|
||
}
|