- 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 截图
87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
import json, os, subprocess, time, urllib.request
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
LAB = os.path.join(ROOT, "lab", "skip_meter")
|
|
NIC = "以太网 2"
|
|
|
|
def ps(cmd):
|
|
return subprocess.check_output(["powershell","-NoProfile","-Command",cmd],
|
|
text=True, encoding="utf-8", errors="replace").strip()
|
|
|
|
def post(p, b=None):
|
|
d = json.dumps(b or {}).encode()
|
|
r = urllib.request.Request("http://127.0.0.1:8090"+p, data=d, headers={"Content-Type":"application/json"})
|
|
return json.loads(urllib.request.urlopen(r, timeout=8).read().decode())
|
|
|
|
def wait_ok(t=30):
|
|
t0=time.time()
|
|
while time.time()-t0<t:
|
|
try:
|
|
h=post("/api/native/health")
|
|
if h.get("loop")==1 and int(h.get("wkc") or 0)>=3: return h
|
|
except Exception: pass
|
|
time.sleep(0.4)
|
|
return None
|
|
|
|
def set_rt():
|
|
p=int(ps("(Get-Process ecat_native).Id"))
|
|
ps(f"$p=Get-Process -Id {p}; $p.PriorityClass='RealTime'; $p.PriorityClass")
|
|
return p
|
|
|
|
def period(ms):
|
|
post("/api/motor/stop"); time.sleep(0.12)
|
|
post("/api/motor/period", {"period_ms": ms}); time.sleep(0.12)
|
|
post("/api/motor/start")
|
|
|
|
def quiet(sec=10):
|
|
st=post("/api/motor/state"); dbg=post("/api/motor/dbg")
|
|
c0,s0=st.get("cycles") or 0, dbg.get("skip_total") or 0
|
|
t0=time.time(); time.sleep(sec)
|
|
st=post("/api/motor/state"); dbg=post("/api/motor/dbg")
|
|
dt=time.time()-t0
|
|
c1,s1=st.get("cycles") or 0, dbg.get("skip_total") or 0
|
|
cu=st.get("cyc_us") or {}
|
|
# also sample dbg ring k distribution
|
|
ring=dbg.get("cyc") or []
|
|
ks=[r[5] for r in ring if len(r)>=6]
|
|
works=[(r[4]-r[0])*1e6 for r in ring if len(r)>=5]
|
|
sends=[(r[2]-r[1])*1e6 for r in ring if len(r)>=3]
|
|
rxs=[(r[3]-r[2])*1e6 for r in ring if len(r)>=4]
|
|
def pct(a,q):
|
|
if not a: return None
|
|
a=sorted(a); return round(a[int(q*(len(a)-1))],1)
|
|
return {
|
|
"pdo_hz": round((c1-c0)/dt,1),
|
|
"late_per_s": round((s1-s0)/dt,2),
|
|
"achieved_vs_4k": round(100*(c1-c0)/dt/4000,1),
|
|
"cyc_us": cu,
|
|
"ring_n": len(ring),
|
|
"k_gt0": sum(1 for k in ks if k>0),
|
|
"k_sum": sum(ks),
|
|
"work_pct": {"p50":pct(works,.5),"p90":pct(works,.9),"p99":pct(works,.99),"max":pct(works,1.0) if works else None},
|
|
"send_pct": {"p50":pct(sends,.5),"p90":pct(sends,.9),"max":pct(sends,1.0) if sends else None},
|
|
"rx_pct": {"p50":pct(rxs,.5),"p90":pct(rxs,.9),"max":pct(rxs,1.0) if rxs else None},
|
|
}
|
|
|
|
# set ITR 200
|
|
ps("Set-NetAdapterAdvancedProperty -Name '以太网 2' -RegistryKeyword '*InterruptModeration' -RegistryValue '1' -NoRestart")
|
|
ps("Set-NetAdapterAdvancedProperty -Name '以太网 2' -RegistryKeyword 'ITR' -RegistryValue '200' -NoRestart")
|
|
ps("Restart-NetAdapter -Name '以太网 2' -Confirm:$false")
|
|
time.sleep(2)
|
|
print("health", wait_ok())
|
|
set_rt()
|
|
|
|
rows=[]
|
|
for ms in (0.25, 0.5, 1.0):
|
|
print(f"\n=== {ms} ms ITR=200 ===")
|
|
period(ms)
|
|
time.sleep(4)
|
|
set_rt()
|
|
r=quiet(10)
|
|
print(json.dumps(r, ensure_ascii=False, indent=2))
|
|
rows.append(r)
|
|
|
|
path=os.path.join(LAB, f"summary_itr200_periods_{int(time.time())}.json")
|
|
json.dump(rows, open(path,"w",encoding="utf-8"), ensure_ascii=False, indent=2)
|
|
print("WROTE", path)
|