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