feat(对时): 8888 对时补一条操作记录留痕——与 104 对时对称,被拒不落痕

task-49 盘点盘出的唯一供给缺口,task-50 实现(道H 交付)。为什么这算缺陷而不是「维护工装不值得留痕」:
对时改变的是**其后每一条记录**的时标,而 104 侧的对时**已经留痕** —— 同一类行为两种结果,这个不对称
本身就是缺陷。FTP 上传不留痕则维持原状(那是传输动作,与「装置时钟被改了」不同类),本笔
刻意不扩面到它。

实现:`time_sync_handle_conn` 里 `sys_clock_set_unix` 的返回值原先是**裸丢弃**,现改为接住判
0,==0 才
投 `SYSLOG_OPER`(走 quiet 口,netTask 侧纪律,栈成本已在注释写明);**被拒零投**(不把失败记成改过时
钟);无合并锁存(连续两次对时留两条,不折叠);**epoch 语义零触碰、零时区补偿** —— 时基口径仍在用
户手里,本笔不替产品决定。

新用例四组十二断言:成功恰一条 / 被拒不落痕 / 连续两次两条 / 魔术字不符皆零。假件把 rc 做成可脚本,
并专门钉「文案出现次数」以证不合并;`sys_log.h` 用真实头(纯 stdint,走 task_watchdog 先例),零桩漂移面。

**停发口径的第一批**:新断言**不配 TC 号**,改用语义化用例名 + 函数名追溯,注释里标了口径出处 —— 裁定
能不能落地看的就是第一批,这条留在这里给后来者当样例。

证据:time_sync 92/92(存量 82 零回归);@build **0 错 0 警**,Code=235268 RO=26308 RW
=1572 ZI=136924
(与上一笔的差值是 728d6067 合账所致,不是本笔);栈**以表为证**:timeSyncTask 链 1272→1272 B 零增
量,own frame 152 不变,handle_conn 200 / post_quiet 40 / post 264 逐函数帧全等 ——
按今天刚定稿的栈判
据,「零增量」这句话必须是表,不能是论证。id-check --strict 0(零新号零裸形)、乱码/嵌套/BOM 净。

台账:`ram_baseline.py --update` 已记当日行(IRAM1 61.9% / CCM 87.9%)。覆盖率基线**留给 L
ead 重建** ——
上一版基线是在 time_sync 套件编译不过的破树下打的,少算了该套件的覆盖。

