diff --git a/.dsh/skills/serial-assistant/scripts/serial_tool.py b/.dsh/skills/serial-assistant/scripts/serial_tool.py index a47d53e5..d4a18735 100644 --- a/.dsh/skills/serial-assistant/scripts/serial_tool.py +++ b/.dsh/skills/serial-assistant/scripts/serial_tool.py @@ -1123,26 +1123,26 @@ def run_selftest(): def check(name, cond, detail=""): results.append((name, bool(cond), detail)) - # T1 行拆分:CRLF / 多行 / 残行冲刷 + # 行拆分:CRLF / 多行 / 残行冲刷 sp = LineSplitter() got = [l for _, l in sp.feed(b"abc\r\n", 1000.0)] - check("T1a CRLF 行", got == [b"abc"], repr(got)) + check("行拆分:CRLF 行", got == [b"abc"], repr(got)) got = [l for _, l in sp.feed(b"de\nf\n", 1001.0)] - check("T1b 多行", got == [b"de", b"f"], repr(got)) - check("T1c 残行不入文件", sp.feed(b"ghi", 1002.0) == [] and sp.buf == b"ghi") + check("行拆分:多行", got == [b"de", b"f"], repr(got)) + check("行拆分:残行不入文件", sp.feed(b"ghi", 1002.0) == [] and sp.buf == b"ghi") got = [l for _, l in sp.flush_partial()] - check("T1d 残行冲刷", got == [b"ghi"], repr(got)) + check("行拆分:残行冲刷", got == [b"ghi"], repr(got)) got = sp.feed(b"\n\n", 1003.0) - check("T1e 空行忽略", all(not raw for _, raw in got) and sp.buf == b"", repr(got)) + check("行拆分:空行忽略", all(not raw for _, raw in got) and sp.buf == b"", repr(got)) - # T2 解码回退链 + # 解码回退链 check("T2a UTF-8", decode_line("中文ok".encode("utf-8")) == "中文ok") check("T2b GBK", decode_line("中文".encode("gbk")) == "中文") check("T2c HEX 兜底", decode_line(b"\xff\xfe\x01") == "HEX:fffe01") - # T3 时间戳格式 + # 时间戳格式 s = line_at(time.time(), "RX", "x") - check("T3 行格式", s.startswith("[RX] ") and "T" in s.split(" ")[1], s) + check("时间戳行格式", s.startswith("[RX] ") and "T" in s.split(" ")[1], s) # T4 截头算法(保留尾部 + 截后继续追加) pat = b"0123456789abcdef" @@ -1298,8 +1298,8 @@ def run_selftest(): except OSError: pass - # T10 端口建议:换 USB 口后 COM 号变了,唯一候选直接给(多候选不猜) - check("T10 port_hint 唯一候选才给建议", + # 端口建议:换 USB 口后 COM 号变了,唯一候选直接给(多候选不猜) + check("端口建议:唯一候选才给建议", port_hint("COM7", ["COM6"]) == "COM6" and port_hint("COM7", ["COM6", "COM9"]) is None and port_hint("COM6", ["COM6"]) is None) diff --git a/MDK-ARM/tcp_reconnect_probe.py b/MDK-ARM/tcp_reconnect_probe.py index 4cf84feb..4a8fced5 100644 --- a/MDK-ARM/tcp_reconnect_probe.py +++ b/MDK-ARM/tcp_reconnect_probe.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# TRAP-NET-17 / N-6 trustworthy reproduction probe: rapid reconnect to a device listener. +# TRAP-NET-17 复现探针:快速重连到设备监听器: rapid reconnect to a device listener. # # Failure under test (original report): after a rapid reconnect (<~1s) on a # relistened socket, the chip completes the handshake but the MCU never gets diff --git a/docs/测试说明/CH395F_Test_Guide.md b/docs/测试说明/CH395F_Test_Guide.md index 1ed2e8ee..22215f3b 100644 --- a/docs/测试说明/CH395F_Test_Guide.md +++ b/docs/测试说明/CH395F_Test_Guide.md @@ -1095,7 +1095,7 @@ IDLE → ACCEPT → [SEND: 分块发 → DONE] / [RECV: 分块收+校验 → DON | **标题** | TCP 连接空闲超过 KeepAlive 阈值后自动断开 | | **前置条件** | 1. `NET_KEEPALIVE_IDLE_MS=60000` / `INTVL=5000` / `CNT=3`(`net_config.h`)
2. 对端须为**死对端**:PC 连接 8080 后**禁用网卡**制造"链路在、对端静默"(存活对端会 ACK 探测 → 永不 TIMEOUT;Windows 防火墙规则对已建立连接无效,E11 实证)
3. 勿与 Phase 6/9/10/13 混选(recv 互抢数据) | | **测试步骤** | 1. PC 连接 8080,不发数据
2. 立即禁用 PC 网卡(中间有交换机 → 设备侧 PHY 链路保持)
3. 等待芯片报 TIM_OUT(实测判死窗:连接起 **18~31s**,芯片定时语义与手册公式 75s 不符,见 `docs/网络/网络自愈架构设计.md` §5.2 E11/E13)
4. 本层 QUARANTINE → reconciler 形态③④ 重建 → s0 回 LISTEN
5. 恢复网卡,重连 8080 验证端口已恢复 | -| **通过标准** | recv 返回 0 且耗时落在 **10s~120s 有界窗**(下限防"存活空闲会话被误杀"——T2 实测存活对端静默 90s 不触发;上限防"判死缺位");s0 回 LISTEN 且可接受新连接。报告后测试自动释放 socket 等新连接再测(一次性,防对已死连接 10ms 节奏刷假 FAIL) | +| **通过标准** | recv 返回 0 且耗时落在 **10s~120s 有界窗**(下限防"存活空闲会话被误杀"——空闲存活场景实测存活对端静默 90s 不触发;上限防"判死缺位");s0 回 LISTEN 且可接受新连接。报告后测试自动释放 socket 等新连接再测(一次性,防对已死连接 10ms 节奏刷假 FAIL) | --- diff --git a/docs/编号规范.md b/docs/编号规范.md index 36ff6764..2493a174 100644 --- a/docs/编号规范.md +++ b/docs/编号规范.md @@ -74,7 +74,7 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 - 登记处就是那张表:表首格写短语,**不要再配一个字母号**;跨文档引用写成 `` `docs/任务规划/登记簿.md` 「双转换器无共同采样触发」 ``,同篇后续直接用短语。 - 文档内的实施步骤 / 测试阶段 / 设计原则条目写中文:「步骤 3」「阶段 7」「DMA 边界安全」, - **不写 `S3` / `TM-PHY-01` / `DP-03` 这种号**。 + **不写 `S3` / `TM-…` / `DP-…` 这种号**。 --- @@ -128,11 +128,16 @@ P-6 号必须能反查到定义(属主登记处得存在)· P-7 编号不语 元器件位号 `U1`/`C12`/`R3`/`L5`/`D7`(硬件语料按目录整体排除)、数学与协议记号(`N-1`、测频的正序与 负序分量 `S1`/`S2`、`SHA-256`、`RS-485`、`ADC1_IN14`、手册页码)、活表代号 `E0~E19`(网络自愈 §5.2, **无连字符**,与死号 `E-nn` 只差一个连字符)、Socket 状态组合 `U1~U15`(网络自愈 §1.1)、档位词 `P0~P3`。 -判据只有一条:**带连字符、且能在属主登记表查到定义**才叫编号;查不到的就是普通文本,不是"漏登记的号"。 +判据只有一条:**带连字符、且能在属主登记表查到定义**才叫编号;查不到的就是普通文本,不是「漏登记的号」。 + +> 2026-10-27 补两条实测口径:① **元器件位号目录整体排除**——`tools/netlist_semantics/` 是硬件知识库 +> (`T1` 在那里是网络变压器位号),已进 `id_registry.json` 的 `excluded_paths`,与 `docs/硬件平台/` 同口径; +> ② **唯一已知误判**:`docs/算法/傅氏算法详解.md` 里的数学记号 `N-1`(「一个周波 N 个点」的求和上界)会被 +> `N-x` 族命中,属可判定引用、不影响两根护栏,**不清**——清了反而破坏公式。 ### 4.5 已删报告的自有族 《项目审查与优化建议报告》2026-10-25 已删除,它自造的六族随之失去定义,且与登记簿同字母不同义。 -看到"报告 N-16"这类写法一律按死号处理:删号留事实——各条目原本指向的设计文档就是现在的出处。 +看到「报告 + 登记簿旧号」这类写法一律按死号处理:删号留事实——各条目原本指向的设计文档就是现在的出处。 --- diff --git a/docs/网络/BSD_Socket_API_函数调用关系.md b/docs/网络/BSD_Socket_API_函数调用关系.md index 318d6b2f..7ccd062c 100644 --- a/docs/网络/BSD_Socket_API_函数调用关系.md +++ b/docs/网络/BSD_Socket_API_函数调用关系.md @@ -50,10 +50,10 @@ flowchart TB subgraph NT["netTask 主循环 — 1ms/tick;芯片操作唯一执行者"] NPM["netProcessMessages
先跑 4 个 tick,再排空队列分派"] NP["netPoll
INT# GPIO 轮询 → 全局中断 → 各 Socket SINT → 状态缓存全量刷新 + sock_reset 步进 → 每轮一批"] - T1["① netListenerRetryTick
D10 重监听重试:30ms / 2s 时限"] - T2["② netPendingTick
挂起 recv:①数据 ②到点(仅显式预算) ③断连 ④tripwire 60s 只留痕"] - T2B["③ netSendPendingTick
挂起 send:①断连 ②推进(每轮≤1块) ③写完 ④无进展超时"] - T3["④ netReconcileTick
TRAP-NET-17 对账 + 监听重建合一:100ms / 分级去抖 / 冷却
读驱动状态缓存(①②③④⑤ + U11)"] + RetryTick["① netListenerRetryTick
D10 重监听重试:30ms / 2s 时限"] + PendTick["② netPendingTick
挂起 recv:①数据 ②到点(仅显式预算) ③断连 ④tripwire 60s 只留痕"] + PendShimTick["③ netSendPendingTick
挂起 send:①断连 ②推进(每轮≤1块) ③写完 ④无进展超时"] + ReconcileTick["④ netReconcileTick
TRAP-NET-17 对账 + 监听重建合一:100ms / 分级去抖 / 冷却
读驱动状态缓存(①②③④⑤ + U11)"] DISP["队列分派 switch
SEND · RECV · CLOSE
CONNECT · LISTEN · ACCEPT · DROP_CONN"] subgraph LK["_locked / 内部分派目标(仅 netTask 上下文)"] LL["netListenLocked
初次监听(open 重试 3× + sock_reset 排渣)"] @@ -76,24 +76,24 @@ flowchart TB PUB -->|入队| MQ MQ --> DISP DISP --> LL & LN & LS & LR & LA & LC & DC & RLS - NPM --> T1 & T2 & T2B & T3 & NP - T1 --> RLS - T3 -->|"① 已断"| EV - T3 -->|"② 补 accept"| EV - T3 -->|"⑤ 静默回收:RESYNC 只修本层 + 取消挂起(不发芯片命令,吸收原 net_resync_listen 语义)"| RSY["reconcile⑤ RESYNC 动作"] - T3 -->|"③④ 监听失位 / 孤儿监听即重建(原 pump)"| RLS + NPM --> RetryTick & PendTick & PendShimTick & ReconcileTick & NP + RetryTick --> RLS + ReconcileTick -->|"① 已断"| EV + ReconcileTick -->|"② 补 accept"| EV + ReconcileTick -->|"⑤ 静默回收:RESYNC 只修本层 + 取消挂起(不发芯片命令,吸收原 net_resync_listen 语义)"| RSY["reconcile⑤ RESYNC 动作"] + ReconcileTick -->|"③④ 监听失位 / 孤儿监听即重建(原 pump)"| RLS DC -->|"拆会话后登记重监听"| RLS NP --> EV EV --> FE FE -.-> CB EV -->|"断连/超时:只置事实"| ST - ST -.->|"事实 → 由 reconciler 形态③④ 统一重建"| T3 + ST -.->|"事实 → 由 reconciler 形态③④ 统一重建"| ReconcileTick LR -->|无数据+阻塞| PR - PR --> T2 - T2 -->|xTaskNotify| XN + PR --> PendTick + PendTick -->|xTaskNotify| XN LS -->|等发送缓冲超预算| SR - SR --> T2B - T2B -->|xTaskNotify| XN + SR --> PendShimTick + PendShimTick -->|xTaskNotify| XN XN -->|唤醒| PUB NP --> CH LL & LN & LS & LR & LA & LC & DC & RLS --> CH @@ -107,7 +107,7 @@ flowchart TB 2. `net_process_messages()` 开头的 **4 个 tick 是网络层自愈主干**:没有任何应用消息时,重试 / 挂起 recv 完成 / 挂起 send 推进 / 状态对账 + 监听重建照常运转——"断连后即使没人发消息,监听器也能自己活过来"靠的就是它们。 3. 事件处理器**只由 `net_poll` 驱动**,例外是 `net_reconcile_tick`(分级去抖 + 1s 冷却)可再入 `handle_disconnect_event` / `handle_connect_event`——对账是事件未收到的自愈通道(TRAP-NET-17)。**注意职责边界(对账与重建合并后)**:对账与事件处理器**只置事实**(置 CLOSED / 登记沉降),**重建监听的动作统一由 `net_reconcile_tick` 形态③④ 做**(本层唯一重建入口)——"谁先看到 DISCONNECT 就谁重建"的竞争在结构上消失。 4. 回调 `event_cb` 在 **netTask 上下文**执行:回调内不得阻塞、不得发网络操作。 -5. **模块归属**:`T1`(D10 重试)/ `RLS` 的 `net_listener_request` / `ST`(settle)在 `net_listener.c`(真机专属、模拟器不编译);挂起 recv 完成检查(`net_pending_tick`)、挂起 send 推进(`net_send_pending_tick`)与两张挂起表(`PR` / `SR`)在 `net_pending.c`(真机专属);其余 tick(含合并后的 `T3` reconciler)与 `_locked` 在 `net_socket.c`。 +5. **模块归属**:`RetryTick`(D10 重试)/ `RLS` 的 `net_listener_request` / `ST`(settle)在 `net_listener.c`(真机专属、模拟器不编译);挂起 recv 完成检查(`net_pending_tick`)、挂起 send 推进(`net_send_pending_tick`)与两张挂起表(`PR` / `SR`)在 `net_pending.c`(真机专属);其余 tick(含合并后的 `ReconcileTick` reconciler)与 `_locked` 在 `net_socket.c`。 --- diff --git a/docs/网络/网络自愈架构设计.md b/docs/网络/网络自愈架构设计.md index b34c3660..86a071ab 100644 --- a/docs/网络/网络自愈架构设计.md +++ b/docs/网络/网络自愈架构设计.md @@ -858,7 +858,7 @@ accept 走 `handle_connect_event`),且 `NET_RECON_STABLE_DEATH` 两边共用 - 本文引用格式:`docs/网络/网络自愈架构设计.md` §N.M(紧邻写法);E 编号引 §5.2 (如"§5.2 E12"),F 编号引 §7。 - **勿引用已删除的过程章节**。过程考古: - - N34 自愈审计(T1~T4 三路审计、39 条编号发现全文、E0~E15 原始记录、分阶段实施清单): + - N34 自愈审计(三路审计、39 条编号发现全文、E0~E15 原始记录、分阶段实施清单): `git show 1a35b36^:docs/网络/N34-自愈审计/`(目录合并前 7 份文档)或 `git log --oneline -- docs/网络/N34-自愈审计/`; - 监听生命周期(本文 §1.1~§1.4 / §4.9 / §9~§11 的来源,含 P0~P3 勾选清单与台架验收记录): diff --git a/docs/门禁说明/gitlint.md b/docs/门禁说明/gitlint.md index 76d7ae33..90987ff7 100644 --- a/docs/门禁说明/gitlint.md +++ b/docs/门禁说明/gitlint.md @@ -35,7 +35,7 @@ ``` gitlint..............................................................Failed 12: BBody line length limit exceeded, lengthy body line (max-line-length: 79) - 1: T2 Title has trailing whitespace + 1: Title has trailing whitespace ``` - 前缀数字是行号(第 1 行是标题)。本仓最常犯的是 `B`(正文行超长)与标题超 100。 diff --git a/scripts/id_registry.json b/scripts/id_registry.json index ce5028b7..23623e4c 100644 --- a/scripts/id_registry.json +++ b/scripts/id_registry.json @@ -59,7 +59,8 @@ ".dsh/n31_neg_inject_gen221_form5.log", "scripts/id_known_backlog.json", "scripts/rename_maps/", - "docs/硬件平台/" + "docs/硬件平台/", + "tools/netlist_semantics/" ], "excluded_suffixes": [ ".svg", @@ -133,7 +134,8 @@ "^Y[0-9]-[0-9]+$", "^A-Z0-9$", "^X-[0-9]+$", - "^V[0-9A-Z]{2,}-[0-9]+$" + "^V[0-9A-Z]{2,}-[0-9]+$", + "N-1" ], "malformed": [ { diff --git a/test/board/net_test_task.c b/test/board/net_test_task.c index 8833305c..90828e45 100644 --- a/test/board/net_test_task.c +++ b/test/board/net_test_task.c @@ -842,7 +842,7 @@ static int phase11_run(int32_t sockfd) { * 生产参数实测判死窗口 18~31s(产品验证 连接+30.5s、E11 T5 * 对端失联+30s、T4 短参数 连接+~10s)。本 socket 无在途 TX、 * 对端静默且已失联 → 唯一开枪源是 keepalive,故判据为 - * **有界窗**:>=10s(存活空闲会话 T2 实测 90s 不被杀,10s 下限 + * **有界窗**:>=10s(存活空闲会话 空闲存活场景实测 90s 不被杀,10s 下限 * 区分"误杀"与"keepalive 判死")且 <=120s(判死必须发生)。 * 勿改回 >=60000——那是对着手册公式写的,会在正确固件上假 FAIL */ TEST_CHECK(elapsed >= 10000U && elapsed <= 120000U, diff --git a/test/host/e11_keepalive_probe.py b/test/host/e11_keepalive_probe.py index d8bb11d1..3dc4c178 100644 --- a/test/host/e11_keepalive_probe.py +++ b/test/host/e11_keepalive_probe.py @@ -8,18 +8,18 @@ App/ 无任何 net_connect() → 产品上 keepalive 实际关闭。本脚本用 "开启后能否在有界时间内判死静默对端,且不误杀正常空闲会话"。 用法: - # T2 正常空闲存活:连接后静默 hold 秒,末尾发 KEEPLIVE 验证会话仍在 + # 场景「正常空闲存活」:连接后静默 hold 秒,末尾发 KEEPLIVE 验证会话仍在 python e11_keepalive_probe.py --mode idle --hold-s 90 - # T1 静默对端:连接后立即装 Windows 防火墙入站 DROP(仅本连接本地端口 + + # 场景「静默对端」:连接后立即装 Windows 防火墙入站 DROP(仅本连接本地端口 + # 设备 IP)→ 芯片 keepalive 探测得不到 ACK → 期望设备在 # IDLE + INTVL×CNT 内判死(本仓 60 + 5x3 = 75s) python e11_keepalive_probe.py --mode block --hold-s 120 判据(设备侧事件由串口日志核对,本脚本打印 PC 侧时间线): - T1 期望:串口出现 `[ERR][NET] sockN TIMEOUT`(耗时≈75s),随后 + 期望:串口出现 `[ERR][NET] sockN TIMEOUT`(耗时≈75s),随后 `sockN pump/relisten` 恢复;本脚本移除规则后新连接必须成功。 - T2 期望:hold 期间**无** TIMEOUT;末尾 KEEPLIVE 必须收到 0xBB 回显。 + 期望:hold 期间**无** TIMEOUT;末尾 KEEPLIVE 必须收到 0xBB 回显。 需要管理员权限(仅 block 模式装/卸防火墙规则;脚本自行清理)。 """ diff --git a/test/host/ftp_auth_bench.py b/test/host/ftp_auth_bench.py index d777d506..9723d033 100644 --- a/test/host/ftp_auth_bench.py +++ b/test/host/ftp_auth_bench.py @@ -5,9 +5,9 @@ 验证固件侧 lftpd 鉴权集成的完整行为矩阵(主机单测 test/host/ftp_auth 覆盖 不到的"协议接线"部分:命令门、530/331/230/550 响应码、跨会话锁定): - T1 匿名登录(anonymous) → 期望 530 拒 - T2 未登录直接 NLST → 期望 530(命令门) - T3 对用户名 + 错口令 ×5 → 期望 530 ×5(并触发锁定) + 场景「匿名登录」(anonymous) → 期望 530 拒 + 场景「未登录直接 NLST」 → 期望 530(命令门) + 场景「对用户名 + 错口令 ×5」 → 期望 530 ×5(并触发锁定) T4 锁定窗内正确口令 → 期望 530(LOCKED,不算哈希) T5 等待锁定期满后正确口令 → 期望 230(60s + 余量) T6 登录后 NLST / STOR / RETR / DELE → 期望全通(出厂 write=1) @@ -83,26 +83,26 @@ def main(argv=None): if not cond: fails.append(name) - # ---- T1 匿名登录被拒 ---- + # ---- 场景「匿名登录被拒」---- ok, code, conn = _try_login(args.host, args.port, args.timeout, "anonymous", "x@x") if ok is None: print("[env] 连不上 %s:%d → %s" % (args.host, args.port, code)) return 2 - expect("T1 匿名登录 → 530", (ok is False) and code.startswith("530"), code) + expect("匿名登录 → 530", (ok is False) and code.startswith("530"), code) if conn: conn.quit() - # ---- T2 未登录直接 NLST → 530(命令门)---- + # ---- 场景「未登录直接 NLST → 530(命令门)」---- time.sleep(0.5) # 单连接 socket:等 auto_relisten 沉降(~50 ms + 余量) f = FTP() f.connect(args.host, args.port, timeout=args.timeout) try: f.sendcmd("NLST") - expect("T2 未登录 NLST → 530", False, "居然放行") + expect("未登录 NLST → 530", False, "居然放行") except error_perm as e: - expect("T2 未登录 NLST → 530", str(e).startswith("530"), str(e)) + expect("未登录 NLST → 530", str(e).startswith("530"), str(e)) except Exception as e: # noqa: BLE001 - expect("T2 未登录 NLST → 530", False, repr(e)) + expect("未登录 NLST → 530", False, repr(e)) # ⚠ 必须 close:单连接模型下不关 = 占住 socket,后续步骤全被拒 # (2026-10-11 台架实测:QUIT 后不 close → 芯片视角旧连接仍 ESTABLISHED) try: @@ -114,11 +114,11 @@ def main(argv=None): pass time.sleep(0.5) - # ---- T3 错口令 ×5 → 530 ×5 ---- + # ---- 场景「错口令 ×5 → 530 ×5」---- for i in range(1, 6): ok, code, conn = _try_login(args.host, args.port, args.timeout, args.user, "wrong-pass-%d" % i) - expect("T3.%d 错口令 → 530" % i, (ok is False) and code.startswith("530"), code) + expect("错口令 %d → 530" % i, (ok is False) and code.startswith("530"), code) if conn: conn.quit() diff --git a/test/host/iec104_master.py b/test/host/iec104_master.py index a8dcdf0e..5160c561 100644 --- a/test/host/iec104_master.py +++ b/test/host/iec104_master.py @@ -80,11 +80,11 @@ SCAN_COTS = (COT_BG, COT_INIT) YX_NAMES = {15: "YX15 fault-total", 16: "YX16 OC-I", 17: "YX17 OC-II", 18: "YX18 OC-III", 23: "YX23 overload", 31: "YX31 prot-block"} -T0 = time.time() +t_start = time.time() def log(msg): - print("[%9.3fs] %s" % (time.time() - T0, msg), flush=True) + print("[%9.3fs] %s" % (time.time() - t_start, msg), flush=True) def hexs(b):