3a2f9b21fb
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。
117 lines
6.7 KiB
C
117 lines
6.7 KiB
C
/*
|
||
* 模块名称: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);
|
||
}
|
||
}
|