Files
STM32F4-Base/Drivers/BSP/NETCFG/net_cfg.c
T
super_agent_bot ad7338365c
CI / 静态门禁 (pre-commit) (push) Failing after 6m14s
CI / 主机侧单测 (MSVC) (push) Successful in 54s
CI / 静态门禁 (pre-commit) (pull_request) Failing after 4m35s
CI / 主机侧单测 (MSVC) (pull_request) Successful in 47s
feat(sntp): 板端 SNTP 校时接入——coreSNTP 序列化器 + [sntp] 段 + 设置页(#73)
- vendored coreSNTP v2.0.0 序列化器(MIT,仅 serializer+LICENSE,零堆),
  48 字节报文编解码在库,节奏与准入判据在本方(RFC 4330 §10 调研口径)
- App/sync/time_sntp.c 挂 timeSyncTask 的 8888 事件循环,等待切片 ≤50 ms,
  不饿任务狗与 IWDG;懒建 UDP socket,代际变化/禁用/死错误码即关闭归还
- 防伪 = OriginTimestamp 逐位等于发出时 T1(含低 16 位随机小数位);
  mode/stratum/闰秒指示/KoD 按 RFC 拒收;DENY·RSTR 换服务器、RATE 只退避
- 主备轮换 + 失败指数退避(5 s 起、60 s 封顶)+ 首轮 1~5 分钟随机错峰;
  成功恢复 poll_min 周期(1~1440 分,出厂默认 60,默认不启用不发包)
- 钟面 = 本地时间:UTC+8 固定偏移落 sys_clock_set_unix(§1.1 时基裁定,
  第三个写入口,装置仍不存时区);|offset|<2 s 只记日志不步进,冷启动或
  更大偏差步进并回写 SD2506,留痕 [SNTP] step 与 syslog OPER
- 首个校验通过的同步置 SYS_EVT_TIMESYNC_READY(就绪位,消费方按需 NoClear)
- [sntp] 配置段(enable/server1/server2/poll_min)属主 sntp_cfg.c,
  注册在 net_cfg_load();UI 页 31「SNTP 设置」(密码叶):启用开关 +
  主/备服务器 octet 编辑 + 周期(分) + 保存,复用 net_cfg_save_request 链
- 测试与门禁:test/host/sntp 新套件(报文/防伪/KoD/退避周期/阶跃域/配置
  注册表)、net_cfg 与 time_sync 套件补 fake、黄金参考 66→74 张零漂移、
  字符集 228 字、Keil 0 错 0 警(RO +5,848 B 含字体)、覆盖率基线回填
- 文档:墙钟维护说明 §16、网络配置说明 [sntp] 节、配置文件设计说明变更记录、
  产品需求 FR-TIME-3 与 8.3/9.1.2 台账、UI 补齐规划/UI补齐 31 叶口径、
  登记簿销项并登记「SNTP 校时真机判据」等台架(PC 时间源 192.168.31.161/.13)
2026-11-04 16:20:22 +08:00

418 lines
15 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.
/*
* 模块名称:Network Configuration Core (配置核心)
* 模块功能:RAM 配置副本 + 工厂默认 + 加载/保存编排 + 实际 MAC 影子 +
* IPv4 字符串转换;存储经 cfg_file 通用配置文件引擎(INI [network]
* 段:ip/mask/gw/mac)
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-09-10
* 修改记录:
* v1.0 2026-09-10 王建锋 创建初始版本
* v1.1 2026-09-14 王建锋 存储迁移至 cfg_file 引擎(INI);旧 NCFG 代码移至 net_cfg_legacy
* v1.2 2026-09-15 王建锋 保存改跨任务交接(request/state/run + 通知回调);
* 引擎属主 = defaultTask(竞态修复)
* v1.3 2026-10-10 王建锋 **删除旧档 NCFG 迁移**(net_cfg_legacy 整体移除):该兼容
* 路径把整条配置写链(net_cfg_save → cfg_file_save →
* cfg_store_write_atomic → f_rename → FTL)挂进启动期调用图,
* 使 defaultTask 静态最坏链 +600 B;本仓库无携带
* /cfg/netcfg.bin 的设备,故按"不保留旧档兼容"处置。
* 旧档若存在 → 被忽略(不读、不删、不迁移),配置回落工厂默认
*/
/* 头文件包含区 */
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include "net_cfg.h"
#include "cfg_file.h"
#include "ftp_cfg.h"
#include "sntp_cfg.h"
/* 深调用链模块(net_cfg_load/save 在 defaultTask 启动链与保存链上):
* 日志行缓冲收到 128 B(默认 192),本模块最长行渲染后 < 128 → 不截断 */
#define DBG_LINE_BUF_MAX 128U
#define DBG_TAG "[NET_CFG]"
#include "dbg_log.h"
/* 私有宏定义区 */
/* 工厂默认值(单一来源:s_cfg 初值与 s_defaults 共用,避免两处漂移) */
#define NET_CFG_DEF_IP { 192u, 168u, 31u, 100u }
#define NET_CFG_DEF_MASK { 255u, 255u, 255u, 0u }
#define NET_CFG_DEF_GW { 192u, 168u, 31u, 1u }
#define NET_CFG_DEF_MAC { 0u, 0u, 0u, 0u, 0u, 0u }
/* 静态变量定义区 */
/* RAM 配置副本:以工厂默认值静态初始化(落 .data),自启动即有效——
* 不依赖 net_cfg_load 是否被调用(load 仅在文件有效时覆盖) */
static net_cfg_t s_cfg = {
.ip = NET_CFG_DEF_IP,
.mask = NET_CFG_DEF_MASK,
.gw = NET_CFG_DEF_GW,
.mac = NET_CFG_DEF_MAC,
};
static uint8_t s_active_mac[6]; /* 芯片实际 MAC 影子(netTask 回写) */
static uint8_t s_active_valid; /* 影子有效标志(0 = 尚未回写) */
static net_cfg_apply_cb_t s_apply_cb; /* "立即应用"平台回调(未注册 = 空操作) */
static volatile uint8_t s_save_pending; /* 1 = 有待执行保存请求(UI 投递/属主执行) */
static volatile uint8_t s_save_busy; /* 1 = 属主任务正在写盘(维持 UI 的 WAIT 显示) */
static volatile uint8_t s_save_result; /* 最近一次保存结果:0 = 无;1 = 成功;2 = 失败 */
static net_cfg_save_req_cb_t s_save_req_cb; /* "保存请求"平台通知(未注册 = 空操作) */
/* 工厂默认(引擎注册 p_defaults 指向;与 s_cfg 初值共用宏单一来源) */
static const net_cfg_t s_defaults = {
.ip = NET_CFG_DEF_IP,
.mask = NET_CFG_DEF_MASK,
.gw = NET_CFG_DEF_GW,
.mac = NET_CFG_DEF_MAC,
};
/* [network] 段键表(net_cfg_t 布局内偏移) */
static const cfg_key_def_t s_net_keys[] = {
{ "ip", CFG_VAL_IP4, offsetof(net_cfg_t, ip), 4u },
{ "mask", CFG_VAL_IP4, offsetof(net_cfg_t, mask), 4u },
{ "gw", CFG_VAL_IP4, offsetof(net_cfg_t, gw), 4u },
{ "mac", CFG_VAL_MAC, offsetof(net_cfg_t, mac), 6u },
};
/* [sntp] 段键表(sntp_cfg_t 布局内偏移;数据本体在 sntp_cfg.c——分层门禁
* layer-deps 要求段描述符的组装集中在本文件:NETCFG 里只有 net_cfg.c /
* ftp_cfg.c 在反向依赖基线上,[sntp] 是新增模块,不再往基线添条目) */
static const cfg_key_def_t s_sntp_keys[] = {
{ "enable", CFG_VAL_UINT, offsetof(sntp_cfg_t, enable), 1u },
{ "server1", CFG_VAL_IP4, offsetof(sntp_cfg_t, server_pri), 4u },
{ "server2", CFG_VAL_IP4, offsetof(sntp_cfg_t, server_bak), 4u },
{ "poll_min", CFG_VAL_UINT, offsetof(sntp_cfg_t, poll_min), 2u },
};
/* 函数定义区 */
/*
* 函数功能:[network] 段变更回调(poll 重载检出差异时由引擎调用)
* 入口参数:无
* 返回值:无
* 限定条件:无(未注册"立即应用"回调时空操作)
* 函数说明:转平台回调(MCU:netTask 置 SYS_EVT_NET_RECONFIG 重建网络)
*/
static void s_net_changed_cb(void) {
if (s_apply_cb != NULL) {
s_apply_cb();
}
}
/*
* 函数功能:向 cfg_file 引擎注册 [network] 段(幂等)
* 入口参数:无
* 返回值:无
* 限定条件:load/save 前调用
* 函数说明:段数据 = s_cfg RAM 副本;工厂默认 = s_defaults;
* 变更回调触发"立即应用"
*/
static void s_net_register(void) {
static uint8_t s_done;
cfg_section_reg_t reg;
if (s_done != 0u) {
return;
}
(void)memset(&reg, 0, sizeof(reg));
reg.name = "network";
reg.p_data = &s_cfg;
reg.p_defaults = &s_defaults;
reg.data_size = sizeof(net_cfg_t);
reg.p_keys = s_net_keys;
reg.key_count = 4u;
reg.p_cb = s_net_changed_cb;
(void)cfg_file_register(&reg);
s_done = 1u;
}
/*
* 函数功能:向 cfg_file 引擎注册 [sntp] 段(幂等)
* 入口参数:无
* 返回值:无
* 限定条件:load/save 前调用(net_cfg_load 内统一编排)
* 函数说明:数据本体与工厂默认由 sntp_cfg.c 出(sntp_cfg_data/defaults);
* 无变更回调——SNTP 客户端每轮直读 RAM 副本,保存后的"立即生效"
* 走既有 SYS_EVT_NET_RECONFIG(网络重建 → 代次变化 → 客户端重开
* Socket)链,本段无需单独挂接
*/
static void s_sntp_register(void) {
static uint8_t s_done;
cfg_section_reg_t reg;
if (s_done != 0u) {
return;
}
(void)memset(&reg, 0, sizeof(reg));
reg.name = "sntp";
reg.p_data = sntp_cfg_data();
reg.p_defaults = sntp_cfg_defaults();
reg.data_size = sizeof(sntp_cfg_t);
reg.p_keys = s_sntp_keys;
reg.key_count = 4u;
reg.p_cb = NULL;
(void)cfg_file_register(&reg);
s_done = 1u;
}
/*
* 函数功能:判断 6 字节 MAC 是否全零
* 入口参数:p_mac - MAC 指针(不可为 NULL) const uint8_t *
* 返回值:true - 全零;false - 非全零
* 限定条件:p_mac 非空
*/
static bool s_mac_is_zero(const uint8_t *p_mac) {
for (int i = 0; i < 6; i++) {
if (p_mac[i] != 0u) {
return false;
}
}
return true;
}
/*
* 函数功能:从存储加载网络配置到 RAM 副本
* 入口参数:无
* 返回值:恒 0(RAM 恒有效:文件缺失/CRC 错回退工厂默认) int
* 限定条件:由配置引擎属主任务调用(MCU = defaultTask,置位
* SYS_EVT_STORAGE_READY 前;模拟器 = 主循环启动期);文件读失败
* 自动回退工厂默认(不阻塞网络启动)
* 函数说明:见 net_cfg.h 同名函数说明
*/
int net_cfg_load(void) {
s_net_register();
/* [ftp] / [sntp] 段一并注册(所有受管段必须先注册、后 load,引擎按注册表解析);
* [sntp] 描述符在本文件组装——见 s_sntp_register 分层门禁注 */
ftp_cfg_register();
s_sntp_register();
/* 旧档 NCFG 迁移已于 2026-10-10 删除(net_cfg_legacy 移除;理由见文件头 v1.3)。
* 若卷上仍残留 /cfg/netcfg.bin:不读、不删、不迁移 → 配置回落工厂默认 */
if (cfg_file_load() != 0) {
/* 严重错误:引擎已将 RAM 副本复位工厂默认,行为仍安全 */
DBG_ERROR("load: cfg_file_load FAIL");
return 0;
}
DBG_INFO("load: OK ip=%u.%u.%u.%u",
(unsigned)s_cfg.ip[0], (unsigned)s_cfg.ip[1],
(unsigned)s_cfg.ip[2], (unsigned)s_cfg.ip[3]);
return 0;
}
/*
* 函数功能:保存当前 RAM 配置到存储
* 入口参数:无
* 返回值:0 - 成功;-1 - 写失败 int
* 限定条件:仅配置引擎属主任务上下文调用(MCU = defaultTask;模拟器 = 主循环)
* 函数说明:经 cfg_file 引擎写盘(外科手术式保存:保留注释/未知内容,
* 临时文件 + 原子改名);跨任务场景请投递 net_cfg_save_request
*/
int net_cfg_save(void) {
int r;
s_net_register();
r = cfg_file_save();
if (r != 0) {
DBG_ERROR("save: cfg_file_save FAIL");
return -1;
}
DBG_INFO("save: OK (config file written)");
return 0;
}
/*
* 函数功能:注册"立即应用"回调
* 入口参数:cb - 回调函数(NULL = 注销) net_cfg_apply_cb_t
* 返回值:无
* 限定条件:应用启动早期注册一次即可
* 函数说明:见 net_cfg.h 同名函数说明
*/
void net_cfg_set_apply_cb(net_cfg_apply_cb_t cb) {
s_apply_cb = cb;
}
/*
* 函数功能:请求立即应用当前 RAM 配置
* 入口参数:无
* 返回值:无
* 限定条件:无(未注册回调时空操作)
* 函数说明:见 net_cfg.h 同名函数说明
*/
void net_cfg_request_apply(void) {
if (s_apply_cb != NULL) {
s_apply_cb();
}
}
/*
* 函数功能:投递"保存网络配置"请求(跨任务交接的投递端)
* 入口参数:无
* 返回值:恒 0 int
* 限定条件:任意任务上下文(UI 保存行);新值须已落 RAM 副本
* 函数说明:置"待保存"标志后经平台回调唤醒属主任务(MCU:置
* SYS_EVT_CFG_SAVE 事件);写盘与"立即应用"由属主任务执行
* (net_cfg_save_run),本函数不触碰配置文件引擎
*/
int net_cfg_save_request(void) {
s_save_pending = 1u;
if (s_save_req_cb != NULL) {
s_save_req_cb();
}
return 0;
}
/*
* 函数功能:取保存请求交接状态(UI 保存行显示)
* 入口参数:无
* 返回值:NET_CFG_SAVE_ST_IDLE / _WAIT / _OK / _FAIL int
* 限定条件:任意任务上下文(单字节读取,无撕裂)
* 函数说明:见 net_cfg.h 同名函数说明
*/
int net_cfg_save_state(void) {
/* "请求已投递未执行"与"正在写盘"都显示 WAIT:pending 在写盘前就被清掉
* (为了不让写盘期间的新请求丢失),单看它会在这几百毫秒里退回上一轮的
* OK/IDLE —— 用户按了 ENTER 却看不到反馈,只会再按一次。busy 补上这一段。 */
if ((s_save_pending != 0u) || (s_save_busy != 0u)) {
return (int)NET_CFG_SAVE_ST_WAIT;
}
if (s_save_result == 1u) {
return (int)NET_CFG_SAVE_ST_OK;
}
if (s_save_result == 2u) {
return (int)NET_CFG_SAVE_ST_FAIL;
}
return (int)NET_CFG_SAVE_ST_IDLE;
}
/*
* 函数功能:执行待处理的保存请求(配置文件引擎属主任务)
* 入口参数:无
* 返回值:0 - 无待处理请求或保存成功;-1 - 写盘失败 int
* 限定条件:仅配置引擎属主任务上下文(MCU = defaultTask;模拟器 = 主循环)
* 函数说明:先清"待保存"标志(写盘期间的新请求重位置位,下轮再执行,
* 不丢失),同时置"写盘中"标志让 UI 在这段窗口里仍显示 WAIT
* (否则退回上一轮结果,见 net_cfg_save_state);写盘成功 → 置 OK
* 并触发"立即应用"(netTask 重建网络,无需重启);失败 → 置 FAIL,
* 不触发应用(网络保持)。两条路径都会清"写盘中"标志
*/
int net_cfg_save_run(void) {
int r;
if (s_save_pending == 0u) {
return 0; /* 无待处理请求(空醒/周期轮询) */
}
s_save_pending = 0u;
s_save_busy = 1u; /* 写盘期间维持 WAIT(见 net_cfg_save_state 注释) */
r = net_cfg_save();
s_save_busy = 0u;
if (r == 0) {
s_save_result = 1u;
net_cfg_request_apply();
}
else {
s_save_result = 2u;
}
return r;
}
/*
* 函数功能:注册"保存请求"通知回调
* 入口参数:cb - 回调函数(NULL = 注销) net_cfg_save_req_cb_t
* 返回值:无
* 限定条件:应用启动早期注册一次即可
* 函数说明:MCU 端 defaultTask 注册(置 SYS_EVT_CFG_SAVE 事件立即唤醒);
* 模拟器不注册(主循环每轮查状态,无需唤醒)
*/
void net_cfg_set_save_req_cb(net_cfg_save_req_cb_t cb) {
s_save_req_cb = cb;
}
/*
* 函数功能:取 RAM 配置副本(只读指针)
* 入口参数:无
* 返回值:RAM 配置副本指针(恒非空) const net_cfg_t *
* 限定条件:无
*/
const net_cfg_t *net_cfg_get(void) {
return &s_cfg;
}
/*
* 函数功能:更新 RAM 配置副本(不写盘)
* 入口参数:p_cfg - 新配置指针(不可为 NULL) const net_cfg_t *
* 返回值:无
* 限定条件:p_cfg 非空
*/
void net_cfg_set(const net_cfg_t *p_cfg) {
if (p_cfg == NULL) {
return;
}
s_cfg = *p_cfg;
}
/*
* 函数功能:回写芯片实际 MAC(影子区)
* 入口参数:p_mac - 芯片读回的实际 MAC(不可为 NULL) const uint8_t *
* 返回值:无
* 限定条件:net_init 成功后由 netTask 上下文调用
*/
void net_cfg_set_active_mac(const uint8_t *p_mac) {
if (p_mac == NULL) {
return;
}
memcpy(s_active_mac, p_mac, 6u);
s_active_valid = 1u;
}
/*
* 函数功能:取显示用 MAC
* 入口参数:p_out - 6 字节输出缓冲(不可为 NULL) uint8_t *
* 返回值:无
* 限定条件:p_out 非空
* 函数说明:存储非全零 → 存储值;否则芯片实际(已回写);否则全零
*/
void net_cfg_get_mac(uint8_t *p_out) {
if (p_out == NULL) {
return;
}
if (!s_mac_is_zero(s_cfg.mac)) {
memcpy(p_out, s_cfg.mac, 6u);
}
else if (s_active_valid != 0u) {
memcpy(p_out, s_active_mac, 6u);
}
else {
memset(p_out, 0, 6u);
}
}
/*
* 函数功能:4 字节 IPv4 格式化为点分十进制字符串(每段 3 位补零)
* 入口参数:p_ip - 4 字节 IP(不可为 NULL) const uint8_t *
* p_buf - 输出缓冲(不可为 NULL) char *
* len - 缓冲长度 size_t
* 返回值:无
* 限定条件:p_ip/p_buf 非空;len ≥ 16
* 函数说明:"192.168.001.100"(定长 **15** 字符 = 4 段 × 3 位 + 3 个点,UI 编辑
* 光标按固定偏移定位字节:段 i 起点 4i、点号在 3/7/11)
*/
void net_cfg_fmt_ipv4(const uint8_t *p_ip, char *p_buf, size_t len) {
if (p_ip == NULL || p_buf == NULL || len == 0u) {
return;
}
(void)snprintf(p_buf, len, "%03u.%03u.%03u.%03u",
(unsigned)p_ip[0], (unsigned)p_ip[1],
(unsigned)p_ip[2], (unsigned)p_ip[3]);
}