Files
STM32F4-Base/App/sync/time_sync.c
T
super_agent_bot 3a2f9b21fb
CI / 静态门禁 (pre-commit) (push) Failing after 43s
CI / 主机侧单测 (MSVC) (push) Failing after 45s
CI / 静态门禁 (pre-commit) (pull_request) Failing after 45s
CI / 主机侧单测 (MSVC) (pull_request) Failing after 45s
refactor(time_sync)!: 退役 TCP :8888 对时任务,timeSyncTask 转纯 SNTP 校时节拍宿主(#78)
SNTP 客户端已随 #73/PR #77 上线合入(merge 2b848c4a),用户裁定
8888 对时任务退出历史舞台(gitea:77 线程原话,跟踪 Issue #78)。

生产侧(App/sync/time_sync.c v2.0,任务入口名不变,freertos.c 零改动):
- 删除 :8888 监听、TIME 包重组、sys_clock 写入、OPER 落痕、
  net_drop_conn 队头自救整条链(TIME_SYNC_PORT 宏随退役删除)
- 任务体收敛为:就绪屏障(100ms 分片、osFlagsNoClear)→
  200ms 校时节拍 task_wd_beat → time_sntp_run(net_get_generation())
  → 分片延时;喂狗节奏(TASK_WD_TIMESYNC=5,required_mask)不变
- 时钟写入口自此 = SNTP / UI 手工 / 104 C_CS,双写竞态(后写者赢)
  随之消失(墙钟说明 §15 退役公告、§16 现势口径)

工具与测试:
- 删除 test/host/time_sync_server.py(对时源)与 e02_probe.py
  (队头阻塞探针,载体随监听退役;判据脚本 v5_log_gap_report.py
  保留供旧日志解读)
- time_sync 主机单测重写 v2.0:33 断言覆盖分片延时 / 屏障 /
  SNTP 节拍与网络代次当轮交接 / 未出网守门;stub 的
  net_socket.h 裁剪至 net_get_generation 作编译期绊线
- 覆盖率基线 --update-baseline 收口(time_sync.c 101→20 行为刻意
  缩减,全仓 48/48 套绿)

文档与图:墙钟说明 §15 改退役公告(教训保留:CH395F FIN 丢未读
缓冲)、§16 单一网络来源;产品需求 FR-TIME-2 退役注记、§6.3 重写;
架构总览 / 软件架构规格 / 网络配置 / 网络自愈 §9.3 / BSD 指南 /
看门狗说明心跳与白名单行 / 实机测试指南 / 部署环境 / 继保规划 /
UI补齐时基口径 / 页面补齐规划 SNTP 页行 / 任务规划 README 叶数与
暂缓项行(SNTP 叶已绑、UI_PAGE_MAP_COUNT=31 口径)等约 30 处现势
口径同步;arch / net_stack 两图生成器与 SVG 同笔重跑。

台账:ram_baseline --update + check_mem_numbers --write + 体积凭证
(Code 254,648→253,428,−1,220 B)。静态链复测发现 timeSyncTask
1,904 B / iec104Task 1,600 B 低于 ISR 硬线:master 2b848c4a 同判据
timeSyncTask 即 2,056 B(本 PR 只减不增,−152 B 旧任务自身帧),
双 FAIL 为 #74/#77 后既有债;抬栈属 Ask-first 未擅自执行,登记簿
新增「静态链硬线双 FAIL」待台架复采,复测快照入 docs/内存管理/data。

验证:keil-build 0 错 0 警;time_sync/sntp 主机单测绿;
ci_local 全绿;doc-links --all 0。
2026-11-04 18:07:03 +08:00

117 lines
6.7 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.
/*
* 模块名称:Time Sync — timeSyncTask 载体(网络校时节拍宿主)
* 模块功能:独立任务 timeSyncTask:等网络就绪后按固定节拍驱动 SNTP 校时客户端
* (App/sync/time_sntp.c),长等待一律分片并逐片喂任务心跳
* 适用平台:STM32F407ZGTx + CH395F
* 作者:王建锋
* 创建日期:2026-08-27
* 修改记录:
* v1.0 2026-08-27 王建锋 创建初始版本(UDP 推送,netTask 内轮询)
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask,
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
* netTask 仅负责 net_poll/net_process_messages,多任务并发安全
* v1.2 2026-10-02 王建锋 第 8b 轮消抖:等待片长 200ms→50ms,并把各处长
* 等待(500/2000/200ms)统一走 time_sync_delay_fed()
* 按 ≤50ms 分片、每片喂心跳——消除"心跳滑出两个连续
* 100ms 抽样窗"的节拍抖动(第 7 轮台架 snap=0x17)
* v1.3 2026-10-27 DTU 项目组 道G task-50:对时**成功应用**后投一条 quiet
* 操作记录("clock set by 8888")——task-49 盘点盘出
* "104 对时留痕、本端口不留"的同类不对称,Lead 批准补齐
* v1.4 2026-11-04 DTU 项目组 SNTP 客户端接入(#57 调研落地):accept 轮询
* 片内驱动 time_sntp_run()(48B UDP 请求/应答状态机
* 在 App/sync/time_sntp.c,节奏/退避/步进策略见该文件)
* v2.0 2026-11-04 DTU 项目组 私有 TCP :8888 对时退役(SNTP 上线后退出历史
* 舞台):监听重建、"TIME" 包重组、OPER 留痕、头阻塞
* 收口整条链随退——历史协议与退役理由见
* docs/子系统设计/系统墙钟时间维护说明.md §15 退役注记;
* PC 推送工具 time_sync_server.py 与探针 e02_probe.py
* 同日删除。本任务收缩为纯节拍宿主:NET_READY 屏障 →
* 200ms 循环驱动 time_sntp_run()。心跳分片纪律
* (v1.2,第 8b 轮消抖)与屏障喂心跳(2026-10-02
* 台架修复)原样保留。
*/
#include "time_sync.h"
#include "time_sntp.h"
#include "net_socket.h"
#include "sys_events.h"
#include "task_watchdog.h"
/* 看门狗分片等待:单次延时的分片上限。须显著小于看门狗抽样窗
* (task_watchdog.c WD_POLL_PERIOD_MS=100ms)——片长 ≤50ms 时每片前喂的心跳
* 必然落在同一个 100ms 抽样窗内,消除"心跳恰好滑过两个连续窗口"的边界抖动
* (第 7 轮台架稳态 snap=0x17 即抽样窗 vs 心跳节拍的良性错位,第 8b 轮消抖)。
* 语义上仅把一次长等待拆成多次短等待,总时长不变。 */
#define TIME_SYNC_WAIT_SLICE_MS 50U
/* 主循环节拍:每轮驱动一次 SNTP 状态机后的等待时长,与 :8888 时代的
* accept 轮询片同值(time_sntp_run 对单次驻留另有 ≤50ms 收片上限,
* 与本延时片叠加不破 100ms 抽样窗,心跳纪律不变)。 */
#define TIME_SYNC_TICK_MS 200U
/*
* 函数功能:可喂心跳的延时(把长等待切成 ≤50ms 短片,每片喂一次心跳)
* 入口参数:ms - 总延时(ms) uint32_t
* 返回值:无
* 限定条件:任务上下文调用
* 函数说明:看门狗抽样窗为 100ms(task_watchdog.c WD_POLL_PERIOD_MS)。把任意长
* 等待切成 ≤TIME_SYNC_WAIT_SLICE_MS 的短片、每片前喂心跳,使心跳必然
* 落在单个抽样窗内——语义等价于一次长延时,仅把"心跳节奏"钉在窗内
* (第 8b 轮消抖;不改变总延时预算)。
*/
static void time_sync_delay_fed(uint32_t ms) {
while (ms > 0U) {
uint32_t slice = (ms > TIME_SYNC_WAIT_SLICE_MS) ? TIME_SYNC_WAIT_SLICE_MS : ms;
task_wd_beat(TASK_WD_TIMESYNC);
vTaskDelay(pdMS_TO_TICKS(slice));
ms -= slice;
}
}
/*
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
* 入口参数:argument - 未使用
* 返回值:无
* 限定条件:net_init() 已由 netTask 完成;netTask 正在处理消息队列
* 函数说明:等待网络栈就绪后,按 TIME_SYNC_TICK_MS 节拍驱动 SNTP 校时客户端
* (time_sntp_run 内部:UDP socket 懒建/代际变化自弃旧归还,
* 等待应答期单次驻留 ≤50ms)。心跳 = 每轮循环头一次 + 延时片每片
* 一次;本任务在 IWDG required_mask 内,节拍上没有任何超过抽样窗
* 的不喂窗口。全部 socket 操作经消息队列由 netTask 串行执行。
*/
void time_sync_task(void *argument) {
(void)argument;
task_wd_bind(TASK_WD_TIMESYNC); /* FTL 心跳路由:登记本任务句柄(入口一次) */
/* 等网络栈真正就绪(取代拍脑袋的 8s 魔法延时),确保 netTask 已开始处理消息;
* osFlagsNoClear:就绪位是状态,不能被本任务消费掉(否则其它等待者永不唤醒)。
* 2026-10-02 台架修复:等待改 100ms 分片 + 每片喂心跳——IWDG 必需掩码含
* TS 位,原 osWaitForever 无心跳 → 启动初期(存储已就绪、网络未就绪)
* 掩码 800ms 不可达 → 停喂 → 复位循环(2026-10-02 台架根因之一)。
* 100ms 分片 = 抽样窗,纯任务睡眠零队列开销。 */
if (sys_evtHandle != NULL) {
for (;;) {
uint32_t r = osEventFlagsWait(sys_evtHandle, SYS_EVT_NET_READY,
osFlagsWaitAny | osFlagsNoClear, 100u);
if ((r & osFlagsError) == 0u) {
break; /* NET_READY 已置位(状态位,未被消费) */
}
task_wd_beat(TASK_WD_TIMESYNC); /* 看门狗:分片喂心跳(屏障等待态) */
}
}
for (;;) {
/* 心跳:本任务在 required_mask 内。time_sntp_run 的收片驻留 ≤50ms
* 落在两次心跳之间(循环头 + 下面延时片每片前置),抽样窗不破。 */
task_wd_beat(TASK_WD_TIMESYNC);
/* 网络代次随参数下传:断网/重建由 time_sntp_run 内部自理(关闭旧
* socket 归还),代次变化后下一轮自然在新网上重新发起。 */
time_sntp_run(net_get_generation());
time_sync_delay_fed(TIME_SYNC_TICK_MS);
}
}