#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ECAT 与 WiFi 两条链路对同一 seq 的 48B STATUS 做逐字节对照。 两条链路读的是 GD32 里同一张 MotorState 表,但读取上下文不同: * ECAT 段2 由 buildInput 取表,跑在 PDI_Isr —— 会抢占主循环的写侧(本次修复点); * WiFi 0xC5/buildPayload 与写侧同在主循环 —— 结构上不可能读到中间态。 所以 WiFi 侧可当参照:同一 seq 在两边必须逐字节相同(seq 由 global_seq 唯一确定, 一次更新只产生一个 seq)。不等 ⇒ ECAT 侧那一份是没写完的半成品。 用法: python scripts/probe_ecat_vs_wifi.py [秒数] """ import json import socket import struct import sys import time import urllib.request HOST, PORT = "127.0.0.1", 8090 DUR_S = float(sys.argv[1]) if len(sys.argv) > 1 else 60.0 ESP = "192.168.31.127" REQ = (f"POST /api/motor/state HTTP/1.0\r\nHost: {HOST}:{PORT}\r\n" "Content-Type: application/json\r\nContent-Length: 2\r\n" "Connection: close\r\n\r\n{}").encode("ascii") def ecat_once(): s = socket.create_connection((HOST, PORT)) try: s.sendall(REQ) buf = bytearray() while True: c = s.recv(65536) if not c: break buf.extend(c) finally: s.close() _, _, d = bytes(buf).partition(b"\r\n\r\n") return json.loads(d.decode("utf-8", "replace")) def wifi_once(): """返回 {node: (seq, 48B)}。协议 ver=2:entry 92B,data 在 +12。""" b = urllib.request.urlopen(f"http://{ESP}/motor/state?bin=1", timeout=3).read() if len(b) < 12 or struct.unpack_from(" len(b): break out[b[off]] = (struct.unpack_from(" {seq: payload}(ECAT 侧见过的每一帧) matched = mism = polls = wpolls = 0 bad = [] t0 = time.time() while time.time() - t0 < DUR_S: try: snap = ecat_once() polls += 1 for m in snap.get("motors") or []: if m.get("online") and m.get("data"): cache.setdefault(m["node"], {})[m["seq"]] = bytes.fromhex(m["data"]) if len(cache[m["node"]]) > 4000: for k in sorted(cache[m["node"]])[:2000]: del cache[m["node"]][k] except Exception: continue try: w = wifi_once() wpolls += 1 except Exception: continue for node, (seq, payload) in w.items(): d = cache.get(node, {}).get(seq) if d is None: continue # 该 seq 未被 ECAT 侧采到,跳过 matched += 1 if d != payload: mism += 1 bad.append((round(time.time() - t0, 2), node, seq, d.hex(), payload.hex())) print(f"轮询 ECAT {polls} 次 / WiFi {wpolls} 次 / {DUR_S:.0f}s") print(f"两侧同 seq 可比对 = {matched} 逐字节不一致 = {mism}") for t, node, seq, e, w in bad[:6]: j = next((i for i in range(48) if e[i] != w[i]), 48) print(f" t={t}s node={node} seq={seq} 首字节差={j}") print(f" ECAT={e}") print(f" WiFi={w}") if not matched: print("功效不足:ECAT 帧率远高于 WiFi 采样,同 seq 交集为 0,本次判定不作数。") if __name__ == "__main__": main()