Files
DCU_DesktopController/scripts/probe_ecat_vs_wifi.py
T

102 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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("<H", b, 0)[0] != 0x4D53 or b[2] != 2:
return {}
out, off = {}, 12
for _ in range(b[9]):
if off + 92 > len(b):
break
out[b[off]] = (struct.unpack_from("<I", b, off + 8)[0],
b[off + 12:off + 60])
off += 92
return out
def main():
cache = {} # node -> {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()