Files
STM32F4-Base/App/cfg/cfg_file.h
T
super_agent_bot d9303e05a4
CI / 静态门禁 (pre-commit) (push) Failing after 8m2s
CI / 主机侧单测 (MSVC) (push) Successful in 40s
CI / 静态门禁 (pre-commit) (pull_request) Failing after 5m10s
CI / 主机侧单测 (MSVC) (pull_request) Successful in 37s
fix(cfg): 热重载读侧三点一致性校验——掐断打回出厂与保存吞段(#73)
真机 #73:运行中改配置触发热重载,卷层(dhara)偶发返回旧/短
视图时,引擎把该视图当成文件全文:逐段按缺省解析后整盘打回
出厂默认且不自愈;此后任何一次保存又按缩小的视图做手术式
重写,文件越写越小(台架 1066B→986B,[sntp] 段整体丢失),
即真机对时静默 10.5 小时的元凶。

处置(引擎侧根因无关防御;卷层回滚归 #79/#81 已合并的 FTL
修复域):
- poll:读完加三点校验(读长==stat.size、读前后 re-stat 的
  size/mtime 一致),判为旧/短视图则 RAM 不动、基线不刷新、
  每 2 拍自动重试自愈;首次拒绝留 1 条 DBG_ERROR+syslog,
  之后每 30 拍一条 WARN 提示重启收敛。
- save:同型三点校验,视图与 stat 不符返回 -1 且文件不动,
  调用方保留 dirty 下拍重试,吞段链在第一次保存即被掐断。
- 边界:「一致但陈旧」的整卷回滚引擎层无法区分,靠重启
  收敛,已写入设计说明风险表与文档口径。

测试:新增 TC-CONFIG-058/059/060(旧视图保持 RAM、保存短
视图拒写、读中改写拒绝+日志限速)。cfg_file 239/239;
iec104 179、run_par 67、adc_cal 117、sntp 98、
cfg_file_store_mcu 34、net_cfg 48、ftp_cfg 16、prot_set 90、
prot_dim 38 全绿;/keil-build 0 Error 0 Warning。

文档:配置文件设计说明 §(3)/§5.3/风险表/变更记录 v1.17;
单元测试说明 058–060 行;网络配置说明与产品需求 FR-CFG-3
同步口径;台账随新鲜 .map 由 ram_baseline --update 回填。
2026-11-05 12:35:46 +08:00

161 lines
8.1 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.
/*
* 模块名称:cfg_file
* 模块功能:通用配置文件引擎(双端共享核心)公共接口——INI 段/键表解析校验、
* 外科手术式保存(保留注释/未知内容,缺失段/键写回)、出厂模板生成、
* 沉降去抖轮询与分段变更回调;平台文件操作经 cfg_file_store 后端
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-09-14
* 修改记录:
* v1.0 2026-09-14 王建锋 创建初始版本(P3:段键表引擎 + net_cfg 集成)
* v1.1 2026-09-15 王建锋 属主任务由 netTask 改为 defaultTask(配置引擎单一属主)
* v1.2 2026-09-21 王建锋 超长守卫契约同步——poll 沉降
* 后先按真实 st.size 判超长(旧实现查截断后读长恒假)、
* save 输出上限收紧至 CFG_FILE_MAX_LEN、写盘唯一闸口拒写
* 超长;超长处置补自检留痕
* v1.3 2026-10-15 王建锋 新增公开常量
* CFG_FILE_MAX_NAME_LEN(段名/键名上限 15 字符)
* v1.4 2026-10-25 DTU 项目组 「数据可信」有符号零点类型:新增 CFG_VAL_INT 有符号十进制值类型
* (len = 1/2/4,小端补码)——标定零点偏移等负值需有符号
* 落盘,动因与取舍见 docs/任务规划/数据可信.md §4.4
* v1.5 2026-11-05 DTU 项目组 契约同步视图一致性守卫(issue #73 SNTP 补验揪出):
* poll 读视图与 stat 令牌不符 = 拒绝应用(RAM/基线不动、2s
* 重试自愈);save 读视图不符 = 拒存返回 -1(原文件不动)。
* 细节见 cfg_file.c v1.6 与《配置文件设计说明》变更节
*/
#ifndef CFG_FILE_H
#define CFG_FILE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
/* 配置文件长度上限(字节);超过 = 文件损坏,按出厂模板重建
* (load 与 poll 同判:poll 在读取前基于真实 st.size 判定) */
#define CFG_FILE_MAX_LEN 4096u
/* 引擎可注册段数上限 */
#define CFG_FILE_MAX_SECTIONS 8u
/* 段名 / 键名最大字符数(不含结尾 NUL);超长的注册描述符被 cfg_file_register 拒绝。
* 该上限是生成文档每行余量(112 字节)可编译期验证的前提,勿放宽 */
#define CFG_FILE_MAX_NAME_LEN 15u
/* 单段键数上限 */
#define CFG_FILE_MAX_KEYS 16u
/* 沉降去抖:变更基线连续 N 次轮询不变才重载(轮询周期 2s → 4s 沉降) */
#define CFG_FILE_SETTLE_CNT 2u
/*
* 键值类型
* UINT:无符号十进制;len = 目标字段字节数(1/2/4,小端)
* IP4 :点分 IPv4 "a.b.c.d";字段 uint8_t[4]
* MAC :十六进制 MAC "xx:xx:xx:xx:xx:xx"(分隔符兼容 - 与 .);字段 uint8_t[6]
* STR :ASCII 字符串;字段 char[len](len = 缓冲长度,自动补 NUL)
* INT :有符号十进制(可带 + / - 号);len = 目标字段字节数(1/2/4,小端补码,
* 范围按宽度收窄:int8/int16/int32;负值合法,如标定零点偏移)
*/
typedef enum {
CFG_VAL_UINT = 0,
CFG_VAL_IP4,
CFG_VAL_MAC,
CFG_VAL_STR,
CFG_VAL_INT
} cfg_val_type_t;
/* 键定义(offset/len 均相对段 RAM 副本起始) */
typedef struct {
const char *name; /* 键名(小写,≤ CFG_FILE_MAX_NAME_LEN 字符) */
cfg_val_type_t type; /* 值类型 */
size_t offset; /* 字段偏移 */
size_t len; /* 字段长度 */
} cfg_key_def_t;
/* 段注册描述符 */
typedef struct {
const char *name; /* 段名(≤ CFG_FILE_MAX_NAME_LEN 字符) */
void *p_data; /* 段 RAM 副本(模块自有静态区,引擎原地更新) */
const void *p_defaults; /* 工厂默认值(与 p_data 同布局;load 先复位再解析) */
size_t data_size; /* RAM 副本字节数 */
const cfg_key_def_t *p_keys; /* 键表 */
uint8_t key_count; /* 键数(1 ~ CFG_FILE_MAX_KEYS) */
void (*p_cb)(void); /* 变更回调(可空;poll 重载后该段有差异时调用;
注册段 data_size 须 ≤ 64 字节方可挂回调) */
} cfg_section_reg_t;
/*
* 函数功能:注册配置段(引擎建立"段 → RAM 副本 + 键表"映射)
* 入口参数:p_reg - 段注册描述符(不可为 NULL) const cfg_section_reg_t *
* 返回值:0 - 成功;-1 - 参数非法/段名重复/段数超限 int
* 限定条件:启动早期调用一次(重复注册同段名拒绝);
* 注册后 p_data/p_defaults/p_keys 生命周期须覆盖引擎使用期
*/
int cfg_file_register(const cfg_section_reg_t *p_reg);
/*
* 函数功能:从配置文件加载全部段(文件 = 配置唯一来源)
* 入口参数:无
* 返回值:0 - 正常(含回退工厂默认);-1 - 严重错误(RAM 已置工厂默认,仍安全) int
* 限定条件:至少注册一个段
* 函数说明: 1. 各段先复位工厂默认(文件缺失/损坏的键 = 工厂默认)
* 2. 文件缺失 → 按当前 RAM 副本生成出厂模板写盘(首启建文件)
* 3. 文件存在 → 解析:有效键写入 RAM 副本;无效键留工厂默认并留痕
* 4. 文件超长(> CFG_FILE_MAX_LEN)= 损坏 → 工厂默认 + 重建模板
* + 自检留痕(poll 检出运行期超长文件执行同一契约)
*/
int cfg_file_load(void);
/*
* 函数功能:保存全部段到配置文件(外科手术式)
* 入口参数:无
* 返回值:0 - 成功;-1 - 写失败(原文件保持不动) int
* 限定条件:至少注册一个段
* 函数说明:文件存在 → 逐行保留注释/未知段/未知键,受管段/键按 RAM 副本
* 值原位改写,缺失段/键补写;文件缺失/损坏 → 按 RAM 副本重建
* 完整文档(含文件头注释);写盘 = 临时文件 + 原子改名;输出超
* CFG_FILE_MAX_LEN → 拒绝保存返回 -1(原文件不动 + 自检上报,
* 引擎写盘闸口同拒);读视图与 stat 基准不符(空读/截断/读中
* 被改写)同样拒存返回 -1——防按短视图外科重写把文件写小、
* 非默认段被静默删除(2026-11-05 视图一致性守卫,调用方脏位
* 保留 = 下一趟自然重试)
*/
int cfg_file_save(void);
/*
* 函数功能:配置文件变更轮询(沉降去抖 + 重载 + 分段变更回调)
* 入口参数:无
* 返回值:无
* 限定条件:由配置引擎属主任务周期调用(建议 2s;MCU = defaultTask,
* 模拟器 = 主循环);引擎静态状态无跨任务保护,属主唯一即无竞态
* 函数说明:(size, mtime 变更令牌) 相对基线变化且连续 CFG_FILE_SETTLE_CNT
* 次轮询稳定 → 先查 st.size 超 CFG_FILE_MAX_LEN = 损坏 → 工厂默认
* + 重建模板 + 自检留痕(不触发段回调:保留在线修复通道,重启
* 收敛);否则过视图一致性守卫(读长 = st.size 且读前后 (size,
* mtime) 不变;不符 = 卷层旧视图/读中被改写 → 拒绝应用:RAM 与
* 基线不动,2s 重试自愈,持续到重启则由 load 全新挂载收敛;首次
* 拒绝自检留痕,其后每 30 趟一条 WARN)后重载全部段(各段复位
* 工厂默认后解析);挂回调段有差异 → 调用其回调(如触发网络重建);
* 重载后刷新基线(回调触发的写盘不引发二次重载)
*/
void cfg_file_poll(void);
/*
* 函数功能:取段 RAM 副本指针
* 入口参数:p_name - 段名 const char *
* 返回值:段 RAM 副本指针;未注册返回 NULL void *
* 限定条件:无
*/
void *cfg_file_section_data(const char *p_name);
#ifdef __cplusplus
}
#endif
#endif /* CFG_FILE_H */