#!/usr/bin/env python3 """重启 ecat_native 进程(让 esp32_server/父进程重拉),测 rx 是否回到 ~90µs。""" import json, os, subprocess, sys, time, urllib.request ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) LAB = os.path.join(ROOT, "lab", "skip_meter") BASE = "http://127.0.0.1:8090" def post(p, b=None, timeout=5): d = json.dumps(b or {}).encode() r = urllib.request.Request(BASE+p, data=d, headers={"Content-Type":"application/json"}) return json.loads(urllib.request.urlopen(r, timeout=timeout).read().decode()) def ps(cmd): return subprocess.check_output(["powershell","-NoProfile","-Command",cmd], text=True, encoding="utf-8", errors="replace").strip() def wait_health(timeout=40): t0 = time.time() while time.time()-t0 < timeout: try: h = post("/api/native/health") if h.get("loop")==1 and int(h.get("wkc") or 0)>=3: return True, h except Exception: pass time.sleep(0.5) return False, None def meter(tag, secs=16): subprocess.run([sys.executable, os.path.join(ROOT,"scripts","skip_rate_meter.py"), "--tag", tag, "--secs", str(secs)], capture_output=True, text=True, encoding="utf-8", errors="replace") files = sorted(f for f in os.listdir(LAB) if f.startswith(tag+"_") and f.endswith(".json")) return json.load(open(os.path.join(LAB, files[-1]), encoding="utf-8")) def main(): # re-enable IP (keep VMware off — it's harmful and was only “enabled” by coincidence in morning) for cid in ("ms_tcpip", "ms_tcpip6"): try: ps(f"Enable-NetAdapterBinding -Name '以太网 2' -ComponentID '{cid}' -Confirm:$false") print("enabled", cid) except Exception as e: print("enable fail", cid, e) info = ps("Get-CimInstance Win32_Process -Filter \"Name='ecat_native.exe'\" | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress") print("before", info[:500]) print("kill ecat_native...") ps("Stop-Process -Name ecat_native -Force -ErrorAction SilentlyContinue") time.sleep(2) # parent may be esp32_server — wait for auto-restart ok, h = wait_health(45) print("auto restart?", ok, h) if not ok: print("manual start") exe = r"G:\Robot_v2\DCU\tools\ecat\build_native\ecat_native.exe" cwd = r"G:\Robot_v2\DCU\tools\ecat" subprocess.Popen( [exe, "--substr", "I210", "--mask", "0x50", "--web", "src/web"], cwd=cwd, stdout=open(os.path.join(cwd,"build_native","native_stdout.log"),"a"), stderr=subprocess.STDOUT, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP, ) time.sleep(3) ok, h = wait_health(30) print("manual health", ok, h) if ok: try: post("/api/op/start") post("/api/motor/start") except Exception as e: print("start fail", e) ok, h = wait_health(20) print("after start", ok, h) # set RealTime try: p = int(ps("(Get-Process ecat_native).Id")) ps(f"$p=Get-Process -Id {p}; $p.PriorityClass='RealTime'; $p.PriorityClass") print("RealTime", p) except Exception as e: print("prio fail", e) print("settle 10s") time.sleep(10) st = post("/api/motor/state") print("cyc", st.get("cyc_us")) print("period", st.get("period_ms"), "wkc", st.get("wkc"), "pcore", st.get("pcore_mask")) data = meter("restart_rt", 16) row = { "late_per_s": data["late_per_s"], "skip_per_s": data["skip_per_s"], "achieved_hz": data["achieved_hz"], "n_late": data["n_late"], "work_p50": data["window_cyc_us"]["work"]["p50"], "rx_p50": data["window_cyc_us"]["rx"]["p50"], "send_p50": data["window_cyc_us"]["send"]["p50"], "work_p90": data["window_cyc_us"]["work"]["p90"], "send_stall_frac": data["send_stall_frac"], "rx_stall_frac": data["rx_stall_frac"], } print(json.dumps(row, ensure_ascii=False, indent=2)) out = os.path.join(LAB, f"summary_restart_{int(time.time())}.json") json.dump(row, open(out,"w",encoding="utf-8"), ensure_ascii=False, indent=2) print("WROTE", out) if __name__ == "__main__": main()