feat(ecat): 启动后把 ecat_native 提到 RealTime;跳拍优化实验脚本与报告
- 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 截图
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
"""查 pdo 线程亲和 + 尝试 ITR 200/400 在 0.25ms 静默下的表现。"""
|
||||
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, timeout=60):
|
||||
return subprocess.check_output(["powershell","-NoProfile","-Command",cmd],
|
||||
text=True, encoding="utf-8", errors="replace",
|
||||
timeout=timeout).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(timeout=35):
|
||||
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 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 quiet_rate(sec=8):
|
||||
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
|
||||
return {
|
||||
"pdo_hz": round((c1-c0)/dt, 1),
|
||||
"late_per_s": round((s1-s0)/dt, 2),
|
||||
"cyc_us": st.get("cyc_us"),
|
||||
"period": st.get("period_ms"),
|
||||
}
|
||||
|
||||
def bus_restart_period(ms):
|
||||
try:
|
||||
post("/api/motor/stop")
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(0.15)
|
||||
post("/api/motor/period", {"period_ms": ms})
|
||||
time.sleep(0.15)
|
||||
post("/api/motor/start")
|
||||
|
||||
def nic_set(kw, val):
|
||||
out, err = "", ""
|
||||
r = subprocess.run(["powershell","-NoProfile","-Command",
|
||||
f"Set-NetAdapterAdvancedProperty -Name '{NIC}' -RegistryKeyword '{kw}' -RegistryValue '{val}' -NoRestart"],
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace")
|
||||
return r.stdout.strip(), r.stderr.strip()
|
||||
|
||||
# affinity of ecat threads via .NET is limited; dump processor affinity mask
|
||||
print("=== process affinity ===")
|
||||
print(ps(r'''
|
||||
$p = Get-Process ecat_native
|
||||
"PID $($p.Id) Priority=$($p.PriorityClass)"
|
||||
# ProcessorAffinity is a bitmask
|
||||
"Affinity=$($p.ProcessorAffinity)"
|
||||
'''))
|
||||
|
||||
print("=== I210 current ITR/moderation ===")
|
||||
print(ps(r'''
|
||||
Get-NetAdapterAdvancedProperty -Name '以太网 2' |
|
||||
Where-Object { $_.RegistryKeyword -in 'ITR','*InterruptModeration','*TransmitBuffers' } |
|
||||
Select-Object RegistryKeyword,DisplayValue | ConvertTo-Json -Compress
|
||||
'''))
|
||||
|
||||
results = []
|
||||
# try ITR values at 0.25ms quiet
|
||||
for itr in ("200", "400", "950", "65535"):
|
||||
print(f"\n===== ITR={itr} @0.25ms =====")
|
||||
o, e = nic_set("*InterruptModeration", "1")
|
||||
o2, e2 = nic_set("ITR", itr)
|
||||
print("set", o2 or o, e2 or e)
|
||||
# restart nic only if needed - ITR may need restart
|
||||
ps(f"Restart-NetAdapter -Name '{NIC}' -Confirm:$false")
|
||||
time.sleep(2)
|
||||
h = wait_ok(30)
|
||||
print("health", bool(h))
|
||||
if not h:
|
||||
try:
|
||||
post("/api/op/start"); post("/api/motor/start")
|
||||
h = wait_ok(20)
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
set_rt()
|
||||
bus_restart_period(0.25)
|
||||
time.sleep(3)
|
||||
set_rt()
|
||||
r = quiet_rate(8)
|
||||
print(json.dumps({k:r[k] for k in ("pdo_hz","late_per_s","period")}, ensure_ascii=False))
|
||||
print("work/send/rx", r["cyc_us"]["work"]["p50"], r["cyc_us"]["send"]["p50"], r["cyc_us"]["rx"]["p50"])
|
||||
results.append({"itr": itr, **{k:r[k] for k in ("pdo_hz","late_per_s")},
|
||||
"work_p50": r["cyc_us"]["work"]["p50"], "send_p50": r["cyc_us"]["send"]["p50"],
|
||||
"rx_p50": r["cyc_us"]["rx"]["p50"]})
|
||||
|
||||
print("\nSUMMARY", json.dumps(results, ensure_ascii=False, indent=2))
|
||||
# restore adaptive
|
||||
nic_set("ITR", "65535")
|
||||
ps(f"Restart-NetAdapter -Name '{NIC}' -Confirm:$false")
|
||||
time.sleep(2)
|
||||
wait_ok(25)
|
||||
set_rt()
|
||||
# leave at 1ms for safety
|
||||
bus_restart_period(1.0)
|
||||
time.sleep(1)
|
||||
print("restored 1ms", post("/api/native/health"))
|
||||
path = os.path.join(LAB, f"summary_itr_4k_{int(time.time())}.json")
|
||||
json.dump(results, open(path,"w",encoding="utf-8"), ensure_ascii=False, indent=2)
|
||||
print("WROTE", path)
|
||||
Reference in New Issue
Block a user