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。
This commit is contained in:
+6
-7
@@ -7,18 +7,18 @@
|
||||
|
||||
| 文件 | 作用 |
|
||||
|---|---|
|
||||
| `time_sync.c/h` | **时间同步任务**:等待网络就绪 → 与时间源交互 → 校准墙钟 |
|
||||
| `time_sntp.c/h` | **SNTP 校时客户端**(2026-11-04 落地):挂在 time_sync 主循环里的 UDP/123 轮询,编解码用 `Lib/coreSNTP/` 序列化器,配置读 `[sntp]` 段(属主 `../../Drivers/BSP/NETCFG/sntp_cfg.c`);单测 `test/host/sntp/` |
|
||||
| `time_sync.c/h` | **时间同步任务载体**:等待网络就绪 → 200 ms 节拍驱动 SNTP 客户端(:8888 私有推送已于 2026-11-04 退役,理由见墙钟维护说明 §15) |
|
||||
| `time_sntp.c/h` | **SNTP 校时客户端**(2026-11-04 落地):由 time_sync 任务循环驱动的 UDP/123 轮询,编解码用 `Lib/coreSNTP/` 序列化器,配置读 `[sntp]` 段(属主 `../../Drivers/BSP/NETCFG/sntp_cfg.c`);单测 `test/host/sntp/` |
|
||||
|
||||
## 工作流
|
||||
|
||||
```
|
||||
timeSyncTask 启动
|
||||
↓ osEventFlagsWait(SYS_EVT_NET_READY, ...|osFlagsNoClear) // 状态位不可消费!
|
||||
time_sync 主循环 → TCP :8888 被动接收("TIME"+u32 → sys_clock 校准)
|
||||
↘ 每轮顺带 time_sntp_run(gen):到点主动向 [sntp] 服务器发 48 B 查询
|
||||
time_sync 主循环(200 ms 节拍,长等待分片 ≤50ms 逐片喂心跳)
|
||||
↘ 每轮 time_sntp_run(gen):到点主动向 [sntp] 服务器发 48 B 查询
|
||||
(单轮等待切片 ≤50 ms),校验通过按策略步进/只记日志,
|
||||
首个通过的同步置 SYS_EVT_TIMESYNC_READY
|
||||
首个通过的同步置 SYS_EVT_TIMESYNC_READY;网络代次变化自弃旧 socket
|
||||
```
|
||||
|
||||
> ⚠️ 等待**就绪位**(`SYS_EVT_NET_READY`)必须 `osFlagsNoClear`——它是状态位,
|
||||
@@ -26,6 +26,5 @@ time_sync 主循环 → TCP :8888 被动接收("TIME"+u32 → sys_clock 校准
|
||||
|
||||
## 相关文档
|
||||
|
||||
- `docs/子系统设计/系统墙钟时间维护说明.md` — 墙钟基准 / tick 回绕防护
|
||||
- `test/host/time_sync_server.py` — PC 端对时推送服务(测试用)
|
||||
- `docs/子系统设计/系统墙钟时间维护说明.md` — 墙钟基准 / tick 回绕防护 / §15 :8888 退役注记 / §16 SNTP
|
||||
- `docs/架构设计/项目启动顺序管理.md` — 就绪屏障
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
#define SNTP_STEP_DEADBAND_MS 2000
|
||||
/* 内部钟面 = 本地时间(#56 裁定 §1.1:sys_clock 存"钟面秒",104 写 UTC 的
|
||||
* 8 小时差已有文档口径)——SNTP 给的是 UTC,落钟面前加固定东八区偏移;
|
||||
* 全仓无时区存储,本常量与 UI 手工对时 / 8888 推送同源 */
|
||||
* 全仓无时区存储,本常量与 UI 手工对时同口径(:8888 推送已退役) */
|
||||
#define SNTP_TZ_LOCAL_OFFSET_SECS 28800U
|
||||
/* 合理性下限:2020-01-01 UTC;上限 = sys_clock 钟面域 0x7FFFFFFF 扣除时区 */
|
||||
#define SNTP_UNIX_MIN_VALID 1577836800U
|
||||
@@ -313,7 +313,7 @@ static int32_t s_off_clamp(int64_t off) {
|
||||
* (含死区内未步进),复位退避并按 poll_min 重排下一轮;
|
||||
* 首个成功投 SYS_EVT_TIMESYNC_READY(状态位,不消费);
|
||||
* 每次实际步进落一条 quiet 操作记录("clock set by SNTP",
|
||||
* 与 8888/104 同口径,失败不落痕)
|
||||
* 与 104 同口径,失败不落痕)
|
||||
*/
|
||||
static void s_handle_success(const SntpResponseData_t *p_resp, uint32_t now) {
|
||||
int64_t utc;
|
||||
|
||||
+39
-226
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 模块名称:Time Sync — PC 本地时间推送接收(TCP)
|
||||
* 模块功能:在独立任务 timeSyncTask 中以 TCP Server 方式监听端口,接收 PC 推送的本地时间包,
|
||||
* 调用 sys_clock_set_unix() 更新系统墙钟并写回 SD2506 RTC
|
||||
* 模块名称:Time Sync — timeSyncTask 载体(网络校时节拍宿主)
|
||||
* 模块功能:独立任务 timeSyncTask:等网络就绪后按固定节拍驱动 SNTP 校时客户端
|
||||
* (App/sync/time_sntp.c),长等待一律分片并逐片喂任务心跳
|
||||
* 适用平台:STM32F407ZGTx + CH395F
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-27
|
||||
@@ -10,58 +10,45 @@
|
||||
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask,
|
||||
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
|
||||
* netTask 仅负责 net_poll/net_process_messages,多任务并发安全
|
||||
* v1.3 2026-10-27 DTU 项目组 道G task-50:对时**成功应用**后投一条 quiet
|
||||
* 操作记录("clock set by 8888")——task-49 盘点盘出
|
||||
* "104 对时留痕、本端口不留"的同类不对称,Lead 批准补齐;
|
||||
* 被拒不落痕;epoch 时标语义未裁定,本改动不碰、不补
|
||||
* 任何时区补偿。
|
||||
* 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,节奏/退避/步进策略见该文件);
|
||||
* 本任务循环结构与 8888 语义不变
|
||||
* 在 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 <string.h>
|
||||
#include "time_sync.h"
|
||||
#include "time_sntp.h"
|
||||
#include "net_socket.h"
|
||||
#include "sys_clock.h"
|
||||
#include "sys_events.h"
|
||||
#include "sys_log.h"
|
||||
#include "task_watchdog.h"
|
||||
|
||||
/* 深调用链模块(timeSyncTask 栈仅 2048 B,本任务链 = 建监听 → net_bind → CH395F):
|
||||
* 日志行缓冲收到 128 B(默认 192),本模块最长行渲染后 < 128 → 不截断 */
|
||||
#define DBG_LINE_BUF_MAX 128U
|
||||
#define DBG_TAG "[TIME_SYNC]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* 监听端口:PC 推送端(TCP 客户端)需连到本端口 */
|
||||
#define TIME_SYNC_PORT 8888
|
||||
|
||||
/*
|
||||
* 头阻塞修复 + 挂起请求(pending)化(timeSync 侧;lftpd 侧见 lftpd_inet.c):
|
||||
* 旧实现用单次 30s 阻塞 net_recv(默认 NET_RECV_TIMEOUT_MS)等时间包——等待期间
|
||||
* netTask(全部 socket 操作的唯一执行者)被独占,其余任务的网络操作(远端显示 /
|
||||
* FTP)整体停摆(历史审计遗留问题)。
|
||||
* 2026-09-22 先落地分片有界读(5ms 片),同日 pending 化后改为"挂起请求":
|
||||
* 每次等待 = 一次挂起登记(netTask 零占用,完成按主循环节拍检查),调用方
|
||||
* 单次超时 = 剩余总预算(总等待 ≤ TIME_SYNC_TOTAL_BUDGET_MS,语义与旧 30s
|
||||
* 一致);等待期间调用方在自己上下文睡任务通知,旧分片协议(片长/片间让出)退役。
|
||||
*/
|
||||
#define TIME_SYNC_TOTAL_BUDGET_MS 30000U
|
||||
|
||||
/* 看门狗分片等待:单次 net_recv_to 的等待片长上限。须显著小于看门狗抽样窗
|
||||
/* 看门狗分片等待:单次延时的分片上限。须显著小于看门狗抽样窗
|
||||
* (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
|
||||
@@ -82,147 +69,16 @@ static void time_sync_delay_fed(uint32_t ms) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:创建并监听 TCP 时间同步服务端口
|
||||
* 入口参数:无
|
||||
* 返回值:监听 Socket 描述符(>=0),失败返回 -1
|
||||
* 限定条件:net_init() 已成功;可在非 netTask 上下文调用
|
||||
* 函数说明:net_socket/net_bind/net_listen 均为线程安全 API,内部经消息队列由 netTask 串行处理。
|
||||
* 单连接模式下监听 Socket 在收到连接后会转为数据通道,连接关闭后即被释放,
|
||||
* 因此每次调用都会新建监听 Socket(与 lftpd 同模式),失败自动重试。
|
||||
*/
|
||||
static int time_sync_open_listener(void) {
|
||||
int retry;
|
||||
|
||||
for (retry = 0; retry < 5; retry++) {
|
||||
task_wd_beat(TASK_WD_TIMESYNC); /* 每次重试前喂心跳(见下方 500ms/2000ms 延时) */
|
||||
int s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||||
if (s < 0) {
|
||||
DBG_ERROR("time_sync: net_socket FAIL");
|
||||
time_sync_delay_fed(500U);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct net_sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = NET_AF_INET;
|
||||
addr.sin_port = net_htons(TIME_SYNC_PORT);
|
||||
addr.sin_addr.s_addr = NET_INADDR_ANY;
|
||||
|
||||
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||
DBG_ERROR("time_sync: bind port %d FAIL", TIME_SYNC_PORT);
|
||||
net_close(s);
|
||||
time_sync_delay_fed(2000U);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (net_listen(s) != 0) {
|
||||
DBG_ERROR("time_sync: listen FAIL");
|
||||
net_close(s);
|
||||
time_sync_delay_fed(2000U);
|
||||
continue;
|
||||
}
|
||||
|
||||
DBG_INFO("time_sync: TCP listen on :%d (sock=%d)", TIME_SYNC_PORT, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:处理一个已建立的时间同步连接
|
||||
* 入口参数:conn_sock - 已建立的连接 Socket 描述符(单连接模式下即监听 Socket 自身)
|
||||
* 返回值:无
|
||||
* 函数说明:pending 读取 PC 发来的时间包("TIME"+uint32 大端 本地时间),解析后
|
||||
* 写回 RTC。**不在此 close**(单连接模式下 conn_sock 即
|
||||
* 监听 fd,close 连监听一起毁、重建窗 ~0.3-1s;net 层 auto_relisten
|
||||
* 已把同 fd 收回监听,外层直接续 accept——lftpd 样板)。每次等待 =
|
||||
* 一次挂起请求(netTask 零占用),单次超时 = 剩余总预算
|
||||
* (≤30s,语义与旧 30s 阻塞一致);跨调用累积,容忍时间包被 TCP 分段送达
|
||||
*/
|
||||
static void time_sync_handle_conn(int conn_sock) {
|
||||
uint8_t buf[16];
|
||||
int got = 0;
|
||||
TickType_t t0 = xTaskGetTickCount();
|
||||
|
||||
while (got < (int)sizeof(buf)) {
|
||||
/* 单次预算 = 剩余总预算(pending:等待期 netTask 零占用,调用方
|
||||
* 在自己上下文睡任务通知,旧片间 vTaskDelay(1) 让出退役)
|
||||
* ⚠ 看门狗约束:本任务在 required_mask 内,而单次 net_recv_to 可睡满
|
||||
* 30s 预算 —— 等待期必须周期性喂心跳,否则"对端连上但不发时间包"这种
|
||||
* 完全正常的场景会被判为任务卡死(见设计说明 §4.4 长耗时白名单)。
|
||||
* 做法:把长等待切成 WD 窗口内的短片(片长 ≤200ms),每片先喂心跳。 */
|
||||
TickType_t elapsed = xTaskGetTickCount() - t0;
|
||||
uint32_t rem = (elapsed >= (TickType_t)pdMS_TO_TICKS(TIME_SYNC_TOTAL_BUDGET_MS)) ? 0U
|
||||
: (uint32_t)((TickType_t)TIME_SYNC_TOTAL_BUDGET_MS - elapsed);
|
||||
if (rem == 0U) {
|
||||
DBG_INFO("time_sync: no TIME packet in 30s, drop conn");
|
||||
break; /* 总预算耗尽(语义同旧 30s 超时) */
|
||||
}
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
if (rem > TIME_SYNC_WAIT_SLICE_MS) {
|
||||
rem = TIME_SYNC_WAIT_SLICE_MS; /* 分片:等待期保持心跳节奏 */
|
||||
}
|
||||
int n = net_recv_to(conn_sock, buf + got, (int)(sizeof(buf) - got), rem);
|
||||
if (n > 0) {
|
||||
got += n;
|
||||
if (got >= 8) {
|
||||
break; /* 时间包收齐,解析 */
|
||||
}
|
||||
continue; /* 包未收齐(分段送达),继续等余下字节 */
|
||||
}
|
||||
if (n == 0) {
|
||||
break; /* 对端关闭(EOF) */
|
||||
}
|
||||
if (n == -2) {
|
||||
continue; /* 本片预算等满无数据 → 下一片重算剩余预算 */
|
||||
}
|
||||
break; /* 真错误 */
|
||||
}
|
||||
|
||||
if (got >= 8) {
|
||||
if (buf[0] == 'T' && buf[1] == 'I' && buf[2] == 'M' && buf[3] == 'E') {
|
||||
uint32_t epoch = ((uint32_t)buf[4] << 24) |
|
||||
((uint32_t)buf[5] << 16) |
|
||||
((uint32_t)buf[6] << 8) |
|
||||
((uint32_t)buf[7]);
|
||||
if (sys_clock_set_unix(epoch) == 0) {
|
||||
/* 操作留痕(道G task-50):对时改的是装置时钟——与 104 的
|
||||
* "clock set by 104"(App/task/iec104_serv.c 对时处)同类同口径,
|
||||
* 补齐 task-49 盘出的不对称。**被拒不投**:失败不得记成"时钟被改过";
|
||||
* 连续两次成功各投一条——每次对时是独立事件,无合并语义。
|
||||
* quiet 口:本任务栈 2048 B(见本文件 DBG_LINE_BUF_MAX 段),
|
||||
* syslog_post 的两个满态留痕分支各内联约 216 B 行缓冲,不选它。
|
||||
* ⚠ 记录里的时标语义(本端口写钟面、104 写 UTC 的 8 小时差)
|
||||
* 未裁定——留痕按现有通道原样落,不做任何时区补偿。 */
|
||||
syslog_post_quiet(SYSLOG_OPER, "clock set by 8888");
|
||||
}
|
||||
DBG_INFO("time_sync: wall clock updated (epoch=%lu)", (unsigned long)epoch);
|
||||
}
|
||||
} else {
|
||||
/* 头阻塞收口(docs/网络/网络自愈架构设计.md §5.2 E13):本层放弃会话 → 主动拆除。
|
||||
* 挂起但 TCP 仍活的客户端(PC 内核自动 ACK keepalive,芯片僵尸判不死)
|
||||
* 会永久占住单连接监听;EOF 路径 drop 幂等返 0(net 层已 relisten)。
|
||||
* 模拟器多连接后端 conn≠ls,由 sim 实现按数据 fd 关闭。 */
|
||||
(void)net_drop_conn(conn_sock);
|
||||
}
|
||||
|
||||
/* 原 net_close(conn_sock) 已删——单连接模式 conn==监听 fd,
|
||||
* close 毁监听(AGENTS 模块约定:EOF 时应用层不得 close);net 层
|
||||
* auto_relisten 已把同 fd 收回 LISTENING,外层循环直接续 accept。
|
||||
* 模拟器多连接后端 conn≠ls,由调用方(外层)负责关数据 fd。 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
|
||||
* 入口参数:argument - 未使用
|
||||
* 返回值:无
|
||||
* 限定条件:net_init() 已由 netTask 完成;netTask 正在处理消息队列
|
||||
* 函数说明:等待网络栈就绪后,循环:建监听 -> 等连接 -> 处理 -> **同 fd 续
|
||||
* accept**(会话收尾不 close,监听跨会话常驻,仅网络
|
||||
* 代次变化时重建)。全部 socket 操作经消息队列由 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;
|
||||
@@ -230,13 +86,12 @@ void time_sync_task(void *argument) {
|
||||
task_wd_bind(TASK_WD_TIMESYNC); /* FTL 心跳路由:登记本任务句柄(入口一次) */
|
||||
|
||||
/* 等网络栈真正就绪(取代拍脑袋的 8s 魔法延时),确保 netTask 已开始处理消息;
|
||||
* osFlagsNoClear:就绪位是状态,不能被本任务消费掉(否则其它等待者永不唤醒) */
|
||||
* osFlagsNoClear:就绪位是状态,不能被本任务消费掉(否则其它等待者永不唤醒)。
|
||||
* 2026-10-02 台架修复:等待改 100ms 分片 + 每片喂心跳——IWDG 必需掩码含
|
||||
* TS 位,原 osWaitForever 无心跳 → 启动初期(存储已就绪、网络未就绪)
|
||||
* 掩码 800ms 不可达 → 停喂 → 复位循环(2026-10-02 台架根因之一)。
|
||||
* 100ms 分片 = 抽样窗,纯任务睡眠零队列开销。 */
|
||||
if (sys_evtHandle != NULL) {
|
||||
/* osFlagsNoClear:就绪位是状态,不能被本任务消费掉(否则其它等待者永不唤醒)。
|
||||
* 2026-10-02 台架修复:等待改 100ms 分片 + 每片喂心跳——IWDG 必需掩码含
|
||||
* TS 位,原 osWaitForever 无心跳 → 启动初期(存储已就绪、网络未就绪)
|
||||
* 掩码 800ms 不可达 → 停喂 → 复位循环(2026-10-02 台架根因之一)。
|
||||
* 100ms 分片 = 抽样窗,纯任务睡眠零队列开销。 */
|
||||
for (;;) {
|
||||
uint32_t r = osEventFlagsWait(sys_evtHandle, SYS_EVT_NET_READY,
|
||||
osFlagsWaitAny | osFlagsNoClear, 100u);
|
||||
@@ -247,57 +102,15 @@ void time_sync_task(void *argument) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 监听 fd 跨会话常驻:仅首次/网络代次变化/被毁时重建 */
|
||||
int ls = -1;
|
||||
|
||||
for (;;) {
|
||||
/* 心跳:本任务在 required_mask 内。以下每一处可能长于 100ms 抽样窗的
|
||||
* 阻塞点(监听重试 2s、accept 轮询 200ms/次、收尾 50ms)都就近补心跳。 */
|
||||
/* 心跳:本任务在 required_mask 内。time_sntp_run 的收片驻留 ≤50ms
|
||||
* 落在两次心跳之间(循环头 + 下面延时片每片前置),抽样窗不破。 */
|
||||
task_wd_beat(TASK_WD_TIMESYNC);
|
||||
|
||||
/* 记录本次监听对应的网络代次;代次变化(网络重建)则放弃旧监听重建 */
|
||||
uint32_t gen = net_get_generation();
|
||||
/* 网络代次随参数下传:断网/重建由 time_sntp_run 内部自理(关闭旧
|
||||
* socket 归还),代次变化后下一轮自然在新网上重新发起。 */
|
||||
time_sntp_run(net_get_generation());
|
||||
|
||||
if (ls < 0) {
|
||||
ls = time_sync_open_listener();
|
||||
if (ls < 0) {
|
||||
DBG_ERROR("time_sync: open listener FAIL, retry later");
|
||||
time_sync_delay_fed(2000U);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* 等待 PC 连接:net_accept 在无连接时立即返回 -1,循环重试同一监听 Socket */
|
||||
int conn = -1;
|
||||
do {
|
||||
conn = net_accept(ls, NULL, NULL);
|
||||
if (conn < 0) {
|
||||
if (net_get_generation() != gen) {
|
||||
net_close(ls); /* 网络重建:旧监听失效,回外层重建 */
|
||||
ls = -1;
|
||||
break;
|
||||
}
|
||||
/* SNTP 客户端节拍:accept 轮询片内驱动(等待应答期单次
|
||||
* 驻留 ≤50ms 收片,心跳仍由下面的分片延时喂,100ms 抽样窗
|
||||
* 不破)。代次变化由 time_sntp_run 内部自理(弃旧 socket)。 */
|
||||
time_sntp_run(gen);
|
||||
time_sync_delay_fed(200U);
|
||||
}
|
||||
} while (conn < 0);
|
||||
|
||||
if (conn < 0) {
|
||||
DBG_INFO("time_sync: network re-init, relisten");
|
||||
continue;
|
||||
}
|
||||
|
||||
time_sync_handle_conn(conn);
|
||||
/* 会话收尾不 close(单连接模式 conn==ls,close 连监听
|
||||
* 一起毁);net 层 auto_relisten 已把同 fd 收回 LISTENING,下轮循环
|
||||
* 直接续 accept。模拟器多连接后端(conn≠ls)才关数据 fd。 */
|
||||
if (conn != ls) {
|
||||
net_close(conn);
|
||||
}
|
||||
|
||||
time_sync_delay_fed(50U);
|
||||
time_sync_delay_fed(TIME_SYNC_TICK_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/*
|
||||
* 模块名称:Time Sync — PC 本地时间推送接收(TCP)
|
||||
* 模块功能:以 TCP Server 方式监听 TIME_SYNC_PORT,接收 PC 推送的本地时间包,
|
||||
* 更新系统墙钟并写回 SD2506 RTC
|
||||
* 模块名称:Time Sync — timeSyncTask 载体(网络校时节拍宿主)
|
||||
* 模块功能:timeSyncTask 任务入口:等网络就绪后按固定节拍驱动 SNTP 校时客户端
|
||||
* 适用平台:STM32F407ZGTx + CH395F
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-08-27 王建锋 创建初始版本(UDP 推送)
|
||||
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask
|
||||
* v2.0 2026-11-04 DTU 项目组 TCP :8888 对时退役,TIME_SYNC_PORT 宏随退,
|
||||
* 任务收缩为 SNTP 节拍宿主(细节见 time_sync.c 文件头)
|
||||
*/
|
||||
|
||||
#ifndef TIME_SYNC_H
|
||||
@@ -19,16 +20,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 监听端口:PC 推送端(TCP 客户端)需连到本端口 */
|
||||
#define TIME_SYNC_PORT 8888
|
||||
|
||||
/*
|
||||
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
|
||||
* 入口参数:argument - FreeRTOS 传入参数(未使用)
|
||||
* 返回值:无
|
||||
* 限定条件:net_init() 已由 netTask 完成;网络消息队列由 netTask 处理
|
||||
* 函数说明:以 TCP Server 方式监听 TIME_SYNC_PORT,接收 PC 推送的本地时间包,
|
||||
* 经消息队列(由 netTask 串行执行)完成 socket 操作,更新系统墙钟。
|
||||
* 函数说明:等网络就绪后按 200ms 节拍驱动 SNTP 校时客户端(App/sync/time_sntp.c),
|
||||
* socket 操作经消息队列(由 netTask 串行执行)完成。
|
||||
* 该任务与 FTP 等其它网络任务并发安全。
|
||||
*/
|
||||
void time_sync_task(void *argument);
|
||||
|
||||
Reference in New Issue
Block a user