diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 201c9bbb..ece9ef28 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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$) diff --git a/App/sync/time_sync.c b/App/sync/time_sync.c index ad13fcf5..2e722931 100644 --- a/App/sync/time_sync.c +++ b/App/sync/time_sync.c @@ -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 { diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index 72edc512..25a5d011 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -47,10 +47,10 @@ dhara 失败类 +84;RO 另 +208,全是该失败类名表)→ `ER_IROM1` | 区域 | 用量 | 容量 | 占比 | |---|---|---|---| -| 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%** | 各区的容量与常量来源: diff --git a/docs/内存管理/RAM预算台账.md b/docs/内存管理/RAM预算台账.md index 869e3703..c5b599ec 100644 --- a/docs/内存管理/RAM预算台账.md +++ b/docs/内存管理/RAM预算台账.md @@ -15,7 +15,7 @@ | 区域 | 用量 | 容量 | 占比 | 门禁(≤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 | diff --git a/scripts/ram_baseline.tsv b/scripts/ram_baseline.tsv index 53697183..b686d4eb 100644 --- a/scripts/ram_baseline.tsv +++ b/scripts/ram_baseline.tsv @@ -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 diff --git a/test/host/time_sync/run_unit_test.py b/test/host/time_sync/run_unit_test.py index 91fb8046..f47b365d 100644 --- a/test/host/time_sync/run_unit_test.py +++ b/test/host/time_sync/run_unit_test.py @@ -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:", diff --git a/test/host/time_sync/time_sync_test.c b/test/host/time_sync/time_sync_test.c index 0a5902ba..11bcf582 100644 --- a/test/host/time_sync/time_sync_test.c +++ b/test/host/time_sync/time_sync_test.c @@ -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);