fix(test/udp): peer 换源端口上报方式并补 PC 回路自检(#73)
CI / 静态门禁 (pre-commit) (push) Failing after 5m4s
CI / 主机侧单测 (MSVC) (push) Successful in 44s
CI / 静态门禁 (pre-commit) (pull_request) Failing after 4m48s
CI / 主机侧单测 (MSVC) (pull_request) Failing after 44s

- test/host/udp_queue_bench.py:Windows 上未 connect 的 UDP socket 调 getsockname()
  直接抛 WinError 10022(请求一个未连接套接句的名称),脚本在第一条用例就崩。
  源端口改为首次收到回显时打印——它本来就是「板端剥头/源地址判定对不对」的直接读数。
- 新增 test/host/udp_queue_bench_loopback.py:不用板子的 peer 自检,在 127.0.0.1 上放
  一个按板端阶段 16 同样三条规矩说话的假板子(原样回显、tag 0x04 只回 64 B、>1KB 按
  1024 拆片),先把步骤表 / 模式函数 / 分片预期验掉。2026-11-03 首跑 27/27 绿,
  实测拆片形状 [1024, 1] 与 [1024, 448]。
- CH395F_Test_Guide.md §12.6:运行方法补「先跑自检」这一步;>1024 B 的 UDP 拆片从
  「多收到一片就 FAIL」改成「实测观测量」(拼接后逐字节一致才算绿),并说清这是既有
  NET_SEND_CHUNK_MAX 行为、不是本轮引入;脚本表补一行。
- 板子自 19:19 起未再上电(ping 不通、SWD No target、串口静默),阶段 16 真机那一趟
  仍未跑,PR #74 的合并前提不变。
This commit is contained in:
2026-11-03 20:46:15 +08:00
parent 8a81fa9e41
commit b0a6394aa1
3 changed files with 173 additions and 30 deletions
+89
View File
@@ -0,0 +1,89 @@
"""PC peer 自身的回路自检:不需要板子,用假板子把 udp_queue_bench.py 的步骤表跑一遍。
为什么要有这个文件:阶段 16 的真机台架是一次性的——板子通电、烧好台架镜像、PC 起对端,
对端脚本自己要是有 bug(取端口、步骤表、拆包预期),一趟台架时间就白费了。这里在
127.0.0.1 上放一个「按板端阶段 16 同样规矩说话」的假板子,把对端脚本的判定逻辑先验一遍。
假板子复刻的三条规矩(与 test/board/net_test_task.c 阶段 16 一致):
1. 收到的数据报原样回显给发送方(tag 字节不动,所以源地址/剥头判据留在板端断言);
2. tag 0x04 这一步板端故意只用 64 字节缓冲去读 200 字节的数据报,所以只回 64 字节;
3. 回显超过芯片发送缓冲(1KB)时按 1024 拆成多个数据报发出(NET_SEND_CHUNK_MAX)。
用法(在本仓库根目录):
python test/host/udp_queue_bench_loopback.py
退出码:0 = 对端脚本判定逻辑全绿;1 = 有问题,先别上台架。
"""
import os
import socket
import sys
import threading
import time
HERE = os.path.dirname(os.path.abspath(__file__))
if HERE not in sys.path:
sys.path.insert(0, HERE)
import udp_queue_bench as peer # noqa: E402
CHIP_SEND_BUF = 1024 # NET_SEND_CHUNK_MAX
BOARD_RX_CAP = 1600 # 板端 s_udp_buf 尺寸 UDP_BENCH_BUF_SIZE
TRUNCATE_TAG = 0x04 # 这一步板端只用 64 字节缓冲
TRUNCATE_CAP = 64
RUNOUT_S = 90.0
LISTEN_PORTS = (6001, 6002) # UDP_BENCH_PORT / UDP_BENCH_PORT2
def echo_one(sock, deadline, stop):
sock.settimeout(0.5)
while (not stop.is_set()) and (time.time() < deadline):
try:
data, addr = sock.recvfrom(4096)
except socket.timeout:
continue
except OSError:
return
if not data:
continue
tag = data[0]
cap = TRUNCATE_CAP if tag == TRUNCATE_TAG else BOARD_RX_CAP
echo = peer.payload(tag, min(len(data), cap))
print("[board] rx 0x{0:02X} n={1} on :{2} -> echo {3}B".format(
tag, len(data), sock.getsockname()[1], len(echo)), flush=True)
for off in range(0, len(echo), CHIP_SEND_BUF):
sock.sendto(echo[off:off + CHIP_SEND_BUF], addr)
def main() -> int:
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
deadline = time.time() + RUNOUT_S
stop = threading.Event()
for listen_port in LISTEN_PORTS:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.bind(("127.0.0.1", listen_port))
except OSError as exc:
print("无法绑定 127.0.0.1:{0}:{1}(端口被占用?先腾出来再跑)".format(listen_port, exc),
file=sys.stderr)
return 2
threading.Thread(target=echo_one, args=(s, deadline, stop), daemon=True).start()
time.sleep(0.3)
# --late 必须落在板端 7000~25000 ms 判定窗内;这里取 9 秒,比默认 12 秒省一点台架外时间
sys.argv = ["udp_queue_bench.py", "--board", "127.0.0.1",
"--first-wait", "15", "--timeout", "6",
"--probe-silence", "2.0", "--late", "9.0"]
try:
rc = peer.main()
finally:
stop.set()
if rc == 0:
print("=== UDP Peer Loopback Self-Test: PASS(对端脚本判定逻辑可用,可上台架)===", flush=True)
else:
print("=== UDP Peer Loopback Self-Test: FAIL(先修对端脚本,别浪费台架时间)===", flush=True)
return rc
if __name__ == "__main__":
raise SystemExit(main())