From 2e608118ce4204f452d229b9dae9bcaeba3ecf4e Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Tue, 27 Oct 2026 13:49:38 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=AF=B9=E6=97=B6):=208888=20=E5=AF=B9?= =?UTF-8?q?=E6=97=B6=E8=A1=A5=E4=B8=80=E6=9D=A1=E6=93=8D=E4=BD=9C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E7=95=99=E7=97=95=E2=80=94=E2=80=94=E4=B8=8E=20104=20?= =?UTF-8?q?=E5=AF=B9=E6=97=B6=E5=AF=B9=E7=A7=B0=EF=BC=8C=E8=A2=AB=E6=8B=92?= =?UTF-8?q?=E4=B8=8D=E8=90=BD=E7=97=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 双双拦下,正是这个原因。 --- .pre-commit-config.yaml | 2 +- App/sync/time_sync.c | 18 +++++- docs/内存管理/RAM使用分析与优化方案.md | 4 +- docs/内存管理/RAM预算台账.md | 2 +- scripts/ram_baseline.tsv | 2 +- test/host/time_sync/run_unit_test.py | 1 + test/host/time_sync/time_sync_test.c | 89 +++++++++++++++++++++++++- 7 files changed, 111 insertions(+), 7 deletions(-) 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);