#!/usr/bin/env python3 # -*- coding: utf-8 -*- """全链路延迟分段归因(pdo-fast-send)。 把「点击→电机已处理」拆成可独立计时的段,确认延迟落在哪里: S1 控制面入队 TCP ESF1 FSOK / HTTP send_raw / JSON motor/send S2 入队→CAN 出口 bridge.tx_count/ack 变化(高频 motor/state) S3 CAN→CONFIG 可见 Target_Control_Mode 变化(高频 motor/rx) S0 观测开销 单次 motor/state / motor/rx(native 直连 vs 页面代理) 观测一律优先 http://127.0.0.1:8090/api,避免页面代理把 S2/S3 抬高。 用法: python scripts/check_latency_breakdown.py python scripts/check_latency_breakdown.py --node 3 --n 20 """ from __future__ import annotations import argparse import json import os import statistics import struct import sys import time import urllib.request sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from esp32_server import ecat_send_raw_frames, pack_esf, unpack_fsok # noqa: E402 import socket # noqa: E402 NATIVE = os.environ.get("DCU_NATIVE_API", "http://127.0.0.1:8090/api") PAGE = os.environ.get("DCU_PAGE_BASE", "http://127.0.0.1:2625") PAGE_API = PAGE + "/ecat/api" def post(url, obj=None, raw=None, ctype="application/json", timeout=5.0): if raw is not None: data, headers = raw, {"Content-Type": ctype} else: data = json.dumps(obj or {}, separators=(",", ":")).encode() headers = {"Content-Type": ctype} req = urllib.request.Request(url, data=data, headers=headers, method="POST") t0 = time.perf_counter() try: with urllib.request.urlopen(req, timeout=timeout) as r: body = r.read() ms = (time.perf_counter() - t0) * 1000.0 try: return ms, json.loads(body.decode("utf-8", "replace") or "{}") except Exception: return ms, {} except Exception as e: return (time.perf_counter() - t0) * 1000.0, {"ok": False, "error": str(e)} def pct(xs, p): if not xs: return None xs = sorted(xs) k = max(0, min(len(xs) - 1, int(round(p / 100.0 * (len(xs) - 1))))) return xs[k] def fmt(xs): if not xs: return "n/a" return ( f"n={len(xs)} mean={statistics.mean(xs):.2f} " f"p50={pct(xs,50):.2f} p90={pct(xs,90):.2f} " f"min={min(xs):.2f} max={max(xs):.2f}" ) def build_cmd(mode, voltage=24.0, current=0.0, velocity=0.0, angle=0.0, pid=100): b = bytearray(32) b[0] = mode & 0xFF struct.pack_into("