Files
DCU_DesktopController/scripts/rec_ecat_slots.py
T

106 lines
3.9 KiB
Python
Raw Normal View History

#!/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()