1a2ca0467d
台架(PR #82 head d9303e05,常授权烧写验证)在三点校验版守卫上继续复现 出打回形态:卷层给出的旧映射块与目标文件恰好同长(1064B)且整块全 0, 读长 == st.size 与读前后复 stat 全部通过,解析零错误、RAM 照样打回默认 (15:21 决定性轮:盘文件 RETR 校验七段完好,重载窗口内 s_sntp 三字节段 全 0)。纯令牌层校验对"内容坏"是盲的,必须补内容判据。 处置(引擎侧,与三点校验同族): - 新增 s_view_bad:重载/保存读到的视图若为空读、或 [0,len) 内任意偏移 含 NUL 字节即判坏——INI UTF-8 文本约定下任何偏移都不得含 0x00;解析 器按行切分遇 NUL 即断行,零块之后的内容本就不可达(静默截断同源故障), 拒之不误伤任何可解析文档。不要求首字节 ';'(手写文档可直接段名起始)。 - poll:坏内容 = 与长度/令牌不符同处置——拒绝应用,RAM 与基线不动,2s 重试自愈,首次 ERROR + 自检留痕,每 30 趟一条 WARN(限速)。 - save:坏内容同族拒存返回 -1,原文件一字节不动,调用方 dirty 保留重试。 - 边界如实保留:内容合法但陈旧的整卷视图(长度/令牌/内容三者皆一致滞后) 引擎层无法检出,属卷层(dhara/FTL)课题,重启必然收敛;"改过配置后 重启一次"在卷层根治前仍是最稳口径。 测试: - TC-CONFIG-061(poll 同尺寸全 0 块 HELD + 视图刷新自愈 + 零回调)、 TC-CONFIG-062(save 遇全 0 块拒存、文件字节/写次数不动、追平后重试 成功);桩新增 vfs_mark_stale_zeros() 注入同长全 0 旧视图。 - cfg_file 套件 259/259;iec104 179/179、run_par 67/67、adc_cal 117/117、 sntp 98/98、cfg_file_store_mcu 34/34 复跑全绿。 - /keil-build 0 错 0 警:Code 254,784 -> 254,820 B(+36 B);台账与同码 凭证已同步。台架实录见 PR #82 评论:NUL 注入块实机 HELD(16:55:46) + WARN 节拍 30/60 趟命中(16:56:42/16:57:41)+ 视图转新鲜后重载自动 追平;残余打回事件均为"内容合法但陈旧"整卷视图(15:19:30/16:34:59/ 16:58:38),属已声明守卫边界,盘文件全程未吞段,重启收敛实机确认 (17:04 RAM 回文件值)。 文档: - 《配置文件设计说明》§4-(3)/§5.3 守卫描述补内容判据与 15:21 实证形态、 风险表行更新、变更记录 v1.17 行同步; - 《配置文件单元测试说明》TC-CONFIG-061/062 行 + 当前结果 259/259 + 扩展指南补 vfs_mark_stale_zeros()。
163 lines
8.3 KiB
C
163 lines
8.3 KiB
C
/*
|
||
* 模块名称: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 基准不符(空读/截断/读中
|
||
* 被改写)或内容坏(INI 文本任意偏移含 NUL——dhara 同尺寸全 0
|
||
* 旧映射块 2026-11-05 台架实证)同样拒存返回 -1——防按短/坏视图
|
||
* 外科重写把文件写小、非默认段被静默删除(视图一致性守卫,
|
||
* 调用方脏位保留 = 下一趟自然重试)
|
||
*/
|
||
int cfg_file_save(void);
|
||
|
||
/*
|
||
* 函数功能:配置文件变更轮询(沉降去抖 + 重载 + 分段变更回调)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:由配置引擎属主任务周期调用(建议 2s;MCU = defaultTask,
|
||
* 模拟器 = 主循环);引擎静态状态无跨任务保护,属主唯一即无竞态
|
||
* 函数说明:(size, mtime 变更令牌) 相对基线变化且连续 CFG_FILE_SETTLE_CNT
|
||
* 次轮询稳定 → 先查 st.size 超 CFG_FILE_MAX_LEN = 损坏 → 工厂默认
|
||
* + 重建模板 + 自检留痕(不触发段回调:保留在线修复通道,重启
|
||
* 收敛);否则过视图一致性守卫(读长 = st.size、内容无 NUL(dhara
|
||
* 同尺寸全 0 旧映射块 2026-11-05 台架实证)且读前后 (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 */
|