- esp32_server: Popen 后外部 SetPriorityClass(RT/High),native 自设会被钳成 High - 入库 skip_* 标定/实验脚本与 REPORT_ecat_skip_opt_20260917 - 前端 WIP:layout/app/polling/boot_ecat/index - 不含 lab/skip_meter 原始采样与 preview 截图
111 lines
4.3 KiB
Python
111 lines
4.3 KiB
Python
#!/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()
|