台账同步:本笔代码改了体积(Code=235268),RAM 预算台账受管区与文档快照区按 12:46 那次权威构建回填(ram_baselin
e --write 加 check_mem_numbers --write)。代码与数字不同笔,就是下一次 mem 钩子假红的来源——上一笔 d
ocs 提交被 mem-numbers 与 mem-budget 双双拦下,正是这个原因。
This commit is contained in:
2026-10-27 13:49:38 +08:00
parent 13001f2ae4
commit 2e608118ce
7 changed files with 111 additions and 7 deletions
+1 -1
View File
@@ -641,7 +641,7 @@ repos:
# 疑似 MSBuild 节点争用;此后 3 次干净树连跑均绿。判据:单次失败先复跑一次再看,
# 连续两次失败才当真(不许直接 --no-verify 绕过)。
- id: ui-golden
name: UI 像素回归(菜单 8 状态 + 内容页 47 页 黄金参考)
name: UI 像素回归(菜单 8 状态 + 内容页 53 页 黄金参考)
entry: python scripts/check_ui_golden.py
language: system
files: ^(Drivers/BSP/UI/|App/task/ui_task\.(c|h)$|simulator/(tests|sim|src)/|simulator/main\.c$|simulator/CMakeLists\.txt$|simulator/lv_conf\.h$|scripts/check_ui_golden\.py$|\.pre-commit-config\.yaml$)
+17 -1
View File
@@ -10,6 +10,11 @@
* 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 分片、每片喂心跳——消除"心跳滑出两个连续
@@ -21,6 +26,7 @@
#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):
@@ -176,7 +182,17 @@ static void time_sync_handle_conn(int conn_sock) {
((uint32_t)buf[5] << 16) |
((uint32_t)buf[6] << 8) |
((uint32_t)buf[7]);
sys_clock_set_unix(epoch);
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 {
@@ -47,10 +47,10 @@ dhara 失败类 +84;RO 另 +208,全是该失败类名表)→ `ER_IROM1`
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
| 区域 | 用量 | 容量 | 占比 |
|---|---|---|---|
| ER_IROM1 | 261,544 B (255.41 KB) | 1,048,576 B (1024 KB) | 24.9% |
| ER_IROM1 | 261,576 B (255.45 KB) | 1,048,576 B (1024 KB) | 24.9% |
| RW_IRAM1 | 81,072 B (79.17 KB) | 131,072 B (128 KB) | 61.9% |
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
| **Flash (ROM 合计)** | **263,116 B (256.9 KB)** | 1,048,576 B (1024 KB) | **25.1%** |
| **Flash (ROM 合计)** | **263,148 B (257.0 KB)** | 1,048,576 B (1024 KB) | **25.1%** |
<!-- MEM_SNAPSHOT:END -->
各区的容量与常量来源:
+1 -1
View File
@@ -15,7 +15,7 @@
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|---|---|---|---|---|
| ER_IROM1 (Flash) | 261,544 B | 1,048,576 B | 24.9% | 参考 |
| ER_IROM1 (Flash) | 261,576 B | 1,048,576 B | 24.9% | 参考 |
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
| RW_IRAM1 | 81,072 B | 131,072 B | 61.9% | PASS |
+1 -1
View File
@@ -9,4 +9,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
2026-10-24 213780 18860 1204 131852 232640 22.2 75632 57.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4128;sys_log_mcu.o=3944;default_task.o=2736
2026-10-25 224860 24640 1332 135100 249500 23.8 79008 60.3 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4128;sys_log_mcu.o=3944;default_task.o=2736
2026-10-26 231712 25560 1520 136216 257272 24.5 80312 61.3 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4132;sys_log_mcu.o=4003;default_task.o=2736
2026-10-27 235236 26308 1572 136924 261544 24.9 81072 61.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4132;sys_log_mcu.o=4055;default_task.o=2736
2026-10-27 235268 26308 1572 136924 261576 24.9 81072 61.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4132;sys_log_mcu.o=4055;default_task.o=2736
1 date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
9 2026-10-24 213780 18860 1204 131852 232640 22.2 75632 57.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4128;sys_log_mcu.o=3944;default_task.o=2736
10 2026-10-25 224860 24640 1332 135100 249500 23.8 79008 60.3 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4128;sys_log_mcu.o=3944;default_task.o=2736
11 2026-10-26 231712 25560 1520 136216 257272 24.5 80312 61.3 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4132;sys_log_mcu.o=4003;default_task.o=2736
12 2026-10-27 235236 235268 26308 1572 136924 261544 261576 24.9 81072 61.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4132;sys_log_mcu.o=4055;default_task.o=2736
+1
View File
@@ -49,6 +49,7 @@ BUILD = TestBuild(
os.path.join(HERE, "stub"),
os.path.join(ROOT, "App", "util"),
os.path.join(ROOT, "App", "sync"),
os.path.join(ROOT, "App", "log"), # sys_log.h 用真实头(纯 stdint,同 task_watchdog.h 先例)
HERE,
],
summary_marker="=== Time Sync Tests:",
+88 -1
View File
@@ -57,6 +57,8 @@
* TC-TIMESYNC-042 屏障未就绪期间持续喂心跳(2026-10-02 台架根因的回归防线)
* TC-TIMESYNC-043 网络代次变化 -> 关闭旧监听并重监听
* TC-TIMESYNC-044 监听建立失败 -> 5 次重试后放弃并退避 2000ms
* 对时操作留痕(道G task-50,语义行名不配号——编号规范已停发测试用例号):
* "sync trace on success" / "… rejected" / "… two in a row" / "… bad magic" 四组
*
* 通过:全部 TC PASS(main 返回 0)。
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
@@ -76,6 +78,7 @@
#include "sys_clock.h"
#include "sys_events.h"
#include "task_watchdog.h" /* 真实头(App/util):task_wd_id_t / TASK_WD_TIMESYNC */
#include "sys_log.h" /* 真实头(App/log,纯 stdint 无 OS 依赖):syslog_id_t / SYSLOG_OPER 契约,假件签名与之逐字对齐 */
/* ===================== FreeRTOS 桩 ===================== */
static TickType_t s_tick;
@@ -152,11 +155,33 @@ void task_wd_bind(task_wd_id_t id) {
/* ===================== sys_clock 桩 ===================== */
static int s_set_unix_calls;
static uint32_t s_set_unix_last;
static int s_set_unix_rc; /* 可脚本化返回值:非 0 = 对时被拒(task-50) */
int sys_clock_set_unix(uint32_t unix_secs) {
s_set_unix_calls++;
s_set_unix_last = unix_secs;
return 0;
return s_set_unix_rc;
}
/* ===================== sys_log 投递桩(task-50) =====================
* 假件按投递顺序记录:条数 / 日志 ID / 末条描述 / 对时文案出现次数。
* "出现次数"专为钉"连续两次不合并"——若生产侧哪天改成边沿锁存,
* 第三条用例会红,而那正是要拦的语义漂移。 */
static int s_post_calls;
static int s_post_id;
static char s_post_last_desc[32];
static int s_post_desc_count;
void syslog_post_quiet(syslog_id_t id, const char *desc) {
s_post_calls++;
s_post_id = (int)id;
if (desc != NULL) {
(void)strncpy(s_post_last_desc, desc, sizeof(s_post_last_desc) - 1u);
s_post_last_desc[sizeof(s_post_last_desc) - 1u] = '\0';
if (strcmp(desc, "clock set by 8888") == 0) {
s_post_desc_count++;
}
}
}
/* ===================== net_socket 桩 ===================== */
@@ -349,6 +374,11 @@ static void fakes_reset(void) {
s_set_unix_calls = 0;
s_set_unix_last = 0;
s_set_unix_rc = 0;
s_post_calls = 0;
s_post_id = -1;
s_post_last_desc[0] = '\0';
s_post_desc_count = 0;
s_htons_last = 0;
s_socket_calls = 0;
@@ -716,6 +746,61 @@ static void tc_task(void) {
CHECK(s_beat_id_bad == 0, "TC-TIMESYNC-044e every beat sets the TIMESYNC bit");
}
/* ===================== 对时操作留痕四组(道G task-50) =========================
* 成功应用投一条 quiet OPER;被拒不投(失败不得记成"改过时钟");
* 连续两次各一条不合并;魔术字不符不投。
* ⚠ 断言标签用语义行名:编号规范 §9 批次三已对测试用例号停发(用户裁定 2026-10-27),
* 本组是新口径下写的——追溯靠函数名 tc_oper_trace + 下面的语义标签。 */
static void tc_oper_trace(void) {
uint8_t pkt[16];
/* 1) 对时成功 → 恰一条 OPER */
fakes_reset();
make_time_packet(pkt, 0x20251027u);
recv_set(0, 8, pkt, 8, 0U);
s_recv_seq_n = 1;
s_set_unix_rc = 0;
time_sync_handle_conn(7);
CHECK(s_set_unix_calls == 1, "sync trace: clock layer applied once");
CHECK(s_post_calls == 1, "sync trace on success: exactly one operation record");
CHECK(s_post_id == (int)SYSLOG_OPER, "sync trace: record goes to the operation log");
CHECK(strcmp(s_post_last_desc, "clock set by 8888") == 0,
"sync trace: wording of the same family as the 104 time-sync record");
/* 2) 对时被拒(时钟层返非 0)→ 零留痕 */
fakes_reset();
make_time_packet(pkt, 7u);
recv_set(0, 8, pkt, 8, 0U);
s_recv_seq_n = 1;
s_set_unix_rc = -1;
time_sync_handle_conn(7);
CHECK(s_set_unix_calls == 1, "sync trace rejected: call still reaches the clock layer once");
CHECK(s_post_calls == 0, "sync trace rejected: NO operation record written");
/* 3) 连续两次成功会话 → 两条独立记录,不合并 */
fakes_reset();
s_set_unix_rc = 0;
make_time_packet(pkt, 100u);
recv_set(0, 8, pkt, 8, 0U);
recv_set(1, 8, pkt, 8, 0U);
s_recv_seq_n = 2;
time_sync_handle_conn(7);
time_sync_handle_conn(7);
CHECK(s_set_unix_calls == 2, "sync trace twice: two applied syncs");
CHECK(s_post_calls == 2, "sync trace twice: two independent records, not merged");
CHECK(s_post_desc_count == 2, "sync trace twice: both records are the time-sync wording (no coalescing latch)");
/* 4) 前缀不是 TIME → 时钟层都没到,更不该留痕 */
fakes_reset();
(void)memset(pkt, 'X', 8);
recv_set(0, 8, pkt, 8, 0U);
s_recv_seq_n = 1;
s_set_unix_rc = 0;
time_sync_handle_conn(7);
CHECK((s_set_unix_calls == 0) && (s_post_calls == 0),
"sync trace bad magic: neither moves the clock nor writes a record");
}
/* ===================== main ===================== */
int main(void) {
@@ -729,6 +814,8 @@ int main(void) {
tc_handle_conn();
printf("-- time_sync_task (barrier / accept / relisten) --\n");
tc_task();
printf("-- operation trace on sync (task-50) --\n");
tc_oper_trace();
printf("\n=== Time Sync Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);