Files
DCU_DesktopController/scripts/rec_ecat_slots.py
T

106 lines
3.9 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 侧收到的每一拍信号全量落盘,供事后分析(不在线判优劣)。
去重规则只有一条:同一 (node,seq) 或同一 (id,data) 帧只在第一次出现时写一行 ——
重复采样不携带新信息,去掉后可以把时长拉长。其余一律保留。
行格式(CSV,第 1 列是相对起始的秒):
M,t,node,ch,seq,online,dlc,hex36 段2 电机槽(STATUS 前 36B,含 cpu%@31/init@32-35)
F,t,kind,ch,dlc,id,hex36 段3 xrx / 段4 rx_batch 的帧
H,t,cycles,wkc,gseq,win_base,win_count,active,rx,tx,drop 每 20 拍一条头部与桥计数
用法: python scripts/rec_ecat_slots.py [秒数=120] [端口=8090] [输出文件]
"""
import csv
import json
import socket
import struct
import sys
import time
HOST = "127.0.0.1"
DUR_S = float(sys.argv[1]) if len(sys.argv) > 1 else 120.0
PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 8090
OUT = sys.argv[3] if len(sys.argv) > 3 else "ecat_slots_raw.csv"
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 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 main():
seen_m, seen_f = set(), set()
polls = rows = 0
t0 = time.time()
with open(OUT, "w", newline="", encoding="ascii") as f:
w = csv.writer(f)
w.writerow(["#kind", "t", "a", "b", "c", "d", "e", "f"])
while True:
dt = time.time() - t0
if dt >= DUR_S:
break
try:
snap = once()
except Exception:
continue
polls += 1
h = snap.get("head") or {}
b = snap.get("bridge") or {}
if polls % 20 == 1:
w.writerow(["H", f"{dt:.6f}", snap.get("cycles"), snap.get("wkc"),
h.get("global_seq"), h.get("win_base"), h.get("win_count"),
h.get("active"), b.get("rx_count"), b.get("tx_count"),
b.get("drop")])
for m in snap.get("motors") or []:
if not m.get("data"):
continue
key = (m["node"], m["ch"], m["seq"])
if key in seen_m:
continue
seen_m.add(key)
if len(seen_m) > 400000: # 去重集合本身的内存上限
seen_m = set(list(seen_m)[-200000:])
d = bytes.fromhex(m["data"])[:36]
w.writerow(["M", f"{dt:.6f}", m["node"], m["ch"], m["seq"],
int(bool(m.get("online"))), m.get("dlc"), d.hex()])
rows += 1
def emit_frames(fr, kind):
if not fr or not fr.get("data"):
return
d = bytes.fromhex(fr["data"])[:36]
key = (fr.get("id"), d.hex())
if key in seen_f:
return
seen_f.add(key)
w.writerow([kind, f"{dt:.6f}", fr.get("ch"), fr.get("dlc"),
fr.get("id"), "", "", d.hex()])
emit_frames(snap.get("xrx"), "X")
for x in ((snap.get("rx_batch") or {}).get("frames") or []):
emit_frames(x, "B")
if len(seen_f) > 400000:
seen_f = set(list(seen_f)[-200000:])
print(f"落盘 {OUT}: 轮询 {polls} 次 / {DUR_S:.0f}s,写入 {rows} 行 "
f"(电机槽 {len(seen_m)} 帧, 接收帧 {len(seen_f)})")
if __name__ == "__main__":
main()