007f6a0e2f
原「配置文件设计说明.md」是 2026-09 的规划文档(P1~P8 实施计划、D1~D15 决策表、差距调研、模块清单动作列、已终结的开放问题),与现在跑的系统 已有明显出入。改写成只描述当前状态的「配置文件设计.md」。 新文档 9 节:概览 / 文件格式(路径与容量、语法、出厂模板原文、值类型)/ 运行时机制(上电加载、外科手术式保存、2s 轮询与沉降、视图一致性守卫、 写者归属)/ 段与键表(8 段全满、注册顺序、接入新段模板、段回调)/ 接口(引擎 API、平台后端)/ 内存占用(编译期钉死的 9217B 暂存)/ 异常与恢复 / 双端差异 / 已知限制。 顺带修正一处事实错误:旧文档称 FTP STOR 是「写 .TMP 再 rename 原子更新」, 实际 lftpd receive_file 是原地覆写,已按代码现状改写。 旧二进制档(NCFG)的叙述按裁定从全仓移除:文档只描述当前状态,不再 交代旧版本兼容性。删掉网络配置说明 §3.2 整节(后续小节重排为 3.2~3.5), 并清理 README / 架构总览 / 产品需求 / 部署环境等 11 处同类叙述。 同步: - 8 段注册、FTP 原地覆写、2s FAT mtime 粒度等数据更新进配置分层架构图, 重跑后与生成器逐字节一致,check_generated_fresh 里这条存量基线下架 - 全仓 24 处引用改用新文档名,旧节号逐条映射到新节号 - 文档规范 §7.1 变更记录例外名单 4 篇减为 3 篇(该档实际无变更记录表) 验证:48 个提交期钩子全通过;Keil 构建 0 错误 0 警告; check_doc_links / check_mermaid / check_generated_fresh 均通过。
180 lines
5.1 KiB
C
180 lines
5.1 KiB
C
/*
|
||
* 模块名称:cfg_file_store (模拟器后端)
|
||
* 模块功能:cfg_file 存储后端——模拟器工作目录宿主文件 sim_config.ini;
|
||
* 原子写 = 临时文件 sim_config.ini.tmp + rename;mtime 变更令牌 =
|
||
* 宿主文件 mtime(秒级)
|
||
* 适用平台:PC 模拟器
|
||
* 作者:王建锋
|
||
* 创建日期:2026-09-14
|
||
* 修改记录:
|
||
* v1.0 2026-09-14 王建锋 创建初始版本
|
||
* v1.1 2026-09-18 王建锋 增 cfg_store_alloc / cfg_store_free / cfg_store_heap_free
|
||
* (宿主 CRT 堆实现,对齐 MCU 端 FreeRTOS 堆语义)
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <sys/stat.h>
|
||
|
||
#include "cfg_file_store.h"
|
||
|
||
/* 静态变量定义区 */
|
||
|
||
static const char s_path[] = "sim_config.ini";
|
||
static const char s_path_tmp[] = "sim_config.ini.tmp";
|
||
|
||
/* 函数定义区 */
|
||
|
||
/*
|
||
* 函数功能:读文件(最多 cap 字节)
|
||
* 入口参数:p_path - 文件路径 const char *
|
||
* p_buf - 接收缓冲(不可为 NULL) uint8_t *
|
||
* cap - 缓冲容量 size_t
|
||
* p_nread - 实际读字节数输出(不可为 NULL) size_t *
|
||
* 返回值:0 - 成功;-1 - 文件缺失/读失败 int
|
||
* 限定条件:无
|
||
*/
|
||
int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nread) {
|
||
FILE *fp;
|
||
size_t n;
|
||
|
||
if (p_buf == NULL || p_nread == NULL) {
|
||
return -1;
|
||
}
|
||
fp = fopen(p_path, "rb");
|
||
if (fp == NULL) {
|
||
return -1;
|
||
}
|
||
n = fread(p_buf, 1u, cap, fp);
|
||
(void)fclose(fp);
|
||
*p_nread = n;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:原子写文件(先写 sim_config.ini.tmp,成功后 rename 覆盖目标)
|
||
* 入口参数:p_path - 目标文件路径 const char *
|
||
* p_buf - 待写数据(不可为 NULL) const uint8_t *
|
||
* len - 数据长度 size_t
|
||
* 返回值:0 - 成功;-1 - 写/改名失败(临时文件清理) int
|
||
* 限定条件:MSVC rename() 不覆盖已存在目标(C1117),须先 remove 再 rename
|
||
* (tmp 已完整写盘关闭后操作,中断窗口仅毫秒级;目标丢失时引擎
|
||
* 下次加载自动重建出厂模板,与 MCU 端 f_rename 直接替换等效)
|
||
*/
|
||
int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len) {
|
||
FILE *fp;
|
||
size_t n;
|
||
|
||
if (p_buf == NULL) {
|
||
return -1;
|
||
}
|
||
fp = fopen(s_path_tmp, "wb");
|
||
if (fp == NULL) {
|
||
return -1;
|
||
}
|
||
n = fwrite(p_buf, 1u, len, fp);
|
||
(void)fclose(fp);
|
||
if (n != len) {
|
||
(void)remove(s_path_tmp);
|
||
return -1;
|
||
}
|
||
(void)remove(p_path); /* 目标可能不存在(首写):remove 失败忽略 */
|
||
if (rename(s_path_tmp, p_path) != 0) {
|
||
(void)remove(s_path_tmp);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:文件状态(size + mtime 变更令牌)
|
||
* 入口参数:p_path - 文件路径 const char *
|
||
* p_st - 输出(不可为 NULL) cfg_file_stat_t *
|
||
* 返回值:0 - 成功;-1 - 文件缺失/读失败 int
|
||
* 限定条件:无
|
||
*/
|
||
int cfg_store_stat(const char *p_path, cfg_file_stat_t *p_st) {
|
||
struct stat st;
|
||
|
||
if (p_st == NULL) {
|
||
return -1;
|
||
}
|
||
if (stat(p_path, &st) != 0) {
|
||
return -1;
|
||
}
|
||
p_st->size = (uint32_t)st.st_size;
|
||
p_st->mtime = (uint32_t)st.st_mtime;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:删除文件
|
||
* 入口参数:p_path - 文件路径 const char *
|
||
* 返回值:0 - 成功;-1 - 文件缺失/删除失败 int
|
||
* 限定条件:无
|
||
*/
|
||
int cfg_store_remove(const char *p_path) {
|
||
if (remove(p_path) != 0) {
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:取配置文件路径
|
||
* 入口参数:无
|
||
* 返回值:"sim_config.ini"(模拟器工作目录) const char *
|
||
* 限定条件:无
|
||
*/
|
||
const char *cfg_store_config_path(void) {
|
||
return s_path;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:取配置文件临时文件路径
|
||
* 入口参数:无
|
||
* 返回值:"sim_config.ini.tmp" const char *
|
||
* 限定条件:无
|
||
*/
|
||
const char *cfg_store_tmp_path(void) {
|
||
return s_path_tmp;
|
||
}
|
||
|
||
/*
|
||
* 旧版遗留档 getter(cfg_store_legacy_path)已于 2026-10-10 随旧档迁移
|
||
* net_cfg_legacy 一并删除:旧格式档不再读取
|
||
*/
|
||
|
||
/*
|
||
* 函数功能:分配共享核心临时缓冲(宿主 CRT 堆)
|
||
* 入口参数:size - 字节数 size_t
|
||
* 返回值:缓冲指针(内容未清零);失败返回 NULL void *
|
||
* 限定条件:释放必须用 cfg_store_free
|
||
* 函数说明:模拟器进程堆以 MB 计,直接 malloc;与 MCU 端差异仅在本后端
|
||
*/
|
||
void *cfg_store_alloc(size_t size) {
|
||
return malloc(size);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:释放 cfg_store_alloc 分配的临时缓冲
|
||
* 入口参数:p_buf - 缓冲指针(NULL 安全) void *
|
||
* 返回值:无
|
||
* 限定条件:p_buf 必须来自 cfg_store_alloc
|
||
*/
|
||
void cfg_store_free(void *p_buf) {
|
||
free(p_buf);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:空闲堆字节数(诊断用)
|
||
* 入口参数:无
|
||
* 返回值:恒 0(宿主 CRT 不提供可移植的空闲堆查询) uint32_t
|
||
* 限定条件:无
|
||
*/
|
||
uint32_t cfg_store_heap_free(void) {
|
||
return 0u;
|
||
}
|