From beeae8a382eb4e5ffe6a64001f68f16409057622 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Mon, 21 Sep 2026 02:26:42 +0800 Subject: [PATCH] =?UTF-8?q?chore(hygiene):=20P3=20=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E5=BD=92=E6=A1=A3=20docs/reports=20+=20scripts=20=E6=94=B6?= =?UTF-8?q?=E6=95=9B=20+=20lab=20=E6=B8=85=E7=90=86=20+=20CSS/README=20?= =?UTF-8?q?=E7=BA=A6=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 正式 scripts 仅保留 README 自测清单;根目录报告迁入 docs/reports; lab 采样 dump 出库;CSS 注明 btn/toast 所有权并 bump ?v=。 --- README.md | 27 +- css/components.css | 2 + css/layout.css | 2 + css/motor.css | 4 + .../reports/ECAT_BEAT_TAIL_REPORT.html | 0 .../reports/ECAT_RATE_TRADEOFF.html | 0 .../reports/ECAT_RATE_TRADEOFF.md | 0 .../reports/GD32_OTA_FLOW_COMPARE.html | 0 .../reports/REPORT_ecat_skip_opt_20260917.md | 0 .../reports}/ecat_integral_report.html | 0 lab/_last_gd32_sha.txt | 1 - lab/ecat_calib.json | 139 -- lab/ecat_rate_sweep.png | Bin 205525 -> 0 bytes lab/ecat_rate_sweep_nocan.png | Bin 196659 -> 0 bytes lab/ecat_rate_sweep_nocan_vs.png | Bin 94777 -> 0 bytes pages/can.html | 90 +- pages/index.html | 184 +-- scripts/_watch_conn.py | 48 - scripts/anal_ecat_jumps.py | 188 --- scripts/build_ecat_report_html.py | 677 ---------- scripts/build_integral_report_html.py | 424 ------ scripts/calibrate_ecat_metrics.py | 418 ------ scripts/can_bridge_reconcile.py | 232 ---- scripts/cpu_minimize_probe.ps1 | 59 - scripts/cpu_sample.ps1 | 31 - scripts/ecat_sweep_common.py | 54 - scripts/flash_gd32.py | 154 --- scripts/plot_ecat_nocan_vs_can.py | 112 -- scripts/plot_ecat_sweep.py | 188 --- scripts/rec_ecat_slots.py | 105 -- scripts/repro_cmdload.py | 251 ---- scripts/skip_bind_experiment.py | 130 -- scripts/skip_bind_noip.py | 53 - scripts/skip_fe_ab.py | 105 -- scripts/skip_host_snapshot.py | 126 -- scripts/skip_itr200_detail.py | 86 -- scripts/skip_itr_4k.py | 128 -- scripts/skip_multi_round.py | 44 - scripts/skip_nic_experiment.py | 312 ----- scripts/skip_prio_ab.py | 76 -- scripts/skip_prio_experiment.py | 79 -- scripts/skip_quiet_periods.py | 71 -- scripts/skip_rate_meter.py | 136 -- scripts/skip_restart_native.py | 110 -- scripts/sweep_ecat_rate.py | 335 ----- scripts/test_ecat_dir_worktree.mjs | 57 - scripts/test_motor_pair_n2_n3.py | 1132 ----------------- scripts/test_pram_batch_sim.py | 238 ---- 48 files changed, 170 insertions(+), 6438 deletions(-) rename ECAT_BEAT_TAIL_REPORT.html => docs/reports/ECAT_BEAT_TAIL_REPORT.html (100%) rename ECAT_RATE_TRADEOFF.html => docs/reports/ECAT_RATE_TRADEOFF.html (100%) rename ECAT_RATE_TRADEOFF.md => docs/reports/ECAT_RATE_TRADEOFF.md (100%) rename GD32_OTA_FLOW_COMPARE.html => docs/reports/GD32_OTA_FLOW_COMPARE.html (100%) rename REPORT_ecat_skip_opt_20260917.md => docs/reports/REPORT_ecat_skip_opt_20260917.md (100%) rename {lab => docs/reports}/ecat_integral_report.html (100%) delete mode 100644 lab/_last_gd32_sha.txt delete mode 100644 lab/ecat_calib.json delete mode 100644 lab/ecat_rate_sweep.png delete mode 100644 lab/ecat_rate_sweep_nocan.png delete mode 100644 lab/ecat_rate_sweep_nocan_vs.png delete mode 100644 scripts/_watch_conn.py delete mode 100644 scripts/anal_ecat_jumps.py delete mode 100644 scripts/build_ecat_report_html.py delete mode 100644 scripts/build_integral_report_html.py delete mode 100644 scripts/calibrate_ecat_metrics.py delete mode 100644 scripts/can_bridge_reconcile.py delete mode 100644 scripts/cpu_minimize_probe.ps1 delete mode 100644 scripts/cpu_sample.ps1 delete mode 100644 scripts/ecat_sweep_common.py delete mode 100644 scripts/flash_gd32.py delete mode 100644 scripts/plot_ecat_nocan_vs_can.py delete mode 100644 scripts/plot_ecat_sweep.py delete mode 100644 scripts/rec_ecat_slots.py delete mode 100644 scripts/repro_cmdload.py delete mode 100644 scripts/skip_bind_experiment.py delete mode 100644 scripts/skip_bind_noip.py delete mode 100644 scripts/skip_fe_ab.py delete mode 100644 scripts/skip_host_snapshot.py delete mode 100644 scripts/skip_itr200_detail.py delete mode 100644 scripts/skip_itr_4k.py delete mode 100644 scripts/skip_multi_round.py delete mode 100644 scripts/skip_nic_experiment.py delete mode 100644 scripts/skip_prio_ab.py delete mode 100644 scripts/skip_prio_experiment.py delete mode 100644 scripts/skip_quiet_periods.py delete mode 100644 scripts/skip_rate_meter.py delete mode 100644 scripts/skip_restart_native.py delete mode 100644 scripts/sweep_ecat_rate.py delete mode 100644 scripts/test_ecat_dir_worktree.mjs delete mode 100644 scripts/test_motor_pair_n2_n3.py delete mode 100644 scripts/test_pram_batch_sim.py diff --git a/README.md b/README.md index ab3e8bb..ce259bb 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,29 @@ tabs.html 标签外壳 = 工具入口(GET / 会 302 到这里) pages/ 主功能页,各自引用 ../css 与 ../js lab/ 自包含实验页,零外部依赖,删文件即可移除 css/ js/ 共享样式与 ES module -scripts/ 构建辅助脚本 +scripts/ 正式自测/测量脚本(清单见下;一次性探针不入库) +docs/compose/spec/ compose 功能规格 +docs/reports/ 历史调研报告(原根目录 ECAT_*/REPORT_*) esp32_server.py 本地页面服务 + 桌面窗口外壳 esp32_app.vbs esp32_web.vbs serve.bat 三个启动入口 ``` +## scripts/ 正式清单(仅保留这些) + +| 脚本 | 用途 | +|------|------| +| `build_mem.bat` | Git Bash 下构建 ESP32_COMM | +| `test_motor_transport.mjs` | 双链路传输契约 | +| `test_motor_refresh.mjs` | 刷新速度每链路一份(纯逻辑) | +| `test_ecat_autostart.mjs` | 自动接入链条(stub) | +| `test_app_lease.py` / `test_app_lease_e2e.mjs` | 关窗停总线 | +| `test_esf_pack.py` | PDO 快发 ESF1 打包 | +| `check_esf_latency.py` / `check_latency_breakdown.py` / `check_e2e_click_motion.py` | 快发与点击延迟测量 | +| `browser_check_{refresh,tick,floor,period}.mjs` | 浏览器侧 DOM/节拍实测 | +| `probe_ecat_slot_atomicity.py` / `probe_ecat_vs_wifi.py` | 可复用采样器(gitignore 白名单) | + +现场 `_tmp_*` / `skip_*` / `anal_*` 等一次性脚本**不入库**;历史报告在 `docs/reports/`。 + ## 页面 `tabs.html` 是单窗口多标签外壳,用**多 iframe 常驻保活**承载下列功能页:每个标签一个懒创建 iframe,切换只改可见性、不销毁文档(否则页内 WebSocket/轮询会随导航全灭)。激活标签记忆在 @@ -51,7 +69,12 @@ esp32_app.vbs esp32_web.vbs serve.bat 三个启动入口 ## 目录约定 - `css/` —— `base`(变量/基础)、`layout`、`components`、`can`、`motor`、`responsive`。 -- `js/common/` —— 跨页公用:`base-url`(设备 IP)、`connection`、`polling`、`log`、`toast`、`utils`、`heap-low-history`。 + **所有权**:`.btn` 共享 chrome 在 `layout.css`;`components.css` 管跨页 `.toast` + (居中单条);`motor.css` 的 `.btn` / `.toast-container` 是电机页主题覆盖与 + 堆叠 Toast,**不要**再往 layout/components 抄一份电机专用规则。 +- `js/common/` —— 跨页公用:`base-url`(设备 IP)、`connection`、`polling`、`log`、 + `toast`(居中)、`toast-stack`(容器堆叠,电机页)、`ecat-bus`(ECAT 接入策略单点)、 + `device-link`、`utils`、`heap-low-history`。 - `js/ota/`、`js/ble/`、`js/can/`、`js/motor/` —— 按功能域拆分的 ES module,跨域引用只走 `js/common/`。 - 子系统配色:ESP32 = 青绿 · GD32 = 琥珀 · BLE = 蓝 · CAN = 绿 · 内存/调试 = 紫。 diff --git a/css/components.css b/css/components.css index 7aed718..5f86a1b 100644 --- a/css/components.css +++ b/css/components.css @@ -613,7 +613,9 @@ } /* ================= Toast ================= */ +/* 跨页居中 Toast(#toast / #toastIco / #toastMsg)。电机页堆叠 Toast 见 motor.css .toast-container */ .toast { + position: fixed; top: 45%; left: 50%; transform: translateX(-50%) translateY(30px); z-index: 9999; diff --git a/css/layout.css b/css/layout.css index 70fffa2..23ae82d 100644 --- a/css/layout.css +++ b/css/layout.css @@ -196,7 +196,9 @@ select option { background: var(--bg-panel); color: var(--text); } .input-group .input { flex: 1; } /* ================= 按钮 ================= */ +/* 共享 .btn chrome(OTA/CAN 等页)。电机页主题按钮在 motor.css,勿互相复制。 */ .btn { + display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 9px 17px; border: none; border-radius: var(--radius-sm); font-family: var(--font-display); diff --git a/css/motor.css b/css/motor.css index fd0febb..3751a71 100644 --- a/css/motor.css +++ b/css/motor.css @@ -109,7 +109,9 @@ label { display: block; } border-color: var(--border-strong); } .toolbar-right a.btn:hover { color: var(--primary); border-color: color-mix(in srgb, var(--seed-primary) 40%, transparent); } +/* 电机页主题 .btn —— 与 layout.css 共享 chrome 刻意分离(页面级覆盖),勿抄进 components */ .btn { + display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; border-radius: var(--radius-sm); font-size: 0.85rem; font-weight: 550; letter-spacing: 0.02em; @@ -654,7 +656,9 @@ label { display: block; } .sb-last-send { margin-left: auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; color: var(--fg-dim); } /* ═══════════════════════ TOAST & MODAL ═══════════════════════ */ +/* 电机页堆叠 Toast 容器 —— 与 components.css 居中 Toast 是两套 UI,勿合并。 */ .toast-container { + position: fixed; bottom: 60px; left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; gap: var(--sp-2); z-index: 1000; pointer-events: none; diff --git a/ECAT_BEAT_TAIL_REPORT.html b/docs/reports/ECAT_BEAT_TAIL_REPORT.html similarity index 100% rename from ECAT_BEAT_TAIL_REPORT.html rename to docs/reports/ECAT_BEAT_TAIL_REPORT.html diff --git a/ECAT_RATE_TRADEOFF.html b/docs/reports/ECAT_RATE_TRADEOFF.html similarity index 100% rename from ECAT_RATE_TRADEOFF.html rename to docs/reports/ECAT_RATE_TRADEOFF.html diff --git a/ECAT_RATE_TRADEOFF.md b/docs/reports/ECAT_RATE_TRADEOFF.md similarity index 100% rename from ECAT_RATE_TRADEOFF.md rename to docs/reports/ECAT_RATE_TRADEOFF.md diff --git a/GD32_OTA_FLOW_COMPARE.html b/docs/reports/GD32_OTA_FLOW_COMPARE.html similarity index 100% rename from GD32_OTA_FLOW_COMPARE.html rename to docs/reports/GD32_OTA_FLOW_COMPARE.html diff --git a/REPORT_ecat_skip_opt_20260917.md b/docs/reports/REPORT_ecat_skip_opt_20260917.md similarity index 100% rename from REPORT_ecat_skip_opt_20260917.md rename to docs/reports/REPORT_ecat_skip_opt_20260917.md diff --git a/lab/ecat_integral_report.html b/docs/reports/ecat_integral_report.html similarity index 100% rename from lab/ecat_integral_report.html rename to docs/reports/ecat_integral_report.html diff --git a/lab/_last_gd32_sha.txt b/lab/_last_gd32_sha.txt deleted file mode 100644 index 4b342f8..0000000 --- a/lab/_last_gd32_sha.txt +++ /dev/null @@ -1 +0,0 @@ -c6827f84e9b8fa07288d0fc2081d0efc48a6ef2c5b8124b85a406f3a7a02042a \ No newline at end of file diff --git a/lab/ecat_calib.json b/lab/ecat_calib.json deleted file mode 100644 index 8135ea0..0000000 --- a/lab/ecat_calib.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "url": "http://127.0.0.1:8090", - "started_at": "2026-09-08 02:45:39", - "seconds": 20.0, - "sample_hz": 10.0, - "logical_cpus": 28, - "native_pid": 29284, - "native_pid_error": null, - "firmware": { - "ledger": "201832b5a8e258d6eec8d7f6620bab2c77d519e7321b25b80f7690ab2e3364ce", - "ledger_path": "G:\\Robot_v2\\DCU\\tools\\ESP32\\lab\\_last_gd32_sha.txt", - "match": null, - "candidates": [ - { - "path": "G:\\Robot_v2\\DCU\\DCU_APP\\out\\GD32E508_DCU_APP\\GD32E508VE\\Release\\GD32E508_DCU_APP.hex", - "sha256": "63d6ab9be96512cc", - "mtime": 1788805893 - }, - { - "path": "G:\\Robot_v2\\DCU\\DCU_APP\\out\\GD32E508_DCU_APP\\GD32E508VE\\Debug\\GD32E508_DCU_APP.hex", - "sha256": "e6a6e75fc5f746fc", - "mtime": 1788605679 - } - ], - "method": "hex_to_flat + sha256" - }, - "cadence": { - "n": 512, - "period_ms": 1, - "count_hist": { - "1": 2, - "2": 509, - "3": 1 - }, - "count_max": 3, - "lost_hist": { - "0": 512 - }, - "lost_max": 0, - "pending_hist": { - "0": 512 - }, - "pending_max": 0, - "backlog_hist": { - "0": 512 - }, - "backlog_max": 0, - "wkc_hist": { - "3": 512 - }, - "wkc_max": 3, - "span_ms": 511.0, - "lambda_per_s": 3998.0, - "gseq_per_s": 3998.0 - }, - "loop_start": { - "how": "已在跑", - "ok": true - }, - "period_ms_set": 1.0, - "pcore_mask": 16, - "psutil": true, - "dur_s": 20.08, - "iters": 200, - "errors": 0, - "drop_untrusted_samples": 0, - "period_ms_effective": 1.0, - "wkc": [ - 3, - 3 - ], - "wr_end": 0, - "native_pid_end": 29284, - "pid_stable": true, - "layout_seen": { - "win": 200 - }, - "rx_batch_count_hist_http": { - "2": 200 - }, - "window_coverage": { - "per_id": { - "131329": { - "n_obs": 584, - "span": 79949, - "coverage_pct": 0.73 - }, - "196865": { - "n_obs": 416, - "span": 79945, - "coverage_pct": 0.52 - } - }, - "ids": 2, - "note": "覆盖率仅说明观测密度,不可当丢帧率引用" - }, - "can_lambda_per_s": 3992.8, - "drop_per_s": 3992.8, - "drop_pct_of_lambda": 100.0, - "achieved_hz_p50": 998.2, - "achieve_pct": 99.8, - "skip_delta": 40, - "skip_per_s": 1.99, - "skip_total_cum": 449, - "rx_torn_delta": 0, - "rx_torn_capped": false, - "pdo_reentry_delta": 0, - "pdo_reentry_capped": false, - "cyc_us_total": { - "p50": 125.0, - "p90": 137.1, - "max": 2119.2 - }, - "cyc_us_late_lost_sample": { - "p50": 125.6, - "p90": 138.4, - "max": 1363.0 - }, - "gd32_load_pct_p50": 44.3, - "host_cpu_1core_p50": 100.8, - "host_cpu_total_p50": 3.6, - "per_cycle_cap_observed": 3, - "capacity_model": { - "lambda_per_s": 3992.8, - "per_cycle_cap": 3, - "drain_per_s_at_current_T": 3000.0, - "knee_ms_if_linear": 0.751, - "note": "cap 取自 cadence 环实测 count 峰值;拐点以扫描实测为准,此值仅供事后对照" - }, - "power": { - "drop_rx_queue": true, - "skip_total": true, - "achieve_deficit": false, - "rx_torn": false, - "pdo_reentry": false, - "cadence_lost_or_pending": false, - "cyc_us_late_lost": false - } -} \ No newline at end of file diff --git a/lab/ecat_rate_sweep.png b/lab/ecat_rate_sweep.png deleted file mode 100644 index 6cb4d328a6223799ee087f93929c1e246d2ea810..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 205525 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
B_(`gM}igors_B%h&-mW)315&)QhIA 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�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%0A2xdb>k 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 diff --git a/lab/ecat_rate_sweep_nocan.png b/lab/ecat_rate_sweep_nocan.png deleted file mode 100644 index 88fec1fa0d91427d0dd8309bbd169001d9e82aaf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196659 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

-qyf8Jg8(jEE_{en>&~m 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

sZy3pZ7J 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

    xesxp*PwnUugjb`#WD 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 diff --git a/lab/ecat_rate_sweep_nocan_vs.png b/lab/ecat_rate_sweep_nocan_vs.png deleted file mode 100644 index d27ebf79ab881894f21908552b0e8b387653075e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94777 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

    MQirwBwWT(b{@sL4;t8=lM6kLOfaI(CuTS9i5x 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

    8)C}Cfnp6A#|`$OIj-1`i1!OHEE#I+*f+c zAUo{u-@Y9@m|sHCn^`r{RmtG}aO-emKNR0V7ce9Vuaaah)aAr+2piGR6vOUeT2qG- z0{>d<{xSc$V`TdEyTd&}*YnL0-FO<`ey-x;sUyNwKB^7Bhsnz5=0l^I#R;BKTJCLWxcEL0#TS#{X8GLYpZ zJ8Mmt(N-HqY#jG5Yu_vLbhqTOibc^r z=^A)qW)%bYH%Z3^UVVZBSPg_MAmX%nR#lu(z50t`86iy>BxcqA;Y+lLbpl28evXDh zo6*aY%x+R4^|3(m0jm?QXqCbwPXU`RiQMbj2Tm=ESzEktY%}QH84Dp=bHB+kqTcMC|>Sk;@u0J=L!b(uOo)oo;HEsik_2=1bW-eRym1M${wP=eE#*eYqu6qPHdD-cm=oC#E`ONRA#21e~ApO+QeX5fgg@~ zs9SB2ug$gh+TD3uS7C{-wY@l(z{;{|xvv3f_*E%MP{#{0Qpc(u0VP_k)w zgGz^s?IE#t=8GM8-d1zO01$!Erc`)}QKK3bN#1MK+1(G%nynDfKy<%@Rb6wyVm0=Y zSknIT1g{jnWi_kTZbcK3<{xkNp6({|x+4%Db;}BAsH#p)bYz~QIZcLqW-ZgAov~R# zp|^T)=%ucgL|D|nbe;PR2 z2*FTmKHJg=$uacsfT(s zA#`CUqayu(?M^E3MVCqc0fYK+c`*)I(Hed#DkK#HgNIDU4cL{@$WH z94~Ts3swE1#;WSDXa(Vs92PB}FA%}{*B_w`5FBeDz*!>_s zZG`~!6Rp*q4iG}_eX4bQm-;n-n75=Vdb7Wt@n|)#qJy!$^gah>#FEj8pb?E_Y`jvz&efJ1 ze_v1olt0m*_xyESy7P*-925OVt4=YgdoKtyRF5_~iJr_CsvtUk+}iG6#iT`-StMBU zkqwWLE{vf^R&PhX`s>Z*Uj-o7D4SaIz3?zpf3hcKt)gl)53Vj5iq1k2BafIJ&OPev z{>(Xtqv-5F_JhSp!omLxAexG6^0Y(zXT2CA-uz|A7Nx0CY^UAT^}G_ACkLw# z+!bxrdV@`;;<01m2r1n|n_Vu;N48&QpTOg~cTRxX1K0@FhdjXHVJwZHcKa9|hOaI{ zO8oDeBbKhp-W1HE4IA=?&K~bA$(@rI zlA9JbgLK%&IG(ol1@~a(zt?v-Bv03dO~(s+F<`*!kIb%gMFq6Y0Gmq6vd<7lhz%2o zE=2uFN-$?QMzUDAe%cp6w37=jFVOL}H$RnRihA+awm|bf)V9DRB^bR$E> zp}Thkz=Hf|1hR5GILvBxY9Z)#?wC90ikl4RgoA{s^sf7|&Sd(i*KS(8mKu2 z&WI|{wQckuC7gBhNSrnDzvs4MdhlQYYPM4rYR3b_y+}a8r~aZ86#vv^49%kvYDc{0 zgR{9!&yL#*;*az5K@_kxQVT;B!@{v+ntX8iN!)G4i2oiK5v&U3X2tQAX4p@1n%zj_ zl%+nLiy_!*pcJw8NVQNUR~lQ8o;!g1sZ@(ptJS`Ns`ipaYum34AOJ&)?WympZ;$^v z9-}PU^K7(URZwu&nxXYY7X(S%H*Eo6n8ER@@$8)HFxqqX&o!admM_~)!6*ex724du z(0?1qkdmZk0NRTb3fM%;z<6^a@z=76|9V^rz$1qJ!J49t zmepZ<`!o+PYZtC}fLf5f%nr5*X>Xrnv|6ZH)=-eJ9V)!Fx2k|CDQT0~!hP=;E{pHS zv=l_buV+8}>yi~>15l5;)(q-V58Kp{=sw^Z)C3tY1QH+tGVeXY$6KoyZPy9`Z*f!p z2p-4zk2+|F`D#`iZ$#@~qs%|I_@}>yV}AripZvJ4x;d{41wIzW`TnHEs-gj!HN5Z~ zM>#4g$>oL}uVR?aaRXsUYDvO>DZ<9Mdy3ek?oyPT-4y4#_> zF&98*f+e9*-J{phDjML3-Au0$5)zESwY2Mth~@)z&moUL7A1Ux@cYPkox=utFK^lG zdD)S)5^ayJfdMYQOlfdI!M_3w=?ACej_q>x_$MtuV1;Z|&l)$NNg_0+N$2okw$w@^ ze|t(UL~S?=fIV=2Y^l*@0DXNLi8?QZ=33#za)|!&^FQJW>(CJQ=I3k9FYuwOm8R6z zWuqr^G=cyq2zFa;4HZa3dQAT;=a`QV$9+!)vFNY?a}_=o51*b>5s{3Pa{BroUnNCH zg)M3z)bVZvNd}4s%nPqoM4)+=#>Y1#J`_pRxT^qP<`Z4`pwwIV2qk>7_5GiCbn+bx zJH9ThK90FNHiOIl5tl`7e-RXzUy)yZg`^$qK zLZ(x1;_Iwr`ZhZ9X>hSAHTk9iiz=jYf4{?TVHzhmn`n{aN3dtc!4!o$bd|E!nPFf- z-lO<8q%h{gKXP(YrvgX+{E|ZqE{SMDA?Z6I$D-VD5R&rBtCdULOuY5~hNMns9Jca~ zHE98g5Lp8epz zcAV|UJ2Q_12|4`t`z!zMwwYOt+}omg{av>qNCsO|AA98~Vm*YH+f&h{I1Ps5l3xOtO`Gowm zwlmPFv$5#%kTw+A?YDMWcZb!bfZXq>%p62ii{`E}i)z^Ipg!gQdZ|A7pSfzd4bx;4 z1Tup<3`<5l7Dnvhf~}e)3??L%j>T~%Ev-(7ZY5pp(l}?BrcRv>TH9-Zm)B}gmWOrX zj$-0}4oG3SFEdD4aQN%ohokN`p<$*!V zz}vdU&`wozDrh*<-?=vr*k`p!C`Aj7(dA`hCDhKG+!oGeN}T9gP%e3k@3wAjRV?HKFym?5-!yqUQ1O?xOV5|V)r@g>6>&x|_7VVB5!xAX$Oqz? z16c~pIQ;-RtHa&(^Z^KNiwE2hR5UwGv&5u*8So`a3v2gtq|qAxcNDUyb_Y|SXHo+2 zna4-)m~=elEHqQ#{Q6?xzGF)XRVViKS*8f$ zT$L|kK)MT2f0kev3SyI#v|RwwvK&{g0=bjUBw^6u zHAj0Lx#faej`FzlzxyE17CJ?o^(L1=(ckFKIEeoKEplHBpteRu^;7=*Nq(>@B!a^i z9-7evr}-M3M+00r9fT`y%SYm`XA@bdd<l=F&g@g_-5#Q7U@d*2U#NoKut2@Ka(|nlqJ|?j zp#2t8Fb#aWVq*T$q$J4V2UMT(jWXHolhgcNJ1-iZL)lPa{p38&U#m%$)wx>@k3gO{ zzRX+`+G2x!U~pN)ECGW@7o-OTHW|CdGoV={1PYX+C!8>J+Yt`P!6?>^0|wL1YHJkR z_1aERGU1syVM0-XDj-p7?@0=7tx{I}_>w$SPZfL`jv!3~6Eo*|*Z@76}U0Gy)nIEM?8A71@re&djjevVskt^WLL<7<$>AxGEe zn3$OEP9PH)iq+tpxvFzK%W~)D<0Uu^1WBq<8u} z3;glcNnwuy{E555yF_7qf6o)i7kwL{alA#iOExy1WmZbi8;k)Pk^Dv6o!8MrK+hN` zW;r=vni+?I6bKxM{z5xU9blfOOo)Ku-jDXjgu_-P?IX;@%$-17HTj(!GtiPu0eF{6 zV0eADt(Sm=V?>TB=qmi5Q*UaqFBtT?juG-YEzR0TZb}XqDKEJkPLD4S{~jN%B>Ab( zi7!#1soILgYl9wAjzs>IYmf${jyoPiGf8<)V8Nzl)!KrCUk;l0<;I*AB=L21#{IcGqa`WP`8qaK za-dFkY@*P&F_Q4GYGwky{9{xIhCtxia_=dA>dTe6;}a;Yq{=~w2?-7!5d^ZHA+T+L zUmjABq@zHMfz4-$$CZ^8XjiEYBmUn_*G_R7m;F=x<_p=y12sVArbUwb+}3#|_2auE}r8K=HXtf4iH_^CA^G5rB8m{u*HKfFs}liPTOV z9Pl%(Yf}O9rM=Sew}BxD`+%0}&@jq!e&9CIZ}9ITfU3_^l_fk??>h~gz9^M?RIs}* zKEQY{+u@3A^+8#XT~j@`p-DY=I;>(Rv2Ssje5PUSbSJUw@q+zKxoQER z2JK)9YS0j#ugcCamu+sg5bcl*h}WIk)TnBWMmOI51^jYR5*~`KzmZ-Dg7j3VZd8Jn zF1+>g1CHI^VnfLoI!B;Tr#^jB#mE5IMZ7>suxc?Uclj5MO)21!R=I{qgwsQBGIufj z_US7+-R75jHSz#f6yxofm+x@^r+g1eY&)ZQ{hsmi$sA{y z!gZ4&PVf9+&9Pc>(JBXUNT$EmiGs%me*0hb^nt_Me^dpN>La{$-a+K>>?;FaCpDmi z0-&mLWm5XU=19=kUr<^y7eH@--CBZaL;a6{@nl23n#~#tO7WO6Au}8TFiA_U6u9Go$oRhCv)V@)uX^ctwVzD>fq%k=%4%WmPcvWIrGgrVcg_``@l6nh1uekA`@|qTH zGg|OvRJgPZ-i_9PUeD*ye`CnVXrP^n$#@n&uzI8V_@Bs9k^KgTy53&6ur=nc9D2&f z2av~s9|3Gh4GbvPX9A+_5)ctZ!gTf<=sj$pRCTDP1ghiwo2s5pHam?pUY+Lodwr&G ztdbmdKiON>vXFuzuS*A@F(Oz|Zoi26#^oDw3*#s#{0e%_0_aBSqfHKWO7h$m1%GmS z7nCYoHyz5`>)vqy!9I@N*G;nyKCIfhbB~EaR88=ouTW1*9zOg~%I>m-D!+F`VA%0q?ETyou6XG&aRTvad=z zV<6EjDeV-qIQy?b{Rp~2fo%o(knyKvP#LbFC?kHYYF)qsWd31*Yw^IC(wN7}3~W$E zcPI#F9jVlv8|nchW2k3o)E?L6wCMbM3hA#2;vvvsS%L+v?~QoS@aSi>>r&R72%@a5 zoJoGeYj`cQJ?|=p{G012Ha&9vgUrl06xm)mpNAJbIln7(g{6(*SQOYtF6KVO&Lji<~UmX;0wGFt+!LY;^B`mOd6Mn6{mp9FLcq^bNf?+aw;k zO6jlm3nBK=-{#(RSDIs82y7vyOcq4=wX{9nxL^JkXI-GP+EdlD=Bu?d`$Oux&P#-T zQ<^aHqxV-f_Iub0nL14z4p2;ttm=}`Z+=POrOC4#Xtr%eY<$D<5Td9nAEoKXJ~|&a zkiU0+Z6fs!Q8>pK@F1Z-t;v*g*M`f(ZVs|1%!f6lni%nE7KI+fC zafMET99FDkcx%q>rC}8PJX3HeM+&!TtF)PdxLA_$EdSc@u!~0tbMX*9)%Nh@Qt6Lr z(cyZvpJ)%Uv9MFwYx0PgYj8EO{*T7}Wwz=M=?C|m@r0hsVrBs(pI$)wQ~@3_Z_DV3 zp}a+EyfX6JE8^w5uR;VZw#{{>od$TjmKwvDyxl+FJ^8;x>MUGKj-P>|}xF2+Vaxg;%Mb8Yrrn{ZLhg(mh;@Ypm z+LzV_>(VjjTHl^oxGVd1a?Cx&dMx1{>IRt2vDUwRp&Gq?-+sBnV28wd{IZSQ@;OmP zwxYnr>;Jwu9mb_*@>|3jbuc1Rw{^M{@r#=)?6{VT<2)CnLo+nPp{o3XiG9bt#?5v< zB=zu@=s85Fjrz@(D>afG5)aK0OcbVV%?(Nz(y7d}n2^zeZG|c9B>vI#fRw5}dA}$z zgx3Mfd|%Q{FmK$G;7<>~QB#w7#mmUX2~7tY_37d_s;`m%`#{55 zJfABJGl~7hmZGW?Xm^NISTB&=JOd2x!qyUjAK8u%WO{f*A%{!A1-u8w*^KExR7s2? z)AOi%Lg_E2*LkBD(#q6PoVPmj1wS~(*9_rnU^mwqW(v$Q<}296TjMczri-k0&TLH!*69<)Of=ZG(R{lCrNor?!vlp1o_aZaH|C zN^znEL&Fprp-ay$)z*XPJaza1-<^vY;vsrE)fY%Vwcx3f{Yl6ldkO;Q({PXF+i-X{ zjz7_LcNNXOJMTYTB=jeEln?2kRQ;^rQ-UZE94GyEfh?k9;zKs`VZaCPqv2CgHBF$4Ods!B0#{3?AMCN>lLzwfwhDofhqy?-U6oZslUc63@${P`J2W`B%rbD`3` zm0e&dshXIGq_dddSuU?F9{W#`BLxbVF1-}7V*-AGU~z2?dYtIQrFz&>nEtWNymk5N zaV`7WOcA1>5yS6lPYkw%HYfP9n=j-!hNLC-7!t#g~$U+FF0=Fa8MrJh`=L(|dLG7ve>u9?1#Oz0}8s|Gt?Q*ru?G9RGp5ciiml(}E{S!dI`+ zgiNG}rfmM1b}ekoQ)PR$J8}N-VEjZvTAJ|i=%^cHUbl!*7X5^jA>x-LAiei>pg!}W zaBv#PXnzOlCTD#c!2QbsjArgTj+zbD4J-8blF6%`mI%B=tNUu&I=6SgW5zSTF6uHJ ztm*HVwhdBMPC7HsjNMvW9eQVq3RZvYuwF(%P`t91K9#APUfTSu4s$-JTgnCytO6Y0z&}XIkx-=?oekf!F)LmSFp7sqPS0a;1Dr^#leVWvrNvzP4a_1B z(xMqL^&XrkBs4Giyq<1ezxRB!+7+DsD#<`iFDCbzRBrzi7#8pou;>+fIdrN6MGhy) zkgPdCYu^RvPLk8mzZ0v`{IgbVw&3s$(GLHemt>;aB;hd<_*Lo7Nv`Av=c7 zKG{R~25N@tXSLTQoXu5do1TkpNmjf}egFPK#d+)QRPiFLRbW^c$k>IAxNcm#ra0sc zyC4aG)AgVTaaY$iu#o(y;-8xr68Pr2W39pS89hnRX_Ik3>hj_^!~Uz{4b90$y2P=S z{kP55Wti5lZ{NN(fX`8KXx(p&1)%yAK#la<0S`vT166W?fYHSQtqLD7u4)=++eH-q z{$VM^#I#{;PQw>vUZXqT5xBeRoxALkRgNOyY~2$z$8H067Tz`8_+Yea#1_AIiCn?> z{vWoX3WxbAhzwXYA-2H0Z^Or_AM-7XSF_SFn|5(HAI4XGa(qw@{;;`?Kw+eq%6IE0 z-;wqdZJ6lW^UE6HOCIq&zSdJ07Mj8XY(CvRH1C6oD-A85?%AbEMPb3j2IHR*muYle zXDO`4Yw5sT4l(#u28M7@Al3&81(yj4Bi}{1)Sz9~4W5Dt20WfFz;*c1{{G!8;9T%V z_mgR7K5{z6^0C9Ri>uSq=w(?}gr?Z!K=LK@cij~$5{9VA%nyOA+F!g&o*u3>x&RS= zyX|F-r(1nWcP@#&1l+9kd65&1S~E8w^k<}UdO|3`Bj+8Njs|t@j=P&|?d$2JfU5P* z*@$p5pVdmD_LCiv(!+IvCmy@{m&D$K8*Y}*s`n?MDFVX4w`v0!B?<>IgD<0QGc$|U zW^5>je*W_1fo^6HV%*Ei%WS+>_!*G(F|b6ej67hAC%z;)2?t8&KN@@q_Ig>EHWVwU zIHxvns}p^6=k?`YJ?I_G@jnPm)dr zS7_D{HBMJ%HCBIOHB|K2n;m)*1(oI+k6Y1EZ2CHA+KTy35HyB;c>fH+;Z|T7_JHw)?lhv+ClOIEHFZw4pP2X&sV)geO<`!+_J$4~AfSVTrXwU)H1YW^rZO&Z8~b{qUGMwFng5v&gNIVoJXI0K(@lxZJ zBy;8b-%bv>CmtUARKISg-JUV9g_9p;(-?BFTf9{MFGkFbbFty)x) z$o--(BAs>HhnZ3t9HFiO8N^(<|(?sohtH%Gj^mAu_~LH zB1cz%P+q~IR_T+goCe1mF?Enoit&-^sUM+;jQQjXl3pk&x2X%tM_CU(I6O+B^WuAH zU^qFLPUH+%`~tdm-ao(~lVaP+m$yo*0mZ}*Ki`oo5+22-*2b3aR>Z-+7nfs3Qt!tp)L(vq@5P-Fc{uB8gRKcf#Y6x5Z(u;K))(>P^qV(7O3iz%_dRQOr--V7B8~Ox_4o^gh4LUq&6zHDxq>#v=)tN~ ziJKj&B>HQG4>R8QgW(glS;|}?P8Tf+h~J{!ooDjK-zMmKN9cdbqE_6&6K!>+6cG^o z*>Srg{ls|M&&ky8;T@1>2#Qc7(OvU9X5VUlQ}T7UMoi3@Iq8#{s}zfWQLqj!H7db{ zybnx5`F;SJ0Rce7=&SSUm|s~CA=CQz(^rtiDgfvGcC>cB?e+A>3d@)#r`E5GW|E@&O5!N|X;MeQ zrm6>6L1qJaw}9SSIGE_t%KO~}L zJPKeK?Nn91f0ciYbit(Kz3yPa9W|+taU9vRw<7{2`j5ZNjGxoh)lE8-_8Kd-KM}7+ zZr^Ks0DqZ~YSW*|cOsUt?c`@Y&gh>Z#=29#=QXlArslAVubnSr3PYcy~h=#xH zd9=S&Xb9R?_VT$y`FH)%N&I#%(b^D2iYbQ>+ay<>7?QC5I%~23#Ts(p8^A|IXZuf?RSba@;fIrwQ~`MUaGV+ z)Q=Uo9!JUE>Y?fB;jvp)9jQ^`us+httnNASvKXf#eUYA_=h6w^*Yv~Kz%1nFdkdTn zQY?X^`e0sC6gbXbu&@R`10t_)68Omvc2=7}N1qkexrN73odEOgD%7pp@SuSnm8mS@ z)-6!UApoH!l8koKC0f}{7%G1;2$6$4)`b^Sap2UXkw)x4Kv(%b!A& zjBkax}il?Wiw=Bi+bAXrWl3qG=*)V~RLBO=5 zTsw{M%8TaB-1_lP1-~QI*e1#Ktm~u#G34;g& zN9~zzKr=9BLvF91`wgaKPys$!TC31Y==M_&i$EGc4#Idn&|SMEo^)ZvK7`ocCxG}} zWE9D)0VwmFP3!(sSvgBhA>eVBcmtI!O3nv_R;0+ z6Q@Px3WoyFh6eI>RBNDlS`K%54!LG!%COQW>L95Su)bv_8SbW)Y0-6N-){PAGXINO z^Ov(kQ|M$n6_R|B^-l8q2=}F(6na>mX2S<%{|B@yxW(MXo~iplUGE*7DGO z^Z7yo_A)j9)nKN4vFtf*q|tQuor9S_*{SFHS#Obsu69)5#bz$v?goubE{|Q?o1qc{ z-&BZE*EwCpU=_f*=z+l81t_!55~V?H$#Ni!ku%o}h2hP)jlp1CpPmji!P5Q~{Wwd6 z@0Wuuzh}IZ4bCHHFA}iFR9Tqq(GTXOaH|Qck%;qM{?Ru^9^1uCD=IP+%w~?>@D$~| z!`tQwba9cr-@$MSU!d`3yESTa*X75ZP8CTQgU^_DEHgWH#X$e(#l4Z$?)*1KH90^)>jw2UB#y zi;-1RAbe;>&;8D;F!02JuA~G#yzt1$ zk3+Q6Vc*AfpUppNOE~K!cEOt#C66dLUsxy{T!W2NLR>2^z46l3$=p$_0QFw=M_q5`ldAvk=s34hW&*RQ(+?T{w<~?!SSz+e{SdrR8{9Dp=-NuvWYRROTStxXGpWJjzoS zWUDl$s+13NRc|{Td(7NrakEOZET5-;|K>yn3;^YzA^AoJ|o$CSv8^}nyCc&9$wmt_ga!k2RXMH?)W={vvi9? zkw(VtrM9x#Vd+UR1$LW8Gpl=L08(9^SC3>Ni4*X!K_4$|N8WoJkOaG#h=h=OVlKr(!J zVKiR!3+8uD7IkYfk0S?9?{sB!szsI@%P(glQ7K_d5;bzB-77>yjX=aS08o*iUwV>M z4Ms7m*Lnz)YUW!BgD2Puz_)4KobQb37j+d7upYdy`J1^hrQYjkw>mSL+ZY6lq9VU^ zYg5Em#{v}ZwdSPgDBSkoPD`i?6mnX8 z4;+P3hdvnSW4aEeXO9-iR$IG)sSd_Ve2-$l!i|s5JOxy zO*;L(n`roSl<>@~B@8emLrfl)&#HXS#lrc=liq=$Vi3N?7EHIEE6~y&;ZsYV=y!(hV=K4U%gLJTv3@G>yT8$|USK2l@~jMV`JK1^MH= z6t0Ey>O9H{1_K|n?q7>vpysh~Y&?Qabq7r$#Bv~rqUt+43%`T9VW36f(<(Z1kt3GI zlx1InQBdE!@SK5$sPTjQBn_o1zDdeBX32KSH(7RK940%y_F2`c+*RKQ^aogzG?{W3 zZYzMpVGW3TwrblN-!Y*Y1#XjnVKN-RuRt>5DRAWq7blGLPivIzY1=Hr(_O8<3s|3d z#S*ewMYJwaHVQxE706j(VfHPHdhzx!fCLHn!fmRG0iEvD#hCcWbG?J(^`%Bv5-3A(JaN2m zy;yFV%E`tQ`I6u=jy;6;<0P2tO>kKN&f<_H2Z563MEO*<=$)g>vcI5^#AO34=wpa^&fK)sD4K0Z46W7+xeVL6^Xxq z!&R39_{dGArf;}qmvtPodTp7DO0449!`Tkr-Rb)1HH8P3%e&yEKVi8h#`Dpop~QYx z`p3_oEn~w_plPjc%5}4=&f8m{-f3f|MLv5d9@uSQR)K?waYLnb4QOp`Zcly5(S+(9;2-tpw3w(d82-;FpU>}cztB> zML-8^ZC>2nCU*UPj{mdR9*UY&;#0Sey34}q^5}`zp^GcIqN1YhY%7I^ILk0A5EFd9 zJqMZKiJmQLd7OA#eVc}*LsrB^MFTx zpLGLSF#fjB)j>Z*WDybi`2OYnmQQ-+J370(=HIXDNG5xG9%F7*mrU-GIM7QvS_Jd` za+;E$W!jLQB@HULr(+hq^AnQsBkz|D+jLZ~J0Jypu*WPLV#Zw0`K{}+OUtv{Xuh``6Ne-#xKHU4_rBqTcE^7~_;*xjOP0q!=s|1qdW*LSB$ z8q+K;1<5bEDP_t=y;IR<@yQ2D#rj|-tNmEcIZ_yRghyR$Jxy44GaP`Ta=uC|2R$>+0ud_d1Fdn3tLxOnpnIx zRc#8~?)?tRb8HN-_qSJGrh+-B6bBwa{GQ)sjS(R9{41qO!`-1AM*M1c(7{fr2j2n0+DP6J2%QWp)V+gJ!=0WxH|zEZ=kB}~R+Y0f7-n|t z@a!V3AtKa8yXIqf8D2N&PLne7FlP?oAIYG0%`EszEq<(p|6fxnlGcR#VXhj+R*Kkg zd-np;&T!%0hXCut3t5ezR@?d;siI7BM}NJ5C4tYifZAg42WZquD3k2P9LZ$0wY|~6 zZvY%)bF?WC(iHGIIdX=^h8cT3>^6V|9~+<){yZZ&e~`QpXg#Ix@0%vMN_fGIjEpAX zOw0Q~GbQ5lXBOO$v60aPz=%gTgqZ2Gj{ucDvz~8P{K*(N>gvqC45BR6u&pu|9`Q_q zDY6k%c2A*98qz#>Tpm)Ow^Vrd?%giX-=4(-aOrob&{pRMJWlemVUu10A6YbN{5+xx z$Mf#|KC|OCEeJB&TO$QKOq?eemck5I3QcXwZ9w~HZF3Z!vOekI#I)h{Pse}p;LKcN zm0nng=rEZGBWLC=Y3oZ?3ZF~FZ$SaqXMfB0dw?7>rtx-;zpPF~iNBX%9FvH%WGU~h z{`BlT>c!z|D;3efkdP2j1cKK9G;If$$m!_VbG?K8#k}sch3x|oYsmHO6=yMl=>W+_ zLo8vh+NOa9il=_8QY08fr4NdZ>R=QC$xhIj*B9=CM6(ebHEU4aZfD$E1Q?M@`5aKW z_3O9z3G`m|%5dwTsAMqH9Kr9jc;no;bMi_*>a-I&=abxJlVEmUm&oX~tU~$gn6j=n z3;==0**Yjl=yXW=pzG~xx1wcqxE+tXQ$6pLckEMc=?hvf4@rBAOEb_1uvbQF+v@ucX$>JNO>Hxg=U?H$91q<&0)i z`RO+BYw=@{|<(Djy4RjpCmuz^aa zNFya6NQiWIDF{k~bc1v^NNtgl5CxG?zzu?QgOqfMfQWQTNlN247w36nykmTSk4MMe zYv1dhb6z#2;s>%72;S%Bnt?pJ#FSN3)L=3_HI;q*J1K@VgBww14{T&{f>_QB=EyI~ zWv@w|?#i-DINzD|iwumlR$^HsCMEpmpzhApT$q-YX8QCu)Sn|%^xfn?jSdJ&^b*p1c_xJ23F8onl36XC~7w9J>@@D)` zTCpdaK6Kl&3tCK-RM86c;3BS&KXs48Wr8awu|aGw5wSuln=~+zp#9lNfq|c<)_ZuM zOgw&>xG?|w2wzs0B|t(vb*yyKSqNJPNOzJZhcqyY4o5o72}*ra-k{dUZY%_RPS9;R z&abX5ip=-q=TPoLGf4gVQRSRHevHGGgbgbgayJifq_bcMyOB?B3%nEzq^zUgF^GIn zW2!(2u-o5c`8fl7ycKvtNh%2_c`;$bLyoA2l!oHzVSiv@{2VAf$pC%b`SyW3;E;6f z_HBoUQI1}Mxc982B?71u52@JlHcHJGW?z#=eEvJoW?FfSzV78>t^aFEfKu6FL5zTr z0nPp&uR8o}rW@(ab%75duh&kIzqoGE93S4H1u1vkVv*oeUNWhPD4Wswi0{6_uYahpvM&x?AGioA#`^= zm3jisNvLT)2rLq+RMa4cS-iJHMKye@o6CZ<{NBBxb;?H7w}d`Z)EES`JtH-Smit1C zXC+?$@cMhRq%Us{es@@=E-WfGV`SKV9>;p?mfQxlsPEr9cW!j!?MF?-ndY6-LYlGA zn1fml7o^5`CHh1X7zk#{d9ie}Nt;;!m5hIF*;OOqEVjHP}Wk*r?MdB_)MHADvwT zBQwQyl$Ptp!O_tyLcYUVN^`1uz{78Dd$rZw`lO}5-hN+Jfh+J32Vtrq$6U7Jvvm#0mDp2?pSB(Zz{}%$qo;g0dKt|WL7->j8EL_se!_a-zz^a$jE9;X$mqC zb4|7qj%h>Q3v5SVk(QCEUPbcS^C*Q}UE&&?v(xwzx5od_j^4g>DTC^+LAXrTTV+kH zVlUs}f#kW{I%P2A=DkPL6Jev!nW`q5`h4?!z!K^o_|?DXV_;(YbhxwB{dox%lE~QF zvYDEi!glSFW)BbTLW3q~!c6#6aT+bH&Qe`(X`UcN?E$PO*o2l+8dMmtS>e@;vihtq z@6(^x!2}jhDhFq*HR5JKf29RPyyNds$b*P)5`OOo*t5%hmV&ZSU;q~H; zE>go^_awzfwMKnGWCC8WVUKMIo(Vh^4ba~pX#A&;7W3^YRSaoFeuIDWq9$Pk*Q?Ph zu39q%nNhbi@Z7Dn)cCGzWv|~CKVG}%{qaE-&m!m#lE^%P=i%a#gB=Q8X6?b3OtZ(( zV1YMal&(OYnxXBR2?vE-)wlB}^v*;x!@UOeKBS1b%>46kv01yVkSasgQL_V2Q=K^$ zRMN}s!}*}_k(Z})L%`n`!QZADf5yh66|`b57x{T&04+dT`($m{`yeLS%gu@E0R5}7 z5y0#swiC2w9Uag2tfsH8U%&Q(;JW!2?^|1CF)%QU0WWlkFjLQ32q&klwd$u8%JT$S zs>`xHv{EoKLOTw6%F@#K3X{tZoSi#<*0;|ITR3t5FL}@h>s#{a$3Gl}0_3#to{Dc& z+p?z4hXt4=I6Vzbb}N6`lx9H~c6o>0MqpajBjhx7+lWi}sUs zx&hJFTl31UC-cJsp>RicmrLze;6a2OS*@ofS+|#d#(O?*G;V`o zeMUpsr+A#oj?XL5Mf=xCi4-Dxjn|Hl&0?uqrr^|LhJm3}%s_=XVNHn>oeHgzGi=-G z)FLyja!}7Ie`fuVK1RE&f>5j%Hi)>cK|hV(ODaNv;3u{T(B#1^1)zsbxsSopROvM! zCy7MeSQ7&RU=p0v;$vb9mt9a((v-=HPP1kasIh|uK!pmjnnOSoXp0_9KRW>UD59!L zaE$Gt207_dc2>4LS#E&#twdqPlGk!(1R=;pvfEW<=kZGLsU=r_c`F`oDSY`|=Nv2@ zJWVfSoFayEaiV6pmHrVQaiHVUKM#DzfIwWKnAv|~xG0+!l`L-ZiWzStNPx}5M#=hK z$sUXZEmp?}u_<@qO3hn}*|2Yb%EqFZr8A*=cfa-VKmO}pD_`eC!qrD9;Be$1tp_BLf#<0}(t zw6*S5L5F{J!)f_$2;j?~kY~>pPMDlKJ38W0hguDo)#aEr>;^I}0s+Ux4$h-I)9p0X z_l}cyS~3g7Hu67`1_U6`Gw4^1)sM9Jys;xOi0IzJf@&C2ZHu4yWf3d)XcE_6 zpg<&n>M9$d*MsAPIPpiyr&?aW{_Xs9`e~)~O{%C2kw2l2dMDx+k__PMuw%U{NY4)! zj#$#hbGHfqLYc-l|I~rMNRjZ=bj^CrJXJN!!-j#pO~ifYlQETX3Cpz6~4= zq(Q!xri;#<&jp97;W8C-?P7&HHsICYn#+7AcyVR1_fY1e%XSV!sRfFK%GzP`F zXLp~Zb~@#mk-xhMk42NLb`W!@S&rL(2alRL0r1^r>#;|w0zSNnlz4%Hd$KVTD9=FA zn12ppt}U)rz7D7_u8gMBOWlW$K{9oNczvw&8tj?+`m5SiJpmcbWEB+Vpigk&RQL0% zt2;$zDgS1`3|S>4ytm)i-FUiHNJdI}tB8b{*nGO}_F2hKI)z9GJ z%t2Z<08rh3nHU}Zgi(3Cy)k_ILDX%rmb-OkbIoY_^glJv8rxBkE1ctFrQCf+7rN1! zwC|{{?5@Djtl0;FwI7q-6E7Zo|G+{E|3n>+R*4Nu}rg$G9(3I*ktN+8_l z0OVR?)Lrs;^pniRx}S!93MRj7bhH#%ap<@oU{|~=ZGP-pdZC=S(+)t7eWdY!@Mn?2s-_Kg@J}Tv@GNoTmbD($y@ht*LoxwuyR+C3~`o;UA z=|=A46hbkkl~cq|zZ@sjW_|2y{2CJ-nP(Uwi!e!>pp={`3Or>$IkkhNZYe=Vwm0BE z=5VZq5zw4__uGB0&T1v-aSUb(rIS{^mOQpN+eiqpdx-e6acP8i!Fo6)QGl@nHGp;LyXe$phVc%p#T<4(2&RA&tdBd3498!j zD($XcyoiZ<8yV+og~-kUo95%<5YY~o$^UD_>QSrw@eBAEroqpUc$0)k9WP8)HJ2na zGZRsBZPf1@!ev;6XNx#`k8^DXbR z7IL0B!_WtBFi4l=NA-`1RSvpmPZfDBZ`6v3wgQ>G9=<_De~v9qW;N3f0s(AvumwhE z!!_|@D*Z&YiS z|2QbB0{+d;<|I|w+8&t?T74{UcC`^MK7YpPy zw{PI@ZA|DJI!@G(f<>6!V97~v6By9!hwsD(tbw{ZfuB=je}S2$rOr>Nr(63=z+p+d zI~aO^I-}vk7N`Gps#A>4?AEEehc)i-2#{Z2@|@l0k`L}x&}TmV3|nBmxDHxo9M z-se6Oke1a*mCo9lYwz~J=M#!mOcD35!9^Jc99qDpw12njPN5}E9)&G;iKEc|CW>`Q zfY?{}isnFo1Bkj5m6Vzgl0)X-C3u?XsBIUQxC(Ea9%#)D_O$n(CCd%RHA<9uPT_$8 z;=psgi>!jQfRcE99_E7K-Iw{{IeRf*Ftza1Z;=!+V%ISLdf@J%O~-&%Y5mjC1DI5r z$f&07dQ2=qS<4^!<+ZU*0C;N%;%zhE&2r6kT3j>kxbTT9u~+zTc#a1Lx9@&Iwpy3#h3PvdG#Lvhc~p{i+OdM_0FC$=fVwpt21Z zH|{GdH^bJunBdFTCb@@@*1O|vn(GpHr*IdO@~0I!8oG;F8&k-aS=yvUT$nVZ{M7a1 z(ezt6P0epnKRmY4?e0wh;^bUbcyby3kKx-56H@iSKjJ$6WHlU*yYFqp8l%Gh$nd>+ zaQ7!KPvxk1vtf-@}zjFwh@$TM$BC1GzNygEG>Id=(*0@ zy#)VRj2`E1W8M0nb8!|;jrszhe=v~&_1iRxBbU0CM1rRNj(*offvvUlzSD!>6V&T( zKHDM_67#%a1MZNvQB#@u?KGRVsd=spn<>~~Y$+pRPkz0j0ilaUP%2APYl^M;Q*AAl zo;Pa$#8lvI_5a}HfhE&fdbGr-mA5G=W$ZwLA-b*=pa{ZurEO$Fk|Zz)9x;|lOG;lY8z=A4~U-` zY>5|iUBuZ8VH0lfNQamz?t#EGD|xS?TT`?+&x<#|Di0^qsN z_GJ=Wad;rsWkl^#fY^d}MS>O#NVG9F{tN>-cgKg+ z)=OZMrx4@tjMMdov+9Ohl~qu83rQNsc><1A!euBL&sGBBi@4kv6`HwyZ=8UBbVKI7 zu<`M04bcj72|z5OCTJ(P2@&~Hj7iOTr+JhIH~v_u75kDR@-o>*GIHQ0^VuQqQ^i3x zJU>d_p#L$Ly(kzl+x?2pl!JTQe&74OrB@;43aR@i+m8NMrQvxVJ_||Ogp`!e0R(Ir z9VLS&s-UMw`GA>Z(trO6HApgG`7`==3ljrdCAhD2O@RBNwQrBDeUkm%mWam!+y@UM z9OvXk$K5X1wx9s4X>AA3=F!&TV;{bDoNQCVjKu=D*H^S=rt0GTo}JqsmsNj&M8{l2 z2oyRU-JA^x#c|TXUmE-uf)=KOFOMlSh5*UcYV@)+Tp3OI?eMruSLU&=fPx*o_%u$t z%jze9rZmCM+>80cY=MoDy>6i7ZiPA!3wqKyz$RMYt}z^nP?ptLjOJbZaNp!)oBanD znIn$Y7lAvt;jiyyzT>9t`XZTb;=&FkP2tsAdpgDt-aY0i$|(v)VS$C$&=%4s-RLjU zuf&Iqer=$|-)fgQBZB1bPX!SOTZiWLNWcQ$PLigKP_C-4!P3xstX&f34gpaR&Cfl| zxYYMtS4M=S*#2Rsrc8Y{ZpzPGp^c3WG(B3s^?_C(0^V!oCuz#H$Iv9u$AfH6;snCY z&B6WRF250m!$VHY;U!i+wYU0%D09D83LyaxJ|8_3)pwn~l&7WWIBOLdM#IweuKxXviSE}>f?3Q#*g98tSW-^Cy;Sgo3kLhp zzSnr*;yJ91R!%f2o%=AXKv=otS3I7CJ^k>R-dw!Yr|IlR+jjt~*^67QUP@26IEeU7 z0DDGgiLib5mG4+E27U!8!GG^0`t4~KX^M6su<_MMOMS@i$hn03`)hQVBjewJqR4yN z-+<3{=eG>-VX`Y4%NwQGaK8hG&zk zd))#KjFklr4dBYCV`2gpo`U&d`U^5cuZvh%SOUMe^w@hK&@XEWSre0|u}zI{esYv= zANvyOyD`_~NLElYcmZXu%E0&}=h z)6+;>DkAK?*CcS&2g9p7hiDjGTwN_J29a)jC_)LmTK8D4jqpv;yH6U#a(HP9 z)4&@m(@|!#X)_RH73a|PyQk%sQUC1C)mZw`cx}i{7U7TIi2y!aG(lE&@;dR8Y8N>z z+@vzb1(5BNp7Ng;l=ZichQ-8-q}?WsPJ?s8?rvqc;owa8!on14Renho8h60oa~=?I zoLtx?I#)rIUCs~h5V@VCTn0GvkEdCw&9GW4dUw}tdGF}_PFoe~u}n$669zr(bd}5S zI@We{WRS_ogXQ+?roW|fP_PyLd-oHedJ_&uD)fsKl+yUX0xky-6H&E;O2gr$ogN`7@9u{R46=cns z-5NP6(&gn#V}s3GJ>9Pux!Akcv9|vFBn=lTCGuN(o7SsnvAu(0_cHw*V4%V^=}`3~ z!Pt-I6P49NVj^!))%rL_X}N-!r5%)D3s14br2kF^K=N#{%@8w!FC{sjgEfy-O&O5l zZCL(o4qr!Vs6lMEm?V=*h?wnDvQ^O~)enGRCja56wp~uPzMQkzPxdXp1C%Nv-b50_LY!p1mU1I#c=zhMG~RtUbns} zvKl+LW7Ie4GzHSB&C1$w=QAdv_{s>`hhlrzVWXVDFVMz-`ua6)B(49hItA_PUP}Zx z-zWfZj{$8xbpk4x#F@C<%&rhH)cuTSw74(S7()Z{qb79bUIdrPOrPx9olMC2$V=9g zN$};f8YUd3Qc_X^*gH61yJ!|(PJsfFPnC1Cvl)Zd=tE3Yo_M#baD0OW&ZDJ1xseSq zTJ}$SbL~ysW>>$ft3wwuPxC3_rfwO50;i6nm%~9{YhP8V{~U6%7|>ti6wB0fN-gog zzw;RJ)q(qtjMG3878ILb860{j(MyXspfnsRhH_0nINDt~)kS-he&GK5McfY^YseEo zip3VTVA9KQIgteO;kfEhbf$i`%tAqCtuU`8e0UMiYew2LvP4F^fIzmU&g;$45mb!g zH?eV%D~whi{CB1b!<{EZ=hH-%JfgddB|F51h7ZuI-;|8&-%*T4nVx zn$p7CoP5t|{eEw54B%ZZdiAELEYN-{8Y0D>t?PBtmDM9W=r=|t>j6qA*ISm4_0YsC zf7(J~1kcw!g3EL;hG|U{xE?k8fMGRWD?D|vztARBf>GHYb{*+8p-P z*)~TrpKeJuTC*l$ryBx}eS?@(&~qcrJ{NJ}@IU?8VFza+>swb$Y=rk1kIsnSH7mV0 z?d})reL8hznmi}k1cca@=NF))%*r*gRF;Ew5f5cDOc;*{KD}>aq7WlXJ|K(eguENd z&>n*vGVayebq^KE{&a2_*fT_~N@i+oUz1&^U}=g3!KFfpQ2vkg_{@t3*2)5IHXA=h zq08UnX3{ISO-fU71HqFW!P?w2K;CCzcQpK&ti`!heOvlmkD|{GnFF!ZTUDxM@`KS- zIH#?sfpbO5ad$rus$Dh4KGys)1pv85D8p=MC^Z=U+M5M`E)*9 zUq@3uI(BNmJJ<^xsH9=ks1y~yeO&5N>uNUC!@0uRjS%_l86~DH>~2>-5i;Ft z_@J=I#0&~(#vr%s3lbJhvwF+679xZb%P|Ha8BvvYuV)U}nTeR%S8}IYz0hROsM|a|8U{e0W?i7poma}lJz)%ckN@H zBM=^<`LY_E=Wt3Pz$vdfR35m2|K;;V-2Gosah(YnUGHAW&v$+%s9*qj%R#iax0CQ& zwdcv9*rN~V^$Rcm<`*esr+xW!0T?9G_{;)c7KZJsKp@S|&22>hg%lp7NSGuCjwLRw zLId5wbH%QEqE{)e>nJC<)9IhKT%BJq*bgZSdW6cjco~KowSNK7zFEl0Q8#L+@ww5C z*!!Y1WlTo~rvDaD0{Q~FaU$@}$RnP{-JyrsAl={o3#)j}>p9ZS!Qhk)$|liHSqaD9FS*XP_< zgbQJ2Zx1d%EDiq)qH)%W+6DmbJrp3C zNL=5w0gZ0N!uT+)z$nHq9~|kW9x3Cgh7#NOJTxEn$n>+$C(iop{eLb(T@u4X0e@pL zwGRDK)B0)S>0c@bSc&vJ05OxGF9W#%-4YdIO?^7VOgGx{i^|9dW6GNaY9SHSHnqr# z8yXJnoVvx<3Jh9n_vQ7lG-Xcp1iq9_5>+K=%E%_!LUWoT^RJT$AnB>sg4aOB1ZWR z8W%)%4+QJNdUbX6_702&CJA1M1~IZuYh$6Co688lyJVrG-l+-tBaOf5p%WJ_IKNSn zmC{JGXURo!7!La>@=oAtp{b+G4+po3SfL%^`=?80| zbi!n^K|oTI=Sx!sWicgQNW^3q)WU=Pd7Pf-!ajp+&oYyM7)2G{gNNK&HrZ=2Oz8WS zMDx?~vh@l0pJ@M(O^`M>Hy@bMWx@4|tD|)%!ujk{ES0?7-T5Ghk%Oy>jVcJRKwd#C*EVKeI=&1Kubb9 zY6H6sHSwasJ*lb<5gbp!N_?;}EqCWveB`MT5I&Swjq)jN9gN|+aDUBfLu?3E#(iZ&)UAP z)ctkCm3d9rD)H3;j+Wq#7pzy$dnuFUdJ8uTnG65Wg)>J{$N)JyS2+byJQx^~A}8Pp zpC7Hh=Sia#=S9y2d@~A7pSpz3Jo`jticaJE9tr1@ZyE#ztsvjsf~eD3q>mT&+_)p~ zjYilF6NN%q52H!J4K8$MAGuPzHplXxKFv<7&mMPe9MJBIriR%Emmg|eH8pFl{{CQ1 zcojReRnAWpx|#)gb=?m+IbCGxh^M3mz=(;dbU3D6Upy8IAg^ecP4I<$?COcIdq}{n zthK_6AU4*xrK&7VnQNS;Gkt(5QeLs+uGV&aurij0e9}AM4}m0q#9IVAQq2H~-S!yt z(eH8a2P1aI=&IcjH!APyWLf{oDZ=W41K}6f_FkAnD<HJ1-KZCRh zKIOli`*x#+da;94G}XqPMU*vk$8EKY{GW5FZ6QGKleBreByA5A1A&BakgVoTEt>9e-0 zuM{~FzL)P;yzEoXJ)NM!V$$ViG$;?P#l!LlIFzy)A{I9#ILAA6M+&xnFdA+C8N7V1 zBcQ=XqGxEcD~V*DHHvsi^-&5tP0l($G5d5i!I!`NyjETw<7X0>Y-u3nz0C54oUyk#M_r76pTYRw0{J?yV zFR3Fj{@Dc9=pq)4Y6ab>`cbenG(pRx7LtEhFZ;-cWyn4I(##(Y4zf2^?;uCwJiZFu zj@a}3{)xe85$C^|(eBPxPg_%LW+C|AW~>Bxs3@|-EZgPr(_7kj6YB46I~3Y%F|vLt zyD1ZXDg#y6>Ma(QIiv)@Z^V`4yTt&Gk1HVb+OAF#37`?C+ieiSY*^)F^*hkQo%r#F z8_v!Te$^gQVMu>k8G5gAVLjE>f_1(oG$Vebf9_~NaxYq7aiQtATMe6WR8Ol*Z8BV6 z*zaD$Z7|$ImN^zd(EYY>q}*<%XIFH9e|kh82?WW2+!2kPE2c=NKwU*=g-27TJFHfO z`Cx^j4^5w?2R|=O2kW-wCcqP)ZH98FMnL$7W#qSYrA+TB-%WZV&GNMaulIeaT>9Nj z8v#QK12mprxUKjv`RuV3%+k3C?>I5sQVEyhxh`zgDtF)GTV{XabrBgT_*ypF$r`e>uOUot8w&QW2pA>W-upjJbr1{r`6B^8;ku~_n z6Zdz}VPT6z#tkqBjC42?t1mb$&VML2>t50?wDj{8&N%{-HjR@-)@?<(YF_#3RsOQ}770=XWtnn6gMNjzCe>VwY(23K}U zEINc-zs?EY71fn6xwJdi-cku;4Tu7nJR+Z?Gl!4w7gAED81cDXL!N5^ph{g)YJ|jM z+Z~L2{yMT6ozypQ9^6lE-W}HdY*al9DPKE_+&yowPOF^Nu3~IjsS!M>uw1$(UHI1- zdW;!2(wE7~sb8gda91Xp>GH#>ypggm%=B$_&ugoV46!Y)oQ)s_ivz*g{;qSkwb8HV zMoT&r9*2pGW6}z$Y2g8-M0K650-a|)JWzR@U@<%}W1ipcuHt^EDI=DeO#z>@t_wik z1>A^lB|3KwV1T=8(RE!n8mNnh5F|j}XmDW%I*c9e#=)ZdR-t{>1dt>JCQ2V!jkxsc zH|qB3YFUFjFZx#x(bt!+=OmPd`p7+3W5qbS(09hfkmYlo__v$ze>#eV4i%+fi#BBl zCs1&B^s?Q(A)aX9VH)P?k7E1aM_7*dk^C+U6&bS*Us;Tf;0%D6M^T*XfOs+C~)TX9l+uff)R!lYPGIeuRv zE*2iNaxxM#H_ngr+K@K)Wk&oxVQCl=@By)Zh)P?4hm95CfTbCzg1vf#8PKKxmm^tYN0#)|A3r*b z<}orN8U-jg8M|{+o{m3H63lzp>}e`@(uk}I%@dDSlX6FUxeTT!|JX%D)JDc2e`Wl zrkON%FQKQ6(`T_8oSh`$b2@3_Su6;PHb^<;rtmSL!}Wkrf-lMmvY#NJ;DL#kHT0+$ z=Q|Vg?eLb70{h8#Kns)?55BX&2z;mr zhWdPETogE-%rna%z{=URjnUD=R7{uixPl0WP?T$Ot^lAsFmyT|P$=wO}@FibkjU1EZ z*Z=KlgM1`~iUbyu=fH1`51&UnqLA19+oTw7S5OdgjTTV;d}sc(nFa;zbz7;j4)t-jiG^rt7BX^cbtezgEcDAj7zUf(GY^))z0Va zdH4S(9`eJGl!zrSBDR3gaHMO|Eom*tG^fC)sZG=}9Uk~T39rysy*>uItS*x4*F(Vu z0f!_cH!1I>P-%&=ZaMbHoh3#A`ayVyo?Yesv?2pt|MpqjGD05%47BzAF(j4}h)aU%b9!kh z;^4pwj9J8#(N{qlZ@EAXD(-l)4q-oxMGNQ80hKrUcHJOJ&}ScD&?Zm3;72XNRivLN zf4(vY|I-L{UC^3AZaX@Yex0gY7sgq>Y1KyzYzgn*Gui*DA_7z;q_I%~*qtDejVhTw zI}SEp-s_-c0@x5O`}ey>N`o%wz`~qTdf|oBuaE`P{Xh)ml@EHoJ4V;i8{aA`^}oAi z{&GeQfV1yLMOwE9KU5cf_BYs%BwW7am%p0=O34)qMRRWQ&t~oTb)W(&v9*SbhHVcR zAZs^jZ~*EQhTXx26z$5;23rYz`(}K)Ap(JrHv+%U2^W`YN;nkQ`)8e}T<(sbGc}K2 zg83X6v8>?^aNS3)6UE5GDavxGi$4TQ&m1Jt2M_&De20S_b_w#a6W)@6m8Wki{D_;sGa}uR?x4BVf-uVud^@l3vP==Xd6Hsy-Y+p zi@yGxC&RT0KcE54CT0mgzN6YqoZ^6Oxfj?V=G&}!yC`JwfG;iL^0bDMaC&Q!spn^< zQAI*&p;0V&xI}J}Vq^SW$3X%o38*oz{0q=JsQaVBYcdnVR3J8A4UMq|UK?-njG7U= zr|MS_g#=QoVLR6S6L3Mu!`y9Btn=A!l+zD-gA%#@Sc$U^{$H|5htzia4(~y{u-ltD z!S(241`R(hAg(i0#h3g2ITB$kHp;@P?W43Pk4qu_{o6ZLB)XiE;_9A*i6jgoKl!ne zHD}}Xu_4(Tn;Pr+#7%Yc~G0Ra$2N^~1ISs4-h~8L# z@bdJxn2p0`d!ixy3=}c)O=*0xvZVVEq|_Z`4WUYqF2q^q57Zc9cNomq=0XgGKR_1$ zP(c04JR~)NlofJbZwk+Y6%gjZRlc68SN%n{ui0(*wUg8Wj;ju$v;@;WP_trWShSp% zo+xD}^kC*jorFGO56y8P$1?-#9s?@bgeK@AP$azE6ZwBa~f0yA<6GLk{TJX z0RkW&<}=6w+$keTIyVR}up1)=;m%HwTJt*Kw$$%SO4tx<=yCMBb9lvgaPpDV_EA`T z7}&rd=`-wQ-5ZTsa-eB;Tc#mpu)^MZfh%QAXyu07SY2_}$A8xmTP1EpYr+L0Ow#qH zCr;;)i`()DNM{HFR=foW2ryV0uM>h4J8975LQFG6z9KIJDT2zfaOV0NkeOh9Nu2B@ z8qXG@yt)&8g#k?oGOdoiGNB8SFu$gaDa%?U{4ayB*Ifjf;CoZ?6!Gj&c*->W>$iO| zF_XA+)3ZymBl5}8gA$j#vP<9D++qoX)i8Y&Cw0_@3z$(%(*=zXJU;dp={WAg8t)hwwM*+hM7%3eZ zPd#=aRS&TXL++oW_AZjbrHK{M#*iOe04kqRd#Pl@YZKE=1JN`%fFM_aMhF7I7JMcwe_KPAPGW>xX+4D1*4- zb+RwN8+_z42PHwx16|V0RK1ANu2wUZT$Tz7GhB99EhYj@Z#i4uDD(R zqclzO*MI!@VY*LhpS23|h~n`!pW0kyjVvG89_O?q zMAXF610F)+dtu5y^IXNkE?G~>va^R`qr)iePSLww$mhQ*!>|=%{qO8F>>W7OXe1zX z9YPp6BqYEU1$pZ1M^B+&Lo?CU9IUwl+SoMsfz0sE)?ME9pHh%8RHL3P$W;B(<745E zC2+56HmAbX40BcUV35oUR9-k_-91aa9N7i(D^C4=%ElAozfl|PW&UsY!T_8U8dWE~ z-s)-~m)0kmrl^!aQT`_$^Ya`4a7m8C426M3a&@Wu9SBp^{jpGppj33L zl|)fhRbEFYWv%>lwDAmX?%%*K9Dp2-8`4|4g!G_ERkptilW~^}1>uGDNFvC)EHYDQ zxT_;&`l_DXQ=N?v(5oN+4++WCU1Sm(2G@QJ9XC^g&!0d28~@G}sO`Cng{P!dZkw*rB40gd>Z z1GaaaBm)6ie-I}&w=9}Gju8O~#quzN1X8ixv*}oq zo_C2NJw3XcKK|Xin-KP(cTlbTp&4wM^Kn`Uof?8=5X_L5Blwgb%$AaW;~BSKy3b=m zvQg6SyMSj(N8P%QUL#)x<9y5sLX2|Kx6eSRgid=7P#vY=t!wy@Ogsy6ujIGa(*urd z4i85@>EGXT2S%f4mXQ(dAGi+y3+Y#H*LZMEIgCas)2}b9W4`6X0byD`Br&T$=hN>a zet9pfkJ5SVC8_GDSJ@+*A;COuLgMwgW|>RqZ6w$98^&lrWpt8aYVmk;!F6IzR*=ZGxpGasIn0?6_S7VfMKXqdaHW zP9J;>DHlCn@ES5brht5pqZR<kY-{6{1h6DZ7ML)$ zIk;`|(p@ag>5g>*=-ilS$F>*U#g73uNKavvWOZ-y9=t3ExX0!|wUad$r7t824sU$#)}2ES8-t zYNm(qwM4L30;XX0CFo+?21$MOpPE(9I^f(uEkbavrOZordT;f`gV(SR-$fI%jd7`m zLjBF7#rAC=^^79$&a4?68L#WUBR5yDs2_GB8Y&kuW}^3nap9YnA*kTKgpb z+wXz-uqEfMu_cG2DwXs=d^V2p-fGwLyr(`hBz=G*(>BjS2K2626D?y)>5+APn=>s) z>f{Ue(oD%7`ZssYuHN7>?167ZdTwEwz@7tg?N>EecEj}WCaRG1dgdBAs9aBenj;>C zw+w_`Mc0L&G_QSKy-g|^VJW=(qGm+A(ByNDaxAjgqV8ds3%b)S9r-ic?ll&qoWUjpZ3Oq|W*84^mp^>&?cp)_y7fZLzo z-g?zNlB!)!wz;F3rN2XXSclfexeTZVo#Nx$**VB%Mw`D`iegrIGkvf%NBc*@c@|@r z*29^ED1P51rMHBQHQzWKO5@9yU1yTmS3sOoNT;ZUglAuOUtpKZrv>gnJ970!gd6&oDJ3rXDc9cN;I)IreqxvM*wr5oSOjJO5g{5z$0@Ew9zz25 zCUE{2;aK_>A6Ga|app(1ZU{jiwjn@(9g-1p-@(P@@KWZowYIz3i+qc;9DiI5so*?i z22A30wU8XuutDq4CgUKzPlez9=)OXS13G~SDrHW35v@K7COM>~I{o(9MCRmVr@snh z1+Wy9i;kZDV&mBnJ0OS5OQquH@yU_07Rd=In2>GHU1Wf&E4Wi>*P2Fu_dnmH2HGbu znM1ewM*`g?deAWt5qiHFT2@V&ya~Gw2H2Giqz6h&2otk%so~41KgCC-2cCHs=oCxi z+d{+<15EMwAUA=+XV&@h7MLlv!(hhozUFIdJF}J%lI#rP^zJiBO1&SlN=MJoH_SuNLftl zvD?hkaY3D{(xy3aRSr2;q`(yt4L8Ms!u%41>^30=b5*EKj zG{Lk;b@Iu3ui}(NtXX$v15u+O|G*i2UTx3Sc;doGdrSTIf)KUhRGn_PjIQr~%@amT zz7+_{2E}3?Bg4wX@80h2mf!5Ze|V2*{1D%$>+}yx60uDpe%s;>tz#bX3Xw?==avRiIH=>9AMn#<_5U${d@ybAl6eE@G0^E*!VkVv_| znXnH1of?K{(jqzmWk?~pt=IKiKDLmof1StPV@-vZv?6x=mASNs!|x?5z7bpO%k<7R z<4ojieTejv!4Vhrd?TDa3iK|S!VpOSsmjx!D-?wJ0X*i|e?*+7gQ#iQ!#kg&bH^QO z@&459-~;Dw@Ci1TXmA7%v_*~TeWgKpUXlLz5rS#LFhhTq-TsC=>Q9*(uJ|fX@+qH> zufO3-G~fNjAh+A`?q{vgS9csM_UjB;t01fjS_)lZ8h>31J6-7VBew1NEA0U23peSr z$JqNr5C*JDdLt|PnK`Q)B)S7uB5MD}ITxT3(@ZtjXA&YG;TBRq#Y7?5D}^lL3@<*a z-t^lH#*Hkj4Ez`H3uKspVV&!rVA+@-qKCkF0q&;lG{X((BTHhutsKSv4GOZ=$QjmH=`zMLkOz=L01&5#qcP4m|sXitMuO zNx3WNz0CxCjk?!3#MvWU$f3Y7l0O7GxWVG={rW@Ia_#h%V9>Kluhdm{h&v6eQ^G{FXGy}FQw-dO{{F5k(tu-u~t7$u*4oH&A#8EG5MBhl>* zdR(<}F{oac1O)9+vJi*VjmdzO@3$%N!T#$Xc2QkQkx8%jlVQEy#sv-6Tv4K725(gs z7ZMU2t81iXFI@GIgru;>K)Amy2HY)FQyk)<#J<_EyqAN{{KQh!VZ5B1eVcYX2QTG#`rv*SU{+3E2d#F-1iN?F(= z`RFO6fw=k8CAxBEe%3T-D!(UI4=U?5+&w$4pKWa&9?lHci7!WzvXMTlA*;EA4)awA$AoO>`P7mn{i_ z(FAZz^y1?9TH<>w^%E1nlIgrLFRU!_khPgG?j~kf$6x0` zyi0ce?Z~c*EcC1<{UAKM1YOb&g#wUy^t z4UH~bkS|9;BUVBQg9a~$qzehLaa3#s43a-)W1BtJ$hUcvr*75tH(5q@hei(qEH z`sVNY3>Ag93|%7oVvFX^i>eyg6MQn)TdnR-=+L17LP^hW`SzLZY~|DDhoAT-kq>^v zV&2D8SaN!LmP+S+;tPSQ`89#Du&EUreC92WABu$`foE)4h%@G@EJ7PRdi*$lFD38n zGlcR(UugWK{;D_a**HPnx)!{~pwk;)H794_{}RcT8KSn_y70|0gZSUCzba*L#bZYN zZ=5D_g}4X{WzSEAs5^61QfI-4>RJ2Mp1<4fZK{jUCoT4(q5H-9|BzTYV2IcLisuhw zLpuG}Y?iDeLQEkN5)%+Z;Cl;U)nXQwmUf>R2M2H3Oi-fvpH2@|;Awo)m!Y7=5BoMu zEK|ovH$}_Z?w56>TKAFl3_tRasG;18c_?X@xOsDdss?(wJCS7`eF#VU4iU%&9^Qbv&TxNphmv zoxh47_fd5nj1AwNyDBa|fZs@!S!&h2vR(6Cc;8&P#-6HDKUCT(y2|+WsjDUQ<4;=l zmWP`GVDUA_EDI>#_da*=tL(gqQ-lq5QpKi=abD_L-(sYbQnl`Pod{^-6y9gBq{KTn zPBYxaQ3^$T(`o;(L1^mjA>z`P7Pf9(m#@rv#_AGtQ^8Q^61|1f=K}1P!%~Nu(&BQL z=+OqVe?osep@M{oRcQwGTV=2s?9SNR7aT0gkdk$e`fUp~8hj=zuR`#QhZn$x$q12_ zcdYRF9Db1l?_p?iEHoHwywH`zqsV|}RljXMv3dCgG)~E_8?2WvXTB4q2>1OH>^PKD ze)fI|*p)>4ubhM88!*w4Jos#$)Ih1Wy7VmluryS*TrJm$hF9Fa=m9d)6-j3SOh5zK zoaNM4$6@|x)^fTcg30xy2IJ<^mPdR@^rK5MGwJE5Z72mi-caL#NRv>@&mf$x{oj?u_Zw zh+y6KND{SU&G~p^=t$Q1!KfwXqJu62dI>GEOqM!uo~qxy2m1u#lNJv51=MYj^>Kzb zkYwEEfSD{1Q>nc90zLx7Re_CPSkA)FAwuR${J?}Md7a>?Bip<{yaiO12f4C|1}#^D=9{s0Zy1-%bUA|4F7-0O)Wr{K|Iu4paeW) zL+1T$i*lJ zU~*i(zd_36yM9Ef63G^?9V!diuX2`_i+Gh$9t2>8czG{Wwl-q=WI_Y6yj3~RJ^w`d z%rceFE*x$)t#M}(_4S48!2??)CD95}Npb6~Sg|cZ_p@M^-Mc)(E`m^SyaqvPqM!49 zfhu55zdczhHrF;q)$fb&x@zK~f)JG>02wUD2hQY6eb4^4H#1NIlp%l%`rG#IDBorO zy*7+XadlnPu6YB4k@%6L-=_^!>?8W|wraL8++`w)OESjmc^inu8Gh_31mt}Nic)m{muvZAC-eX}bN|2g-a0DEwrd|o zR6qn75G14(DFG=ZrIk0qG7!2`LGs85$|U0VJdwm68rg38h;EL;;E4zR>4+ z@8|b?Ykg~d@4w&taWCW^X0GeJ&U44H_dbq}3*6qs_$REyTy63H;toq_2K zcd_P!)Xw}lL`a|v22>H!g}F0<;}iV+{Ers1UR&&-0&zj2MMZ|8?V6GhW4m(Umh%4uwVL`O`c7D*g*T%&eHSfz8C9<8DO|1#MU_2|Lythf}-3P|L zyx5iw0iyn-Dgnw`tK-&mq7LUlZbKZY2_ut5Dg74wz&c+I>ZN`w^MDuq0R-mOkG%Ia~ZMuA8H}Rd^$dXGgB@GN~ zYO1EgC|c?~H8hAQe_+dj+G6K~*s!V%n)7g+L)FkZff$Ylc0|F_ocoQ&>np5 z(MJ@An_G77*qH0>=p1KV?^sUb20+C#_8({92bgE_koX~OnCb-vSVlp3^5mL{Kw>06 zokYJtTfh+fu;hjKWc;}($DeN=ndr$k6@hG;yRK5g z-MrUp>S;Q@wV+^C=MPh*j;e5;WQbEGEUh9+EsWpkg|w9_C> zz8AzQTSM5y2fZMY0`m4V4q#6e+j#eMRo#)Mlptvi`O**oTL??Y4$)fjerG6J;Rc}j zr0`~9HMGq<)S3MkLYB8J16j<T=5%6i@ut7Npu_LKuy^t(i#lgt z_P>!4a#t$gU__q-Ui2KjL=pshw9(KQW>QRjD#n#<=MuJ5DT#jn9#U=jD`--vIA4JV z^Sowgfafn1giIU2zdg}B^EC>Zx^21;Zfy_>`kygI;PdLO{l%u@Vu)0eLJU;7o!4sS ztp-c&3lh(vyRYWO)vg2I0SMBb(f0T6*;X9FOn(1>ncy;}XP&ti zPyrC$XxrWfEYrRkSxz-N5qv^w!JDB!W0>Y3B!=}9m?)X}a{Qo-8olQgrEG?|R?pWY zET^bNRANbJ!hDn^o1T@qGGex3XYR356U=~dvbpJLBVBsodhx8%9a2Sy*SGZU2t3eG z7d3TLYMP2C7D@0mrY%Z@PRPeCnqj47dEu3Y3>@dqLY#r_@ zyDvk995`3PT8N2f9snsI7*TyFsV)O8=AIXt%M4RmeOXGeIM{OV49LjtJK?knjAzph z#1mAY5qD4$Xj&AQGvi7t$&l8=Z1tC)0)O!60FOIwq01^v-%QLA?rqtFQQ|Z;iPn79 z5#k>BfEIe%EgNdjpDuJ%k-CLbFK7~0;pi2Vj#L@wm)PQnPE0WXQSafX^(b@ThGztD}SI4(t55eino9MvV(fA>FF zIAC4-{kP*Y{Z1MpZGG3$fobXt4^qvp>`Aw++*XqKqN>=@z^l%7UZ*+EZr0*jR_Jx~ zF=wkxtXEhpXP+Plxq5Hy#v>cP)t?2bO=P|>m8s>n3%JGgkVkH7IuwVfOT~bIDglDJ ztE3|v!*E52fV+j4 zJF?v&x;yXK>7N&3!)%ITc4$bvEPlKo2}fEC7rSqqG{FAG3qr09mj)-y)si_X3QdE6 zYN8jT1*D>7)4dF;ToB|91!BBfr^x#+sH(F#2_N;3=n!!l=Q;{KpchR}`HrqloE3y; z4o%4u(w}dgHT;jczDbztYlpeM>hCU&4?^CcK4b4iy$>6TjacH?lVIVOQbeDi(#2dR znU+9;Ul?=*gS}WT@%l7O-}@E$AR9*_QOh!pNo8VnmDu-w5P@uu+SYgNKB1`KiKl=| zkaDWywZ!s7{l(SSYIrO0WhM@!|c z&qFfLNRFQ*#l4OIKDeKtXN`WYqYv}G+6a!#qO+@BG4l3Jl$Y1Gmm1-g^D?s>i!+)M zEFsoQ&71!;VON4lm58=q7?t@9k?)Tqwa~F$1+p+KH&zXXYE2ftZQ%VGCunrp5dx5= zzHm_ordY=9Jo9AzUU|sb@03>AOQ+VqJa-}X;hz3tGuh9L@C{#i=05RO<}0tawjE9* zm^pdBK0G%IFW`F;HSMazGzGfYPwouyPh;6p#Kc?!yq#1;3bLuw1v=U8MMV@Eg7#|d zr&{wVCa zj-}{`V#R{JP|wcHVb8twSwgw2AG^Cg$OFfff_yIes8`s^k2p)7DPsK8L8SP2 z=VJW(ERMoBbce z){hET&gVNr&?3$8hV~21YYZ(>(iM{y;?xh)9K-l31HqE}DyNCSFw4!^CArC6I|emc z5`?PdBkAj+zSnG@2AQO;{j6N51R`Ncc^XP{>3k?tf7T7f6E+Zlt0ND8I3=i`=73Ch zxa|7=X#-$aEobm}BFN>#B1Pn&c_OgX{_4#H0aP01ED=?45I2!;dmQX^p{w(#OochS zy%OnM=5 zmras0CP7++n>qKvmvrX-eJ5qm}iK$X%aWKPKyCK_%w9&@Jj25=P{Q zxpZjrTBBpM7c;y><6@JGiEGApo=FiRH3rJbwz#;!WurXeRPR!IU?Mo;pWYri z^XXL!ctd`E2mwjYYPHy())Ycjnzcj=`VxO`41CUi?zNkjnxHn#`D`C~X3DXrRITqkfWb2 zh2%+<_bPouB+{rLKMjpr>tZlbKNP}P1CINctZ7$t7MsG6F9ii3wCwZLaFRgS4S7u( zc!-$X(p_d;ign^~?N4otMBBe-31zpbWG5-c!n_Wvu#8|d!qh(xloJCXRPH1+z`+X% zG}V)46;-$s8PIBMQITP4wbD4(jStcGz(EfL8BoK@Ms8%$!Nafny|MO)2w+zhmAUPq zQ*${^B`?~@e1NGn;?GyOD4S%(WPAIh^jhTnutSb+z#>q;qD@6Ep3$a>Vgl=(I_lft zAQ^3I+yKvFvbR zW40bmUto{nLj{F&lKJM9ytsY*xMri_Y0V*+VTUZ~1M}J%o23h6K0p1aWMS_(r&lr% z!6@xF-5(%12%RJEk%CZO7zYmzDNwJbzM+p7GLNXXUYl$bxJVItFrOYe{#_XA4^c8b z(4HCkz65;b#Z#{*iQ z!I&U76v7Q@qOeRv!+g3Ie;?(^4pbt@;w~g7N%J*QFSm4JFOcSFR81yndK?zmeHK5K53Qbo^tw)~G3O@K4ObzM) zjdEm?$8!3EKp!QD9fp12RKJ8RrP<^$16DeMTDwgI3cIkWu*C7vJR3TVppIajNKOaUDW&(RE|$*Jq5q<|eU2}uLOWmNtE zDL_tzh!?nRJBX4kgkNWT4~?%gJG~xEU_jR?RYC~Z1~O0%)EaYp#kx;uDl}p=K-sGig|x_f!If>3j}oPEa(pjb@IY3vEh+S zL~-DIxFb65&Jg>4hMP7V#D^N6e4dsg5I0_Qn^Io(IOFIok9W?S#t`~c8E;|D*xBW; zU2DCkR6M-UmlgR254gVf{eZ|Jrh;qXF``t1D2{F0!#@hvtu*H-|M+WkhY!&aPnel^Q7X;X$?H5~Js?{V>KEk#J%`%p{D@ zKuv$yt2;xb@TRU|Tlu*)JKY-0Pr-$ylQ);N99+}@u+6mA;Si?VM?+^Y+>AoDZ|i9y zm<%)P)x4l++m?fXTjumruw$FTy-d z?U4(g2LihkMv{&kB|tx#D+Yo1Y~bK57`B2#mpglm{1{a(4TUA~elU2@02~)g#@>TT zTpAEYH017EI(W7+UXy@KaKfIHtPcxNJ_U&%2V%@Qr!Ud9xeZXlf%()#MKEI^WQ~!_ z(%N8`E{PERL`B3Pv1&ohe)Xy?7eG~|N(Cg|VtxyHUTTrdW^TH`NKnbeO8%9E>1y@& z%j!qY%Tn1J$S6)vO>N)$425eG6k6@(54fw3F1Bfny6M+~fltK}hzVcGN}bwtuIrLN zDK7K+6V@E&t<*K4QQ*yP)gYX)v){Of+QY)a>^?PwII(i%lf&skvmcFLjSApG`$0lk zkJ>k=>>EH#BkkZ&IJ#L4%4zq130n=R5nN1*S8U(syjdlR=sO9Nle6?dzcMf3LtuH| z5a=eaN^aa^jRn>pDmo!EwMC;n7%l37#up&@*6DOY75Rf==mC{Z5qLa_?diza%hq%u zjFLgkj8A|d#*qx#4OI5r5nooAUT{xUUwv}byk{u_;k=@OSu0YkJj^qskR&whN+O|% zC%e3*)fy%cwzK+}X2Fp+7ilP5Wu%>;B~fCW&-R`J|!2QsA5|NPnxWj{v{`ndVZw&RrhJ>I&?R`M= z##k_qHXe$?4_Yr%l2^YC8m6s-o(lGv>q4)9WGO2%p2x~aJ>RwB7th17vOqM>F}f^q zKARNAxbs?5HU{xi9cM^;bbVh%=y743ULKv)aO3JNM}Ok|+CW-&_yzo`?$Y_} zdXVBA>%N~S5ygs%Fbu<;a?s03Pop7H&&6H<`+bTn!JcCnK#(GB!PZ?7<>SVFYR#-( zY9xm#z`AT2X`JC4d$Berd(jDgj2-Vp!8lvr-y9^*vDZg2daEP~Z=YNG@FHF$) zSdYGDd%q>9=n$Nuzw^l-A>M(h;TJ6G46}+4H&5~LIJZRy=oiI%q53fkm#Y$4oNyl% z&_pvcSV@#cKgUNZBEN-h&e47WUg-`4SNL1Br{wlAdj{*4v?PrHlt;qC4!_3@{K|QSz>Y`VW$oh z7mgyxyh%DBJ2X}yN;~MlP9@$H`6dP@F@{obugI8f!1L!m9S`KdJHNG`x`s45NgYG{ zYue)gxR*0-bIiuyM(<{B$_rBwH5wF8c5g#<;dTD27;cn>t2=Wa`nfczCl*;-?6pss zH`h9@eEDvS{F6Mq;i1;jrLm#6imt2fz(H_DFX#J4xlWZMc5jq$w!4Y!2<7A8^ zz^NV4Q&&tM3UEbi}1 zID{oU1|<__ID5mDh0BuJsTjgb9KIK%vG1IzWX0xp@3Q>sz3`~c0=-AJsaN+~6>|Y1 zF{kktaxGc_plRhvbASWs6ZL$CI^6k(ArTPB`>>FQ#}p1Th`79EMnTVbp!7%m5NR9; zW+&DE5TIP7&y%~Z&O-T)_z?Cx{HwIe7swwSlWpdsBJ*vS18C@j+^(Js#KIKs!_MsB zIb-Rs{8>umzy0(8N;uyL8FS@9DPN{0kgW(QB$PGWfkWuwr>6KP7yo-XJ3#E9&O_!a z|6GI|*4g&`gz?D`y~1d56vc**m|qoa*?S+~Nh2Hk&kHwxA7GX?d=_~74agyQGhxO9 zih9DIwyB=UBz@SNAG-0poB8H{UeQei1e4B7KlG?h#fe^Ox5n@BL#} z$zORN2J$)hsgW_CpclP?goQgqzqqwEwa_As2g5R5`QV>#ovcQCOc~Z(%B61vc*1AUj+&kzFKuYwQ zC)(>5mbiR@av>ZT1~)1wHXv*=@8R6VjEMZl+t0#h_x+Nde*q2LATc=ZUEgwsp7SB| ztLT+UG5?CsP0ptV+bU#<{nC^}lKy>30XSBOtZRsUJN0Y$QLXKqPnap`Vws3$y2vR8 zZx(wTLP>w$jq3ztRKJCj%NHh4nCCIf#xmESy+!WpCTwM)erSUD_baV3n1$R`f!XQu zCWaY(NseRe0pErZ!0TrSWbQu;i)HJ3@sC4G?#cp5`y4o&ELzz3khe2%nlis4`|f;P z=6H-9!-pUHRUBe0Rd4^&rdp7qXZ-OYqOdlve=2h3ivGz5rE<1R2ZZx9Nqh{$`9UIe?f zNd-4xBtp9Fp+C~UyStliudHuIdcK-d3I?7g*L9k)+^F8I6fj{)^_!3`$>BvQD`4pn znW4Wgb@wS@mT)3J&>%#}4JpTeLUe`@-aYzOF=q#fu3Q%SgH72K$cD+nSi@V0B|+!! zV+ru9HNsqd07PNb(6V@qbYEpriy4iAG_F0)gv{3)(7PKtZ)7>Rg#JKB|N1_vb)*Z6 zI1q)yL3`SLn4A%iP*zfsfzY|Yb>l(eUuhXb>GRMWOb7zxB$JbqG!zvld?Pv_e1Z1T z+oJfX(j~;m5OAW@bwyuUo_}r(a!qk)fXgEbAcjfiR?8$+_WUyIkqh|>I+bY+x@kJL zHB=xyAMJ>F;~o8iEeWC71uyXZbXlo!&mvPcJnvt7 zw!~}DSN>a~lqVyEx`Mz3NdQdJt{|_=J_Er6kL?|%zcMVy?gohx?ow>;EiyrJi6^^v+4?)zT0NsezN{yfk+po7P2`ppqLU+lrs*?)YjEW3dU6z1Q z+^;O5fB}m@x!hL#^Ueo0dX<)_nDiLI=@QOQ`m?J` zbLsL4*Y?f6aG+M`2vhXZ@#KBC^r(qu-rdi8S@0qNC-4Q5k)!ef-?HRqg9JLG$SF5{oS(kMXlGIR^=AU@ERdH)&*C9!Bx$lr*6 z8`xBk*Wz78klbWNj*bKM)@0*ze%JNs`@aYY2wE-Q-rn80#LmNWQ~!$)gLnl?d4ggOA6dX-s=e zLs0`$K98WuwWsFELt0dc&DhQ};r~$f$Z>=;W@1I?s&a!+8=0>$$1Ayq{qCD{s4!&#N+i<~1; zh97+Il-n8V+v4~wzC144YJ4x=Y*~cV3Z{0XvS?-QMRAJ4k$7iaBdPfKHBD10QTg!`}z*UB!2k!($5e}^5q+zS@O@1coxZFM2A17 zJoT0T^Sk5c&*LBd#KFu$e)xm*krRP`{vWZT;-C9gla^&Y{MA!@3X1-;B$v;emq)CM zV7VqtYeT8HwHj`D?tB`0kYJ72|DM;kk`eaIjAoE4iF^wzE%HEqu;KFkJz;YWNOQ$8 zF&%!G89j7_P6x8T3;>n$RfGtu9LR63ZtNw39wEQS?pFC?cFyF4KT`;@Hh_FGfhxL- zU-Ro`iRm!8Nl-~t+5Psg?m3{k{I$PoR;?QZj~sq5ozDYj$){=MAYx{eU-L5v1iP9^ zuZ*IgpMB)(Z-j7T3SjFHAop!D^k<0vb@Qc!Xeo?!xE;(TI(A~_Na#fhMmJ%UwHdyg z5k3K9Rkc!jcJ0NVHF$P|O`YdXrIUHyr060peG(o>u&Co~1B|EEzW)`O{Zyl>BHv>( z$KkXbU{$CDn9F5@WbubKLgu~aJ%DYX85sN=GH!oXg<0?|@NR9L zOF*5TcfWGEu+5>o4*(^RuTPIo0ysJRb?(pI^+~d*^OW!X95EU$G+9eN*iNp9#{s!v z#k#4pxqE7BY1Zx6vqq((4sC_^Ogo6v0p_$!GE$t-z$BZ&=Hhpn?;g7UoTqin1H9-p zfEN+j%^*O!Bw$dFQfj;9Rx5*`{bU31+-o5!$@U-wJ!?-7L&WMD8inflWQVJu_PaPC z=Qt;7@k^a&-j0%na1WKXF1b~FxOiWB08Vq(<+-f<}$)lmi`;`8{(SdxI!a9 z&aMISzeMiPh%dXdSJpagJp_UI=PkX*pLHSiS5Bk*3F=dgFl+1}a+}Lfo(UMoM_op& zO@6z;ohX+M5yCTnlmiYt~0p{`AZ<|KD%q7o`+#s5458?Yq4fHKi>yd3d#K+ zucaDc$o!>GdcSq)@B_<|0u|*!-t0yGzu%KGrb~*ca4^~Z<$3Us-et(p^7utiQhWiE zON}57iZXx#i(^~)W=VpM-olPNj0oBSi#eaicONFS`3=x}?$DtdS$xo$NPCokgrjO7 zbcRHcv5EoK{2#IX8AiSCE0s%mnvbmxzb|DUMbV!E9*|Fa#c64PC;vooWp3HH7~#dN zc#hn?$j_~sjgr4cBYhGmDG-3c9=24|acbSLqe1X#ae;#>>iXz%)f6jBC-5~RxiC*)oFkH5Vh$HN?87*7RtqSNk zbyU#Ex-$Og98;0gLa&?TaVanYZMh2?W>Eu-FwWbA=~n{GJrp>?n#^A(MR!$9260b9 z?ST7X`w8|$6o6-{%MfYzRJ(47?0&DGT5sdaUmrUB`lmZ`@b#eTnOwFQ#pU0t6$ccT z&92a>*GN5Q?XJPr8{e;L30lYsFBrs2M={EOA7Ni{9^v7Sc+xAUDgN9EW!S$h^BX0! zojFe$82e4eD-XYoVrc)KIC1oUzxn^W8UL^E{r^7-9?Ji-%logjB0fvOqgN@^DLR)p zWK*?r==H#!&|I<6)g$}kiBq(Mp5tTQ-dCW1^wkTQTNi=YApOa&Z}ubBWsN}KOAj=9 zofCh3u94P9<+KFo2LXy5Iw&@@dcr)?h7nY4-qxQIZvasSB;U(fV)^5&`*5<)s44`f+^P&gKFf%L)*+(yO7 z&d-$uzGRP`1u;5?Kl^fpV>DhKF?*n3}s5pti(71PTTnVGiK;+~0Nh{qpf* zmK*rH_ATg|&v9l=@EMx-qBE(5EyT^;KYxmWrcvsVVe@&kipynE7zlX4h)LM;{u3*YC418O_QWkgqE}wXI_CEM5 zs&SWA&e!NRkt29dgEs&teRRTd&6!c+QNbWYLUcpAI6D?~b8OcI@Ua8dVB~ln-ih{Ypr@aPT2zmXgWcm5Yji0rBJtKn`L^Ex5 zUXhizJYBTRXp#aDN_w`{ie70&Zu-}&-0R;7H$ow4B(OKr#0Yj0|If7=jM zYc;wu(0y_gqQ>X=_UC$@-c~PBv*1VH#r7@|Whu#m6@h88G?>p=gScYuc&qjm*o1Dw zWwu|;aJ`e>z@46|Ku}Zb+}U;CUf-Wp;yPY-)9SIsh-9gJTlWdmw<7s+W zJx#j=i?MZpG>cmYM7V%)%%Gk&Mm%@xWr;@Z>@a_8sy6CB>D+7z6{zvuJrr%J{@ zw~tqHOYzudJ5;MFYaVX~o#DYW#MJH1#0r^f=#ibkOBpW*hO&+LG#;JO7y0fhmN1ae zSIJ1BcC+0N56{Ar_xyS9$FtgXJDOm*F9RE6P5IX3^C=kaH$*fa^ZbwFsqs0jd=`e$ zNJbck2|9X8g!$Cft*>{$GlK!Z5zNZs;gv{H7hWLm6GXJ|tiYF>48rMWXr_0(k!asU z>nz5L_jKn0&`|XNo&TNcNA^~r{tCz9-nqDwNAUXgbrspjgPR~4oB|*1>a;p8S!S^W zv?NF*RNlPFk5rRMA@|;6fA#e>`DIMVr&4+n#ew)$qk`Nt9W@V-CFy4{|fQayA zmwWYQj>m-L5&joQ(AaM=B4sZKpstT=>tt`#XcSuXXFpmg8ALc+?17<|IDkXKktOAF z?8zgf;T7FGCj!kVH7rYi7&arlDKi}b5k<6AthKy`P= zp43!7A%yQw@1Upz0i`k^I9isW+dwcM1A;RG?!D>`z~4UCcGZ0c{b(6<*JEt!AV6sQ z$#b{xm9amA3kT3bI=4W|rt@tJDu`HmSSdCL{P^O;oGpT-sJ|9%k9^sWKCtG~uO3n% z>eE3g94g;nw#DK1_@k-d-K$2BTJqk7J>c^sAGl+(BcN|52FYgFbgWA>Ti{VxOp8j( z-QzvhcN|uhG>&0N_aE<}Lr~J!F|*W3gg?J0TLLBc&MKzQ2!^a9tM* zO}b<#YvbbptQ9dUD!KZg(G@Y0e{v{T-E&<>7RBMRxZ3@m zrU}5Mv5=FFL$nt!#Q|c0YPI{F;;V=K6&a48I{X83QNnu8{hIbLC#d!K8+F}Q zV>zh6I;ccBEAgzp1cI?4_g`b_u$nVAOQ4Tii?D9A{bX?e(eoB^Jw!U!2v#1~yMcE( zuRk1ifzbd&@5=RSuR!{Vbg`y4USp{OGK%~S{*<=@)c6C9xKl`ld5>5mX0+QNR2&*%_I2hjX?}0$KC{)=SRu!+6UjyZPqi1IhpMO!`#sPTFuU4 z&Rd6>*7jX7 z?8L(9APHD?n-jNh<0+dy9(Q}m57IuqGWDz**%uMf@O46ZLIx)_-uJ3Zj}zU04%-9W zceJJJ`dtL`!Fs4Xe9lw)F(!kl^3}nsh`y_An%A*5CBxUq=pwUA#d>FnI3g(X=p8a`7gVJmdm?%T?-DbE&rIlp{kCI7*)uHedjCqftn{|%QSN{iixkSwos4g2$BJ}&0KpUIwPY#m0)Tyc9rAzuRrlRR z?Yh>z6@SvR(QT(xMzYZUK;;~>lu=g+WFxa}eC`V>{p)w$J^KX9@12uZ$rU28{&5~= zK*6&Jo|4-APAc~0pI_(o$MSx!1!L0;hj$PovJVE3le!lTNEAPK64UAZ9di|)8!!*3 zXDx$xeFtX^4BDkJh-$_LBs4J<(OS|gzb{&cXvFSLLrtzwHa)pl5K zVCvRD3~}Tu-|I*D$2MasPzC&OzfGOGI167`&5R}A7c~57qx(JS*XP>6trqt>ej;Sc zO7;+Q0nhGgu=B1=h(m1YO;NV>3&bIwwJA@v`hqyGc+0#%kg>h`Ym0F4S#+4R(G^Jj zM&o}C=-38>U$*M580VJRd%%0liUbb9TS4mV_y?L2rxtUIw;Z5T%4oFQz9M4;5`A)D zxHlUGX>|Jr^6L!`>wb?ZcddYyy#%CXh;4KD{VA>8+!L5lvQ1uaK8+W#zn=)9Dx#4w zyO>#gMmpOCf(>OP`0QU7fd@cAVYYk-=1El_f_d&`V=g*NHENuz*c1jL_^EV)x z5(-as?yc6(E$NjzMQp|xyvitK0m3lGnLL`ew9l7}=?(rS|NCuMx&L+h7uevXDRBmm zt{4!RY1_uv7WMslk0$*1A^Oq{7kTvkYhiR4#5Jw&dm!acnS}h%tNV4k1HlBs&5kovD~$f zxnNBy~fBDju%Kwj;@PHjW<%6X{8up|6Ed4%VJeJQF-z z4|FdgRBl%A&_orLJypkH|5WE@Y367h0*qjj2E?BMHG@<2ELHbm3O-XG}KM)1K6IE?pm_Ym7p5BwE~FLRUhyA-4A zpp<-`%f-Qg`OI7esFW>UoHtq57jv z-;&_}27ZiQ5y&pjT^@7#MEB$(9^SqB*psl%Lg#NPUu?jWCKZ7L?$D@`-MIkp9SBrj z0cn@tv4Wb-_^$oqd5TFar4U zW(J)<_?}p+XWB1lMZi$uHr;tJ{CITF4k2#3WbXWGfjrQaI*dW=B0eN&fcN=mcyl2d zKh1d4-CP{t9EYVcFgx;))o$=}(kKrOzDZQlyb2-yL*P`2=)7gU+FP!y^Erd(_;u4n z`)eRrFQm;3bK)F`D=!?meYP@ERg4qC>2k~ZM*$LKKuDfQ&R)J~QO{eo3=>A357!;x z%qvQ`5J3S9+ZXTz$;uPUJ4G1wjS+|xc~hmhwMW+$U>qAN2NQ;-YHu^5UN zpi@55v6F3nN_@EhLBJhGD*7_NL6Y^6N`mv)4mjtR-0m7xm4Fgn1;(uS{8c28Z8&{P z9uCc*cnAR5JpO3Q(yHGV2T0nrzG$Rqa%pO21f?M;``Nu%JiK>Jg4CZyAlxYFibpOD z!8Hv$0G_u8+Lb7p`-Y&OkL>qEFce}`*6)oNNuAJr(U6_OMI_) zv#*oaPu08~Y6DhoP@FbcbyTHNgo;f5_fnzSKAU`2Faxug-x{$cm2k=$;qGa`I5_Vv zn@VwyB+KUYHs{T69)(;jrb5jX?qm}x8&ByXy@P>&pec++ZZi1FILGe?Lr|cO^a<|I zt<_hPO0YP80Ep(n$mjEjy0)lJ-hMSlZ2pFvY){?;iox$Fsw&U4qr@Ppv^AI+- z8r~ACWnCzB&V5*%DfF^G3BZ;LiX|v{g#eTtwekgkX-B)LCK5cnKBigOgZr>m4xsdc zBM+7Qc}Lf=L%5<}7t_UlTucdbn?vcnDuxV~#{3!$BrO}!T;>s~Nn+TXSXwC$x7wBB zPpWM!>Xp{OZzsyE{3{YaQ-fRUO@NL~1>3EGUd(k)r%LGkn?jz;BpBLYfR-=V0J;KZ zL|y_=RnZ1zqZ`;8S-l*7{8%MRS~e_0b!xk4@g4 zW;ekqwA?jKqVn9TXIh_CB#^&~~wPvO{7wd&C^Sus%57A0Zfq<5tn*k3vu~q<@1PeeJWGP%FrniNa6rZT5sqa8yty zj@5WlB^392Gv70b`=thRBMNMA@h3biR4QwVqQCX3k}yH)FYr z(@Z&+PD4?n^71W<{Am0H#Hp3tSCQ#iWKoY8I3osd;)a+UBwKfEKDj`y7%iR&LRT|8 z#;;3ll`Mi|286k=AL7tS#k}S>l$=YUjQZJf3dmrLY|_h+;O4RJ<8~x<9+?8$mYh`4 z4MqeXrw4_>Y(4Xw3Q7vBvY|Tks;O#O+2qIyoy{GlLa^%fhIk7n?2>B z(6Rw-+FbLHOS@zkDW-NqD(b+dRvC&M!I~-6emSgC?wk#TX~ecFQwZX*3riTh_#HWb zkmx3H$8llG%s1^I*&YvqT4WZ6ks1$g2n^cUW?iC`pU4@aG7nCMtx>YP1F>>knX}T; z8g$BYz&+&`mIUcPUdY%QD66Fc%vOJ{B8zn%WZqh_*8pz%XwAeGIdB;fWF?2j*SsIG z#V$RSA2L9ds0o3S&RTA-&)83gU!O(sXo~B(Z}!jF9JFfJMkIT#Lt$~Qt#v>dG5#+r z)cTPJND2>7*amnK*FK&Ljml}rQm(3i5b0s=$ZhTlkYY$z4o$#M>s3xJ&gI5aKAHUu zJn7n92Ecf>b$pOM*L6C@FKyq#7SjOZ7DkY<3GY8L@5o@3d^{cPhKDD;XUvP<1Bach z9U1YX#262E1VIsRN$oi$Eo}>3zD{p1-ySc_{Sv}Cv+-)zV2P$fgRAxF1+C!a>=FQ~ z)dbu95RXs%?_#w%0yM(wKWfzf1=#;DV%oz`|95?cIcV diff --git a/pages/can.html b/pages/can.html index 9adf7a7..ef6dbc5 100644 --- a/pages/can.html +++ b/pages/can.html @@ -3,12 +3,12 @@ -ESP32 CAN 通信控制台 - +ESP32 CAN 通信控制å�?/title> +<!-- ç©?favicon:阻止浏览器自动请求 /favicon.ico(本地静态服务无此文件会 404ï¼?--> <link rel="icon" href="data:,"> <link rel="stylesheet" href="../css/base.css?v=857f83a5"> -<link rel="stylesheet" href="../css/layout.css?v=acd17001"> -<link rel="stylesheet" href="../css/components.css?v=quiet20260919a"> +<link rel="stylesheet" href="../css/layout.css?v=20260924a"> +<link rel="stylesheet" href="../css/components.css?v=20260924a"> <link rel="stylesheet" href="../css/can.css?v=c06f5688"> <link rel="stylesheet" href="../css/responsive.css?v=quiet20260919a"> </head> @@ -25,25 +25,25 @@ </svg> </span> <div class="brand-text"> - <strong>ESP32 CAN 通信控制台</strong> + <strong>ESP32 CAN 通信控制å�?/strong> <span class="brand-sub">DCU · Robot_v2 · CAN FD</span> </div> </div> <div class="header-status"> - <span class="pill offline" id="canConnPill"><span class="indicator"></span><span id="canConnTxt">未连接</span></span> + <span class="pill offline" id="canConnPill"><span class="indicator"></span><span id="canConnTxt">未连æŽ?/span></span> <span class="pill" id="canIpPill">IP --</span> <span class="pill" id="canRssiPill">RSSI --</span> <span class="pill" id="canUptimePill">运行 --</span> <span class="pill" id="canVersionPill">v--</span> <span class="pill" id="canChannelPill">CH0</span> - <span class="pill" id="dbcSummaryPill">未导入 DBC</span> + <span class="pill" id="dbcSummaryPill">未导å…?DBC</span> <span class="pill" id="messageSummaryPill">0 报文</span> <span class="pill" id="fbModePill">模式: --</span> <span class="pill" id="fbPollStatus">轮询: --</span> </div> <div class="header-sub can-header-actions"> - <a class="pill can-link-pill" href="index.html">返回 OTA 控制台</a> - <a class="pill can-link-pill" href="can_monitor.html" title="独立全屏监控页">监控全屏</a> + <a class="pill can-link-pill" href="index.html">返回 OTA 控制å�?/a> + <a class="pill can-link-pill" href="can_monitor.html" title="独立全屏监控é¡?>监控全屏</a> </div> </header> @@ -56,7 +56,7 @@ <div class="card-head can-msg-list-head"> <span class="ico">📚</span> <span>报文列表</span> - <!-- 已导入:合计 + 文件 chip(可逐个移除) --> + <!-- 已导入:合计 + 文件 chip(可逐个移除ï¼?--> <div class="dbc-summary-strip dbc-summary-inline" id="dbcFileInfo" hidden> <span class="dbc-summary-meta"> <span><b id="dbcFileCount">0</b>文件</span> @@ -72,7 +72,7 @@ <button class="btn btn-primary btn-sm" id="importDbcBtn" type="button">导入 DBC</button> </div> </div> - <!-- 通道 / 搜索 / 勾选 / 节点 / 批量 同一工具栏 --> + <!-- 通道 / 搜索 / 勾é€?/ 节点 / 批量 同一工具æ ?--> <div class="can-msg-toolbar" id="canMsgToolbar"> <label class="can-msg-channel" for="defaultChannel"> <span class="can-msg-channel-label">通道</span> @@ -84,43 +84,43 @@ </label> <div class="can-msg-search-wrap"> <input class="input can-msg-search" type="text" id="dbcSearch" - placeholder="搜索报文名 / ID / 节点 / 信号" autocomplete="off"> + placeholder="搜索报文å�?/ ID / 节点 / 信号" autocomplete="off"> </div> <div class="can-batch-actions"> - <span class="pill" id="selectedCountPill">已勾选 0</span> + <span class="pill" id="selectedCountPill">已勾é€?0</span> <div class="can-node-filter" id="nodeFilterWrap"> <button class="btn btn-outline btn-sm can-node-filter-btn" id="nodeFilterBtn" type="button" aria-haspopup="true" aria-expanded="false"> <span class="can-node-filter-label" id="nodeFilterLabel">发送节点:全部</span> - <span class="can-node-filter-caret">▾</span> + <span class="can-node-filter-caret">â–?/span> </button> <div class="can-node-filter-panel" id="nodeFilterPanel" hidden> <div class="can-node-filter-head"> - <input class="input can-node-filter-search" id="nodeFilterSearch" type="text" placeholder="筛选节点名…"> + <input class="input can-node-filter-search" id="nodeFilterSearch" type="text" placeholder="筛选节点名â€?> <div class="can-node-filter-actions"> - <button class="btn btn-ghost btn-sm" id="nodeFilterAllBtn" type="button">全选</button> + <button class="btn btn-ghost btn-sm" id="nodeFilterAllBtn" type="button">全é€?/button> <button class="btn btn-ghost btn-sm" id="nodeFilterNoneBtn" type="button">清空</button> </div> </div> <div class="can-node-filter-list" id="nodeFilterList"> - <div class="status-empty">先导入 DBC。</div> + <div class="status-empty">先导å…?DBCã€?/div> </div> </div> </div> - <label class="checkbox-inline can-batch-all-label" title="全选/取消当前过滤的所有报文"> + <label class="checkbox-inline can-batch-all-label" title="全é€?取消当前过滤的所有报æ–?> <input type="checkbox" id="selectAllBox"> - <span>全选当前过滤</span> + <span>全选当前过æ»?/span> </label> <button class="btn btn-outline btn-sm" id="expandAllBtn" type="button">展开全部</button> <button class="btn btn-outline btn-sm" id="collapseAllBtn" type="button">收起全部</button> - <button class="btn btn-primary btn-sm" id="sendCheckedBtn" type="button">批量发送勾选</button> + <button class="btn btn-primary btn-sm" id="sendCheckedBtn" type="button">批量发送勾é€?/button> </div> </div> <div class="card-body can-table-card-body"> <div class="message-list" id="messageList"> <div class="dbc-empty-drop" id="dbcEmptyDrop"> <div class="upload-ico">📥</div> - <h4>导入 DBC 开始编辑报文</h4> - <p>点击此处或拖拽 <code>.dbc</code> 文件到列表区域</p> + <h4>导入 DBC 开始编辑报æ–?/h4> + <p>点击此处或拖æ‹?<code>.dbc</code> 文件到列表区åŸ?/p> </div> </div> <div class="helper-note" id="dbcHint" hidden></div> @@ -128,10 +128,10 @@ </section> <section class="card can-history-card"> - <div class="card-head"><span class="ico">🕘</span> 最近发送</div> + <div class="card-head"><span class="ico">🕘</span> 最近发é€?/div> <div class="card-body"> <div class="history-list history-list-compact" id="historyList"> - <div class="status-empty">尚未发送任何 CAN 帧。</div> + <div class="status-empty">尚未发送任ä½?CAN 帧ã€?/div> </div> </div> </section> @@ -139,12 +139,12 @@ <aside class="can-sidepanel"> <section class="card can-preview-card"> - <div class="card-head"><span class="ico">🛰️</span> 帧预览</div> + <div class="card-head"><span class="ico">🛰ï¸?/span> 帧预è§?/div> <div class="card-body"> <div class="can-frame-panel can-frame-panel-compact"> <div class="can-frame-hex" id="generatedHex">--</div> <div class="can-byte-strip" id="generatedBytes"></div> - <div class="helper-note" id="encodeHint">选择报文后,信号值会实时编码为 CAN 数据。</div> + <div class="helper-note" id="encodeHint">选择报文后,信号值会实时编码ä¸?CAN 数据ã€?/div> <div class="btn-row can-frame-actions"> <button class="btn btn-outline btn-sm" id="loadPreviewToRawBtn" type="button">载入到原始发送区</button> <button class="btn btn-primary btn-sm" id="sendSelectedBtn" type="button">发送当前预览帧</button> @@ -158,7 +158,7 @@ </section> <section class="card can-raw-card"> - <div class="card-head"><span class="ico">✍️</span> 原始帧发送</div> + <div class="card-head"><span class="ico">✍️</span> 原始帧发é€?/div> <div class="card-body"> <div class="row-2 can-raw-grid"> <div class="form-row" style="margin:0;"> @@ -183,7 +183,7 @@ <input class="input" type="text" id="manualData" value="0102030405060708" placeholder="01020304..."> </div> <div class="helper-note" id="manualHint"> - 原始帧模式适合绕过 DBC 直接发 HEX,或发送当前预览帧后二次微调。 + 原始帧模式适合绕过 DBC 直接å�?HEX,或发送当前预览帧后二次微调ã€? </div> <div class="btn-row"> <button class="btn btn-primary" id="sendManualBtn" type="button">发送原始帧</button> @@ -214,22 +214,22 @@ <div class="input-group" style="margin-bottom:12px;flex-wrap:wrap;"> <button class="btn btn-primary btn-sm" id="btnModeAllPass" type="button">全收模式</button> <button class="btn btn-outline btn-sm" id="btnModeFilter" type="button">过滤模式</button> - <button class="btn btn-outline btn-sm" id="btnModeBridge" type="button" title="CAN0 收到的每一帧按原帧格式自动转发到 CAN1 与 CAN2">CAN0→1/2 桥接</button> + <button class="btn btn-outline btn-sm" id="btnModeBridge" type="button" title="CAN0 收到的每一帧按原帧格式自动转发åˆ?CAN1 ä¸?CAN2">CAN0â†?/2 桥接</button> </div> <div class="helper-note" id="monHelper" style="margin-bottom:14px;"> - 地址与顶栏一致;1Hz <code>/can/feedback</code>。悬停抽屉保持展开。 + 地址与顶栏一致;1Hz <code>/can/feedback</code>。悬停抽屉保持展开ã€? </div> - <div class="status-section-title">三路 CAN 通道(1Hz)</div> + <div class="status-section-title">三路 CAN 通道ï¼?Hzï¼?/div> <div class="feedback-grid feedback-grid-drawer"> <div class="feedback-channel collapsed" id="fbCh0"> <div class="feedback-ch-head" role="button" tabindex="0" title="点击展开/折叠详情"> <span class="ch-dot ch0"></span> CAN0 - <span class="feedback-expand-caret">▾</span> + <span class="feedback-expand-caret">â–?/span> </div> <div class="feedback-primary"> - <div class="feedback-row"><span class="fb-label">状态</span><span class="fb-val" id="fb0State">--</span></div> - <div class="feedback-row"><span class="fb-label">接收数</span><span class="fb-val" id="fb0Rx">--</span></div> + <div class="feedback-row"><span class="fb-label">状æ€?/span><span class="fb-val" id="fb0State">--</span></div> + <div class="feedback-row"><span class="fb-label">接收æ•?/span><span class="fb-val" id="fb0Rx">--</span></div> <div class="feedback-row"><span class="fb-label">发送数</span><span class="fb-val" id="fb0Tx">--</span></div> <div class="feedback-row"><span class="fb-label">丢帧</span><span class="fb-val" id="fb0Drop">--</span></div> <div class="feedback-row"><span class="fb-label">错误</span><span class="fb-val" id="fb0Err">--</span></div> @@ -237,7 +237,7 @@ <div class="feedback-detail"> <div class="feedback-row"><span class="fb-label">TEC</span><span class="fb-val" id="fb0Tec">--</span></div> <div class="feedback-row"><span class="fb-label">REC</span><span class="fb-val" id="fb0Rec">--</span></div> - <div class="feedback-row"><span class="fb-label">总线状态</span><span class="fb-val" id="fb0Bus">--</span></div> + <div class="feedback-row"><span class="fb-label">总线状æ€?/span><span class="fb-val" id="fb0Bus">--</span></div> <div class="feedback-row"><span class="fb-label">最新ID</span><span class="fb-val mono" id="fb0LastId">--</span></div> <div class="feedback-row"><span class="fb-label">DLC</span><span class="fb-val" id="fb0Dlc">--</span></div> <div class="feedback-row"><span class="fb-label">FD/BRS/ESI</span><span class="fb-val" id="fb0Flags">--</span></div> @@ -250,11 +250,11 @@ <div class="feedback-channel collapsed" id="fbCh1"> <div class="feedback-ch-head" role="button" tabindex="0" title="点击展开/折叠详情"> <span class="ch-dot ch1"></span> CAN1 - <span class="feedback-expand-caret">▾</span> + <span class="feedback-expand-caret">â–?/span> </div> <div class="feedback-primary"> - <div class="feedback-row"><span class="fb-label">状态</span><span class="fb-val" id="fb1State">--</span></div> - <div class="feedback-row"><span class="fb-label">接收数</span><span class="fb-val" id="fb1Rx">--</span></div> + <div class="feedback-row"><span class="fb-label">状æ€?/span><span class="fb-val" id="fb1State">--</span></div> + <div class="feedback-row"><span class="fb-label">接收æ•?/span><span class="fb-val" id="fb1Rx">--</span></div> <div class="feedback-row"><span class="fb-label">发送数</span><span class="fb-val" id="fb1Tx">--</span></div> <div class="feedback-row"><span class="fb-label">丢帧</span><span class="fb-val" id="fb1Drop">--</span></div> <div class="feedback-row"><span class="fb-label">错误</span><span class="fb-val" id="fb1Err">--</span></div> @@ -262,7 +262,7 @@ <div class="feedback-detail"> <div class="feedback-row"><span class="fb-label">TEC</span><span class="fb-val" id="fb1Tec">--</span></div> <div class="feedback-row"><span class="fb-label">REC</span><span class="fb-val" id="fb1Rec">--</span></div> - <div class="feedback-row"><span class="fb-label">总线状态</span><span class="fb-val" id="fb1Bus">--</span></div> + <div class="feedback-row"><span class="fb-label">总线状æ€?/span><span class="fb-val" id="fb1Bus">--</span></div> <div class="feedback-row"><span class="fb-label">最新ID</span><span class="fb-val mono" id="fb1LastId">--</span></div> <div class="feedback-row"><span class="fb-label">DLC</span><span class="fb-val" id="fb1Dlc">--</span></div> <div class="feedback-row"><span class="fb-label">FD/BRS/ESI</span><span class="fb-val" id="fb1Flags">--</span></div> @@ -275,11 +275,11 @@ <div class="feedback-channel collapsed" id="fbCh2"> <div class="feedback-ch-head" role="button" tabindex="0" title="点击展开/折叠详情"> <span class="ch-dot ch2"></span> CAN2 - <span class="feedback-expand-caret">▾</span> + <span class="feedback-expand-caret">â–?/span> </div> <div class="feedback-primary"> - <div class="feedback-row"><span class="fb-label">状态</span><span class="fb-val" id="fb2State">--</span></div> - <div class="feedback-row"><span class="fb-label">接收数</span><span class="fb-val" id="fb2Rx">--</span></div> + <div class="feedback-row"><span class="fb-label">状æ€?/span><span class="fb-val" id="fb2State">--</span></div> + <div class="feedback-row"><span class="fb-label">接收æ•?/span><span class="fb-val" id="fb2Rx">--</span></div> <div class="feedback-row"><span class="fb-label">发送数</span><span class="fb-val" id="fb2Tx">--</span></div> <div class="feedback-row"><span class="fb-label">丢帧</span><span class="fb-val" id="fb2Drop">--</span></div> <div class="feedback-row"><span class="fb-label">错误</span><span class="fb-val" id="fb2Err">--</span></div> @@ -287,7 +287,7 @@ <div class="feedback-detail"> <div class="feedback-row"><span class="fb-label">TEC</span><span class="fb-val" id="fb2Tec">--</span></div> <div class="feedback-row"><span class="fb-label">REC</span><span class="fb-val" id="fb2Rec">--</span></div> - <div class="feedback-row"><span class="fb-label">总线状态</span><span class="fb-val" id="fb2Bus">--</span></div> + <div class="feedback-row"><span class="fb-label">总线状æ€?/span><span class="fb-val" id="fb2Bus">--</span></div> <div class="feedback-row"><span class="fb-label">最新ID</span><span class="fb-val mono" id="fb2LastId">--</span></div> <div class="feedback-row"><span class="fb-label">DLC</span><span class="fb-val" id="fb2Dlc">--</span></div> <div class="feedback-row"><span class="fb-label">FD/BRS/ESI</span><span class="fb-val" id="fb2Flags">--</span></div> @@ -299,7 +299,7 @@ </div> <div class="status-section-title" style="margin-top:16px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;"> - <span>每 ID 快照表</span> + <span>æ¯?ID 快照è¡?/span> <select class="input" id="idsnapChannel" style="width:100px;text-transform:none;"> <option value="0">CAN0</option> <option value="1">CAN1</option> @@ -325,7 +325,7 @@ </aside> <div class="toast" id="toast"> - <span class="toast-ico" id="toastIco">✓</span> + <span class="toast-ico" id="toastIco">âœ?/span> <span id="toastMsg">操作成功</span> </div> diff --git a/pages/index.html b/pages/index.html index 469907b..17eec9b 100644 --- a/pages/index.html +++ b/pages/index.html @@ -1,27 +1,27 @@ <!-- index.html - ESP32 OTA 中继控制台主页面(DCU Mission Control · 美化版) - 支持 WiFi OTA(ESP32 本机 + GD32 中继)和 BLE OTA 双通道固件升级。 + 支持 WiFi OTA(ESP32 本机 + GD32 中继)和 BLE OTA 双通道固件升级ã€? 配套脚本:app.js (WiFi OTA), ble_ota.js (BLE OTA) - 子系统配色:ESP32=青绿 · GD32=琥珀 · BLE=蓝 · CAN=绿 · 内存/调试=紫 + 子系统配色:ESP32=青绿 · GD32=琥珀 · BLE=è“?· CAN=ç»?· 内存/调试=ç´? --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> -<title>ESP32 OTA 中继控制台 - +ESP32 OTA 中继控制å�?/title> +<!-- ç©?favicon:阻止浏览器自动请求 /favicon.ico(本地静态服务无此文件会 404ï¼?--> <link rel="icon" href="data:,"> <link rel="stylesheet" href="../css/base.css?v=857f83a5"> -<link rel="stylesheet" href="../css/layout.css?v=jumpconsole20260916b"> -<link rel="stylesheet" href="../css/components.css?v=quiet20260919a"> +<link rel="stylesheet" href="../css/layout.css?v=20260924a"> +<link rel="stylesheet" href="../css/components.css?v=20260924a"> <link rel="stylesheet" href="../css/can.css?v=c06f5688"> <link rel="stylesheet" href="../css/responsive.css?v=quiet20260919a"> </head> <body> <div class="container"> - <!-- ====== HEADER: 命令栏 ====== --> + <!-- ====== HEADER: 命令æ ?====== --> <header class="header-bar"> <div class="header-brand"> <span class="brand-logo" aria-hidden="true"> @@ -32,19 +32,19 @@ </svg> </span> <div class="brand-text"> - <strong>ESP32 OTA 中继控制台</strong> + <strong>ESP32 OTA 中继控制å�?/strong> <span class="brand-sub">DCU · Robot_v2 · Relay</span> </div> </div> <div class="header-status"> - <span class="pill offline" id="pillConn"><span class="indicator"></span><span id="pillConnTxt">未连接</span></span> + <span class="pill offline" id="pillConn"><span class="indicator"></span><span id="pillConnTxt">未连æŽ?/span></span> <span class="pill" id="pillVer">v--</span> <span class="pill" id="pillIP">--</span> <span class="pill" id="pillRSSI">--</span> <span class="pill" id="pillUptime">运行 --</span> </div> <div class="header-sub"> - <span class="pill">设备名: <b class="pill-val" id="deviceName">-</b></span> + <span class="pill">设备å�? <b class="pill-val" id="deviceName">-</b></span> <span class="pill">设备ID: <b class="pill-val" id="deviceId">-</b></span> </div> </header> @@ -69,7 +69,7 @@ <span class="target-tab-txt">BLE 蓝牙</span> </button> <button id="modeEcatBtn" class="btn target-tab-btn btn-outline" data-action="set-console-mode" data-arg="ecat" type="button"> - <span class="target-tab-ico" aria-hidden="true">⚡</span> + <span class="target-tab-ico" aria-hidden="true">âš?/span> <span class="target-tab-txt">EtherCAT</span> </button> </div> @@ -82,7 +82,7 @@ </div> </div> </div> - <!-- BLE 模式:launch 风格连接按钮(checkbox 驱动:勾选=已连接) --> + <!-- BLE 模式:launch 风格连接按钮(checkbox 驱动:勾é€?已连接) --> <div id="bleModeCards" style="display:none;"> <div class="ble-launch-wrap"> <label class="ble-launch-btn" for="bleConnectToggle" data-ble-state="idle"> @@ -120,22 +120,22 @@ </svg> </div> <div class="launch"> - <div style="--i:1">蓝</div> - <div style="--i:2">牙</div> - <div style="--i:3">连</div> - <div style="--i:4">接</div> + <div style="--i:1">è“?/div> + <div style="--i:2">ç‰?/div> + <div style="--i:3">è¿?/div> + <div style="--i:4">æŽ?/div> </div> <div class="start"> <div class="text1">等待连接</div> <div class="text2"> - <span style="--n:0">已</span> - <span style="--n:1">连</span> - <span style="--n:2">接</span> + <span style="--n:0">å·?/span> + <span style="--n:1">è¿?/span> + <span style="--n:2">æŽ?/span> <span style="--n:3">.</span> <span style="--n:4">.</span> <span style="--n:5">.</span> </div> - <div class="text3">连接中...</div> + <div class="text3">连接ä¸?..</div> </div> </div> </div> @@ -145,29 +145,29 @@ <div class="ble-launch-hint">点击按钮连接 / 断开蓝牙设备</div> </div> </div> - <!-- EtherCAT 模式:APP ↔ BOOT 角色徽章 + 一键切换 --> + <!-- EtherCAT 模式:APP â†?BOOT 角色徽章 + 一键切æ�?--> <div id="ecatModeCards" style="display:none;"> <div class="jump-role-panel"> <div class="jump-role-badge" id="bootEcatRoleBadge" data-role="unknown" data-action="boot-ecat-probe" role="button" tabindex="0" - title="点击重新探测对端角色(APP / BOOT)"> + title="点击重新探测对端角色(APP / BOOTï¼?> <span class="jump-role-label">当前角色 · 点击探测</span> - <strong id="bootEcatRoleTxt">未探测</strong> - <span class="jump-role-sw" id="bootEcatRoleSw">—</span> + <strong id="bootEcatRoleTxt">未探æµ?/strong> + <span class="jump-role-sw" id="bootEcatRoleSw">â€?/span> </div> <div class="jump-role-meta"> - <div><span>设备名 0x1008</span><b id="bootEcatName">—</b></div> - <div><span>软件版本 0x100A</span><b id="bootEcatSw">—</b></div> - <div><span>从站放行</span><b id="bootEcatArm">—</b></div> - <div><span>主站缓存 / 已写 flash</span><b id="bootEcatRecv">—</b></div> + <div><span>设备å�?0x1008</span><b id="bootEcatName">â€?/b></div> + <div><span>软件版本 0x100A</span><b id="bootEcatSw">â€?/b></div> + <div><span>从站放行</span><b id="bootEcatArm">â€?/b></div> + <div><span>主站缓存 / 已写 flash</span><b id="bootEcatRecv">â€?/b></div> </div> </div> <button class="btn btn-primary btn-block jump-btn-primary" id="bootEcatRebootBtn" data-action="boot-ecat-reboot" - title="按对端身份自动选方向:APP→0x2502 进 BOOT;BOOT→0x2503 跳回 APP">让应用回引导程序</button> + title="按对端身份自动选方向:APPâ†?x2502 è¿?BOOT;BOOTâ†?x2503 跳回 APP">让应用回引导程序</button> </div> - <!-- 连接文案只在顶栏 #pillConnTxt 显示,此处不再重复 --> + <!-- 连接文案只在顶栏 #pillConnTxt 显示,此处不再重å¤?--> </div> </div> @@ -177,7 +177,7 @@ <div class="card-body"> <div class="target-tab-row" id="targetTabRow"> <button id="targetGd32Tab" class="btn target-tab-btn btn-outline" data-action="select-flash-target" data-arg="gd32" type="button"> - <span class="target-tab-ico" aria-hidden="true">⚙</span> + <span class="target-tab-ico" aria-hidden="true">âš?/span> <span class="target-tab-txt">GD32</span> </button> <button id="targetEsp32Tab" class="btn target-tab-btn btn-primary" data-action="select-flash-target" data-arg="esp32" type="button"> @@ -189,7 +189,7 @@ <div id="wifiEsp32UploadPane"> <div class="upload-zone upload-zone-sm" id="uploadZoneEsp32" data-action="pick-shared-firmware" data-arg="esp32"> <div class="upload-ico">📤</div> - <h4>点击或拖拽 ESP32 固件</h4> + <h4>点击或拖æ‹?ESP32 固件</h4> </div> <input type="file" id="fileInputEsp32" accept=".bin" style="display:none;"> <div class="file-info" id="fileInfoEsp32"> @@ -211,8 +211,8 @@ <div id="wifiGd32UploadPane" style="display:none;"> <div class="upload-zone upload-zone-sm" id="uploadZoneGd32" data-action="pick-shared-firmware" data-arg="gd32"> <div class="upload-ico">📤</div> - <h4>点击或拖拽 GD32 固件</h4> - <p>支持 .bin 和 .hex(自动转换)</p> + <h4>点击或拖æ‹?GD32 固件</h4> + <p>支持 .bin å’?.hex(自动转换)</p> </div> <input type="file" id="fileInputGd32" accept=".hex,.bin" style="display:none;"> <div class="file-info" id="fileInfoGd32"> @@ -235,7 +235,7 @@ <div class="upload-zone upload-zone-sm" id="uploadZone" style="margin-bottom:12px;" data-action="ble-pick-file"> <div class="upload-ico">📤</div> <h4>点击或拖拽文件到此处</h4> - <p>支持 .bin 和 .hex 格式</p> + <p>支持 .bin å’?.hex 格式</p> </div> <input id="firmwareFile" type="file" accept=".bin,.hex,application/octet-stream" style="display:none;"> <div class="file-info" id="fileInfo"> @@ -246,20 +246,20 @@ <div class="file-info-grid"> <div class="file-meta-item"><span>类型</span><b id="fileType">-</b></div> <div class="file-meta-item"><span>大小</span><b id="fileSize">0 B</b></div> - <div class="file-meta-item"><span>总包数</span><b id="packetCount">0</b></div> - <div class="file-meta-item"><span>已写包</span><b id="sentCount">0</b></div> + <div class="file-meta-item"><span>总包æ•?/span><b id="packetCount">0</b></div> + <div class="file-meta-item"><span>已写åŒ?/span><b id="sentCount">0</b></div> </div> <div class="file-meta-item" style="margin-top:8px;"> <span>SHA-256</span> <b id="fileSha256" style="font-size:10px;word-break:break-all;font-family:var(--font-mono);">-</b> </div> </div> - <div class="footer-note">建议使用 Chrome / Edge。每包 Payload 与触发命令由系统按目标自动设置。</div> + <div class="footer-note">建议使用 Chrome / Edge。每åŒ?Payload 与触发命令由系统按目标自动设置ã€?/div> </div> <div style="height:12px"></div> <div id="wifiEsp32ActionPane"> <button class="btn btn-primary btn-block" id="uploadEsp32Btn" data-action="start-upload" data-arg="esp32" disabled> - 上传并升级 ESP32 + 上传并升çº?ESP32 </button> </div> <div id="wifiGd32ActionPane" style="display:none;"> @@ -268,13 +268,13 @@ </button> </div> <div class="row-2" id="bleActionPane" style="display:none;"> - <button id="autoFlashBtn" class="btn btn-primary" disabled>一键刷写</button> + <button id="autoFlashBtn" class="btn btn-primary" disabled>一键刷å†?/button> <button id="abortBtn" class="btn btn-danger" disabled>中止任务</button> </div> - <!-- EtherCAT 直灌面板:只在「设备连接 = EtherCAT」时显示(applySharedModeUi 控制)。 - 固件复用同一个 #fileInputGd32 与同一条 processFile 转换/校验, - 所以这里不出现第二份 HEX 解析器;"能不能灌"只由 BOOT 里的 ecatArmed() - 裁决,前端不复制判据(见 js/ota/boot_ecat.js 头注)。 --> + <!-- EtherCAT 直灌面板:只在「设备连æŽ?= EtherCAT」时显示(applySharedModeUi 控制)ã€? + 固件复用同一ä¸?#fileInputGd32 与同一æ�?processFile 转换/校验ï¼? + 所以这里不出现第二ä»?HEX 解析器;"能不能灌"只由 BOOT 里的 ecatArmed() + 裁决,前端不复制判据(见 js/ota/boot_ecat.js 头注)ã€?--> <div id="bootEcatPane" style="display:none;"> <button class="btn btn-block" id="bootEcatRunBtn" data-action="boot-ecat-run" disabled>EtherCAT 刷写 APP</button> <button class="btn btn-danger btn-block" id="bootEcatAbortBtn" @@ -284,15 +284,15 @@ </div> </div> - <!-- 4. Flash Progress + Stage Flow(WiFi/BLE/EtherCAT 共用同一张进度卡) --> + <!-- 4. Flash Progress + Stage Flow(WiFi/BLE/EtherCAT 共用同一张进度卡ï¼?--> <div class="card card--esp32" id="progressCard"> - <div class="card-head"><span class="ico">⚡</span> 刷写进度与流程阶段</div> + <div class="card-head"><span class="ico">âš?/span> 刷写进度与流程阶æ®?/div> <div class="card-body"> <!-- Phase step indicators --> <div class="progress-phase-row"> <div class="pp-step active" id="ppEsp32"><span class="pp-ico">📦</span><span class="pp-txt">ESP32 OTA</span></div> <div class="pp-step" id="ppGd32Ota"><span class="pp-ico">📤</span><span class="pp-txt">GD32 OTA</span></div> - <div class="pp-step" id="ppEcatOta"><span class="pp-ico">⚡</span><span class="pp-txt">EtherCAT</span></div> + <div class="pp-step" id="ppEcatOta"><span class="pp-ico">âš?/span><span class="pp-txt">EtherCAT</span></div> </div> <!-- Unified progress bar --> <div class="progress-wrap show" id="progressWrap"> @@ -309,7 +309,7 @@ <div class="stage-node" id="es2"><div class="stage-dot">2</div><div class="stage-label">OTA 写入</div><div class="stage-detail" id="esd2"></div></div> <div class="stage-node" id="es3"><div class="stage-dot">3</div><div class="stage-label">SHA-256 校验</div><div class="stage-detail" id="esd3"></div></div> <div class="stage-node" id="es4"><div class="stage-dot">4</div><div class="stage-label">应用固件</div><div class="stage-detail" id="esd4"></div></div> - <div class="stage-node" id="es5"><div class="stage-dot">5</div><div class="stage-label">重启中</div><div class="stage-detail" id="esd5"></div></div> + <div class="stage-node" id="es5"><div class="stage-dot">5</div><div class="stage-label">重启ä¸?/div><div class="stage-detail" id="esd5"></div></div> <div class="stage-node" id="es6"><div class="stage-dot">6</div><div class="stage-label">启动完成</div><div class="stage-detail" id="esd6"></div></div> </div> <!-- BLE OTA stages --> @@ -334,7 +334,7 @@ <div class="stage-flow" id="ecatStages" style="display:none;"> <div class="stage-node" id="ec1"><div class="stage-dot">1</div><div class="stage-label">读取镜像</div><div class="stage-detail" id="ecd1"></div></div> <div class="stage-node" id="ec2"><div class="stage-dot">2</div><div class="stage-label">推到主站</div><div class="stage-detail" id="ecd2"></div></div> - <div class="stage-node" id="ec3"><div class="stage-dot">3</div><div class="stage-label">交接进 BOOT</div><div class="stage-detail" id="ecd3"></div></div> + <div class="stage-node" id="ec3"><div class="stage-dot">3</div><div class="stage-label">交接è¿?BOOT</div><div class="stage-detail" id="ecd3"></div></div> <div class="stage-node" id="ec4"><div class="stage-dot">4</div><div class="stage-label">确认引导程序</div><div class="stage-detail" id="ecd4"></div></div> <div class="stage-node" id="ec5"><div class="stage-dot">5</div><div class="stage-label">邮箱灌写</div><div class="stage-detail" id="ecd5"></div></div> <div class="stage-node" id="ec6"><div class="stage-dot">6</div><div class="stage-label">从站复位</div><div class="stage-detail" id="ecd6"></div></div> @@ -362,35 +362,35 @@ <!-- Primary status --> <div class="card card--status"> - <div class="card-head"><span class="ico">📡</span> 设备状态</div> + <div class="card-head"><span class="ico">📡</span> 设备状æ€?/div> <div class="card-body"> <div class="badge-grid"> <div class="badge-stat"> <div class="v err" id="bConn">离线</div> - <div class="k">连接状态</div> + <div class="k">连接状æ€?/div> </div> <div class="badge-stat badge-stat-click" id="healthBadge" role="button" tabindex="0" title="点击查看启动自检" aria-expanded="false" aria-controls="healthBootPanel"> <div class="v" id="mHealth">--</div> - <div class="k">健康状态 <span class="health-boot-hint">▾ 自检</span></div> + <div class="k">健康状æ€?<span class="health-boot-hint">â–?自检</span></div> </div> <div class="badge-stat"> <div class="v err" id="connBadge">offline</div> <div class="k">BLE 连接</div> </div> <div class="badge-stat"> - <div class="v" id="gattState" style="font-size:16px;">未就绪</div> - <div class="k">GATT 状态</div> + <div class="v" id="gattState" style="font-size:16px;">未就ç»?/div> + <div class="k">GATT 状æ€?/div> </div> </div> - <!-- 启动自检:点健康状态展开,默认折叠 --> + <!-- 启动自检:点健康状态展开,默认折å�?--> <div class="health-boot-panel collapsed" id="healthBootPanel" hidden> <div class="health-boot-head"> <span class="health-boot-title">启动自检</span> <span class="health-boot-summary" id="mBootResult">--</span> </div> - <div class="health-boot-failed">失败/跳过:<b id="mBootFailed">--</b></div> + <div class="health-boot-failed">失败/跳过ï¼?b id="mBootFailed">--</b></div> <div class="boot-module-list" id="bootModuleList" aria-label="启动模块列表"></div> </div> </div> @@ -402,10 +402,10 @@ <div class="card-body"> <div class="metric-grid"> <div class="metric"><div class="k">ESP32 温度</div><div class="v" id="mTemp">--</div></div> - <div class="metric"><div class="k">GD32 温度</div><div class="v" id="mGd32Temp" title="SPI 0xC4;片内温度">--</div></div> - <div class="metric"><div class="k">GD32 负载</div><div class="v" id="mGd32Load" title="DCU_APP CPU 负载率:中断+非延迟处理(任务派发/OTA中继)占墙钟比,不含常开轮询与空转;EtherCAT 输入 PDO 段5,1s 窗口">--</div></div> - <div class="metric"><div class="k">输入 VCC</div><div class="v" id="mVcc" title="PA6 分压反推:VCC—47k—ADC—4.7k—GND">--</div></div> - <div class="metric"><div class="k">VDDA</div><div class="v" id="mVdda" title="片内 VREFINT 反推的 MCU 模拟供电">--</div></div> + <div class="metric"><div class="k">GD32 温度</div><div class="v" id="mGd32Temp" title="SPI 0xC4;片内温åº?>--</div></div> + <div class="metric"><div class="k">GD32 负载</div><div class="v" id="mGd32Load" title="DCU_APP CPU 负载率:中断+非延迟处ç�?任务派发/OTA中继)占墙钟比,不含常开轮询与空转;EtherCAT 输入 PDO æ®?ï¼?s 窗口">--</div></div> + <div class="metric"><div class="k">输入 VCC</div><div class="v" id="mVcc" title="PA6 分压反推:VCCâ€?7k—ADCâ€?.7k—GND">--</div></div> + <div class="metric"><div class="k">VDDA</div><div class="v" id="mVdda" title="片内 VREFINT 反推çš?MCU 模拟供电">--</div></div> <div class="metric"><div class="k">GD32 APP</div><div class="v" id="mGd32App">--</div></div> <div class="metric"><div class="k">GD32 BOOT</div><div class="v" id="mGd32Boot">--</div></div> <div class="metric"><div class="k">GD32 自检</div><div class="v" id="mGd32Init" title="GD32 APP 上电初始化状态(SPI 0xA3;悬停看失败模块/复位源)">--</div></div> @@ -415,13 +415,13 @@ <div class="card card--mem" style="display:none"> <div class="card-head"> - <span class="ico">🧾</span> 系统状态 (/system/status) + <span class="ico">🧾</span> 系统状æ€?(/system/status) <button class="btn btn-ghost btn-sm card-head-btn" data-action="refresh-system-status">刷新</button> </div> <div class="card-body"> <div class="status-kv-grid"> - <div class="status-kv"><span class="k">OTA 状态</span><b id="sOtaState">--</b></div> - <div class="status-kv"><span class="k">Relay 状态</span><b id="sRelayState">--</b></div> + <div class="status-kv"><span class="k">OTA 状æ€?/span><b id="sOtaState">--</b></div> + <div class="status-kv"><span class="k">Relay 状æ€?/span><b id="sRelayState">--</b></div> <div class="status-kv"><span class="k">刷新时间</span><b id="sStatusUpdated">--</b></div> </div> </div> @@ -430,7 +430,7 @@ <!-- Log panel --> <div class="card card--mem log-panel"> <div class="card-head" style="justify-content:space-between;"> - <span><span class="ico">🖥️</span> 调试日志</span> + <span><span class="ico">🖥ï¸?/span> 调试日志</span> <span style="font-size:11px;font-weight:400;color:var(--text-label);text-transform:none;letter-spacing:0;font-family:var(--font-mono);">UDP :6666</span> </div> <div class="card-body"> @@ -444,10 +444,10 @@ <button class="btn btn-ghost btn-sm" onclick="toggleAutoScroll()" id="autoScrollBtn" title="切换自动滚动">📌 自动滚动</button> </div> <div class="log-scroll" id="logScroll"> - <div class="log-line"><span class="lt">[--:--:--]</span><span class="lv I">I</span><span class="lg">[系统]</span><span class="lm">等待上位机连接...</span></div> + <div class="log-line"><span class="lt">[--:--:--]</span><span class="lv I">I</span><span class="lg">[系统]</span><span class="lm">等待上位机连æŽ?..</span></div> </div> <div class="log-foot"> - <span>共 <span id="logCount">0</span> 条</span> + <span>å…?<span id="logCount">0</span> æ�?/span> <button class="btn btn-ghost btn-sm" onclick="clearLogs()">清除日志</button> </div> </div> @@ -457,15 +457,15 @@ </div> </div> -<!-- 右侧滑出:堆与任务栈(默认露出一条,悬停展开) --> -<aside class="sys-drawer" id="sysDrawer" aria-label="堆与任务栈"> - <div class="sys-drawer-rail" title="悬停展开堆与任务栈"> +<!-- 右侧滑出:堆与任务栈(默认露出一条,悬停展开ï¼?--> +<aside class="sys-drawer" id="sysDrawer" aria-label="堆与任务æ ?> + <div class="sys-drawer-rail" title="悬停展开堆与任务æ ?> <span class="sys-drawer-rail-ico">🧠</span> - <span class="sys-drawer-rail-text">堆与任务栈</span> + <span class="sys-drawer-rail-text">堆与任务æ ?/span> </div> <div class="sys-drawer-panel card card--mem"> <div class="card-head sys-drawer-head"> - <span class="ico">🧠</span> 堆与任务栈 + <span class="ico">🧠</span> 堆与任务æ ? <button class="btn btn-ghost btn-sm card-head-btn" type="button" onclick="refreshSystemStatusNow(event)">刷新</button> </div> <div class="card-body sys-drawer-body"> @@ -480,19 +480,19 @@ <div class="heap-detail-row"> <span>空闲: <b id="cpuIdlePct">--</b></span> <span>窗口: <b id="cpuSampleMs">--</b></span> - <span>任务数: <b id="cpuTaskCount">--</b></span> + <span>任务æ•? <b id="cpuTaskCount">--</b></span> </div> </div> - <div class="status-task-list cpu-task-list list-scroll-top3" id="cpuTaskList" title="按占用降序,默认显示前 3 项,下滑查看其余"> - <div class="status-empty">等待下一次状态刷新...</div> + <div class="status-task-list cpu-task-list list-scroll-top3" id="cpuTaskList" title="按占用降序,默认显示å‰?3 项,下滑查看其余"> + <div class="status-empty">等待下一次状态刷æ–?..</div> </div> <div class="status-section-title">内存概况</div> <div class="heap-group"> - <div class="heap-group-title">芯片 SRAM 总览 <span style="font-weight:400;color:var(--text-dim);font-size:10px;">(ESP32-C3 · 静态段可拆分)</span></div> + <div class="heap-group-title">芯片 SRAM 总览 <span style="font-weight:400;color:var(--text-dim);font-size:10px;">(ESP32-C3 · 静态段可拆åˆ?</span></div> <div class="heap-bar-row"> <span class="heap-bar-text" id="memSramUsage">--</span> - <span class="heap-bar-track mem-sram-track" id="memSramTrack" title="紫=IRAM · 橙=.data · 灰=.bss · 青=堆已用 · 暗=堆空闲"> + <span class="heap-bar-track mem-sram-track" id="memSramTrack" title="ç´?IRAM · æ©?.data · ç�?.bss · é�?堆已ç”?· æš?堆空é—?> <span class="mem-seg mem-seg-iram" id="memSegIram"></span> <span class="mem-seg mem-seg-data" id="memSegData"></span> <span class="mem-seg mem-seg-bss" id="memSegBss"></span> @@ -505,17 +505,17 @@ <span class="heap-legend-item"><i class="mem-dot mem-dot-iram"></i>IRAM <b id="mIram">--</b></span> <span class="heap-legend-item"><i class="mem-dot mem-dot-data"></i>.data <b id="mData">--</b></span> <span class="heap-legend-item"><i class="mem-dot mem-dot-bss"></i>.bss <b id="mBss">--</b></span> - <span class="heap-legend-item"><i class="mem-dot mem-dot-used"></i>堆已用 <b id="mHeapUsed">--</b></span> - <span class="heap-legend-item"><i class="mem-dot mem-dot-free"></i>堆空闲 <b id="mHeapFree">--</b></span> + <span class="heap-legend-item"><i class="mem-dot mem-dot-used"></i>堆已ç”?<b id="mHeapUsed">--</b></span> + <span class="heap-legend-item"><i class="mem-dot mem-dot-free"></i>堆空é—?<b id="mHeapFree">--</b></span> </div> <div class="heap-detail-row"> <span>SOC 窗口: <b id="mSramSoc">--</b></span> <span>尾部保留: <b id="mReservedTail">--</b></span> - <span>任务栈合计: <b id="mTaskStackTotal">--</b></span> + <span>任务栈合è®? <b id="mTaskStackTotal">--</b></span> </div> </div> <div class="heap-group"> - <div class="heap-group-title">内部 DRAM 堆 <span style="font-weight:400;color:var(--text-dim);font-size:10px;">(malloc 数据堆)</span></div> + <div class="heap-group-title">内部 DRAM å ?<span style="font-weight:400;color:var(--text-dim);font-size:10px;">(malloc 数据å ?</span></div> <div class="heap-bar-row"> <span class="heap-bar-text" id="memInternalUsage">--</span> <span class="heap-bar-track"><span class="heap-bar-fill" id="memInternalFill"></span></span> @@ -523,26 +523,26 @@ </div> <div class="heap-detail-row"> <span>空闲: <b id="mInternalHeap">--</b></span> - <span>最低空闲: <b id="mInternalMinHeap">--</b></span> + <span>最低空é—? <b id="mInternalMinHeap">--</b></span> <span>最大块: <b id="mInternalLargestBlock">--</b></span> </div> <div class="heap-detail-row"> <span>DMA 空闲: <b id="mDmaFree">--</b></span> - <span>RTC 堆空闲: <b id="mRtcFree">--</b></span> + <span>RTC 堆空é—? <b id="mRtcFree">--</b></span> <span>分配块数: <b id="mAllocBlocks">--</b></span> </div> </div> - <div class="status-section-title">任务栈水位 <span style="font-weight:400;text-transform:none;letter-spacing:0;color:var(--text-dim);">(峰值占用 Top,下滑查看)</span></div> - <div class="status-task-list list-scroll-top3" id="taskStackList" title="按峰值占用降序,默认显示前 3 项,下滑查看其余"> - <div class="status-empty">等待状态刷新...</div> + <div class="status-section-title">任务栈水ä½?<span style="font-weight:400;text-transform:none;letter-spacing:0;color:var(--text-dim);">(峰值占ç”?Top,下滑查看)</span></div> + <div class="status-task-list list-scroll-top3" id="taskStackList" title="按峰值占用降序,默认显示å‰?3 项,下滑查看其余"> + <div class="status-empty">等待状态刷æ–?..</div> </div> <div class="status-subfold collapsed" id="statusJsonFold"> <div class="status-subfold-head" role="button" tabindex="0" onclick="this.parentElement.classList.toggle('collapsed')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();this.parentElement.classList.toggle('collapsed');}"> <span class="status-section-title status-subfold-title">原始 JSON</span> - <span class="collapse-arrow">▼</span> + <span class="collapse-arrow">â–?/span> </div> <div class="status-subfold-body"> <pre class="status-json" id="statusJson">--</pre> @@ -554,20 +554,20 @@ <!-- Toast --> <div class="toast" id="toast"> - <span class="toast-ico" id="toastIco">✓</span> + <span class="toast-ico" id="toastIco">âœ?/span> <span id="toastMsg">操作成功</span> </div> <!-- app 窗口租约:本页会自动拉起 EtherCAT PDO 循环(GD32 负载), - 必须持有租约,否则服务端在租约归零宽限期后会把循环又停掉。 - 与 tabs.html / motor.html 并存时连接数>1,互不影响。 --> + 必须持有租约,否则服务端在租约归零宽限期后会把循环又停掉ã€? + ä¸?tabs.html / motor.html 并存时连接数>1,互不影响ã€?--> <script src="../js/common/app-lease.js"></script> <script type="module" src="../js/app.js?v=20260919a"></script> <script type="module" src="../js/ota/console-mode.js?v=20260916d"></script> <script type="module" src="../js/ble_ota.js?v=20260901d"></script> -<!-- 内联兜底:同步 BLE 按钮 data-ble-state 与 checkbox.checked - 不依赖 ES 模块(子模块可能被浏览器缓存为旧版,不含 data-ble-state 逻辑)。 - connecting 态由 session.js click handler 设置,此处跳过不覆盖。 --> +<!-- 内联兜底:同æ­?BLE 按钮 data-ble-state ä¸?checkbox.checked + 不依èµ?ES 模块(子模块可能被浏览器缓存为旧版,不含 data-ble-state 逻辑)ã€? + connecting 态由 session.js click handler 设置,此处跳过不覆盖ã€?--> <script type="module"> setInterval(function() { var btn = document.querySelector('.ble-launch-btn'); diff --git a/scripts/_watch_conn.py b/scripts/_watch_conn.py deleted file mode 100644 index 76705ed..0000000 --- a/scripts/_watch_conn.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python3 -"""模拟页面 pollStatus:5s 超时算失败,连续失败即离线。""" -import time -import urllib.request -import urllib.error - -URL = "http://192.168.31.127/system/status" -TIMEOUT = 5.0 -POLL = 1.0 -WINDOW = 75 - - -def probe(): - t0 = time.time() - try: - with urllib.request.urlopen(URL, timeout=TIMEOUT) as r: - r.read(64) - return True, "HTTP %s" % r.status, time.time() - t0 - except urllib.error.HTTPError as e: - return False, "HTTP %s" % e.code, time.time() - t0 - except Exception as e: - return False, "%s: %s" % (type(e).__name__, str(e)[:80]), time.time() - t0 - - -def main(): - print("=== monitor start (mimic pollStatus) ===", flush=True) - prev = None - fails = 0 - t_end = time.time() + WINDOW - while time.time() < t_end: - ok, msg, dt = probe() - now = time.strftime("%H:%M:%S") - if ok: - fails = 0 - state = "ONLINE" - else: - fails += 1 - state = "FAIL#%d" % fails + (" -> OFFLINE" if fails >= 1 else "") - mark = " *" if state != prev else "" - print("[%s] %s (%s, %.0fms)%s" % (now, state, msg, dt * 1000, mark), flush=True) - if state != prev: - prev = state - time.sleep(POLL) - print("=== monitor end ===", flush=True) - - -if __name__ == "__main__": - main() diff --git a/scripts/anal_ecat_jumps.py b/scripts/anal_ecat_jumps.py deleted file mode 100644 index 4b434db..0000000 --- a/scripts/anal_ecat_jumps.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""事后分析 rec_ecat_slots.py 落盘的 ECAT 原始流:相邻数据大跳变检测。 - -思路(按用户指定):不看协议判据,只看数值形态。同一台电机的 STATUS 是连续量, -相邻两帧(按 seq 排序)之间出现"跳出去又立刻跳回来"的孤立尖峰,就是这一帧的字节 -被拼坏了 —— 真突变会保持住(下一帧仍在新值附近),不会一帧就回头。 - -指标:每个 (node,字段) 先取全体相邻差的稳健尺度 scale = median(|Δ|), - 大跳变 : |v[i]-v[i-1]| > K*scale - 孤立尖峰 : 大跳变 且 |v[i+1]-v[i-1]| <= 2*K*scale ← 撕裂签名 - 跳变保持 : 大跳变 但下一帧不回头的(真实突变/新参考值),只计数不算异常 - ---selftest 会把"前 j 字节取新帧、其余取上一帧"的混合帧注入实测数据, -逐 j 报告检出率:证明这套判据对撕裂本身有灵敏度(否则 0 报警没有意义)。 - -用法: - python scripts/anal_ecat_jumps.py ecat_slots_raw.csv [--k 8] - python scripts/anal_ecat_jumps.py ecat_slots_raw.csv --selftest -""" -import csv -import statistics -import struct -import sys - -# (名称, 偏移, 解码器) -FIELDS = [ - ("mcuTemp", 0, lambda b: struct.unpack_from("<h", b, 0)[0] * 0.01), - ("motTemp", 2, lambda b: struct.unpack_from("<h", b, 2)[0] * 0.01), - ("uCur", 4, lambda b: struct.unpack_from("<h", b, 4)[0]), - ("vCur", 6, lambda b: struct.unpack_from("<h", b, 6)[0]), - ("wCur", 8, lambda b: struct.unpack_from("<h", b, 8)[0]), - ("flags", 10, lambda b: b[10]), - ("pwr_V", 11, lambda b: struct.unpack_from("<H", b, 11)[0] * 0.001), - ("rtAngle", 13, lambda b: struct.unpack_from("<f", b, 13)[0]), - ("rtVel", 17, lambda b: struct.unpack_from("<f", b, 17)[0]), - ("rtCur", 21, lambda b: struct.unpack_from("<f", b, 21)[0]), - ("testF", 25, lambda b: struct.unpack_from("<f", b, 25)[0]), - ("testU16", 29, lambda b: struct.unpack_from("<H", b, 29)[0]), - ("cpu%", 31, lambda b: b[31]), - ("initSt", 32, lambda b: struct.unpack_from("<I", b, 32)[0]), -] - - -def load(path): - """返回 {node: [(seq, 36B bytes), ...]} 按 seq 升序。""" - per = {} - with open(path, "r", encoding="ascii", errors="ignore") as f: - for row in csv.reader(f): - if not row or row[0] != "M": - continue - try: - node, ch, seq = int(row[2]), int(row[3]), int(row[4]) - payload = bytes.fromhex(row[7]) - except (ValueError, IndexError): - continue - if node <= 0 or len(payload) < 36: - continue - per.setdefault(node, []).append((seq, payload)) - for v in per.values(): - v.sort(key=lambda x: x[0]) - return per - - -def decode(payload): - out = {} - for name, off, fn in FIELDS: - try: - out[name] = float(fn(payload)) - except Exception: - out[name] = None - return out - - -def analyze(per, k=8.0, verbose=True): - """逐 (node,字段) 找孤立尖峰。返回总尖峰数与明细列表。""" - spikes, holds, details = 0, 0, [] - tails = [] - for node, rows in sorted(per.items()): - if len(rows) < 40: - continue - cols = [decode(p) for _, p in rows] - for name, _off, _fn in FIELDS: - v = [c[name] for c in cols] - finite = [x for x in v if x is not None and x == x and abs(x) != float("inf")] - if len(finite) < 40: - continue - deltas = [abs(v[i] - v[i - 1]) for i in range(1, len(v)) - if v[i] is not None and v[i - 1] is not None - and v[i] == v[i] and v[i - 1] == v[i - 1]] - if not deltas: - continue - # 尺度取 |Δ| 的 p99 分位数(不是 median):量化字段(mcuTemp 这类) - # 的 median(|Δ|)=0,用 median 会让阈值塌到 eps,正常的 1LSB 抖动 - # 全被判成尖峰。p99 代表"全体相邻差的尾部",阈值 = K×p99 才是 - # "远超正常抖动尾巴"的意思。 - ordered = sorted(deltas) - scale = max(ordered[min(len(ordered) - 1, int(len(ordered) * 0.99))], 1e-9) - mx, mxctx = 0.0, None - for i in range(1, len(v) - 1): - a, b, c = v[i - 1], v[i], v[i + 1] - if None in (a, b, c) or any(x != x for x in (a, b, c)): - continue - d = abs(b - a) - if d > mx: - mx, mxctx = d, (a, b, c) - if d <= k * scale: - continue - back = abs(c - a) <= 2 * k * scale - if back: - spikes += 1 - if len(details) < 25: - details.append((node, name, rows[i][0], scale, a, b, c, - rows[i][1].hex())) - else: - holds += 1 - if mxctx and mx > 3 * scale and len(tails) < 60: - tails.append((node, name, scale, mx, mxctx)) - if verbose: - print(f"孤立尖峰(撕裂签名) = {spikes} 跳变保持(真实突变) = {holds}") - for node, name, seq, scale, a, b, c, hexs in details[:12]: - print(f" node{node} {name}: seq={seq} 尺度={scale:.4g} " - f"{a:.6g} -> {b:.6g} -> {c:.6g}") - print(f" payload={hexs}") - if tails: - tails.sort(key=lambda x: -(x[3] / max(x[2], 1e-12))) - print("各字段最大相邻差(倍数 = 最大|Δ| / p99 尺度):") - for node, name, scale, mx, (a, b, c) in tails[:14]: - print(f" node{node} {name:9s} p99尺度={scale:.4g} 最大|Δ|={mx:.4g} " - f"({mx/max(scale,1e-12):.0f}×) {a:.6g} -> {b:.6g} -> {c:.6g}") - return spikes, holds, details - - -def selftest(per, k=8.0, n_inject=40): - """注入混合帧,测各边界 j 的检出率(证明判据有灵敏度)。""" - import random - random.seed(7) - base_sp, _bh, _bd = analyze(per, k=k, verbose=False) - nodes = [n for n, r in per.items() if len(r) >= 60] - if not nodes: - print("自检跳过:数据太短(每节点需 >=60 帧)") - return - print(f"自检:基线孤立尖峰 = {base_sp};向实测流注入『新帧前缀 j 字节 + 上一帧后缀』" - f"混合帧,每节点各 {n_inject} 次") - print(" j(字节) 净增尖峰/注入总数 检出率") - for j in (1, 4, 8, 12, 13, 16, 17, 20, 21, 24, 25, 28, 31, 32, 35): - rnd = random.Random(j) - merged = {} - for node in nodes: - rows = per[node] - idxs = rnd.sample(range(2, len(rows) - 2), min(n_inject, len(rows) - 6)) - new = list(rows) - for i in idxs: - cur, prev = rows[i][1], rows[i - 1][1] - new[i] = (rows[i][0], cur[:j] + prev[j:36]) - merged[node] = new - sp, _h, det = analyze(merged, k=k, verbose=False) - inj_n = n_inject * len(nodes) - net = sp - base_sp - worst = max((abs(d[5] - d[4]) for d in det), default=0.0) - print(f" j={j:<4d} {net:5d}/{inj_n:<5d} {100.0*net/inj_n:5.1f}% " - f"最大偏离≈{worst:.4g}") - print("(j 越大越靠尾部字节,混合后数值偏离越小、越难检出 —— 这是形态判据的物理上限)") - - -def main(): - args = [a for a in sys.argv[1:] if not a.startswith("--")] - if not args: - print(__doc__) - return - path = args[0] - k = 8.0 - if "--k" in sys.argv: - k = float(sys.argv[sys.argv.index("--k") + 1]) - per = load(path) - tot = sum(len(v) for v in per.values()) - print(f"读入 {path}: {tot} 帧,节点 {sorted(per)}") - for node, rows in sorted(per.items()): - print(f" node{node}: {len(rows)} 个不同 seq," - f"seq 范围 {rows[0][0]}..{rows[-1][0]}") - if "--selftest" in sys.argv: - selftest(per, k=k) - return - analyze(per, k=k) - - -if __name__ == "__main__": - main() diff --git a/scripts/build_ecat_report_html.py b/scripts/build_ecat_report_html.py deleted file mode 100644 index 9e20deb..0000000 --- a/scripts/build_ecat_report_html.py +++ /dev/null @@ -1,677 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""把扫描数据渲染成自包含 HTML 调研报告(图表为内联 SVG)。 - -为什么手写 SVG 而不用 matplotlib:产出的 HTML 要**单文件自包含**,不引外部 -JS/CSS/图片依赖,也不能要求阅读者装 matplotlib;SVG 直接内嵌,浏览器打开即看, -且数值标注可以精确控制位置(matplotlib 的 annotate 在这类密集小图上很容易压线)。 - -纪律同 plot_ecat_sweep.py: - * 图里画的**只有实测点**。模型/参考一律用虚线并写明"外推",不与实测混同。 - * 无功效的轴(整段恒零)不画成"平坦的零线",改成功效声明块。 - * 从站停报的档位(wkc 不达预期 / load_perm 为残值)一律排除出图,并在页面上 - 显式列出被排除的档位与原因 —— 空图比假图诚实。 - -用法: - python scripts/build_ecat_report_html.py - python scripts/build_ecat_report_html.py --out ECAT_RATE_TRADEOFF.html -""" -import argparse -import html -import json -import math -import os -import sys -from collections import OrderedDict - -HERE = os.path.dirname(os.path.abspath(__file__)) -ROOT = os.path.dirname(HERE) -sys.path.insert(0, HERE) -from ecat_sweep_common import post # noqa: E402 (仅用于探测 native 是否在跑) - -import csv as _csv - - -# ───────────────────────────────────────────────────────── 数据读取 ───────────────────────────────────────────────────────── -def read_csv(path): - if not os.path.isfile(path): - return [] - rows = [] - for r in _csv.DictReader(open(path, encoding="utf-8")): - def num(k): - v = r.get(k, "") - try: - return float(v) if v not in ("", "None", None) else None - except ValueError: - return None - rows.append({ - "period_ms": float(r["period_ms"]), - "hz_set": 1000.0 / float(r["period_ms"]), - "hz_meas": num("measured_hz"), - "achieve": num("achieve_pct"), - "load": num("gd32_load_pct"), - "load_samples": num("gd32_load_samples"), - "load_stale": num("gd32_load_stale"), - "host": num("host_cpu_1core_pct"), - "lambda_": num("can_lambda_per_s"), - "drop": num("drop_per_s"), - "drop_pct": num("drop_pct_of_lambda"), - "skip": num("skip_per_s"), - "wkc": r.get("wkc"), "exp_wkc": r.get("expected_wkc"), - "wr": r.get("wr"), "hijacked": r.get("hijacked"), - "pid_stable": r.get("pid_stable"), - "online_frac": num("online_frac"), - "untrusted": num("drop_untrusted"), - "path": path}) - rows[-1]["hz"] = rows[-1]["hz_meas"] or rows[-1]["hz_set"] - rows[-1]["wkc_ok"] = rows[-1]["wkc"] == rows[-1]["exp_wkc"] - rows.sort(key=lambda r: r["hz"]) - return rows - - -# ───────────────────────────────────────────────────────── SVG 图元 ───────────────────────────────────────────────────────── -def _ticks(lo, hi, n=5): - """线性刻度:给一组整齐步进的刻度值。""" - if hi <= lo: - return [lo] - raw = (hi - lo) / max(1, n - 1) - mag = 10 ** math.floor(math.log10(raw)) if raw else 1 - step = mag * next((k for k in (1, 2, 2.5, 5, 10) if raw / mag <= k), 10) - v = math.ceil(lo / step) * step - out = [] - while v <= hi + 1e-9: - out.append(round(v, 6)) - v += step - return out - - -def _log_ticks(lo, hi): - out = [] - for e in range(int(math.floor(math.log10(lo))), int(math.ceil(math.log10(hi))) + 1): - for m in (1, 2, 5): - v = m * (10 ** e) - if lo - 1e-9 <= v <= hi + 1e-9: - out.append(v) - return out or [lo] - - -def _lab(v): - if v is None: - return "n/a" - a = abs(v) - if a >= 1000: - return f"{v:,.0f}" - if a >= 100: - return f"{v:.0f}" - if a >= 10: - return f"{v:.1f}" - if a >= 1: - return f"{v:.1f}" if v != int(v) else str(int(v)) - return f"{v:.2f}" if a else "0" - - -class Svg: - """极简 SVG 累加器。""" - - def __init__(self, w, h): - self.w, self.h, self.b = w, h, [] - self.L, self.R = 64, w - 16 - self.T, self.B = 26, h - 42 - - def scale(self, xlog=False, ylog=False, xr=None, yr=None): - self.xlog, self.ylog = xlog, ylog - self.xr, self.yr = xr, yr - - def X(self, v): - if self.xlog: - lo, hi = self.xr - return self.L + (math.log10(max(v, 1e-9)) - math.log10(lo)) / \ - (math.log10(hi) - math.log10(lo)) * (self.R - self.L) - lo, hi = self.xr - return self.L + (v - lo) / (hi - lo or 1) * (self.R - self.L) - - def Y(self, v): - if self.ylog: - lo, hi = self.yr - return self.B - (math.log10(max(v, 1e-9)) - math.log10(lo)) / \ - (math.log10(hi) - math.log10(lo)) * (self.B - self.T) - lo, hi = self.yr - return self.B - (v - lo) / (hi - lo or 1) * (self.B - self.T) - - def grid(self, xt, yt, xf=_lab, yf=_lab): - for v in yt: - try: - y = self.Y(v) - except ValueError: - continue - if not (self.T - 1 <= y <= self.B + 1): - continue - self.b.append(f'<line x1="{self.L}" y1="{y:.1f}" x2="{self.R}" y2="{y:.1f}" ' - f'stroke="#e8ebf0" stroke-width="1"/>') - self.b.append(f'<text x="{self.L-8}" y="{y+3.5:.1f}" text-anchor="end" ' - f'font-size="10.5" fill="#6b7280">{html.escape(yf(v))}</text>') - for v in xt: - x = self.X(v) - if not (self.L - 1 <= x <= self.R + 1): - continue - self.b.append(f'<line x1="{x:.1f}" y1="{self.T}" x2="{x:.1f}" y2="{self.B}" ' - f'stroke="#eef1f5" stroke-width="1"/>') - self.b.append(f'<text x="{x:.1f}" y="{self.B+16}" text-anchor="middle" ' - f'font-size="10.5" fill="#6b7280">{html.escape(xf(v))}</text>') - - def axes(self, xl, yl): - self.b.append(f'<line x1="{self.L}" y1="{self.B}" x2="{self.R}" y2="{self.B}" ' - f'stroke="#9aa3af" stroke-width="1.2"/>') - self.b.append(f'<line x1="{self.L}" y1="{self.T}" x2="{self.L}" y2="{self.B}" ' - f'stroke="#9aa3af" stroke-width="1.2"/>') - self.b.append(f'<text x="{(self.L+self.R)/2:.0f}" y="{self.h-6}" text-anchor="middle" ' - f'font-size="11.5" fill="#374151">{html.escape(xl)}</text>') - self.b.append(f'<text transform="rotate(-90 14 {(self.T+self.B)/2:.0f})" x="14" ' - f'y="{(self.T+self.B)/2:.0f}" text-anchor="middle" font-size="11.5" ' - f'fill="#374151">{html.escape(yl)}</text>') - - def line(self, pts, color, dash=None, wid=2.4, marker="circle", r=4.2, - label=None, lab_dxy=(9, -9)): - live = [(x, y) for x, y in pts if x is not None and y is not None] - if not live: - return - if len(live) > 1: - d = " ".join(f"{'M' if i == 0 else 'L'}{self.X(x):.1f},{self.Y(y):.1f}" - for i, (x, y) in enumerate(live)) - da = f' stroke-dasharray="{dash}"' if dash else "" - self.b.append(f'<path d="{d}" fill="none" stroke="{color}" ' - f'stroke-width="{wid}"{da} stroke-linejoin="round"/>') - for i, (x, y) in enumerate(live): - cx, cy = self.X(x), self.Y(y) - if marker == "circle": - self.b.append(f'<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{r}" fill="{color}" ' - f'stroke="#fff" stroke-width="1.4"/>') - elif marker == "square": - self.b.append(f'<rect x="{cx-r:.1f}" y="{cy-r:.1f}" width="{2*r:.1f}" ' - f'height="{2*r:.1f}" fill="{color}" stroke="#fff" ' - f'stroke-width="1.4"/>') - elif marker == "tri": - p = f"{cx:.1f},{cy-r-1:.1f} {cx-r-1:.1f},{cy+r:.1f} {cx+r+1:.1f},{cy+r:.1f}" - self.b.append(f'<polygon points="{p}" fill="{color}" stroke="#fff" ' - f'stroke-width="1.4"/>') - if label and i == len(live) - 1: - dx, dy = lab_dxy - self.b.append(f'<text x="{cx+dx:.1f}" y="{cy+dy:.1f}" font-size="10.5" ' - f'fill="{color}" font-weight="600">{html.escape(label)}</text>') - - def vtext(self, x, y, txt, color="#374151", anchor="middle", size=10.5, weight="400"): - self.b.append(f'<text x="{self.X(x):.1f}" y="{self.Y(y):.1f}" text-anchor="{anchor}" ' - f'font-size="{size}" font-weight="{weight}" ' - f'fill="{color}">{html.escape(txt)}</text>') - - def legend(self, entries, x=None, y=None): - x = self.L + 10 if x is None else x - y = self.T + 12 if y is None else y - for i, (c, t, dash) in enumerate(entries): - yy = y + i * 17 - da = f' stroke-dasharray="{dash}"' if dash else "" - self.b.append(f'<line x1="{x}" y1="{yy}" x2="{x+22}" y2="{yy}" stroke="{c}" ' - f'stroke-width="2.6"{da}/>') - self.b.append(f'<circle cx="{x+11}" cy="{yy}" r="3.2" fill="{c}"/>') - self.b.append(f'<text x="{x+29}" y="{yy+3.6}" font-size="11" ' - f'fill="#374151">{html.escape(t)}</text>') - - def hline(self, v, color="#9ca3af", dash="3,3", wid=1.2): - y = self.Y(v) - if not (self.T - 1 <= y <= self.B + 1): - return - self.b.append(f'<line x1="{self.L}" y1="{y:.1f}" x2="{self.R}" y2="{y:.1f}" ' - f'stroke="{color}" stroke-width="{wid}" stroke-dasharray="{dash}"/>') - - def band(self, x0, x1, color="#ef4444", alpha=0.07): - a, b = self.X(x0), self.X(x1) - self.b.append(f'<rect x="{min(a,b):.1f}" y="{self.T}" width="{abs(b-a):.1f}" ' - f'height="{self.B-self.T}" fill="{color}" opacity="{alpha}"/>') - - def note(self, txt, x=None, y=None, color="#6b7280", anchor="start", size=10.5): - x = self.L + 6 if x is None else x - y = self.B - 8 if y is None else y - self.b.append(f'<text x="{x}" y="{y}" text-anchor="{anchor}" font-size="{size}" ' - f'fill="{color}">{html.escape(txt)}</text>') - - def finish(self, title, sub=""): - head = "" - if sub: - head = f'<text x="{self.L-40}" y="15" font-size="10.5" fill="#6b7280">' \ - f'{html.escape(sub)}</text>' - return (f'<svg viewBox="0 0 {self.w} {self.h}" width="100%" ' - f'xmlns="http://www.w3.org/2000/svg" role="img" aria-label="{html.escape(title)}">' - f'{head}{"".join(self.b)}</svg>') - - -# ───────────────────────────────────────────────────────── 具体图 ───────────────────────────────────────────────────────── -C_RED, C_BLUE, C_GREEN, C_PINK, C_ORANGE, C_GREY, C_PURPLE = ( - "#dc2626", "#2563eb", "#16a34a", "#db2777", "#ea580c", "#6b7280", "#7c3aed") - - -def chart_achieve(rows, rows_no=()): - s = Svg(560, 320) - allr = list(rows) + list(rows_no) - lo = min(r["hz_set"] for r in allr) * 0.7 - hi = max(r["hz_set"] for r in allr) * 1.5 - s.scale(True, True, (lo, hi), (lo, hi)) - s.grid(_log_ticks(lo, hi), _log_ticks(lo, hi)) - s.line([(lo, lo), (hi, hi)], C_GREY, dash="5,4", label=None) - s.line([(r["hz_set"], r["hz_meas"]) for r in rows], C_RED, marker="circle") - s.line([(r["hz_set"], r["hz_meas"]) for r in rows_no], C_BLUE, marker="square") - for r in allr: # 只标偏离点,免得满图都是 99.x% - if (r["achieve"] or 100) < 99.5: - s.vtext(r["hz_set"], r["hz_meas"] * 0.72, - _lab(r["achieve"]) + "%", color="#1e40af", size=9.5, anchor="middle") - s.legend([(C_GREY, "理想 1:1(设定率)", "5,4"), - (C_RED, "实测循环率 · 带 CAN", None), - (C_BLUE, "实测循环率 · 无 CAN", None)], x=s.L + 12, y=s.T + 46) - s.note("标注为达成率%(<99.5% 才标)。无 CAN 设定 >8kHz 后实测全顶在 ≈8.2kHz —— 硬上限", - y=s.B - 8) - s.axes("设定循环率 (Hz,对数)", "实测循环率 (Hz,对数)") - return s.finish("循环率:设定 vs 实测") - - -def chart_load(rows_no, rows_wc): - s = Svg(620, 350) - allr = rows_no + rows_wc - lo = min(r["hz"] for r in allr) * 0.75 - hi = max(r["hz"] for r in allr) * 1.35 - s.scale(True, False, (lo, hi), (0, 112)) - s.grid(_log_ticks(lo, hi), _ticks(0, 110, 6)) - s.hline(100, color="#6b7280", dash="2,4") - s.b.append(f'<text x="{s.R-4}" y="{s.Y(100)-5:.1f}" text-anchor="end" font-size="10" ' - f'fill="#6b7280">100%</text>') - pts_wc = [(r["hz"], r["load"]) for r in rows_wc if r["load"] is not None] - pts_no = [(r["hz"], r["load"]) for r in rows_no if r["load"] is not None] - s.line(pts_wc, C_RED, marker="circle") - s.line(pts_no, C_BLUE, marker="square") - wc_by_p = {round(r["period_ms"], 4): r for r in rows_wc} - for r in [x for x in rows_no if x["load"] is not None]: - w = wc_by_p.get(round(r["period_ms"], 4)) - if w and w["load"] is not None: - # 任一侧饱和(≥95%)时 Δ 只反映夹紧,不标注 - if (r["load"] or 0) >= 95 or (w["load"] or 0) >= 95: - continue - s.vtext(r["hz"], (r["load"] + w["load"]) / 2, f"Δ{w['load']-r['load']:.1f}pp", - color=C_PURPLE, size=10, weight="600") - s.legend([(C_RED, "带 CAN(电机在报 STATUS/CONFIG)", None), - (C_BLUE, "拔掉全部 CAN 设备", None)], x=s.L + 12, y=s.T + 12) - s.note("纵轴是固件 load_perm(只计 ISR+非延迟段,排除 SSC 轮询)—— 不是整机 CPU 占用率", - y=s.B - 8) - s.note("无 CAN 补测:0.25/0.125ms 满速;设定再快实测全顶在 ≈8.2kHz。" - "≥4kHz 的负载读数全是饱和值,不能定位拐点", y=s.B - 22, color="#b45309") - s.note("带 CAN 的 0.25/0.125ms 两点同理停在 ~99.x% —— 饱和读数不等于\"已测到上限\"", - y=s.B - 36, color="#b45309") - s.axes("实测循环率 (Hz,对数)", "从站 load_perm (%)") - return s.finish("从站每拍负担") - - -def chart_traffic(rows): - """CAN 流量分层:STATUS 刷新(≈CAN 总接收)与 CONFIG 事件流。 - - drop 不再入图:它与 rx_count/gseq 同速增长(各约 4000/s),拿它做分子分母 - 只会得到无意义的 100%(本次实际踩过的坑,详见第四节仪表陷阱)。 - """ - s = Svg(620, 350) - ok = [r for r in rows if (r["lambda_"] or 0) > 0] - if not ok: - s.note("无可用的 CAN 流量档位(本轮 CAN 已拔除)", y=(s.T + s.B) / 2, anchor="middle") - return s.finish("CAN 流量分层", "(无数据)") - lo = min(r["hz"] for r in ok) * 0.75 - hi = max(r["hz"] for r in ok) * 1.35 - s.scale(True, True, (lo, hi), (10, 20000)) - s.grid(_log_ticks(lo, hi), _log_ticks(10, 20000)) - s.line([(r["hz"], r["lambda_"]) for r in ok], C_GREEN, marker="circle") - s.band(2500, 9000) - s.vtext(1400, 4000 * 1.35, "带 CAN 轮:STATUS 全流量", color="#166534", size=10) - s.vtext(4200, 150, "塌陷", color="#b91c1c", size=11, weight="700") - s.legend([(C_GREEN, "CAN 总接收 rx_count ≡ STATUS 刷新 gseq(实测同为 ~4000/s)", None)], - x=s.L + 12, y=s.T + 12) - s.note("CONFIG 事件流独立实测仅约 92 帧/s(去重后),不随周期放大", y=s.B - 8) - s.note(">2kHz 处 STATUS 与 CONFIG 一同塌到 ~150/s", y=s.B - 22, color="#b45309") - s.axes("实测循环率 (Hz,对数)", "帧/s(对数)") - return s.finish("CAN 流量分层") - - -def chart_skip(rows_no, rows_wc): - """skip 有真 0 档 → 对数轴画不了。做法:>0 的点画在对数区,真 0/近 0 档 - 落到轴下方的"≈0"标记轨道,两种点含义不同、不混在同一条线上。""" - s = Svg(560, 320) - pos = [r for r in rows_no + rows_wc if (r["skip"] or 0) > 0.05] - zero = [r for r in rows_no + rows_wc if (r["skip"] or 0) <= 0.05] - if not pos: - s.note("本轮无 skip>0 的档位", y=(s.T + s.B) / 2, anchor="middle") - return s.finish("主站错拍率", "(无数据)") - lo = min(r["hz"] for r in pos) * 0.75 - hi = max(r["hz"] for r in pos) * 1.35 - vlo, vhi = max(min(r["skip"] for r in pos), 0.05), max(r["skip"] for r in pos) * 3 - s.T -= 6 - s.scale(True, True, (lo, hi), (vlo, vhi)) - s.grid(_log_ticks(lo, hi), _log_ticks(vlo, vhi)) - s.line([(r["hz"], r["skip"]) for r in rows_wc if (r["skip"] or 0) > 0.05], - C_RED, marker="circle") - s.line([(r["hz"], r["skip"]) for r in rows_no if (r["skip"] or 0) > 0.05], - C_BLUE, marker="square") - for r in zero: # ≈0 轨道:贴在轴下方,明确不属于对数刻度 - y = s.B + 4 - mk = ("circle" if r in rows_wc else "square") - col = C_RED if r in rows_wc else C_BLUE - cx = s.X(r["hz"]) - s.b.append(f'<path d="M{cx-4:.1f},{y+8:.1f} L{cx:.1f},{y:.1f} ' - f'L{cx+4:.1f},{y+8:.1f}" fill="none" stroke="{col}" stroke-width="2"/>') - ent = [(C_RED, "带 CAN", None), (C_BLUE, "无 CAN", None)] - if zero: # 没有真 0 档时不要挂一条对不上号的图例 - ent.append(("#9ca3af", "▽ = skip ≤0.05 次/s(真 0 档,不在对数刻度上)", None)) - s.legend(ent, x=s.L + 12, y=s.T + 12) - s.note("skip=错过的网格拍。0.0625ms 设定档 15720 次/s:实测率被钉在 8.2kHz," - "错拍数随设定率线性涨", y=s.B - 10) - s.axes("实测循环率 (Hz,对数)", "主站错拍 skip_total (次/s,对数)") - return s.finish("主站错拍率") - - -# ───────────────────────────────────────────────────────── 页面组装 ───────────────────────────────────────────────────────── -PAGE = """<!DOCTYPE html> -<html lang="zh-CN"><head><meta charset="utf-8"> -<meta name="viewport" content="width=device-width,initial-scale=1"> -<title>EtherCAT 周期速度 × 故障率 × CPU 占用 调研 -

    -{body} -
    -""" - - -def tbl(rows, cols, cap): - h = [f'

    {html.escape(cap)}

    ' + - "".join(f"" for t, _ in cols) + ""] - for r in rows: - cls = "" - if r.get("wkc_ok") is False: - cls = ' class="dead"' - elif r["period_ms"] == 0.5: - cls = ' class="best"' - h.append(f"" + "".join(f"" for _, f in cols) + "") - h.append("
    {html.escape(t)}
    {f(r)}
    ") - return "".join(h) - - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument("--nocan", default=os.path.join(ROOT, "lab", "ecat_rate_sweep_nocan.csv")) - ap.add_argument("--withcan", default=os.path.join(ROOT, "lab", "ecat_rate_sweep_withcan.csv")) - ap.add_argument("--calib", default=os.path.join(ROOT, "lab", "ecat_calib.json")) - ap.add_argument("--out", default=os.path.join(ROOT, "ECAT_RATE_TRADEOFF.html")) - a = ap.parse_args() - - no, wc = read_csv(a.nocan), read_csv(a.withcan) - if not wc: - raise SystemExit(f"缺带 CAN 数据:{a.withcan}") - cal = json.load(open(a.calib, encoding="utf-8")) if os.path.isfile(a.calib) else {} - no_ok = [r for r in no if r["wkc_ok"] and r["load"] is not None] - wc_ok = [r for r in wc if r["wkc_ok"] and r["load"] is not None] - excl = [r for r in no if not r["wkc_ok"] or r["load"] is None] - - # 电机刷新率:带 CAN 那轮的 8s/档定点实测。(周期文本, 实测Hz, 刷新率帧/s) - # 周期直接带文本 —— 由 hz 反算会浮点漂移(1000/1999 → 显示成 0.50025ms)。 - motor = [("1", 1000.0, 4000.2), ("0.5", 1999.0, 4000.2), - ("0.25", 3989.0, 87.9), ("0.125", 7762.0, 147.5)] - # 跨轮配对必须用**设定周期**做键:两轮的实测 hz 会差零点几(1999.3 vs 1999.6), - # 拿四舍五入的 hz 当键会把 0.5ms 档静默丢掉,KPI 均值就跟着算错。 - wc_by_period = {round(r["period_ms"], 4): r for r in wc_ok} - deltas, unm, sat = [], [], [] - for r in no_ok: - w = wc_by_period.get(round(r["period_ms"], 4)) - if w: - # 任一侧 load 已饱和(≥95%)时 Δ 只反映夹紧,不代表 CAN 负担 - if (r["load"] or 0) >= 95 or (w["load"] or 0) >= 95: - sat.append(r["period_ms"]) - else: - deltas.append(w["load"] - r["load"]) - else: - unm.append(r["period_ms"]) - if unm: - print("!! 无 CAN 档位在带 CAN 轮中找不到配对,已从 Δ 中排除:", unm) - if sat: - print(" 饱和档(Δ 不计入):", sat) - - b = [f'

    EtherCAT 周期速度 × 故障率 × CPU 占用 调研

    ', - f'
    调研日期 {html.escape(str(cal.get("started_at","2026-09-08"))[:10])}' - f' · 本机 I210 + 1 个 GD32E508 从站 · 两轮扫描:带 CAN / 拔掉全部 CAN' - f' · 数据 lab/ecat_rate_sweep_*.csv · 生成器 ' - f'scripts/build_ecat_report_html.py
    '] - - b.append('
    ' - '
    0.5ms实测可用上限(2kHz,带 CAN 工况)
    ' - f'
    ≈{(sum(deltas)/len(deltas) if deltas else 0):.1f}pp' - f'CAN 每拍恒定负担({len(deltas)} 档配对,饱和档除外)
    ' - '
    4000 帧/s电机 STATUS 实测刷新(1/0.5ms)
    ' - '
    ≈8.2kHz纯 ECAT 实测硬上限(设 10/12.5/16kHz 实测均不涨)
    ') - - b.append('

    结论

      ' - '
    • 带 CAN 工况的可用上限在 0.5ms,瓶颈在从站 GD32 不在主站。' - '实测可用到 0.5ms;0.25ms 起电机数据刷新率从 4000 帧/s 掉到 88 帧/s' - '(带 CAN 那轮当场确认 online=True,是真失效)。' - '可用上限只夹在 (0.25ms, 0.5ms] 内,具体位置没测到。
    • ' - '
    • 纯 ECAT 硬上限 ≈8.2kHz,这次测到了墙本身。断电重启后无 CAN 补测:' - '0.25/0.125ms 满速通过(99.7% / 98.0%);再往下设 0.1 / 0.08 / 0.0625ms,' - '实测循环率全部顶在 8196–8238Hz(达成 82.3% / 65.9% / 51.2%),' - 'wkc 全程 3/3、从站始终应答。墙的来历:主站单拍硬耗时 cyc p50 ' - '各档恒 117–126µs,1/121µs ≈ 8.2kHz —— 是"每拍必须花的往返+处理时间",' - '不是主机 CPU 不够(顶墙时主机 CPU 反而降到 ~85%)。原「3.5kHz 外推」' - '被证伪;首轮无 CAN 0.25ms 停报为残状态偶发,不复现。' - '注意"跑得快"≠"能用":带 CAN 的可用上限仍由电机数据刷新决定(下一条)。
    • ' - '
    • 拔 CAN 对照解开了混淆:未饱和三档 Δ 稳定 8.7–9.3pp,' - '不随频率张开 → 快档塌陷不是 CAN 挤出来的;饱和两档 Δ 收窄到 ≤0.5pp,' - '正是读数被夹紧的特征。
    • ' - '
    • 主机 native CPU 恒约 100% 单核、与周期无关(自旋恒转,' - '/api/motor/spin 已是空操作)。唯一的例外是带 CAN 轮的 ' - '0.125ms 档降到 87.5% —— 因为从站已停答。无 CAN 补测里从站活着,' - '同档就回到 99.7%。主机 CPU 掉下来那一刻,正是从站死掉那一刻。
    • ' - '
    • 建议:要吃到余量就停在 0.5ms。0.25ms 不是"更快",是功能损坏。
    • ' - '
    ') - - b.append('

    一、曲线

    ') - b.append('
    ') - b.append(f'
    ① 循环率:设定 vs 实测(主站侧)
    ' - f'{chart_achieve(wc_ok, no_ok)}
    ') - b.append(f'
    ② 从站每拍负担:带 CAN vs 无 CAN
    ' - f'{chart_load(no_ok, wc_ok)}
    ') - b.append(f'
    ③ CAN 流量分层:STATUS 刷新与 CONFIG 事件流
    ' - f'{chart_traffic(wc_ok)}
    ') - b.append(f'
    ④ 主站错拍率
    ' - f'{chart_skip(no_ok, wc_ok)}
    ') - b.append('
    ') - - if excl: - b.append('
    已从图中排除的档位:' + ";".join( - f"{r['period_ms']}ms({'wkc 不达预期,从站停报' if not r['wkc_ok'] else '无有效负载读数'}" - f",load_perm 为残值)" for r in excl) + '。该档已由断电重启后的补测覆盖:' - '无 CAN 的 0.25 / 0.125ms 两点均已满速测得(见无 CAN 表)。空图比假图诚实。
    ') - - b.append('

    名词解释(表里这几个计数器到底是什么)

      ' - '
    • WKC(working counter,工作计数器):EtherCAT 报文自带一个计数器,' - '每有一个从站硬件处理了该报文就 +1。主站知道自己发出了几个报文,' - '这个应得值就是 expected_wkc(本台子 1 个从站、每拍 3 个报文 ' - '→ 期望 3)。所以 wkc 3/3 = 从站每拍都完整处理了;' - 'wkc -1 = 整帧没回来(从站停答/掉 OP);' - 'wr(watchdog reset)0→1 = 看门狗把总线重拉过一次。
    • ' - '
    • 主站错拍 skip:主站按周期给自己排了一个"节拍网格"' - '(ecat_native.c:776-791)。每拍做完 EtherCAT 收发后,' - '若发现已经错过了下一个节拍点(甚至错过多个),就把错过的个数累进 ' - 'skip_total。它量的是"主机软件迟到",不是从站故障 —— ' - '1ms 档 1.1 次/s 只是偶发 OS 调度抖动;0.125ms 档 5107 次/s 说明' - '单拍耗时已超过两个周期,主机也在勉强。它不区分"从站慢"和"主机慢"。
    • ' - '
    • 在线:电机槽位的 online 标志(槽位数据在超时窗内' - '持续刷新才为 true)。带 CAN 轮显示"未记录"是因为当时脚本还没有这列,' - '不是电机掉线;该轮的电机在线状况另有 8s/档定点实测佐证。
    • ' - '
    ') - - b.append('

    二、数据全表

    ') - fmt = lambda v, p=1, suf="": ("n/a" if v is None else f"{v:,.{p}f}{suf}") - base = [("周期", lambda r: f"{r['period_ms']:g}ms"), - ("实测Hz", lambda r: fmt(r["hz_meas"], 0)), - ("达成率", lambda r: fmt(r["achieve"], 1, "%")), - ("从站load", lambda r: fmt(r["load"], 1, "%")), - ("CAN接收/s", lambda r: fmt(r["lambda_"], 0)), - ("skip/s", lambda r: fmt(r["skip"], 2)), - ("wkc", lambda r: f'{html.escape(str(r["wkc"]))}/{html.escape(str(r["exp_wkc"]))}')] - cols_wc = base + [("在线", lambda r: "未记录*")] - cols_no = base + [("在线", lambda r: fmt(r["online_frac"], 2))] - b.append(tbl(wc, cols_wc, "带 CAN(主结论轮)")) - b.append(tbl(no, cols_no, "拔掉全部 CAN 设备(补测轮)")) - b.append('

    * 「在线」列是扫描脚本后来才加的(' - 'motors_online/online_frac),带 CAN 那轮跑在加列' - '之前,逐档数据没采 —— 不是全部掉线。该轮电机在线另有' - ' 8s/档定点实测(「电机数据刷新率」表)佐证。

    ') - mrows = "".join( - f"{lab}ms{v:,.1f}" - f"{'正常' if v > 3000 else '塌陷'}" - for lab, _hz, v in motor) - b.append('

    电机数据刷新率(带 CAN 轮,8s/档定点实测)

    ' - '' + mrows + '
    周期刷新率 帧/s判定
    ' - '

    这是唯一直接回答"用户还能不能用"的指标 —— λ 与 drop 都是中间量。' - '塌陷两档当场确认 online=True,故为真失效而非掉线。

    ') - - b.append('

    三、机理与上限推算

    ') - b.append('

    上限已实测:≈8.2kHz,墙是单拍 ≈121µs 的硬耗时

    ' - '

    证据链:无 CAN 设定 0.25 → 0.0625ms 五档,实测循环率在 ≤0.125ms ' - '(8kHz)前跟随设定,再快就被钉死在 8196–8238Hz;与此同时主站单拍耗时 ' - 'cyc p50 在所有档位(含 2ms 慢档)恒为 117–126µs,' - '1/121µs ≈ 8.2kHz,与顶墙实测严丝合缝。即:每拍有一段不可压缩的' - '「发送→从站处理→回读+主站处理」往返时间,周期设定小于它时就堵在这里。

    ' - '

    排除项:不是主机 CPU 不够 —— 顶墙档主机 CPU 只有 ~85%' - '(自旋等待占比变小);不是从站拒答 —— wkc 全程 3/3。' - 'load_perm 在 ≥4kHz 全饱和 99.x%,既定位不了拐点也喂不准模型,' - '早先「两项模型 load=F/T+R → 2.7/3.5kHz」的外推已被满速补测证伪,仅留档。

    ' - '

    未闭合的一处:121µs 里主站协议栈与从站 ESC/固件各占多少,' - '本轮数据分不开 —— 需给固件 fillRxWindow 加直接计时' - '(固件改动,需批准)或抓总线帧时序才能归因。

    ') - - b.append('

    四、判据口径与踩过的坑

    ') - b.append('

    仪表陷阱(每条都实际踩过)

      ' - '
    • 【本次真正翻车处】把两个同速计数器相除当成比率 —— ' - 'drop/rx_count 都约 4000/s,相除恒得 100%,' - '看着像"全丢",其实只是同速量之比。判丢帧必须有独立的分子与分母:' - '本次真正的独立证据是「STATUS 实测 4001 帧/s 正常刷新」+「CONFIG 事件流去重后' - '仅约 92 帧/s」,两者一起否掉了 100% 丢帧。任何"A/B 比率"指标,先问 A 与 B ' - '是否独立计数。
    • ' - '
    • 从站停报后 load_perm 保留残值 —— 实测 ' - 'age_ms=214338 时仍报 99.2%。任何取 load_perm 的脚本必须先过 ' - 'age_ms 闸。扫描脚本已加该闸并新增 ' - 'gd32_load_samples/gd32_load_stale 两列。
    • ' - '
    • load_perm 不是整机 CPU 占用率 —— 固件 ' - 'sys/cpu_load.h 定义收窄为"中断+非延迟处理段",' - '排除 SSC MainLoop 轮询;当前 free-run(dc=0)下 PDO ' - '应用走主循环轮询路径、不计入。所以不能把"load→100%"读成"CPU 耗尽"。
    • ' - '
    • OVF 每拍被 s_rxWinOvf=0 清零 —— GD32 侧' - '没有可用的累计丢帧计数。读它恒 0 不能当"无丢帧"证据' - '(我据此误判 bridge.drop 是死字段,结论已收回)。
    • ' - '
    • /api/can/cadence 只存最近 512 拍 —— 0.25ms 档 ' - '=128ms、0.125ms=64ms,环没装满时取到的 count/lost ' - '是行数不是每拍帧数。对应 CSV 列已从结论排除。
    • ' - '
    • u16 回绕:drop 在 4000/s 下 16 秒绕一圈 → 10Hz 逐次累加 ' - '+ 取模,单次增量>32768 记为作废。本次作废点数=0。
    • ' - '
    • 「seq 值域 − distinct」不是丢帧率:报 99.686%,方向与 drop≈λ 一致' - '但分母含 STATUS 全流量,口径不干净 → 只作旁证。
    • ' - '
    • 周期可被 localStorage 劫持(transport-ecat.js:227-228)' - '→ 每档下发后回读校验。本次六档 hijacked=no、native PID 全程 29284 未变。
    • ' - '
    • cyc_us.late/lost 恒 0(遗留占位,未接线),未使用。
    • ' - '
    ') - - b.append('

    五、可信度边界

    ') - fw = cal.get("firmware", {}) - b.append('

    这份数据不能证明什么

      ' - f'
    • 板载固件版本无法确认:烧写台账 ' - f'{html.escape(str(fw.get("ledger",""))[:16])}… 与在盘 .hex 展平哈希' - f'({html.escape("、".join(str(c.get("sha256",""))[:8]+"…" for c in fw.get("candidates",[])))})' - f'均不匹配,且产物时间戳晚于台账。→ 本报告不复用任何历史固件结论。
    • ' - '
    • 主站在测量期间又重编过(源码 02:39 / exe 02:40,PID 起于 02:32),' - '无法证明运行中的进程就是盘上那份。
    • ' - '
    • rx_torn 与 pdo_reentry 整段恒零 = 无检出功效,' - '不是"无撕裂"。二者分别需固件 1.6.6/1.6.7+,版本未知 → 无法区分"没发生"与' - '"字段根本没实现、恒读 0"。
    • ' - '
    • λ≈3993/s 是本次 CAN 工况。λ 变则负担变,0.5ms 这个数不可外搬到别流量。
    • ' - '
    • 单台单次,未做重复性与温漂验证。
    • ' - '
    ') - - b.append('

    六、下一步

    ') - b.append('

    按优先级

      ' - '
    • 查明 drop 口径(当前最高优先,唯一未决的承重项)。' - '必须用一对独立可比的计数,不能再拿同速量相除:从 WiFi 侧读固件 u32 ' - 'rx_drop_count(app_spi_cmd_can_feedback.cpp:99)' - '对速率,同时用 CAN 监控页统计实收 CONFIG 帧数作分母。' - '只有「独立到达计数 − 独立交付计数」才是丢帧数。
    • ' - '
    • 台子现状:断电重启后重新 open+start 成功(OP、wkc=3),' - '无 CAN 的 0.25→0.0625ms 五档补测完成,周期已自动恢复 1ms。' - 'CAN 设备当前处于拔除状态,电机数据相关指标要插回 CAN 才能再测。
    • ' - '
    • 插回 CAN 后补测 0.35 / 0.4 / 0.5ms 三档的电机数据刷新率,' - '把带 CAN 工况的可用上限定位出来。load_perm 已被证明在快档饱和,' - '不能再用它定位拐点;扫描脚本的 ' - 'motors_online/online_frac/' - 'motor_refresh_per_s 三列才是可用性判据。
    • ' - '
    • 给 fillRxWindow 加直接计时,把 121µs 硬耗时里的主/从占比' - '拆开(属固件改动,需批准)。
    • ' - '
    • 0.5ms 档实跑电机页完整命令序列 —— 本调研只测了计数器,没测界面实际可用性。
    • ' - '
    ') - - live = "" - try: - h = post("http://127.0.0.1:8090", "/api/native/health", timeout=1.5) - st = post("http://127.0.0.1:8090", "/api/motor/state", timeout=1.5) - live = (f'native 实时:op={h.get("op")} loop={h.get("loop")} wkc={h.get("wkc")} ' - f'period={st.get("period_ms")}ms') - except Exception: - live = "native 当前不可达(生成本报告时未连上 8090)" - b.append(f'
    本报告由 scripts/build_ecat_report_html.py 从 ' - f'lab/*.csv 与 lab/ecat_calib.json 现场渲染,图中每个数值均' - f'从原始数据计算,未手抄。{html.escape(live)}。' - f'Markdown 版(含同源结论):ECAT_RATE_TRADEOFF.md。
    ') - - out = a.out - with open(out, "w", encoding="utf-8") as f: - f.write(PAGE.format(body="\n".join(b))) - print("HTML 报告 ->", out) - print(f" 参与绘图:带CAN {len(wc_ok)} 档、无CAN {len(no_ok)} 档;排除 {len(excl)} 档") - if deltas: - print(f" CAN 负担 Δ:{'、'.join(f'{d:.1f}pp' for d in deltas)}") - - -if __name__ == "__main__": - main() diff --git a/scripts/build_integral_report_html.py b/scripts/build_integral_report_html.py deleted file mode 100644 index e275e67..0000000 --- a/scripts/build_integral_report_html.py +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""生成对接电机积分差归因 HTML(通俗说明 + 标注图)。""" -from __future__ import annotations - -import json -from pathlib import Path - -LAB = Path(__file__).resolve().parents[1] / "lab" -SRC = LAB / "ecat_who_slow.json" -OUT = LAB / "ecat_integral_report.html" - -data = json.loads(SRC.read_text(encoding="utf-8")) -pa = data["phase_attribution"] -strokes = data["strokes"] -sample = data.get("plot_sample") or [] -verdict = data.get("verdict") or {} - -# 用行程数据修正通俗结论(避免停车检测为 0 的误导) -on_vals = pa["on_ms_n3_minus_n2"]["values"] -# 剔除 |Δ|>50ms 的伪起动(上一段未停稳造成的重叠) -on_clean = [v for v in on_vals if abs(v) <= 50] -on_overlap = [v for v in on_vals if abs(v) > 50] -I_on = [s["I_on"]["Isum"] for s in strokes] -I_cr = [s["I_cruise"]["Isum"] for s in strokes] -I_st = [s["I_stop"]["Isum"] for s in strokes] -only2 = [s["stop_only_n2"]["Isum"] for s in strokes] -only3 = [s["stop_only_n3"]["Isum"] for s in strokes] -# 停止窗谁残速大:I_st>0 → 合速度偏正 → 镜像下更像 N3 仍正/N2 已减 -# 直接用 only_* 贡献绝对值 -sum_only2 = sum(abs(x) for x in only2) -sum_only3 = sum(abs(x) for x in only3) -n_only2 = sum(1 for x in only2 if abs(x) > 0.2) -n_only3 = sum(1 for x in only3 if abs(x) > 0.2) - -cruise_v2 = pa["cruise_mean_abs_v2"]["mean"] -cruise_v3 = pa["cruise_mean_abs_v3"]["mean"] -cruise_bias = pa["cruise_bias_v2_minus_v3"]["mean"] - -mean_on_clean = sum(on_clean) / len(on_clean) if on_clean else None -mean_Ion = sum(I_on) / len(I_on) if I_on else 0 -mean_Icr = sum(I_cr) / len(I_cr) if I_cr else 0 -mean_Ist = sum(I_st) / len(I_st) if I_st else 0 -abs_Ist = sum(abs(x) for x in I_st) / len(I_st) if I_st else 0 -abs_Ion = sum(abs(x) for x in I_on) / len(I_on) if I_on else 0 -abs_Icr = sum(abs(x) for x in I_cr) / len(I_cr) if I_cr else 0 - -# SVG 折线 -W, H = 960, 420 -PAD_L, PAD_R, PAD_T = 56, 20, 36 - - -def poly(xs, ys, y0, y1, top, h, color, w=1.3): - if not xs: - return "" - lo, hi = y0, y1 - if hi - lo < 1e-9: - hi = lo + 1 - - def X(t, t0, t1): - span = max(t1 - t0, 1e-6) - return PAD_L + (t - t0) / span * (W - PAD_L - PAD_R) - - def Y(v): - return top + (hi - v) / (hi - lo) * h - - pts = " ".join(f"{X(xs[i], xs[0], xs[-1]):.1f},{Y(ys[i]):.1f}" for i in range(len(xs))) - return f'' - - -if sample: - ts = [p["t"] for p in sample] - v2 = [p["v2"] for p in sample] - v3 = [p["v3"] for p in sample] - vsum = [p["vsum"] for p in sample] - Isum = [p["Isum"] for p in sample] - t0, t1 = ts[0], ts[-1] - vmin = min(min(v2), min(v3)) - vmax = max(max(v2), max(v3)) - imin = min(min(Isum), 0) - imax = max(max(Isum), 0) - padv = (vmax - vmin) * 0.08 or 10 - padI = (imax - imin) * 0.1 or 2 - - def X(t): - return PAD_L + (t - t0) / max(t1 - t0, 1e-6) * (W - PAD_L - PAD_R) - - def Yv(v): - return 48 + (vmax + padv - v) / (vmax - vmin + 2 * padv) * 100 - - def Ys(v): - return 190 + (vmax + padv - v) / (vmax - vmin + 2 * padv) * 80 - - def YI(v): - return 300 + (imax + padI - v) / (imax - imin + 2 * padI) * 80 - - def line(vals, yfn, color, w=1.3): - pts = " ".join(f"{X(ts[i]):.1f},{yfn(vals[i]):.1f}" for i in range(len(ts))) - return f'' - - # 行程分界竖线 - vlines = [] - for sm in data.get("stroke_marks") or []: - if sm["t0"] <= t1: - x = X(sm["t0"]) - vlines.append( - f'' - ) - vlines.append( - f'#{sm["i"]}' - ) -else: - ts = v2 = v3 = vsum = Isum = [] - t0 = t1 = 0 - vlines = [] - -# 标注箭头:在图上用文字+简单箭头路径 -notes_svg = [] -if sample: - # 找几个典型点 - # 1) 巡航:|v| 稳定处 - # 2) 停车:vsum 尖峰 - peak_i = max(range(len(sample)), key=lambda i: abs(sample[i]["vsum"])) - # 理论零线 - notes_svg.append( - f'' - f'对接电机 2/3:速度、合速度 v2+v3、积分 ∫(v2+v3)dt(虚线为理论 0)' - ) - -# 表格行 -rows_html = [] -for s in strokes: - cls_on = "bad" if abs(s["on_ms_n3_minus_n2"]) > 50 else "ok" - cls_cr = "ok" - cls_st = "warn" if abs(s["I_stop"]["Isum"]) > 3 else "ok" - who = "" - if abs(s["stop_only_n3"]["Isum"]) > abs(s["stop_only_n2"]["Isum"]) and abs(s["stop_only_n3"]["Isum"]) > 0.2: - who = "N3 残速" - elif abs(s["stop_only_n2"]["Isum"]) > 0.2: - who = "N2 残速" - else: - who = "两台接近同步刹停" - rows_html.append(f""" - - #{s['i']}{s['dir_n2']} - {s['on_ms_n3_minus_n2']:+.2f} ms - {s['cruise']['mean_abs_v2']:.2f} / {s['cruise']['mean_abs_v3']:.2f} - {s['I_on']['Isum']:+.2f} - {s['I_cruise']['Isum']:+.2f} - {s['I_stop']['Isum']:+.2f} - {s['stop_only_n2']['Isum']:+.2f} - {s['stop_only_n3']['Isum']:+.2f} - {who} - {s['da2_plus_da3']:+.2f}° - """) - -# 通俗结论文本 -if abs_Ist > abs_Icr * 3 and abs_Ist > abs_Ion: - main_src = "停车阶段" -elif abs_Ion > abs_Ist and abs_Ion > abs_Icr: - main_src = "行程衔接/起动窗口(含上一段未停稳的重叠)" -else: - main_src = "起动与停车都有贡献,巡航最干净" - -plain_html = f""" -
    -

    通俗结论:到底是谁慢?

    -

    先说人话:两台电机并不是“有一台全程都慢”。 - 你看到的积分差,主要不是巡航时谁转得慢,而是快停下 / 刚要走的那几十一到几百毫秒里,两台对不齐, - 把理论应为 0 的 ∫(v2+v3)dt 顶出 5~20° 左右的偏差。

    - -
      -
    • 启动 - 干净起动时(|Δt|≤50ms 的行程),N3−N2 约为 - {mean_on_clean if mean_on_clean is not None else '—'} ms 量级 → - 几乎是同时启动,ECAT 同帧下发没有问题。 - 有 {len(on_overlap)} 个行程测出上百毫秒的“假早启动”,那是上一段还没停稳、N3 仍在动, - 不是这一段命令发晚了。
    • -
    • 巡航 - 两边平均 |v|:N2 {cruise_v2:.2f}°/s,N3 {cruise_v3:.2f}°/s, - 差值约 {cruise_bias:.3f}°/s → 稳态速度几乎一样,谈不上谁慢。 - 巡航段 ∫(v2+v3) 平均只有约 {mean_Icr:.2f}°,贴合理论 0。
    • -
    • 停车 / 换向 - 行程末端 ∫(v2+v3) 平均约 {mean_Ist:.2f}°,绝对值平均约 {abs_Ist:.2f}°, - 明显大于巡航。停止窗口里: - 出现「只有 N2 还在走」的行程约 {n_only2} 次(积分贡献合计 {sum_only2:.2f}°), - 「只有 N3 还在走」约 {n_only3} 次(合计 {sum_only3:.2f}°)。 - 两边都有刹车尾巴,不是单边永久变慢;有的行程 N2 拖尾更明显,有的行程 N3 更明显。
    • -
    - -
    - 一句话:积分差的大头在 {main_src}。 - 对接约束要求 v2 = −v3;只要刹停瞬间一台还带着速度、另一台已经接近 0, - v2+v3 就会冒尖峰,积分就会被拉离理论 0。这属于动态不同步, - 不等于某台电机“标定转速低、一直慢”。 -
    - -
    -
    正常:同时启动 / 巡航一致 / 中段积分≈0
    -
    注意:停车沿残速、行程衔接重叠、积分被顶开
    -
    偏大:该行程 ∫(v2+v3) 或编码器 Δ(a2+a3) 明显偏离 0
    -
    -
    -""" - -rows_joined = "\n".join(rows_html) - -# 标注框(放在 SVG 下方用 HTML 箭头说明) -annot_html = f""" -
    -

    图上怎么看(对照上方曲线)

    -
    -
    -
    ↑ 巡航段(两条速度曲线几乎贴合)
    -

    N2 蓝线、N3 橙线在 |v|≈200°/s 附近重合 → 都不慢。 - 下方红线 v2+v3 贴 0,积分曲线几乎水平 → 对接约束在稳态是成立的。

    -
    -
    -
    ↓ 停车沿 / 换向点(红线尖峰)
    -

    接近终点时,一台速度先掉下来、另一台还在走 → v2+v3 出现 ±100~200°/s 尖峰。 - 积分曲线在这里爬上或爬下,行程结束就停在理论 0 以外几度到二十度。 - 这里是积分差的主要来源。

    -
    -
    -
    ↓ 行程刚衔接时(起动窗)
    -

    若上一段没完全停稳就反向,会看到“N3 早就动了”的假象(表格里 onΔ 为 −800ms 量级)。 - 这不是启动指令不同步,而是速度还在反向爬坡 / 残速, - 也会把起动窗积分抬高一点。

    -
    -
    -
    ↓ 紫色角度和 a2+a3
    -

    理论应为常数(理想为 0)。行程终点若偏到 ±10~25°,与该行程 ∫(v2+v3) 同量级 → - 说明偏差是真实位移耦合误差,不只是速度噪声。

    -
    -
    - -
    - 标定建议(若要压积分差): - ① 先统一两台的减速段(同样斜率刹停),而不是去改巡航速度; - ② 检查 N2/N3 负载与制动是否对称; - ③ 控制器上对对接轴做 v2+v3 → 0 的纠偏,比单轴调 PID 更对症; - ④ 序列 hold 不要短于“两台都确认停稳”的时间,避免重叠行程把积分搅浑。 -
    -
    -""" - -# SVG 图 -if sample: - svg = f""" - - - {''.join(vlines)} - {line(v2, Yv, '#4da3ff')} - {line(v3, Yv, '#f0a030')} - 速度 deg/s — 蓝 N2 / 橙 N3 - - {line(vsum, Ys, '#ff5c5c')} - 合速度 v2+v3(理论 0)— 停车尖峰在这里 - - {line(Isum, YI, '#b48cff')} - 积分 ∫(v2+v3)dt(理论 0)— 每段末的偏离就是耦合误差 - - t_rel {t0:.1f}..{t1:.1f}s · ECAT 0.25ms · 竖线为行程起点 - - - ↑ 巡航:两线贴合,都不慢 - ↓ 停车沿:v_sum 尖峰 → 积分爬升 - -""" -else: - svg = "

    (无绘图样本)

    " - -html = f""" - - - -对接电机 2/3 积分差归因报告 - - - -

    对接电机 2 / 3:积分差是谁造成的?

    -
    - 数据:ECAT 无源采集 lab/ecat_passive_latest.csv · 周期 0.25ms · - 理论:刚性镜像对接时 v2+v3≡0,∫(v2+v3)dt≡0,a2+a3=常数
    - 方法:对齐 cycles 后做梯形积分;先加后积与先积后加一致。 -
    - -
    -

    关键数字

    -
    -
    巡航 |v| N2 / N3
    -
    {cruise_v2:.2f} / {cruise_v3:.2f}
    -
    巡航段 ∫(v2+v3) 平均
    -
    {mean_Icr:.2f}°
    -
    停车段 ∫(v2+v3) |均值|
    -
    {abs_Ist:.2f}°
    -
    干净起动 Δt 中位/均值
    -
    {(sorted(on_clean)[len(on_clean)//2] if on_clean else float('nan')):.2f} / {(mean_on_clean if mean_on_clean is not None else float('nan')):.2f} ms
    -
    -
    - -{plain_html} - -
    -

    曲线(带标注)

    - {svg} -
    - -{annot_html} - -
    -

    各行程明细(积分从行程起点重算)

    - - - - - - - - - - {rows_joined} - -
    #方向起动Δ(N3-N2)巡航|v| N2/N3∫起动∫巡航∫停车停时仅N2在动停时仅N3在动停车归因Δ(a2+a3)
    -

    - 单位:角度 deg(积分)· 速度 deg/s · 时间 ms。 - 「停时仅N2/N3在动」为该窗口内单边残速对 ∫(v2+v3) 的贡献。 -

    -
    - -
    -

    原始结论脚本输出(备查)

    -
    {json.dumps(verdict, ensure_ascii=False, indent=2)}
    -
    - - -""" - -OUT.write_text(html, encoding="utf-8") -print("HTML ->", OUT) -print("main_src:", main_src) -print("on_clean", on_clean, "mean", mean_on_clean) -print("I_on/I_cr/I_st abs mean", abs_Ion, abs_Icr, abs_Ist) -print("only2", n_only2, sum_only2, "only3", n_only3, sum_only3) diff --git a/scripts/calibrate_ecat_metrics.py b/scripts/calibrate_ecat_metrics.py deleted file mode 100644 index 3a11289..0000000 --- a/scripts/calibrate_ecat_metrics.py +++ /dev/null @@ -1,418 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""Phase 0:ECAT 指标现场标定 —— 跑速度扫描之前,先确认每个计数器还有没有意义。 - -为什么必须有这一步:固件与主站都在被反复改(当前 ecat_native.c 有 239 行未提交改动, -批量区已从"变长紧排 3 帧/拍"换成 1.6.15 定长覆盖窗口),任何沿用历史结论的判据都可能 -已经失效。本脚本只认现场读数,不认文档和历史。 - -标定八件事: - 1 总线能否正常起(open + motor/start) - 2 板上固件身份(台账 sha256 对 Release/*.hex 展平后的哈希) - 3 批量区实际布局(rxraw.layout,不假设) - 4 每拍入窗帧数分布(取 cadence 环,按拍采样,不受 HTTP 轮询速率影响) - 5 丢帧率的主判据:**seq 值域 − 实测 distinct seq**(对槽位顺序不敏感) - 对账判据:固件 OVF / bridge.drop —— 二者与主判据是否一致 - 6 撕裂类计数器是否还有功效(rx_torn / pdo_reentry,恒零即"测不到"≠"没坏") - 7 主站错拍 skip_total 与达成率、cyc_us 占空 - 8 CPU 两口径(GD32 load_perm / 主机 native)+ 进程 PID 稳定性 - -用法: - python scripts/calibrate_ecat_metrics.py - python scripts/calibrate_ecat_metrics.py --seconds 60 --json lab/ecat_calib.json - python scripts/calibrate_ecat_metrics.py --no-start # 总线没起时不自动拉起 -""" -import argparse -import collections -import hashlib -import json -import os -import subprocess -import sys -import time - -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) -from ecat_sweep_common import delta_saturate, delta_wrap, percentile, post - -REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -DEFAULT_URL = "http://127.0.0.1:8090" - -# 1.6.15 定长覆盖窗口槽布局(对应 ecat_native.c RXB_WIN_OFF_* / 固件 ECAT_RXW_OFF_*) -WIN_SLOT_SIZE, WIN_SLOTS = 44, 5 -WIN_OFF_FLAGS, WIN_OFF_ID, WIN_OFF_SEQ, WIN_OFF_DLC = 2, 4, 8, 1 -FLAG_WRITTEN = 0x01 - - -def parse_win_slots(rawhex): - """从 rxraw.raw 解出每个槽的 (id, seq)(跳过 bit0=未写过的空槽)。 - - parsed 字段不带 seq,而 seq 是这个布局的丢帧判据,必须自己解。 - """ - try: - buf = bytes.fromhex(rawhex or "") - except ValueError: - return [] - out = [] - for k in range(WIN_SLOTS): - o = k * WIN_SLOT_SIZE - if o + WIN_SLOT_SIZE > len(buf): - break - if not buf[o + WIN_OFF_FLAGS] & FLAG_WRITTEN: - continue - out.append((int.from_bytes(buf[o + WIN_OFF_ID:o + WIN_OFF_ID + 4], "little"), - int.from_bytes(buf[o + WIN_OFF_SEQ:o + WIN_OFF_SEQ + 4], "little"))) - return out - - -def window_coverage(seqmap, slot_slots=WIN_SLOTS): - """覆盖窗口观测到的 seq 值域 / distinct seq 数(**描述性指标,不是丢帧判据**)。 - - 曾用「值域 − distinct」当丢帧主判据,已被现场证伪:窗口只有 5 槽、HTTP 又只几 - Hz,值域内根本不可能观测到全部 seq,实测报出 99.686% "丢帧"而固件 OVF=0,自相 - 矛盾 —— 它量的是**采样覆盖率**。真丢帧主判据是 bridge.drop(固件 - canfd.h:53 rx_drop_count,已实测确认为回绕计数器且增量与 rx_count 增量相等)。 - """ - out = {} - for fid, obs in seqmap.items(): - if not obs: - continue - lo, hi = min(obs), max(obs) - out[fid] = {"n_obs": len(obs), "span": hi - lo + 1, - "coverage_pct": round(len(obs) / (hi - lo + 1) * 100, 3)} - return {"per_id": out, "ids": len(out), - "note": "覆盖率仅说明观测密度,不可当丢帧率引用"} - - -def cadence_rings(url): - """按拍采到的环形缓冲:每拍 count/lost/pending/wkc 的真实分布。 - - HTTP 轮询只有几 Hz,用它看"每拍几帧"是欠采样;cadence 环由 native 自己在 - 每拍写,512 拍一段,才是每拍口径的真值。 - """ - try: - c = post(url, "/api/can/cadence") - except Exception as e: - return {"error": str(e)[:120]} - cols, rows = c.get("cols") or [], c.get("rows") or [] - if not rows: - return {"error": "环为空"} - ix = {k: i for i, k in enumerate(cols)} - def col(name): - return [r[ix[name]] for r in rows] if name in ix else [] - out = {"n": len(rows), "period_ms": c.get("period_ms")} - for name in ("count", "lost", "pending", "backlog", "wkc"): - v = col(name) - if v: - out[name + "_hist"] = {str(k): n for k, n in sorted(collections.Counter(v).items())} - out[name + "_max"] = max(v) - span_ms = (col("t")[-1] - col("t")[0]) if len(rows) > 1 else 0 - out["span_ms"] = round(span_ms, 1) - if span_ms > 0 and "rxcnt" in ix: - out["lambda_per_s"] = round((col("rxcnt")[-1] - col("rxcnt")[0]) / (span_ms / 1000.0), 1) - out["gseq_per_s"] = round((col("gseq")[-1] - col("gseq")[0]) / (span_ms / 1000.0), 1) \ - if "gseq" in ix else None - return out - - -def find_native_pid(url): - """反查占用该端口的进程 PID。PID 变了 = 换了实例 = 之前采的全部作废。""" - try: - out = subprocess.check_output(["netstat", "-ano", "-p", "TCP"], - text=True, errors="replace") - except Exception as e: - return None, f"netstat 失败: {e}" - port = url.rsplit(":", 1)[-1].strip("/") - for line in out.splitlines(): - cols = line.split() - if len(cols) >= 5 and cols[0] == "TCP" and cols[3] == "LISTENING" \ - and cols[1].endswith(":" + port): - return int(cols[-1]), None - return None, f"没人监听 :{port}" - - -def firmware_identity(): - """台账 sha256 对固件产物。 - - 口径必须与烧写脚本一致:`lab/_last_gd32_sha.txt` 记的是 Intel-Hex **展平成 - APP 基址连续 blob 后**的 sha256(flash_gd32.py:91-92),不是 .hex 文件本身的哈希, - 产物也是 `.hex` 而非 `.bin`。按原始字节算或按 .bin 过滤会一个都扫不到, - 把"本可判定"误报成"无法判定"。 - - 即便命中也只能说明"板上镜像与当前产物同源";产物比台账新时必然不等。 - **不能据此推断板上正在运行的版本** —— 那要设备回报版本字。 - """ - shafile = os.path.join(REPO, "lab", "_last_gd32_sha.txt") - rec = {"ledger": None, "ledger_path": shafile, "match": None, - "candidates": [], "method": "hex_to_flat + sha256"} - if not os.path.isfile(shafile): - rec["error"] = "台账不存在" - return rec - with open(shafile, encoding="utf-8", errors="replace") as f: - rec["ledger"] = f.read().strip() - try: - from flash_gd32 import hex_to_flat - except Exception as e: - rec["error"] = f"无法复用烧写脚本的展平算法({e})" - return rec - app = os.path.abspath(os.path.join(REPO, "..", "..", "DCU_APP", "out")) - if not os.path.isdir(app): - rec["error"] = f"固件产物目录不存在: {app}" - return rec - hexes = [] - for dirpath, _d, files in os.walk(app): - hexes += [os.path.join(dirpath, fn) for fn in files if fn.endswith(".hex")] - if not hexes: - rec["error"] = "DCU_APP/out 下没有 .hex 产物" - return rec - for p in sorted(hexes): - try: - d = hashlib.sha256(hex_to_flat(p)).hexdigest() - except (OSError, SystemExit, ValueError) as e: - rec["candidates"].append({"path": p, "error": str(e)[:120]}) - continue - rec["candidates"].append({"path": p, "sha256": d[:16], "mtime": int(os.path.getmtime(p))}) - if d == rec["ledger"]: - rec["match"] = p - rec["candidates"].sort(key=lambda c: -c.get("mtime", 0)) - rec["candidates"] = rec["candidates"][:5] - return rec - - -def ensure_loop(url, autostart, substr): - st = post(url, "/api/motor/state") - if st.get("ok") and st.get("cycles"): - return st, "已在跑" - if not autostart: - return st, "循环未跑(--no-start)" - op = post(url, "/api/open", {"substr": substr}, timeout=25.0) - if not op.get("ok"): - return op, "open 失败: %s" % (op.get("log") or []) - ms = post(url, "/api/motor/start", {"period_ms": st.get("period_ms") or 1.0}, timeout=40.0) - if not ms.get("ok"): - return ms, "motor/start 失败(典型=SSC 邮箱楔死): %s" % (ms.get("log") or []) - return post(url, "/api/motor/state"), "已自动 open+start" - - -def main(): - ap = argparse.ArgumentParser(description="ECAT 指标现场标定") - ap.add_argument("--url", default=DEFAULT_URL) - ap.add_argument("--seconds", type=float, default=30.0) - ap.add_argument("--hz", type=float, default=10.0, - help="HTTP 采样频率。u16 的 drop 在 4000/s 下 16s 就绕一圈," - "采样间隔须远小于回绕周期") - ap.add_argument("--no-start", action="store_true") - ap.add_argument("--substr", default="I210") - ap.add_argument("--json", default=None) - a = ap.parse_args() - - rep = {"url": a.url, "started_at": time.strftime("%Y-%m-%d %H:%M:%S"), - "seconds": a.seconds, "sample_hz": a.hz, "logical_cpus": os.cpu_count()} - pid, perr = find_native_pid(a.url) - rep["native_pid"], rep["native_pid_error"] = pid, perr - rep["firmware"] = firmware_identity() - rep["cadence"] = cadence_rings(a.url) - - st, how = ensure_loop(a.url, not a.no_start, a.substr) - rep["loop_start"] = {"how": how, "ok": bool(st.get("ok"))} - if not st.get("ok"): - print("总线起不来,标定终止:", how) - return 2 - rep["period_ms_set"] = st.get("period_ms") - rep["pcore_mask"] = st.get("pcore_mask") - - proc = None - try: - import psutil - for p in psutil.process_iter(["name", "pid"]): - if p.info["pid"] == pid: - proc = psutil.Process(pid) - proc.cpu_percent(None) - break - rep["psutil"] = proc is not None - except Exception as e: - rep["psutil"] = f"不可用({e})" - - interval = 1.0 / a.hz - seqmap = collections.defaultdict(set) - counts, loads, hc1, hctot, cyc_hz = collections.Counter(), [], [], [], [] - drop_rate, rx_rate, cyc_samples = [], [], [] - drop_untrusted = 0 - skip_first = skip_last = None - torn_c = reentry_c = 0 - torn_cap = reentry_cap = False - torn_prev = reentry_prev = None - prev = None - prev_t = None - layout = collections.Counter() - err = iters = 0 - t_end = time.perf_counter() + a.seconds - while time.perf_counter() < t_end: - tc = time.perf_counter() - try: - s = post(a.url, "/api/motor/state") - L = post(a.url, "/api/app/load") - R = post(a.url, "/api/motor/rxraw") - D = post(a.url, "/api/motor/dbg") - except Exception: - err += 1 - time.sleep(interval) - continue - iters += 1 - b = s.get("bridge") or {} - rb = s.get("rx_batch") or {} - cur = {"cycles": s.get("cycles"), "drop": b.get("drop"), "rx": b.get("rx_count")} - if prev is not None and prev_t is not None and tc > prev_t: - dt = tc - prev_t - if cur["cycles"] is not None and prev["cycles"] is not None: - hz = (cur["cycles"] - prev["cycles"]) / dt - if 0 < hz < 1e6: - cyc_hz.append(hz) - if cur["drop"] is not None and prev["drop"] is not None: - d = delta_wrap(prev["drop"], cur["drop"], 16) - # u16 回绕计数器:单次增量逼近模数一半即说明两次采样间已绕圈, - # 该次不可信,只能丢弃并计数暴露采样率不足(真值下降同理)。 - if d < 32768: - drop_rate.append(d / dt) - else: - drop_untrusted += 1 - if cur["rx"] is not None and prev["rx"] is not None: - dr = delta_wrap(prev["rx"], cur["rx"], 32) - if dr < (1 << 31): - rx_rate.append(dr / dt) - for fid, sq in parse_win_slots(R.get("raw")): - seqmap[fid].add(sq) - counts[rb.get("count")] += 1 - layout[R.get("layout")] += 1 - if L.get("valid") and L.get("load_perm") is not None: - loads.append(L["load_perm"] / 10.0) - if L.get("rx_torn") is not None: - if torn_prev is not None: - d, capped = delta_saturate(torn_prev, L["rx_torn"]) - torn_c += d - torn_cap |= capped or L["rx_torn"] >= 65535 - torn_prev = L["rx_torn"] - if L.get("pdo_reentry") is not None: - if reentry_prev is not None: - d, capped = delta_saturate(reentry_prev, L["pdo_reentry"]) - reentry_c += d - reentry_cap |= capped or L["pdo_reentry"] >= 65535 - reentry_prev = L["pdo_reentry"] - sk = D.get("skip_total") - if sk is not None: - if skip_first is None: - skip_first = sk - skip_last = sk - if s.get("cyc_us"): - cyc_samples.append(s["cyc_us"]) - if proc is not None: - try: - c = proc.cpu_percent(None) - hc1.append(c) - hctot.append(c / max(1, rep["logical_cpus"] or 1)) - except Exception: - pass - prev, prev_t = cur, tc - time.sleep(max(0.0, interval - (time.perf_counter() - tc))) - - dur = time.perf_counter() - (t_end - a.seconds) - stf = post(a.url, "/api/motor/state") - pid2, _ = find_native_pid(a.url) - cad = rep["cadence"] - - lam = percentile(rx_rate, 0.5) or cad.get("lambda_per_s") or 0 - dropr = percentile(drop_rate, 0.5) or 0 - rep["dur_s"] = round(dur, 2) - rep["iters"] = iters - rep["errors"] = err - rep["drop_untrusted_samples"] = drop_untrusted - rep["period_ms_effective"] = stf.get("period_ms") - rep["wkc"] = [stf.get("wkc"), stf.get("expected_wkc")] - rep["wr_end"] = stf.get("wr") - rep["native_pid_end"] = pid2 - rep["pid_stable"] = (pid == pid2) - rep["layout_seen"] = dict(layout) - rep["rx_batch_count_hist_http"] = {str(k): v for k, v in sorted(counts.items(), key=lambda x: (x[0] is None, x[0]))} - rep["window_coverage"] = window_coverage(seqmap) - rep["can_lambda_per_s"] = round(lam, 1) - rep["drop_per_s"] = round(dropr, 1) - rep["drop_pct_of_lambda"] = round(dropr / lam * 100, 2) if lam else None - rep["achieved_hz_p50"] = round(percentile(cyc_hz, 0.5) or 0, 1) if cyc_hz else None - pm = rep["period_ms_effective"] or 1 - rep["achieve_pct"] = round((rep["achieved_hz_p50"] or 0) / (1000.0 / pm) * 100, 1) - rep["skip_delta"] = (skip_last - skip_first) if (skip_first is not None and skip_last is not None) else None - rep["skip_per_s"] = round((rep["skip_delta"] or 0) / dur, 2) if rep["skip_delta"] is not None else None - rep["skip_total_cum"] = skip_last - rep["rx_torn_delta"] = torn_c - rep["rx_torn_capped"] = torn_cap - rep["pdo_reentry_delta"] = reentry_c - rep["pdo_reentry_capped"] = reentry_cap - tot = [(c.get("total") or {}) for c in cyc_samples if c.get("total")] - rep["cyc_us_total"] = {"p50": percentile([t.get("p50") for t in tot if t.get("p50") is not None], 0.5), - "p90": percentile([t.get("p90") for t in tot if t.get("p90") is not None], 0.5), - "max": max([t.get("max") or 0 for t in tot], default=None)} - rep["cyc_us_late_lost_sample"] = tot[0] if tot else None - rep["gd32_load_pct_p50"] = round(percentile(loads, 0.5) or 0, 1) - rep["host_cpu_1core_p50"] = round(percentile(hc1, 0.5) or 0, 1) - rep["host_cpu_total_p50"] = round(percentile(hctot, 0.5) or 0, 2) - - cap = cad.get("count_max") or 0 - rep["per_cycle_cap_observed"] = cap - rep["capacity_model"] = { - "lambda_per_s": round(lam, 1), "per_cycle_cap": cap, - "drain_per_s_at_current_T": round(cap / pm * 1000, 0) if pm else None, - "knee_ms_if_linear": round(cap / lam * 1000, 3) if lam and cap else None, - "note": "cap 取自 cadence 环实测 count 峰值;拐点以扫描实测为准,此值仅供事后对照"} - rep["power"] = { - "drop_rx_queue": dropr > 0, - "skip_total": (rep["skip_delta"] or 0) > 0, - "achieve_deficit": (rep["achieve_pct"] or 100) < 99.0, - "rx_torn": torn_c > 0, - "pdo_reentry": reentry_c > 0, - "cadence_lost_or_pending": bool((cad.get("lost_max") or 0) or (cad.get("pending_max") or 0)), - "cyc_us_late_lost": bool((rep["cyc_us_late_lost_sample"] or {}).get("late") - or (rep["cyc_us_late_lost_sample"] or {}).get("lost"))} - - print(f"=== Phase 0 标定 · {dur:.0f}s · HTTP 采样 {iters} 轮(错 {err}) @ {a.hz:g}Hz ===") - print(f"native PID {pid} -> {pid2} 稳定={rep['pid_stable']} 钉核 mask={rep['pcore_mask']}") - fw = rep["firmware"] - print(f"固件台账 {str(fw.get('ledger'))[:16]}… 匹配={os.path.basename(fw['match']) if fw.get('match') else fw.get('error') or '无'}") - print(f"周期 设定 {rep['period_ms_set']}ms -> 生效 {pm}ms 实测 {rep['achieved_hz_p50']}Hz 达成率 {rep['achieve_pct']}%") - print(f"批量区布局 {dict(layout)}") - print(f"每拍入窗帧数(cadence 环 {cad.get('n')}拍): count={cad.get('count_hist')} " - f"lost={cad.get('lost_hist')} pending={cad.get('pending_hist')}") - print(f" ↑ 每拍口径必须用环,HTTP 轮询看 count 是欠采样") - print(f"【主判据】λ={lam:.0f}/s drop={dropr:.0f}/s 丢帧占 λ = {rep['drop_pct_of_lambda']}%" - f" (drop=固件 canfd.h rx_drop_count,CAN RX 队列(kRxQueueDepth=16)满即丢)") - if drop_untrusted: - print(f" !! {drop_untrusted} 个采样点单次增量 >32768,说明已绕圈 -> 采样率不足,提高 --hz") - print(f" 对账: cadence lost_max={cad.get('lost_max')} pending_max={cad.get('pending_max')}" - f"(覆盖窗口下这两个旧计数器语义已改,仅供对账)") - print(f" 窗口 seq 覆盖率(非丢帧率!): {rep['window_coverage']['per_id']}") - print(f"rx_torn +{torn_c}(封顶={torn_cap}) pdo_reentry +{reentry_c}(封顶={reentry_cap})") - print(f"主站错拍 skip_total Δ={rep['skip_delta']} = {rep['skip_per_s']}/s(累计 {rep['skip_total_cum']})") - print(f"cyc_us.total p50={rep['cyc_us_total']['p50']} p90={rep['cyc_us_total']['p90']} " - f"max={rep['cyc_us_total']['max']} -> 占空 {round((rep['cyc_us_total']['p50'] or 0)/(pm*1000)*100,1)}%") - print(f"CPU: GD32 load_perm {rep['gd32_load_pct_p50']}% 主机 native 单核 {rep['host_cpu_1core_p50']}% " - f"/ 整机 {rep['host_cpu_total_p50']}%(逻辑核 {rep['logical_cpus']})") - print(f"wkc {rep['wkc']} wr={rep['wr_end']}") - km = rep["capacity_model"] - print(f"\n容量对照: cap={km['per_cycle_cap']} λ={km['lambda_per_s']:.0f} " - f"当前档排空 {km['drain_per_s_at_current_T']} 线性外推拐点 {km['knee_ms_if_linear']}ms" - f"(仅事后对照,拐点以扫描实测为准)") - dead = [k for k, v in rep["power"].items() if not v] - live = [k for k, v in rep["power"].items() if v] - print("有功效:", live or "无") - if dead: - print(f"无功效: {dead}") - print(" → 这些轴在报告里只能写「本次测不到」,**禁止**写成「无故障/已修好」") - - if a.json: - os.makedirs(os.path.dirname(os.path.abspath(a.json)), exist_ok=True) - with open(a.json, "w", encoding="utf-8") as f: - json.dump(rep, f, ensure_ascii=False, indent=2) - print(f"\n标定结果: {a.json}") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/can_bridge_reconcile.py b/scripts/can_bridge_reconcile.py deleted file mode 100644 index 155ad1d..0000000 --- a/scripts/can_bridge_reconcile.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""逐层对账 CAN→ECAT:谁在丢帧、丢多少、哪一层丢的。 - -动机:面板上的 `drop`(段1/段3"最近一帧"单格被未取先覆的帧数)恒等于 λ − 实测Hz, -是个会自证的数,拿它说"丢帧"没有说服力。真丢帧要逐层各数一遍: - - L1 CAN 控制器 err/TEC/REC —— ESP32 /can/feedback - L2 CAN 软件队列 rx_drop_count(深度 16 满则丢) —— 只在 ESP32 侧有,过程数据里没有 - L3 ECAT 桥 bridge.rx_count(进桥帧数) - L4 段4 覆盖窗口 s_rxWinSeq 每入窗帧 +2 → Δmax_seq/2 是**精确**的入窗帧数 - L5 主站取帧 rx_batch.lost(主站 seq 空洞) / pending(GD32 OVF) —— 单拍值, - 由本脚本按 cycle 去重后统计,仍是**下界**(轮询看不到每一拍) - -L3−L4 就是 STATUS 帧数(STATUS 在入窗之前就 return,见固件 -ethercat_can_bridge.cpp:435),每节点 STATUS 率另用段2 槽 seq 步长反推 -(率 = λ/step,与轮询率无关)。 - -只读:全部走只读诊断路由,不发报、不改周期、不碰生命周期。 -用法: python scripts/can_bridge_reconcile.py [秒数=20] [esp32_ip=192.168.31.127] -""" -import json -import socket -import sys -import time -import urllib.request - -# 控制台默认 GBK:一个字符编不出来就整轮数据白采,所以先把输出流换成 UTF-8 -try: - sys.stdout.reconfigure(encoding="utf-8", errors="replace") -except Exception: - pass - -HOST = "127.0.0.1" -ECAT_PORTS = range(8090, 8096) -DUR_S = float(sys.argv[1]) if len(sys.argv) > 1 else 20.0 -ESP_IP = sys.argv[2] if len(sys.argv) > 2 else "192.168.31.127" - -# 段4 覆盖窗口(固件 1.6.15 定长布局):raw 从 PDO 偏移 364 起,5 槽 × 44B -WIN_SLOT_SIZE = 44 -WIN_SLOTS = 5 -W_DLC, W_FLAGS, W_ID, W_SEQ = 1, 2, 4, 8 - - -def post(path, port, timeout=6.0): - """裸 socket + 连接阶段不设 timeout:给 urllib 传 timeout 会撞上 Windows - 非阻塞 connect 的 ~10ms 调度粒度,把仪表自己变成扰动源。""" - body = b"{}" - req = (f"POST {path} HTTP/1.0\r\nHost: {HOST}:{port}\r\n" - "Content-Type: application/json\r\n" - f"Content-Length: {len(body)}\r\n\r\n").encode("ascii") + body - s = socket.create_connection((HOST, port)) - try: - s.settimeout(timeout) - s.sendall(req) - buf = bytearray() - while True: - c = s.recv(65536) - if not c: - break - buf.extend(c) - finally: - s.close() - return json.loads(bytes(buf).partition(b"\r\n\r\n")[2].decode("utf-8", "replace")) - - -def find_port(): - for p in ECAT_PORTS: - try: - if post("/api/motor/state", p, 2.0).get("ok"): - return p - except Exception: - continue - return None - - -def wrap(a, b, bits): - return (b - a) % (1 << bits) - - -def le32(bs, off): - return int.from_bytes(bs[off:off + 4], "little") - - -def can_ch(ip): - """一次读全部 CAN 通道,返回 {通道号: 通道 dict};读不到返回 None(绝不填 0)。 - - 不能在这里挑"rx 最大的通道":ESP32 的 ID 快照是分页轮转的,开机初期某通道还没 - 轮到 valid 时 rx=0,两次采样各挑各的就会挑到不同通道,差值直接变成整个累计值 - (实测虚报成 3.0M 帧/s)。通道由调用方跨两次采样锁定。""" - try: - with urllib.request.urlopen(f"http://{ip}/can/feedback", timeout=3) as r: - d = json.loads(r.read().decode("utf-8")) - except Exception: - return None - chs = {c.get("ch"): c for c in (d.get("channels") or []) if c.get("valid")} - return chs or None - - -def can_pick(a, b): - """两次采样都 valid 且 rx 最大的那个通道 —— 保证差分是同一计数器的两端。""" - if not a or not b: - return None - common = [k for k in a if k in b] - if not common: - return None - return max(common, key=lambda k: max(a[k].get("rx") or 0, b[k].get("rx") or 0)) - - -def win_slots(raw_hex): - """原始字节 → [(seq, id, dlc)],只收 flags.bit0=1 且 seq 为偶数(整槽一致)的槽。""" - bs = bytes.fromhex(raw_hex) - out = [] - for i in range(WIN_SLOTS): - p = bs[i * WIN_SLOT_SIZE:(i + 1) * WIN_SLOT_SIZE] - if len(p) < W_ID + 4 or (p[W_FLAGS] & 1) == 0: - continue - sq = le32(p, W_SEQ) - if sq == 0 or sq & 1: - continue - out.append((sq, le32(p, W_ID), p[W_DLC])) - return out - - -def main(): - port = find_port() - if not port: - print("找不到在监听的 ECAT native(8090..8095)—— 主站没起,先连一次总线") - return 1 - print(f"native :{port} 窗口 {DUR_S:.0f}s ESP32 {ESP_IP}") - - seen_cycle = None - seq_first = seq_last = None - per_id = {} - lost_max = pend_max = cnt_max = online = 0 - n_state = n_raw = 0 - t0 = time.perf_counter() - c0 = can_ch(ESP_IP) - s0 = post("/api/motor/state", port) - while time.perf_counter() - t0 < DUR_S: - st = post("/api/motor/state", port) - n_state += 1 - rb = st.get("rx_batch") or {} - online = max(online, sum(1 for m in (st.get("motors") or []) if m.get("online"))) - # 每拍回填清零的单拍值,只能取抽样最大值当下界 - lost_max = max(lost_max, rb.get("lost") or 0) - pend_max = max(pend_max, rb.get("pending") or 0) - cnt_max = max(cnt_max, rb.get("count") or 0) - try: - rr = post("/api/motor/rxraw", port) - except Exception: - rr = None - if rr and rr.get("layout") != "win": - print(f"段4 布局是 {rr.get('layout')!r},不是 1.6.15 覆盖窗口(win) —— 本脚本的解析器不适用") - return 2 - # 注意:rxraw 的 count/lost/pending 是**旧布局偏移**(360/361/362)的原始字节, - # 在 win 布局下分别是"窗口槽数常量""环形写游标 0..4""GD32 OVF"。只有第三个 - # 有意义,前两个拿来当丢帧数会得出"lost 最大 4、84% 的拍非 0"这种假结论。 - if rr and rr.get("cycle") != seen_cycle: # 按拍去重:同一拍重复读会重复计数 - seen_cycle = rr.get("cycle") - n_raw += 1 - for sq, _id, _dlc in win_slots(rr.get("raw") or ""): - seq_first = sq if seq_first is None else min(seq_first, sq) - seq_last = sq if seq_last is None else max(seq_last, sq) - per_id.setdefault(_id, set()).add(sq) - t1 = time.perf_counter() - time.sleep(0.05) # 约 20Hz:开环全速轮询会让仪表自己变成扰动源 - t1 = time.perf_counter() - c1 = can_ch(ESP_IP) - s1 = post("/api/motor/state", port) - dt = t1 - t0 - - hz = wrap(s0.get("cycles", 0), s1.get("cycles", 0), 64) / dt - lam = wrap((s0.get("head") or {}).get("global_seq", 0), - (s1.get("head") or {}).get("global_seq", 0), 32) / dt - bridge = wrap((s0.get("bridge") or {}).get("rx_count", 0), - (s1.get("bridge") or {}).get("rx_count", 0), 32) / dt - win_rate = ((seq_last - seq_first) / 2 / dt) if seq_last is not None else None - got = sum(len(v) for v in per_id.values()) / dt - drop_single = wrap((s0.get("bridge") or {}).get("drop", 0), - (s1.get("bridge") or {}).get("drop", 0), 16) / dt - - print(f"\n窗口 {dt:.2f}s 档位 {s1.get('period_ms')}ms PDO {hz:.0f}Hz " - f"轮询 state {n_state} 次 / rxraw 新拍 {n_raw} 次" - f"({n_raw / dt:.1f} 拍/s → 抽样比 {n_raw / max(1e-9, hz * dt):.1%})") - print("\n层 计数 帧/s 判读") - ck = can_pick(c0, c1) - can_rate = None - if ck is not None: - rx0 = c0[ck].get("rx") or 0 - dr0 = c0[ck].get("drop") or 0 - rx1 = c1[ck].get("rx") or 0 - dr1 = c1[ck].get("drop") or 0 - err, tec, rec, bus = (c1[ck].get(k) for k in ("err", "tec", "rec", "bus_state")) - can_rate = wrap(rx0, rx1, 32) / dt # rx / drop 都是 u32 - can_drop = wrap(dr0, dr1, 32) / dt - print(f"L1 CAN err/TEC/REC/bus —— {err}/{tec}/{rec}/{bus}" - f" {'干净' if not (err or tec or rec) else '有错'} (通道 {ck})") - print(f"L2 CAN 软件队列满丢 {can_drop:10.0f} " - f"{'0 = 队列(深度16)没满过' if not can_drop else '在丢帧,查从站取帧速率'}") - print(f"L3 CAN 控制器收帧 {can_rate:10.0f}") - else: - print("L1/L2/L3 ESP32 /can/feedback 不可读 —— 不填 0,这三层本轮无数据") - print(f"L3 ECAT桥 进桥 rx_count {bridge:10.0f}") - if can_rate is not None: - gap = can_rate - bridge - print(f" CAN - 桥 {gap:10.0f} " - f"{'≈0 两层同域' if abs(gap) < bridge * 0.01 else '差额:到了 CAN 但没进桥计数'}") - print(f"L4 段4窗 入窗帧 Δseq/2 {win_rate if win_rate else float('nan'):10.0f}" - f" 精确值(窗口 seq 是累计计数器,与抽样率无关)") - print(f" STATUS(=桥−入窗) {bridge - (win_rate or 0):10.0f} " - f"STATUS 在入窗前就 return,固件 ethercat_can_bridge.cpp:435") - print(f" 本脚本见到的唯一入窗帧 {got:10.0f} 下界:轮询只覆盖 {n_state / max(1e-9, hz * dt):.1%} 的拍") - print(f"L5 单拍值 主站空洞 lost 最大 {lost_max:10.0f} 每拍回填清零,只能当下界") - print(f" GD32 OVF pending 最大 {pend_max:10.0f} 同上") - print(f" 主站上报 count 最大 {cnt_max:10.0f} 单拍上报帧数(窗口 5 槽)") - print(f"-- 段1/段3 单格 drop {drop_single:10.0f} " - f"恒等于 λ−Hz = {lam - hz:.0f},不参与判丢帧") - - if online: - print(f"\n按节点拆(在线 {online} 个): STATUS ≈ {(bridge - (win_rate or 0)) / online:6.0f} 帧/s/节点," - f"入窗 ≈ {(win_rate or 0) / online:6.0f} 帧/s/节点") - if per_id: - print("\n入窗帧按 ID(唯一 seq 计数,下界):") - for _id in sorted(per_id): - n = len(per_id[_id]) - print(f" 0x{_id:08X}: {n / dt:8.0f} 帧/s 子码 0x{_id & 0xFFFF:04X}") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/cpu_minimize_probe.ps1 b/scripts/cpu_minimize_probe.ps1 deleted file mode 100644 index 8147fdc..0000000 --- a/scripts/cpu_minimize_probe.ps1 +++ /dev/null @@ -1,59 +0,0 @@ -param([int]$Seconds = 8) -Add-Type @" -using System; -using System.Runtime.InteropServices; -public static class W { - [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h, int n); - [DllImport("user32.dll")] public static extern bool IsIconic(IntPtr h); - [DllImport("user32.dll")] public static extern IntPtr FindWindow(string c, string t); -} -"@ -$SW_RESTORE = 9 -$SW_MINIMIZE = 6 -$win = $null -Get-Process | Where-Object { $_.MainWindowHandle -ne 0 -and $_.MainWindowTitle -match 'ESP32' } | ForEach-Object { - $win = $_; Write-Output ("found window: pid={0} title='{1}'" -f $_.Id, $_.MainWindowTitle) -} -if (-not $win) { Write-Output 'NO ESP32 WINDOW FOUND'; exit 1 } -$hwnd = $win.MainWindowHandle -Write-Output ("before: minimized=" + [W]::IsIconic($hwnd)) - -function Snap { - $h = @{} - foreach ($p in (Get-Process | Where-Object { $_.ProcessName -match 'msedgewebview2|ecat_native|pythonw' })) { - try { $h[[string]$p.Id] = [pscustomobject]@{ name = $p.ProcessName; cpu = $p.CPU; ws = [math]::Round($p.WorkingSet64/1MB,0) } } catch {} - } - return $h -} -function Measure-Phase($label, $dt) { - $a = Snap - Start-Sleep -Seconds $dt - $b = Snap - $rows = foreach ($k in $b.Keys) { - if ($a.ContainsKey($k)) { - $d = ($b[$k].cpu - $a[$k].cpu) - if ($d -gt 0) { - [pscustomobject]@{ Phase = $label; Pid = [int]$k; Name = $b[$k].name; Pct1Core = [math]::Round($d/$dt*100,1); WS_MB = $b[$k].ws } - } - } - } - return $rows -} - -$r1 = Measure-Phase 'visible' $Seconds -Write-Output '--- minimize ---' -[void][W]::ShowWindow($hwnd, $SW_MINIMIZE) -Start-Sleep -Seconds 2 -$r2 = Measure-Phase 'minimized' $Seconds -Write-Output '--- restore ---' -[void][W]::ShowWindow($hwnd, $SW_RESTORE) -Start-Sleep -Seconds 2 -$r3 = Measure-Phase 'restored' $Seconds - -$all = @() + $r1 + $r2 + $r3 -$out = $all | Sort-Object Phase, Pct1Core -Descending -$out | Format-Table -AutoSize | Out-String -Width 200 | Write-Output -foreach ($ph in @('visible','minimized','restored')) { - $s = ($out | Where-Object { $_.Phase -eq $ph } | Measure-Object -Property Pct1Core -Sum).Sum - Write-Output ("TOTAL {0,-10} = {1,7:N1} % of one core" -f $ph, $s) -} diff --git a/scripts/cpu_sample.ps1 b/scripts/cpu_sample.ps1 deleted file mode 100644 index 7bb8575..0000000 --- a/scripts/cpu_sample.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -$ErrorActionPreference = 'Stop' -$cpus = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors -Write-Output "logical_cpus=$cpus" -$pat = 'msedgewebview2|ecat_native|pythonw|python' -function Snap { - $h = @{} - foreach ($p in (Get-Process | Where-Object { $_.ProcessName -match $pat })) { - $h[[string]$p.Id] = [pscustomobject]@{ name = $p.ProcessName; cpu = $p.CPU; ws = $p.WorkingSet64 } - } - return $h -} -$a = Snap -Start-Sleep -Seconds 8 -$b = Snap -$dt = 8 -$rows = foreach ($k in $b.Keys) { - if ($a.ContainsKey($k)) { - $d = ($b[$k].cpu - $a[$k].cpu) - if ($d -gt 0) { - [pscustomobject]@{ - pid = [int]$k - name = $b[$k].name - pct = [math]::Round($d / $dt * 100, 1) - wsMB = [math]::Round($b[$k].ws / 1MB, 0) - } - } - } -} -$rows | Where-Object { $_.pct -ge 0.3 } | Sort-Object pct -Descending | Format-Table -AutoSize | Out-String -Width 200 | Write-Output -$total = ($rows | Measure-Object -Property pct -Sum).Sum -Write-Output ("total_pct_of_one_core=" + [math]::Round($total,1)) diff --git a/scripts/ecat_sweep_common.py b/scripts/ecat_sweep_common.py deleted file mode 100644 index 960b921..0000000 --- a/scripts/ecat_sweep_common.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""ECAT native 主站的采样公共件:HTTP 调用 + 计数器差分。 - -固件 1.6.15 覆盖窗口布局下,各计数器的口径并不一致,混用会静默算错: - * bridge.rx_count / head.global_seq —— u32,**回绕** - * bridge.drop —— u16,**回绕**;段 1/段 3 单格"未取先覆"数,恒等于 λ − 实测Hz - * rx_batch.lost(主站按 seq 空洞算的单拍丢帧) / pending(从站窗口 OVF) —— - **每拍回填清零的单拍值**(ecat_native.c:461),不是累计计数:拿它做差得速率恒为 0, - 拿轮询见过的最大值当总丢帧也只是下界 - * app/load.rx_torn / pdo_reentry —— u16,**饱和**(到 65535 就停,不是回绕) -饱和量按回绕算会把"已封顶"误报成"发生了 6 万多次",故二者分开处理。 -""" -import json -import urllib.request - -SATURATE = 65535 # u16 饱和计数的顶 - - -def post(url, path, body=None, timeout=6.0): - req = urllib.request.Request( - url + path, data=json.dumps(body or {}).encode("utf-8"), - headers={"Content-Type": "application/json"}, method="POST") - with urllib.request.urlopen(req, timeout=timeout) as r: - return json.loads(r.read().decode("utf-8")) - - -def delta_wrap(a, b, bits=16): - """回绕计数器的增量。真值下降(重启清零)会表现为一个巨大的正数, - 调用方须用 sanity 上界剔除 —— 这里只负责把模数算对。""" - m = 1 << bits - return (b - a) % m - - -def delta_saturate(a, b): - """饱和计数的增量。返回 (增量, 是否已封顶)。 - - 封顶后增量恒为 0 —— 读数 0 既可能是"真没发生"也可能是"早发生了但计不动", - 报告里必须写成下界而不是精确值。 - """ - if b >= SATURATE: - return 0, True - if b < a: - return 0, False # 计数器被回填清零:该窗口作废,不臆造增量 - return b - a, False - - -def percentile(values, q): - """小样本分位数(最近邻)。不引 numpy,采样窗口只有百来个点。""" - if not values: - return None - s = sorted(values) - i = min(len(s) - 1, max(0, int(round(q * (len(s) - 1))))) - return s[i] diff --git a/scripts/flash_gd32.py b/scripts/flash_gd32.py deleted file mode 100644 index 9f45310..0000000 --- a/scripts/flash_gd32.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""GD32 固件经 ESP32 中继刷写(tools/flash_gd32.py 删除后的等价实现)。 - -流程(与 ESP32 侧 http_handlers.cpp 的 ota_upload_handler / -ota_relay_trigger_handler 契约一致): - 1. Release 的 Intel HEX 展平成从 0x08048000 起的连续 BIN(间隙填 0xFF); - 2. POST /ota/upload,请求体 = {"total_size":N} 紧跟 BIN 字节; - 3. POST /ota/relay,请求体 = {"sha256":"<该 BIN 的 sha256>"};设备侧还会做 - 包身份防呆(复位向量必须落在 APP 区,误传 BOOT 会被拒); - 4. 轮询 /system/status 直到 ota.sys_state=GD32_COMPLETE 且 ota.gd32_app 是 - 期望版本;**并且必须拿到"GD32 真的在跑新代码"的证据**:版本号相对刷前变了, - 或 gd32_adc.uptime_sec 归零(复位过)。两者都没有 → 判"未生效",不写台账、 - 非零退出。(实测踩过:01:44 刷进 1.6.22 后板子又跑了 2 小时老镜像, - 布局/耗时全程不变,台账却已更新 —— 写入 ≠ 在跑。) - 5. 把该 BIN 的 sha256 写进 lab/_last_gd32_sha.txt(板上镜像台账)。 - -用法: python scripts/flash_gd32.py [期望版本 如 1.6.5.0] -""" -import hashlib -import json -import os - -import struct -import sys -import time -import urllib.error -import urllib.request - -ESP = os.environ.get("DCU_ESP_IP", "192.168.31.127") -APP_BASE = 0x08048000 -HERE = os.path.dirname(os.path.abspath(__file__)) -ROOT = os.path.abspath(os.path.join(HERE, "..", "..", "..")) -HEX = os.path.join(ROOT, "DCU_APP", "out", "GD32E508_DCU_APP", "GD32E508VE", - "Release", "GD32E508_DCU_APP.hex") -LEDGER = os.path.join(HERE, "..", "lab", "_last_gd32_sha.txt") - - -def hex_to_flat(path): - """Intel HEX → 从 APP_BASE 起的连续字节。间隙填 0xFF(擦除态)。""" - mem = {} - base = 0 - with open(path, "r", encoding="ascii", errors="ignore") as f: - for ln, line in enumerate(f, 1): - line = line.strip() - if not line.startswith(":"): - continue - raw = bytes.fromhex(line[1:]) - cnt, addr, rtype = raw[0], struct.unpack(">H", raw[1:3])[0], raw[3] - data = raw[4:4 + cnt] - if len(raw) != cnt + 5 or (sum(raw) & 0xFF) != 0: - raise SystemExit(f"{path}:{ln} 校验和不对") - if rtype == 0x00: - for i, b in enumerate(data): - mem[base + addr + i] = b - elif rtype == 0x02: - base = struct.unpack(">H", data[:2])[0] << 4 - elif rtype == 0x04: - base = struct.unpack(">H", data[:2])[0] << 16 - elif rtype == 0x01: - break - if not mem: - raise SystemExit("HEX 里没有数据记录") - lo, hi = min(mem), max(mem) - if lo > APP_BASE: - raise SystemExit(f"镜像起点 0x{lo:08X} 高于 APP 基址 0x{APP_BASE:08X},不是 APP 包") - if lo < APP_BASE: - raise SystemExit(f"镜像含 0x{lo:08X} 的地址,落在 APP 区之外(疑似 BOOT 包)") - out = bytearray(b"\xFF" * (hi - APP_BASE + 1)) - for a, b in mem.items(): - out[a - APP_BASE] = b - return bytes(out) - - -def post(path, body, timeout=60): - req = urllib.request.Request(f"http://{ESP}{path}", data=body, method="POST") - req.add_header("Content-Type", "application/octet-stream") - try: - with urllib.request.urlopen(req, timeout=timeout) as r: - return r.status, r.read().decode("utf-8", "replace") - except urllib.error.HTTPError as e: - return e.code, e.read().decode("utf-8", "replace") - - -def get(path, timeout=10): - with urllib.request.urlopen(f"http://{ESP}{path}", timeout=timeout) as r: - return json.loads(r.read().decode("utf-8", "replace")) - - -def main(): - want = sys.argv[1] if len(sys.argv) > 1 else None - if not os.path.isfile(HEX): - raise SystemExit(f"找不到 {HEX}") - blob = hex_to_flat(HEX) - sha = hashlib.sha256(blob).hexdigest() - print(f"镜像 {HEX}\n 展平 {len(blob)}B @0x{APP_BASE:08X} sha256={sha}") - - hdr = json.dumps({"total_size": len(blob)}).encode("ascii") - b_ver, b_up = "", None - try: - b = get("/system/status") - b_ver = str((b.get("ota") or {}).get("gd32_app") or "").lstrip("v") - b_up = (b.get("gd32_adc") or {}).get("uptime_sec") - except Exception as e: - print(f"刷前 /system/status 读失败({type(e).__name__}):生效判定只能靠 uptime") - print(f"刷前: gd32_app=v{b_ver or '?'} gd32 uptime={b_up}") - t0 = time.time() - st, body = post("/ota/upload", hdr + blob, timeout=120) - print(f"upload -> {st} {body[:200]} ({time.time()-t0:.1f}s)") - if st != 200: - raise SystemExit("上传失败") - - t0 = time.time() - st, body = post("/ota/relay", json.dumps({"sha256": sha}).encode("ascii"), 60) - print(f"relay -> {st} {body[:300]} ({time.time()-t0:.1f}s)") - - deadline = time.time() + 90 - last = "" - while time.time() < deadline: - time.sleep(2) - try: - s = get("/system/status") - except Exception as e: - print(f" 等待中… status 读失败 {type(e).__name__}") - continue - o = s.get("ota", {}) - line = (f" sys_state={o.get('sys_state')} gd32_app={o.get('gd32_app')} " - f"alive={o.get('gd32_app_alive')} uptime={s.get('uptime_sec')}") - if line != last: - print(line) - last = line - if o.get("sys_state") == "GD32_COMPLETE" and o.get("gd32_app"): - ver = str(o["gd32_app"]).lstrip("v") - a_up = (s.get("gd32_adc") or {}).get("uptime_sec") - if want and ver != want: - raise SystemExit(f"板上版本 {o['gd32_app']} 与期望 {want} 不符 —— 刷写未生效") - changed = bool(b_ver) and ver != b_ver - rebooted = a_up is not None and (b_up is None or a_up < b_up) - if not changed and not rebooted: - raise SystemExit( - f"写入完成,但没有 GD32 换跑新代码的证据:板上仍是 v{ver}、" - f"uptime={a_up}(刷前 {b_up})—— 老镜像还在跑,台账不更新。" - "断电重启(或复位 APP)后重跑本脚本再确认") - with open(LEDGER, "w", encoding="ascii") as f: - f.write(sha) - print(f"完成: 板上 gd32_app=v{ver}(依据: " - f"{'版本 ' + b_ver + '→' + ver if changed else 'uptime 归零 ' + str(b_up) + '→' + str(a_up)}" - f"),台账已写 {os.path.normpath(LEDGER)}") - return - raise SystemExit("90s 内未见 GD32_COMPLETE —— 刷写未完成,别继续测试") - - -if __name__ == "__main__": - main() diff --git a/scripts/plot_ecat_nocan_vs_can.py b/scripts/plot_ecat_nocan_vs_can.py deleted file mode 100644 index 00b70fd..0000000 --- a/scripts/plot_ecat_nocan_vs_can.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""对比「带 CAN」与「拔掉 CAN」两轮扫描,分离每拍负担的组成。 - -用途:判断 ECAT 速度上限究竟由什么决定 —— 是每拍都要跑的过程数据交换 -(PDI_Isr / PDO 填充,与 CAN 无关),还是叠加在上面的 CAN 收发处理。 - -判读:两条 load 曲线的**垂直间距**就是 CAN 在每个周期档上贡献的负担。 -间距近似恒定 → CAN 负担与周期无关,上限由纯 ECAT 那条线单独决定; -间距随频率张开 → CAN 负担本身也按拍放大,提速会被 CAN 先拖垮。 - -用法: - python scripts/plot_ecat_nocan_vs_can.py \ - --nocan lab/ecat_rate_sweep_nocan.csv --withcan lab/ecat_rate_sweep_withcan.csv -""" -import argparse -import csv -import os - -import matplotlib -matplotlib.use("Agg") -import matplotlib.pyplot as plt -from matplotlib import rcParams - -rcParams["font.sans-serif"] = ["Microsoft YaHei", "SimHei", "DejaVu Sans"] -rcParams["axes.unicode_minus"] = False -rcParams["figure.dpi"] = 130 - - -def load(path): - out = {} - for r in csv.DictReader(open(path, encoding="utf-8")): - def num(k): - v = r.get(k, "") - try: - return float(v) if v not in ("", "None", None) else None - except ValueError: - return None - out[float(r["period_ms"])] = { - "load": num("gd32_load_pct"), "hz": num("measured_hz"), - "achieve": num("achieve_pct"), "skip": num("skip_per_s"), - "wkc_ok": r.get("wkc") == r.get("expected_wkc"), - "stale": num("gd32_load_stale")} - return out - - -def main(): - ap = argparse.ArgumentParser(description="带 CAN vs 无 CAN 每拍负担分离") - d = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - ap.add_argument("--nocan", default=os.path.join(d, "lab", "ecat_rate_sweep_nocan.csv")) - ap.add_argument("--withcan", default=os.path.join(d, "lab", "ecat_rate_sweep_withcan.csv")) - ap.add_argument("--out", default=None) - a = ap.parse_args() - - no, wc = load(a.nocan), load(a.withcan) - common = sorted(set(no) & set(wc), reverse=True) - out = a.out or os.path.splitext(a.nocan)[0] + "_vs.png" - - fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13.0, 5.2)) - fig.suptitle("每拍负担分离:拔掉 CAN 之后,速度上限由谁决定", fontsize=13) - - # 只取两侧 wkc 都达预期的档位:从站停报后 load_perm 会保留**残值** - # (实测楔死后仍报 99.2%),留在图里会被当成真数据、把 Δ 趋势带歪。 - healthy = [t for t in common if no[t]["wkc_ok"] and wc[t]["wkc_ok"]] - dropped = [t for t in common if t not in healthy] - T = [t for t in healthy if no[t]["load"] is not None and wc[t]["load"] is not None] - if not T: - raise SystemExit("两文件没有可对比的公共档位(且均需有效负载读数)") - Hz = [1000.0 / t for t in T] - ax1.plot(Hz, [wc[t]["load"] for t in T], "o-", color="#d62728", lw=2, - label="带 CAN(电机在报 STATUS/CONFIG)") - ax1.plot(Hz, [no[t]["load"] for t in T], "s-", color="#1f77b4", lw=2, - label="拔掉全部 CAN 设备") - for t, h in zip(T, Hz): - ax1.annotate(f"Δ={wc[t]['load'] - no[t]['load']:.1f}pp", (h, no[t]["load"]), - textcoords="offset points", xytext=(0, -14), ha="center", fontsize=8) - ax1.axhline(100, ls=":", color="k", lw=1) - ax1.set_xscale("log"); ax1.set_ylim(0, 112) - ax1.set_xlabel("循环率 (Hz)"); ax1.set_ylabel("load_perm (%)") - ax1.set_title("从站每拍负担(load_perm 口径,非整机 CPU)") - ax1.legend(fontsize=8, loc="upper left"); ax1.grid(alpha=0.3) - - ax2.plot(Hz, [wc[t]["skip"] for t in T], "o-", color="#d62728", lw=2, label="带 CAN") - ax2.plot(Hz, [no[t]["skip"] for t in T], "s-", color="#1f77b4", lw=2, label="无 CAN") - ax2.set_xscale("log"); ax2.set_yscale("symlog", linthresh=1) - ax2.set_xlabel("循环率 (Hz)"); ax2.set_ylabel("skip_total (次/s)") - ax2.set_title("主站错拍率:CAN 有无对节拍精度的影响") - ax2.legend(fontsize=8, loc="upper left"); ax2.grid(alpha=0.3) - - fig.tight_layout(rect=(0, 0, 1, 0.94)) - fig.savefig(out, bbox_inches="tight") - print(f"对比图 -> {out}\n") - print(f"{'周期':>7} {'循环率':>8} {'带CAN':>8} {'无CAN':>8} {'Δpp':>7} {'带CAN skip':>11} {'无CAN skip':>11}") - for t in T: - print(f"{t:>6}ms {1000.0/t:>7.0f}Hz {wc[t]['load']:>7.1f}% {no[t]['load']:>7.1f}% " - f"{wc[t]['load']-no[t]['load']:>6.1f} {wc[t]['skip']:>10.2f} {no[t]['skip']:>10.2f}") - skipped = [t for t in common if t not in T] - if skipped: - print(f"\n排除的档位:{skipped}") - for t in skipped: - why = [] - if not no[t]["wkc_ok"]: - why.append("无CAN 侧 wkc 不达预期(从站已停报,load_perm 为残值)") - if not wc[t]["wkc_ok"]: - why.append("带CAN 侧 wkc 不达预期") - if no[t]["load"] is None or wc[t]["load"] is None: - why.append("缺有效负载读数") - print(f" {t}ms: " + "; ".join(why or ["未知"])) - - -if __name__ == "__main__": - main() diff --git a/scripts/plot_ecat_sweep.py b/scripts/plot_ecat_sweep.py deleted file mode 100644 index 2cb5e96..0000000 --- a/scripts/plot_ecat_sweep.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""把 sweep_ecat_rate.py 的 CSV 画成「速度-故障率-CPU」四联图。 - -画法纪律(对应调研报告的判据要求): - * 先画实测点/折线,参考模型只在事后叠加,且线型与实测明显区分 —— 避免拿模型 - 冒充数据。容量参考线用的是**本次实测**的每拍入窗帧数,不是源码常量。 - * 无功效的轴(整段恒零)不画成"平坦的零线" —— 那会被读成"没问题"。改为在子图 - 里写明"本次测不到",并保留轴刻度供对比。 - * 横轴用频率(=1000/周期)而不是周期:物理上限在时间轴上是反比双曲线, - 在频率轴上是直线,读者不会看错方向。 -""" -import argparse -import csv -import os -import sys - -import matplotlib -matplotlib.use("Agg") -import matplotlib.pyplot as plt -from matplotlib import rcParams - -rcParams["font.sans-serif"] = ["Microsoft YaHei", "SimHei", "DejaVu Sans"] -rcParams["axes.unicode_minus"] = False -rcParams["figure.dpi"] = 130 - -MX = MY = [] -DEFAULT_CSV = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), - "lab", "ecat_rate_sweep.csv") - - -def load(path): - rows = list(csv.DictReader(open(path, encoding="utf-8"))) - if not rows: - raise SystemExit(f"{path} 没有数据行") - for r in rows: - r["hz_set"] = 1000.0 / float(r["period_ms"]) - for k in ("measured_hz", "achieve_pct", "can_lambda_per_s", "drop_per_s", - "drop_pct_of_lambda", "skip_per_s", "torn_per_s", "reentry_per_s", - "gd32_load_pct", "host_cpu_1core_pct", "cyc_p50_us", "cyc_p90_us", - "cyc_duty_pct", "lambda_u16_crosscheck", "rxb_count_mode_per_cycle"): - v = r.get(k, "") # 旧 CSV 可能没有后加的列 - try: - r[k] = float(v) if v not in ("", "None", None) else None - except ValueError: - r[k] = None - # CPU / 丢帧曲线的真正自变量是**实测**循环率:快档未必设得到设定值, - # 拿设定周期当横轴会把点画到错的位置上。 - r["hz"] = r["measured_hz"] or r["hz_set"] - rows.sort(key=lambda r: r["hz"]) - return rows - - -def ys(rows, key): - """取该列的 y 值序列;x 一律用 xs()。col() 曾按 (y, x) 顺序返回,调用处按 - (x, y) 解包会把 y 当横轴画(实测把 CPU% 画成了 2–100Hz),故改名并各自独立。""" - return [r[key] for r in rows] - - -def xs(rows): - return [r["hz"] for r in rows] - - -def no_power(rows, key): - """该轴整段恒零 → 无检出功效,不能画成"平坦的零线"。""" - vals = [r[key] for r in rows if r[key] is not None] - return (not vals) or max(vals) == 0 - - -def annotate_flags(ax, rows): - """把采样作废/劫持/PID 易主这些不可信标记写在图边上,避免读者信了坏数据。""" - bad = [] - for r in rows: - if r.get("hijacked") not in ("no", None): - bad.append(f"{r['period_ms']}ms 周期被劫持") - if r.get("pid_stable") != "yes": - bad.append(f"{r['period_ms']}ms native PID 易主") - if int(r.get("drop_untrusted") or 0) > 0: - bad.append(f"{r['period_ms']}ms drop 有 {r['drop_untrusted']} 点绕圈作废") - if bad: - ax.set_xlabel(ax.get_xlabel() + " ⚠ " + "; ".join(bad[:3]), color="darkred") - - -def main(): - ap = argparse.ArgumentParser(description="画 ECAT 速度-故障率-CPU 曲线") - ap.add_argument("--csv", default=DEFAULT_CSV) - ap.add_argument("--out", default=None, help="输出 PNG(默认与 CSV 同名 .png)") - a = ap.parse_args() - - rows = load(a.csv) - out = a.out or os.path.splitext(a.csv)[0] + ".png" - # 电机槽 seq 前进速率:扫描后另用 8s/档定点实测(判据见报告"方法"节)。 - # 单独测是因为它要看的是 u32 槽 seq 的秒增量,与扫描的 60s 平均口径不同源。 - global MX, MY - MX = [1000.0, 1999.0, 3989.0, 7762.0] - MY = [4000.2, 4000.2, 87.9, 147.5] - fig, axes = plt.subplots(2, 2, figsize=(13.5, 9.2)) - fig.suptitle("EtherCAT 周期速度 × 故障率 × CPU 占用(实测)", fontsize=13, y=0.985) - - # ---- 1 速度达成:设定 vs 实测 ---- - ax = axes[0][0] - xset = [r["hz_set"] for r in rows] - ax.plot([min(xset), max(xset)], [min(xset), max(xset)], "k--", lw=1, - label="理想 1:1(设定率)") - ax.plot(xset, [r["measured_hz"] for r in rows], "o-", color="#1f77b4", label="实测达成") - ax.set_xscale("log"); ax.set_yscale("log") - ax.set_xlabel("设定循环率 (Hz)"); ax.set_ylabel("实测循环率 (Hz)") - ax.set_title("① 循环率:设定 vs 实测(主站侧)"); ax.legend(fontsize=8, loc="upper left") - ax.grid(alpha=0.3, which="both") - txt = "达成率 " + "\n".join( - f"{r['period_ms']}ms: {r['achieve_pct']:.1f}%" for r in rows if r["achieve_pct"]) - ax.annotate(txt, (0.98, 0.5), xycoords="axes fraction", fontsize=7.5, - ha="right", va="center") - - # ---- 2 CPU 占用(两路:从站负载 vs 主机自旋)---- - ax = axes[0][1] - X = xs(rows) - gy, hy = ys(rows, "gd32_load_pct"), ys(rows, "host_cpu_1core_pct") - ax.plot(X, gy, "s-", color="#d62728", lw=2, label="GD32 负载 load_perm") - ax.plot(X, hy, "^-", color="#7f7f7f", lw=1.4, label="主机 native(自旋,单核%)") - ax.axhline(95, ls=":", color="darkred", lw=1) - ax.annotate("95% 逼近饱和", (1.02, 95), fontsize=7.5, color="darkred", va="bottom") - ax.set_xscale("log"); ax.set_ylim(0, 112) - ax.set_xlabel("循环率 (Hz)"); ax.set_ylabel("CPU 占用 (%)") - ax.set_title("② CPU 占用:GD32 与主机 native 两路") - ax.legend(fontsize=8, loc="center left"); ax.grid(alpha=0.3) - sat = [r for r in rows if r["gd32_load_pct"] is not None and r["gd32_load_pct"] >= 95] - if sat: - ax.annotate(f"≥95% 的档位: {', '.join(str(r['period_ms'])+'ms' for r in sat)}", - (0.02, 0.04), xycoords="axes fraction", fontsize=7.5, color="darkred") - - # ---- 3 故障率主曲线:功能是否还活着(电机数据刷新率)vs 从站负载 ---- - # 横轴用对数;λ 与 drop 两条线**刻意画在一起**,用来展示"加速对丢帧无改善" - ax = axes[1][0] - ax.plot(X, ys(rows, "can_lambda_per_s"), "o-", color="#2ca02c", lw=2, - label="λ CAN 到帧(u32 gseq)") - ax.plot(X, ys(rows, "drop_per_s"), "v--", color="#e377c2", lw=2, - label="drop 队列满丢帧(与 λ 重合)") - ax.plot(MX, MY, "^-", color="#d62728", lw=2.2, - label="电机数据刷新率(真故障轴)") - ax.axvspan(2000, 4000, color="red", alpha=0.07) - ax.annotate("GD32 饱和 → 功能塌陷", (2550, 40), fontsize=8, color="darkred") - ax.set_xscale("log"); ax.set_yscale("log") # 帧/s 恒为正,用 symlog 会把轴拖到负值 - ax.set_ylim(10, 20000) - ax.set_xlabel("实测循环率 (Hz)"); ax.set_ylabel("帧/s") - ax.set_title("③ 故障率:加速对丢帧毫无改善,且 >2kHz 直接打断功能") - ax.grid(alpha=0.3, which="both") - ax.legend(fontsize=7.5, loc="lower left") - - # ---- 4 错拍率 + 撕裂/重入功效声明 ---- - ax = axes[1][1] - ax.plot(X, ys(rows, "skip_per_s"), "D-", color="#ff7f0e", lw=2, - label="主站错拍 skip_total(次/s)") - ax.set_xscale("log"); ax.set_yscale("symlog", linthresh=1) # skip 有真 0 档,需 symlog - ax.set_xlabel("循环率 (Hz)"); ax.set_ylabel("次/s") - ax.set_ylim(-10, None) - ax.set_title("④ 主站错拍率,及撕裂/重入轴功效声明"); ax.legend(fontsize=8, loc="upper left") - ax.grid(alpha=0.3) - dead = [n for n, k in (("rx_torn", "torn_per_s"), ("pdo_reentry", "reentry_per_s")) - if no_power(rows, k)] - if dead: - ax.annotate("恒零轴(无检出功效):" + ", ".join(dead) + - "\n→ 本次测不到 ≠ 无故障,不得据此判\"已修好\"\n" - " (且固件版本无法与在盘产物对账)", - (0.03, 0.06), xycoords="axes fraction", fontsize=8, - color="darkred", bbox=dict(fc="#fff3f3", ec="darkred", alpha=0.9)) - annotate_flags(ax, rows) - - fig.tight_layout(rect=(0, 0, 1, 0.975)) - fig.savefig(out, bbox_inches="tight") - print(f"曲线图 -> {out}") - for r in rows: - def fmt(v, w=7, p=0, unit=""): - return f"{v:>{w}.{p}f}{unit}" if v is not None else f"{'n/a':>{w}}" - ld = r["gd32_load_pct"] - extra = (f"{ld:.1f}%" if ld is not None else "n/a") - if r.get("gd32_load_stale") is not None: - extra += f"(样{r.get('gd32_load_samples')}/废{r['gd32_load_stale']})" - print(f" {r['period_ms']:>6}ms {fmt(r['hz']):>7}Hz 实测{fmt(r['measured_hz'])}Hz " - f"GD32={extra:<18} λ={fmt(r['can_lambda_per_s'])}/s " - f"drop={fmt(r['drop_per_s'])}/s skip={fmt(r['skip_per_s'], 6, 1)}/s " - f"在线={r.get('online_frac', 'n/a')}") - if dead: - print(f" 无功效轴: {dead} —— 报告按「测不到」写") - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/rec_ecat_slots.py b/scripts/rec_ecat_slots.py deleted file mode 100644 index 0d6fe03..0000000 --- a/scripts/rec_ecat_slots.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""把 ECAT 侧收到的每一拍信号全量落盘,供事后分析(不在线判优劣)。 - -去重规则只有一条:同一 (node,seq) 或同一 (id,data) 帧只在第一次出现时写一行 —— -重复采样不携带新信息,去掉后可以把时长拉长。其余一律保留。 - -行格式(CSV,第 1 列是相对起始的秒): - M,t,node,ch,seq,online,dlc,hex36 段2 电机槽(STATUS 前 36B,含 cpu%@31/init@32-35) - F,t,kind,ch,dlc,id,hex36 段3 xrx / 段4 rx_batch 的帧 - H,t,cycles,wkc,gseq,win_base,win_count,active,rx,tx,drop 每 20 拍一条头部与桥计数 - -用法: python scripts/rec_ecat_slots.py [秒数=120] [端口=8090] [输出文件] -""" -import csv -import json -import socket -import struct -import sys -import time - -HOST = "127.0.0.1" -DUR_S = float(sys.argv[1]) if len(sys.argv) > 1 else 120.0 -PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 8090 -OUT = sys.argv[3] if len(sys.argv) > 3 else "ecat_slots_raw.csv" - -REQ = (f"POST /api/motor/state HTTP/1.0\r\nHost: {HOST}:{PORT}\r\n" - "Content-Type: application/json\r\nContent-Length: 2\r\n" - "Connection: close\r\n\r\n{}").encode("ascii") - - -def once(): - s = socket.create_connection((HOST, PORT)) - try: - s.sendall(REQ) - buf = bytearray() - while True: - c = s.recv(65536) - if not c: - break - buf.extend(c) - finally: - s.close() - _, _, d = bytes(buf).partition(b"\r\n\r\n") - return json.loads(d.decode("utf-8", "replace")) - - -def main(): - seen_m, seen_f = set(), set() - polls = rows = 0 - t0 = time.time() - with open(OUT, "w", newline="", encoding="ascii") as f: - w = csv.writer(f) - w.writerow(["#kind", "t", "a", "b", "c", "d", "e", "f"]) - while True: - dt = time.time() - t0 - if dt >= DUR_S: - break - try: - snap = once() - except Exception: - continue - polls += 1 - h = snap.get("head") or {} - b = snap.get("bridge") or {} - if polls % 20 == 1: - w.writerow(["H", f"{dt:.6f}", snap.get("cycles"), snap.get("wkc"), - h.get("global_seq"), h.get("win_base"), h.get("win_count"), - h.get("active"), b.get("rx_count"), b.get("tx_count"), - b.get("drop")]) - for m in snap.get("motors") or []: - if not m.get("data"): - continue - key = (m["node"], m["ch"], m["seq"]) - if key in seen_m: - continue - seen_m.add(key) - if len(seen_m) > 400000: # 去重集合本身的内存上限 - seen_m = set(list(seen_m)[-200000:]) - d = bytes.fromhex(m["data"])[:36] - w.writerow(["M", f"{dt:.6f}", m["node"], m["ch"], m["seq"], - int(bool(m.get("online"))), m.get("dlc"), d.hex()]) - rows += 1 - def emit_frames(fr, kind): - if not fr or not fr.get("data"): - return - d = bytes.fromhex(fr["data"])[:36] - key = (fr.get("id"), d.hex()) - if key in seen_f: - return - seen_f.add(key) - w.writerow([kind, f"{dt:.6f}", fr.get("ch"), fr.get("dlc"), - fr.get("id"), "", "", d.hex()]) - - emit_frames(snap.get("xrx"), "X") - for x in ((snap.get("rx_batch") or {}).get("frames") or []): - emit_frames(x, "B") - if len(seen_f) > 400000: - seen_f = set(list(seen_f)[-200000:]) - print(f"落盘 {OUT}: 轮询 {polls} 次 / {DUR_S:.0f}s,写入 {rows} 行 " - f"(电机槽 {len(seen_m)} 帧, 接收帧 {len(seen_f)})") - - -if __name__ == "__main__": - main() diff --git a/scripts/repro_cmdload.py b/scripts/repro_cmdload.py deleted file mode 100644 index dc96a64..0000000 --- a/scripts/repro_cmdload.py +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""复现实验:高频回发 CMD(与电机当前目标一致的 no-op)+ 全拍监控。 - -用户反馈:数据错乱要"频繁发指令"才出现。本脚本把 TX 压力拉起来,同时 -段2(全拍)与段4(全拍)双路监控,命中即记录 dlc/完整48B/当时CONFIG/时刻, -用于判定残余拼接的方向与机制。 - -安全性:CMD 载荷 = 电机当前自报 CONFIG 的回声(mode/limitV/cur/vel/ang) -+ 页面默认 PID(100/100/100),不改变电机行为。 - -用法: python scripts/repro_cmdload.py [秒数=90] [发送Hz=500] -""" -import json -import math -import socket -import struct -import sys -import threading -import time - -HOST, PORT = "127.0.0.1", 8090 -DUR_S = float(sys.argv[1]) if len(sys.argv) > 1 else 90.0 -SEND_HZ = float(sys.argv[2]) if len(sys.argv) > 2 else 500.0 - - -def post(path, body=b"{}"): - req = (f"POST {path} HTTP/1.0\r\nHost: {HOST}:{PORT}\r\n" - f"Content-Type: application/json\r\nContent-Length: {len(body)}\r\n" - "Connection: close\r\n\r\n").encode("ascii") + body - s = socket.create_connection((HOST, PORT)) - try: - s.settimeout(5) - s.sendall(req) - buf = bytearray() - while True: - c = s.recv(65536) - if not c: - break - buf.extend(c) - finally: - s.close() - return json.loads(bytes(buf).partition(b"\r\n\r\n")[2].decode("utf-8", "replace")) - - -def build_cmd(cfg, ang, vel): - """CMD 帧:mode3 速度+位置,limitV 回声当前 CONFIG,角度=正弦轨迹。 - vel 按节点取 200/150 —— 给 CONFIG 回显的 B7-10 速度字段打节点指纹。""" - b = bytearray(32) - b[0] = 3 # mode: Velocity_Angle - b[1:3] = cfg[1:3] # limitV u16 回声 - b[3:7] = struct.pack(" 最近 4 个已接受 CONFIG hex(页面过滤器模拟) -cfg_susp = {} # node -> 连续疑似计数 -torn_base = [0] - - -def sender(): - """每拍把两电机当前 CONFIG 回声成 CMD 发出去。""" - cfgs = {} - interval = 1.0 / SEND_HZ - while not stop.is_set(): - t0 = time.time() - try: - rr = post("/api/motor/rxraw") - raw = bytes.fromhex(rr.get("raw") or "") - off = 0 - for _ in range(rr.get("count") or 0): - if off + 8 > len(raw): - break - dlc = raw[off + 1] - fid = struct.unpack_from("> 16) & 0xFF] = raw[off + 8: off + 8 + w] - off += 8 + w - except Exception: - time.sleep(0.05) - continue - if len(cfgs) >= 2 or time.time() - stats["t0"] > 3: - # 电机偶尔暂停 CONFIG 自报(如正弦轰炸后):拿不到回声就用页面默认 - # 限压 24.0V(u16 LE 0x5DC0 → bytes c0 5d)兜底,保证指令压力不断。 - fallback = bytes([0x03, 0xc0, 0x5d]) + bytes(21) - s = math.sin(2.0 * math.pi * (time.time() - stats["t0"]) / 10.0) - frames = [] - for n in (2, 3): - c = cfgs.get(n, fallback) - ang = 1000.0 + (200.0 * s if n == 2 else -200.0 * s) - vel = 200.0 if n == 2 else 150.0 - frames.append({"ch": 2, "id": hex((n << 16) | 0x0200), "dlc": 32, - "data": build_cmd(c, ang, vel)}) - try: - post("/api/motor/send", json.dumps({"frames": frames}).encode()) - with lock: - stats["sends"] += 1 - except Exception: - with lock: - stats["send_err"] += 1 - dt = interval - (time.time() - t0) - if dt > 0: - time.sleep(dt) - - -def is_cfg_head(d, cfg): - """段2槽头 8B 是否等于该节点当前 CONFIG 头(污染判据,比量程更准)。""" - return cfg and len(d) >= 8 and d[:8] == cfg[:8] and d[0] == 0x03 - - -def monitor(): - seen_s, seen_r = set(), set() - cfgs = {} - while not stop.is_set(): - try: - st = post("/api/motor/state") - except Exception: - time.sleep(0.01) - continue - cyc = st.get("cycles") - if cyc in seen_s: - continue - seen_s.add(cyc) - if len(seen_s) > 400000: - seen_s.clear() - for m in st.get("motors") or []: - if not m.get("online") or len(m.get("data") or "") < 96: - continue - d = bytes.fromhex(m["data"]) - mcu = struct.unpack_from(" 400000: - seen_r.clear() - raw = bytes.fromhex(rr.get("raw") or "") - off = 0 - for _ in range(rr.get("count") or 0): - if off + 8 > len(raw): - break - dlc = raw[off + 1] - fid = struct.unpack_from("> 16) & 0xFF - if (fid & 0xFFFF) == 0x0101: - cfgs[node] = d - cfg_total[0] += 1 # 活性自证:本拍监控实际看到的 CONFIG 帧数 - if any(d[15:24]): - with lock: - if len(batch_events) < 40: - batch_events.append((round(time.time() - stats["t0"], 2), - node, d.hex())) - # CONFIG↔CONFIG 拼接:结构校验抓不到,用速度指纹暴露。 - # 预热 3s:sender 尚未生效时电机回显的是页面/上一轮的 vel, - # 不是拼接,不能算违规。 - if len(d) >= 15 and time.time() - stats["t0"] > 3.0: - vel = struct.unpack_from(" 1.0 - with lock: - # 模拟页面 CONFIG 跨节点拼接过滤器(transport-ecat.js - # dispatchFrame 同逻辑),统计过滤后残余违规与误杀。 - hx = d.hex() - seen = cfg_seen.setdefault(node, []) - own = hx in seen - foreign = (not own) and any( - hx in s2 for n2, s2 in cfg_seen.items() if n2 != node) - if own or not foreign or cfg_susp.get(node, 0) >= 5: - cfg_susp[node] = 0 - seen.insert(0, hx) - del seen[4:] - if viol: - fp_after[0] += 1 - if len(fp_events) < 40: - fp_events.append((round(time.time() - stats["t0"], 2), - node, vel, exp_vel, hx)) - else: - cfg_susp[node] = cfg_susp.get(node, 0) + 1 - if viol: - fp_dropped[0] += 1 - else: - fp_falsekill[0] += 1 - off += 8 + w - - -def main(): - try: - r = post("/api/app/load") - torn_base[0] = r.get("rx_torn") or 0 - except Exception: - pass - ts = threading.Thread(target=sender, daemon=True) - tm = threading.Thread(target=monitor, daemon=True) - ts.start(); tm.start() - time.sleep(DUR_S) - stop.set(); ts.join(3); tm.join(3) - try: - r = post("/api/app/load") - torn_now = r.get("rx_torn") or 0 - except Exception: - torn_now = -1 - with lock: - print(f"时长 {DUR_S:.0f}s 发送 {stats['sends']} 次(错{stats['send_err']}) " - f"≈{stats['sends']/DUR_S:.0f} CMD拍/s") - print(f"rx_torn: {torn_base[0]} -> {torn_now} (Δ={torn_now-torn_base[0]})") - print(f"段2 污染事件: {len(torn_events)}(前 8)") - for e in torn_events[:8]: - print(f" t={e[0]}s node={e[1]} dlc={e[2]}") - print(f" slot: {e[3]}") - print(f" cfg : {e[4]}") - print(f"段4 污染事件: {len(batch_events)}(前 8)") - for e in batch_events[:8]: - print(f" t={e[0]}s node={e[1]} data={e[2]}") - print(f"CONFIG↔CONFIG 指纹(预热后): 违规={fp_after[0]+fp_dropped[0]} " - f"过滤器正确丢弃={fp_dropped[0]} 过滤后残余={fp_after[0]} " - f"误杀真帧={fp_falsekill[0]}(样本 {len(fp_events)})") - print(f"监控消费的 CONFIG 帧总数: {cfg_total[0]}(活性自证:>0 才说明违规=0 是真结果)") - for e in fp_events[:8]: - print(f" t={e[0]}s node={e[1]} vel={e[2]:.1f} 应为{e[3]:.0f} data={e[4]}") - - -if __name__ == "__main__": - main() diff --git a/scripts/skip_bind_experiment.py b/scripts/skip_bind_experiment.py deleted file mode 100644 index 17298d7..0000000 --- a/scripts/skip_bind_experiment.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python3 -"""在「以太网 2」(I210) 上关闭非 EtherCAT 绑定,测跳拍率;可 --restore 还原。""" -from __future__ import annotations - -import json -import os -import subprocess -import sys -import time - -ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) -LAB = os.path.join(ROOT, "lab", "skip_meter") -NIC = "以太网 2" - - -def ps(cmd, timeout=60): - r = subprocess.run( - ["powershell", "-NoProfile", "-Command", cmd], - capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout, - ) - return (r.stdout or "").strip(), (r.stderr or "").strip() - - -def snapshot(): - out, err = ps( - f"Get-NetAdapterBinding -Name '{NIC}' | " - "Select-Object DisplayName,ComponentID,Enabled | ConvertTo-Json -Compress" - ) - data = json.loads(out) - return data if isinstance(data, list) else [data] - - -def disable(component_ids): - logs = [] - for cid in component_ids: - out, err = ps( - f"Disable-NetAdapterBinding -Name '{NIC}' -ComponentID '{cid}' -Confirm:$false; " - f"Get-NetAdapterBinding -Name '{NIC}' -ComponentID '{cid}' | " - "Select-Object ComponentID,Enabled | ConvertTo-Json -Compress" - ) - logs.append({"cid": cid, "out": out[:300], "err": err[:300]}) - return logs - - -def enable(component_ids): - logs = [] - for cid in component_ids: - out, err = ps( - f"Enable-NetAdapterBinding -Name '{NIC}' -ComponentID '{cid}' -Confirm:$false; " - f"Get-NetAdapterBinding -Name '{NIC}' -ComponentID '{cid}' | " - "Select-Object ComponentID,Enabled | ConvertTo-Json -Compress" - ) - logs.append({"cid": cid, "out": out[:300], "err": err[:300]}) - return logs - - -def set_prio(level="RealTime"): - p = int(subprocess.check_output( - ["powershell", "-NoProfile", "-Command", "(Get-Process ecat_native).Id"], - text=True).strip()) - subprocess.check_output( - ["powershell", "-NoProfile", "-Command", - f"$p=Get-Process -Id {p}; $p.PriorityClass='{level}'; $p.PriorityClass"], - text=True) - return p - - -def meter(tag, secs=18): - r = 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")) - - -# Offloads that are useless/dangerous for raw EtherCAT on a dedicated NIC -OFFLOADS = [ - "vmware_bridge", # VMware Bridge — intercepts/injects, classic ECAT killer - "ms_pacer", # QoS packet scheduler - "ms_lldp", # LLDP - "ms_rspndr", # LLTD responder - "ms_lltdio", # LLTD mapper - "ms_server", # file/printer sharing - "ms_msclient", # Microsoft network client - # keep ms_tcpip / ms_tcpip6 for now in case anything uses IP on this NIC - # Npcap must stay: INSECURE_NPCAP -] - - -def main(): - os.makedirs(LAB, exist_ok=True) - snap = snapshot() - snap_path = os.path.join(LAB, f"nic_bind_{int(time.time())}.json") - json.dump(snap, open(snap_path, "w", encoding="utf-8"), ensure_ascii=False, indent=2) - print("bind snapshot", snap_path) - - if "--restore" in sys.argv: - print(enable(OFFLOADS)) - return 0 - - print("disable", OFFLOADS) - print(json.dumps(disable(OFFLOADS), ensure_ascii=False, indent=2)) - print("set RealTime") - print("pid", set_prio("RealTime")) - # binding change usually doesn't reset link; brief settle - time.sleep(3) - data = meter("bind_off", 18) - row = { - "late_per_s": data["late_per_s"], - "skip_per_s": data["skip_per_s"], - "late_frac_pct": data["late_frac_pct"], - "achieved_hz": data["achieved_hz"], - "n_late": data["n_late"], - "send_stall_frac": data["send_stall_frac"], - "rx_stall_frac": data["rx_stall_frac"], - "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"], - } - print("ROW", json.dumps(row, ensure_ascii=False, indent=2)) - out = os.path.join(LAB, f"summary_bind_off_{int(time.time())}.json") - json.dump({"row": row, "disabled": OFFLOADS, "snap": snap_path}, - open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=2) - print("WROTE", out) - - -if __name__ == "__main__": - main() diff --git a/scripts/skip_bind_noip.py b/scripts/skip_bind_noip.py deleted file mode 100644 index 0d6cc51..0000000 --- a/scripts/skip_bind_noip.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -import json, os, subprocess, sys, time -ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -LAB = os.path.join(ROOT, "lab", "skip_meter") -NIC = "以太网 2" - -def ps(cmd): - r = subprocess.check_output(["powershell","-NoProfile","-Command",cmd], - text=True, encoding="utf-8", errors="replace") - return r.strip() - -def set_prio(level="RealTime"): - p = int(ps("(Get-Process ecat_native).Id")) - ps(f"$p=Get-Process -Id {p}; $p.PriorityClass='{level}'; $p.PriorityClass") - return p - -def meter(tag, secs=18): - 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(): - restore = "--restore" in sys.argv - ids = ["ms_tcpip", "ms_tcpip6"] - for cid in ids: - if restore: - ps(f"Enable-NetAdapterBinding -Name '{NIC}' -ComponentID '{cid}' -Confirm:$false") - else: - ps(f"Disable-NetAdapterBinding -Name '{NIC}' -ComponentID '{cid}' -Confirm:$false") - print(ps(f"Get-NetAdapterBinding -Name '{NIC}' -ComponentID '{cid}' | Select-Object ComponentID,Enabled | ConvertTo-Json -Compress")) - if restore: - return - print("pid", set_prio("RealTime")) - time.sleep(3) - data = meter("bind_noip", 18) - row = { - "late_per_s": data["late_per_s"], "skip_per_s": data["skip_per_s"], - "late_frac_pct": data["late_frac_pct"], "achieved_hz": data["achieved_hz"], - "n_late": data["n_late"], - "send_stall_frac": data["send_stall_frac"], "rx_stall_frac": data["rx_stall_frac"], - "work_p50": data["window_cyc_us"]["work"]["p50"], - "rx_p50": data["window_cyc_us"]["rx"]["p50"], - "send_p50": data["window_cyc_us"]["send"]["p50"], - } - print(json.dumps(row, ensure_ascii=False, indent=2)) - out = os.path.join(LAB, f"summary_bind_noip_{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() diff --git a/scripts/skip_fe_ab.py b/scripts/skip_fe_ab.py deleted file mode 100644 index 6150ac9..0000000 --- a/scripts/skip_fe_ab.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -"""0.25ms 下 A/B:静默 vs 模拟 spi_test 前端轮询。""" -import json, time, urllib.request, os - -ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -LAB = os.path.join(ROOT, "lab", "skip_meter") - -def post(p, b=None, timeout=5): - d = json.dumps(b or {}).encode() - r = urllib.request.Request("http://127.0.0.1:8090"+p, data=d, headers={"Content-Type":"application/json"}) - with urllib.request.urlopen(r, timeout=timeout) as resp: - return json.loads(resp.read().decode()) - -def snap(): - st = post("/api/motor/state") - dbg = post("/api/motor/dbg") - return { - "cycles": st.get("cycles") or 0, - "skip_total": dbg.get("skip_total") or 0, - "gseq": (st.get("head") or {}).get("global_seq") or 0, - "period": st.get("period_ms"), - "wkc": st.get("wkc"), - "pcore": st.get("pcore_mask"), - "cyc_us": st.get("cyc_us"), - } - -def rate(a, b, dt): - dc = b["cycles"] - a["cycles"] - ds = (b["skip_total"] - a["skip_total"]) # may wrap? long long, no - return { - "dt": round(dt, 3), - "pdo_hz": round(dc / dt, 1) if dt else None, - "late_per_s": round(ds / dt, 2) if dt else None, - "skip_frac": round(100.0 * ds / dc, 2) if dc else None, - "gseq_per_s": round(((b["gseq"] - a["gseq"]) & 0xFFFFFFFF) / dt, 1) if dt else None, - "dcyc": dc, "dskip": ds, - } - -def arm_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") - time.sleep(0.5) - -print("ensure 0.25ms") -arm_period(0.25) -time.sleep(3) - -# --- A: fully quiet --- -a0 = snap() -t0 = time.time() -# no HTTP -time.sleep(10) -a1 = snap() -A = rate(a0, a1, time.time() - t0) -print("QUIET", json.dumps(A, ensure_ascii=False)) -print(" cyc_us", json.dumps(a1.get("cyc_us"), ensure_ascii=False)) - -# --- B: hammer like spi_test (state+load+dbg+rxraw) --- -time.sleep(1) -b0 = snap() -t0 = time.time() -n = 0 -while time.time() - t0 < 10: - try: - post("/api/motor/state") - post("/api/app/load") - post("/api/motor/dbg") - post("/api/motor/rxraw") - n += 1 - except Exception as e: - print("hammer err", e) - break - time.sleep(0.05) # ~20Hz full set, similar to busy panel -b1 = snap() -B = rate(b0, b1, time.time() - t0) -print("HAMMER", json.dumps(B, ensure_ascii=False), "polls", n) -print(" cyc_us", json.dumps(b1.get("cyc_us"), ensure_ascii=False)) - -# --- C: only motor/state at max (no dbg/rxraw) --- -time.sleep(1) -c0 = snap() -t0 = time.time() -n = 0 -while time.time() - t0 < 10: - try: - post("/api/motor/state") - n += 1 - except Exception: - break - time.sleep(0.05) -c1 = snap() -C = rate(c0, c1, time.time() - t0) -print("STATE_ONLY", json.dumps(C, ensure_ascii=False), "polls", n) - -out = {"quiet": A, "hammer": B, "state_only": C, - "quiet_cyc": a1.get("cyc_us"), "hammer_cyc": b1.get("cyc_us")} -path = os.path.join(LAB, f"summary_fe_ab_{int(time.time())}.json") -json.dump(out, open(path, "w", encoding="utf-8"), ensure_ascii=False, indent=2) -print("WROTE", path) diff --git a/scripts/skip_host_snapshot.py b/scripts/skip_host_snapshot.py deleted file mode 100644 index fa71e87..0000000 --- a/scripts/skip_host_snapshot.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""快照当前主机侧可回滚现场:I210 高级属性、中断亲和、ecat 进程。""" -from __future__ import annotations - -import json -import os -import subprocess -import sys -import time - -ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) -LAB = os.path.join(ROOT, "lab", "skip_meter") - - -def ps(cmd: str) -> str: - r = subprocess.run( - ["powershell", "-NoProfile", "-Command", cmd], - capture_output=True, - text=True, - encoding="utf-8", - errors="replace", - ) - return (r.stdout or "") + (("ERR:" + r.stderr) if r.stderr else "") - - -def main() -> int: - os.makedirs(LAB, exist_ok=True) - snap = {"ts": time.time(), "host": os.environ.get("COMPUTERNAME")} - - snap["adapters"] = ps( - "Get-NetAdapter | Select-Object Name,InterfaceDescription,LinkSpeed,Status," - "ifIndex,MacAddress | ConvertTo-Json -Compress" - ) - # I210 specifically - snap["i210"] = ps( - "Get-NetAdapter | Where-Object {$_.InterfaceDescription -match 'I210'} | " - "Select-Object Name,ifIndex,Status,LinkSpeed | ConvertTo-Json -Compress" - ) - - # advanced properties (restore-able) - snap["i210_adv"] = ps( - "Get-NetAdapterAdvancedProperty -Name '以太网 2' -ErrorAction SilentlyContinue | " - "Select-Object Name,DisplayName,DisplayValue,RegistryKeyword,RegistryValue | " - "ConvertTo-Json -Compress" - ) - - # interrupt moderation / RSS / power - for kw in ( - "*InterruptModeration", - "*ReceiveBuffers", - "*TransmitBuffers", - "*EEE", - "*EnergyEfficient", - "*FlowControl", - "*PriorityVLANTag", - "*TCPChecksumOffload*", - "*IPChecksumOffload*", - "*VMQ", - "*RSS", - "*InterruptModerationRate", - ): - snap[f"adv_{kw.strip('*').replace('*','_')}"] = ps( - f"Get-NetAdapterAdvancedProperty -Name '以太网 2' -ErrorAction SilentlyContinue | " - f"Where-Object {{$_.RegistryKeyword -like '{kw}'}} | " - f"Select-Object RegistryKeyword,DisplayValue,RegistryValue | ConvertTo-Json -Compress" - ) - - snap["power"] = ps( - "Get-NetAdapterPowerManagement -Name '以太网 2' -ErrorAction SilentlyContinue | " - "Select-Object Name,AllowComputerToTurnOffDevice,WakeOnMagicPacket," - "WakeOnPattern,DeviceSleepOnDisconnect | ConvertTo-Json -Compress" - ) - - snap["ecat_proc"] = ps( - "Get-Process ecat_native -ErrorAction SilentlyContinue | " - "Select-Object Id,Path,StartTime,CPU,WorkingSet | ConvertTo-Json -Compress" - ) - - snap["ecat_cmdline"] = ps( - "Get-CimInstance Win32_Process -Filter \"Name='ecat_native.exe'\" | " - "Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress" - ) - - # interrupt affinity via Get-NetAdapterInterrupt? not always available - snap["msi_affinity"] = ps( - "Get-NetAdapterAdvancedProperty -Name '以太网 2' -ErrorAction SilentlyContinue | " - "Where-Object {$_.DisplayName -match 'Interrupt|Affinity|RSS|Moderation|Buffer|Energy|Green|Flow|Offload'} | " - "Select-Object DisplayName,DisplayValue,RegistryKeyword | ConvertTo-Json -Compress" - ) - - # CPU topology quick - snap["cpu"] = ps( - "$env:NUMBER_OF_PROCESSORS; (Get-CimInstance Win32_Processor | " - "Select-Object Name,NumberOfCores,NumberOfLogicalProcessors | ConvertTo-Json -Compress)" - ) - - out = os.path.join(LAB, f"host_snap_{int(time.time())}.json") - with open(out, "w", encoding="utf-8") as f: - json.dump(snap, f, ensure_ascii=False, indent=2) - - # also human-readable restore snippet - lines = [] - lines.append("# Host snapshot restore notes") - lines.append(f"time={snap['ts']}") - lines.append("## I210 adapter advanced (raw)") - lines.append(snap.get("i210_adv", "")[:8000]) - lines.append("## power") - lines.append(snap.get("power", "")[:4000]) - lines.append("## ecat cmdline") - lines.append(snap.get("ecat_cmdline", "")[:2000]) - txt = os.path.join(LAB, f"host_snap_{int(snap['ts'])}.txt") - with open(txt, "w", encoding="utf-8") as f: - f.write("\n".join(lines)) - - print("I210:", snap.get("i210", "")[:500]) - print("CMDLINE:", snap.get("ecat_cmdline", "")[:800]) - print("ADV keys sample:") - print(snap.get("msi_affinity", "")[:2500]) - print("WROTE", out) - print("WROTE", txt) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/skip_itr200_detail.py b/scripts/skip_itr200_detail.py deleted file mode 100644 index 1b47346..0000000 --- a/scripts/skip_itr200_detail.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/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=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) diff --git a/scripts/skip_itr_4k.py b/scripts/skip_itr_4k.py deleted file mode 100644 index 310181f..0000000 --- a/scripts/skip_itr_4k.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/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) diff --git a/scripts/skip_multi_round.py b/scripts/skip_multi_round.py deleted file mode 100644 index 2095488..0000000 --- a/scripts/skip_multi_round.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python3 -import json, os, subprocess, sys, time -ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -LAB = os.path.join(ROOT, "lab", "skip_meter") - -def set_prio(level="RealTime"): - p = int(subprocess.check_output(["powershell","-NoProfile","-Command","(Get-Process ecat_native).Id"], text=True).strip()) - subprocess.check_output(["powershell","-NoProfile","-Command", - f"$p=Get-Process -Id {p}; $p.PriorityClass='{level}'; $p.PriorityClass"], text=True) - return p - -def meter(tag, secs=15): - 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")) - d = json.load(open(os.path.join(LAB, files[-1]), encoding="utf-8")) - return { - "tag": tag, - "late_per_s": d["late_per_s"], "skip_per_s": d["skip_per_s"], - "achieved_hz": d["achieved_hz"], "n_late": d["n_late"], - "work_p50": d["window_cyc_us"]["work"]["p50"], - "rx_p50": d["window_cyc_us"]["rx"]["p50"], - "send_p50": d["window_cyc_us"]["send"]["p50"], - "work_p90": d["window_cyc_us"]["work"]["p90"], - } - -def main(): - set_prio("RealTime") - rows = [] - for i in range(1, 4): - print(f"round {i} settle...") - time.sleep(4) - r = meter(f"multirt_{i}", 15) - print(r) - rows.append(r) - lates = [x["late_per_s"] for x in rows] - print("mean late", sum(lates)/len(lates), "min", min(lates), "max", max(lates)) - path = os.path.join(LAB, f"summary_multirt_{int(time.time())}.json") - json.dump(rows, open(path,"w",encoding="utf-8"), ensure_ascii=False, indent=2) - print("WROTE", path) - -if __name__ == "__main__": - main() diff --git a/scripts/skip_nic_experiment.py b/scripts/skip_nic_experiment.py deleted file mode 100644 index 39f00b6..0000000 --- a/scripts/skip_nic_experiment.py +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""可回滚的 I210 网卡参数实验:停环 → 改高级属性 → 恢复 OP → 标定跳拍率。 - -默认只测、不永久破坏:原值会写入 restore json;`--restore` 按快照还原。 -改网卡会触发短暂链路重协商,期间从站可能掉 OP,脚本会等 link+OP 恢复。 -""" -from __future__ import annotations - -import argparse -import json -import os -import subprocess -import sys -import time -import urllib.request - -ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) -LAB = os.path.join(ROOT, "lab", "skip_meter") -NIC = "以太网 2" -BASE = os.environ.get("ECAT_BASE", "http://127.0.0.1:8090") - - -def post(path, body=None, timeout=8.0): - data = json.dumps(body or {}).encode() - req = urllib.request.Request( - BASE + path, data=data, headers={"Content-Type": "application/json"} - ) - with urllib.request.urlopen(req, timeout=timeout) as r: - return json.loads(r.read().decode()) - - -def ps(cmd, timeout=60): - r = subprocess.run( - ["powershell", "-NoProfile", "-Command", cmd], - capture_output=True, - text=True, - encoding="utf-8", - errors="replace", - timeout=timeout, - ) - return (r.stdout or "").strip(), (r.stderr or "").strip() - - -def nic_snapshot(): - out, err = ps( - f"Get-NetAdapterAdvancedProperty -Name '{NIC}' | " - "Select-Object RegistryKeyword,DisplayValue,RegistryValue | " - "ConvertTo-Json -Compress" - ) - if not out: - return [] - data = json.loads(out) - if isinstance(data, dict): - return [data] - return data - - -def nic_set(keyword: str, value: str): - # value as string; Intel accepts "0"/"1"/numbers - cmd = ( - f"Set-NetAdapterAdvancedProperty -Name '{NIC}' " - f"-RegistryKeyword '{keyword}' -RegistryValue '{value}' -NoRestart" - ) - out, err = ps(cmd) - return out, err - - -def nic_restart(): - out, err = ps( - f"Restart-NetAdapter -Name '{NIC}' -Confirm:$false; Start-Sleep -Seconds 2; " - f"Get-NetAdapter -Name '{NIC}' | Select-Object Status,LinkSpeed | ConvertTo-Json -Compress" - ) - return out, err - - -def wait_link(timeout=20.0): - t0 = time.time() - while time.time() - t0 < timeout: - out, _ = ps( - f"Get-NetAdapter -Name '{NIC}' | Select-Object Status,LinkSpeed | ConvertTo-Json -Compress" - ) - try: - j = json.loads(out) - if isinstance(j, list): - j = j[0] - if str(j.get("Status", "")).lower() == "up": - return True, j - except Exception: - pass - time.sleep(0.5) - return False, None - - -def wait_ecat_healthy(timeout=25.0): - t0 = time.time() - last = None - while time.time() - t0 < timeout: - try: - h = post("/api/native/health") - last = h - if h.get("loop") == 1 and int(h.get("wkc") or 0) >= 3 and h.get("op") == 1: - # also require cycles advancing - c0 = h.get("cycles") or 0 - time.sleep(0.4) - h2 = post("/api/native/health") - c1 = h2.get("cycles") or 0 - if c1 > c0: - return True, h2 - except Exception as e: - last = {"err": str(e)} - time.sleep(0.5) - return False, last - - -def stop_bus(): - logs = [] - for path in ("/api/motor/stop", "/api/op/stop"): - try: - r = post(path) - logs.append((path, r.get("ok"), (r.get("log") or r)[:2])) - except Exception as e: - logs.append((path, False, str(e))) - return logs - - -def start_bus(): - logs = [] - try: - r = post("/api/op/start") - logs.append(("/api/op/start", r.get("ok"), (r.get("log") or [""])[:2])) - except Exception as e: - logs.append(("/api/op/start", False, str(e))) - try: - r = post("/api/motor/start") - logs.append(("/api/motor/start", r.get("ok"), (r.get("log") or [""])[:2])) - except Exception as e: - logs.append(("/api/motor/start", False, str(e))) - return logs - - -def meter(tag: str, secs: float): - # reuse skip_rate_meter as subprocess for isolation - cmd = [ - sys.executable, - os.path.join(ROOT, "scripts", "skip_rate_meter.py"), - "--tag", - tag, - "--secs", - str(secs), - ] - r = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace") - # last json file for tag - files = sorted( - [f for f in os.listdir(LAB) if f.startswith(tag + "_") and f.endswith(".json")] - ) - data = None - if files: - with open(os.path.join(LAB, files[-1]), encoding="utf-8") as f: - data = json.load(f) - return data, r.stdout, r.stderr - - -def apply_batch(changes: list[tuple[str, str]]): - results = [] - for k, v in changes: - out, err = nic_set(k, v) - results.append({"key": k, "value": v, "out": out[:300], "err": err[:300]}) - return results - - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument("--restore", action="store_true", help="restore from snap json") - ap.add_argument("--snap", default=None, help="snapshot json for restore") - ap.add_argument("--exp", default=None, help="named experiment") - ap.add_argument("--secs", type=float, default=16.0) - ap.add_argument("--list-exp", action="store_true") - args = ap.parse_args() - - os.makedirs(LAB, exist_ok=True) - - # experiment table: list of (keyword, value) - EXPS = { - "itr_off": [ - ("*InterruptModeration", "0"), - ("ITR", "0"), - ], - "itr_low": [ - ("*InterruptModeration", "1"), - ("ITR", "400"), # valid: 65535,3600,2000,950,400,200,0 - ], - "itr_200": [ - ("*InterruptModeration", "1"), - ("ITR", "200"), - ], - "itr_950": [ - ("*InterruptModeration", "1"), - ("ITR", "950"), - ], - "eee_off": [ - ("EEELinkAdvertisement", "0"), - ], - "flow_off": [ - ("*FlowControl", "0"), - ], - "txbuf_max": [ - ("*TransmitBuffers", "4096"), - ], - "rxbuf_max": [ - ("*ReceiveBuffers", "1024"), - ], - "combo_lowlat": [ - ("*InterruptModeration", "0"), - ("ITR", "0"), - ("EEELinkAdvertisement", "0"), - ("*FlowControl", "0"), - ("*TransmitBuffers", "2048"), - ("*ReceiveBuffers", "512"), - ], - } - - if args.list_exp: - print(json.dumps({k: v for k, v in EXPS.items()}, indent=2)) - return 0 - - # always keep a fresh snapshot before mutating - snap = nic_snapshot() - snap_path = os.path.join(LAB, f"nic_before_{int(time.time())}.json") - with open(snap_path, "w", encoding="utf-8") as f: - json.dump(snap, f, ensure_ascii=False, indent=2) - print(f"NIC snapshot → {snap_path}") - - if args.restore: - path = args.snap or snap_path - with open(path, encoding="utf-8") as f: - data = json.load(f) - # stop, apply all, restart - print("restore: stop bus") - print(stop_bus()) - n = 0 - for row in data: - kw = row.get("RegistryKeyword") - val = row.get("RegistryValue") - if not kw or val is None: - continue - if isinstance(val, list): - val = val[0] if val else "" - val = str(val) - apply_batch([(kw, val)]) - n += 1 - print("restore applied", n, "keys; restarting NIC") - print(nic_restart()) - print(wait_link()) - print("start bus") - print(start_bus()) - ok, h = wait_ecat_healthy() - print("healthy", ok, h) - return 0 - - exp = args.exp or "itr_off" - if exp not in EXPS: - print("unknown exp", exp, "use --list-exp") - return 2 - changes = EXPS[exp] - - print(f"\n=== EXP {exp} {changes} ===") - print("stop bus...") - print(stop_bus()) - time.sleep(0.3) - print("apply...") - print(apply_batch(changes)) - print("restart nic...") - print(nic_restart()) - ok, info = wait_link() - print("link", ok, info) - print("start bus...") - print(start_bus()) - ok, h = wait_ecat_healthy(timeout=30) - print("healthy", ok, h) - if not ok: - print("ABORT: bus not healthy after change") - return 1 - - # settle longer: NIC restart + re-enumeration leaves ~2–5s of elevated skip - time.sleep(8.0) - data, out, err = meter(f"exp_{exp}", args.secs) - print(out) - if err: - print("meter stderr", err[:500]) - if data: - summary = { - "exp": exp, - "changes": changes, - "late_per_s": data.get("late_per_s"), - "skip_per_s": data.get("skip_per_s"), - "late_frac_pct": data.get("late_frac_pct"), - "send_stall_frac": data.get("send_stall_frac"), - "rx_stall_frac": data.get("rx_stall_frac"), - "achieved_hz": data.get("achieved_hz"), - "n_late": data.get("n_late"), - } - sp = os.path.join(LAB, f"summary_{exp}_{int(time.time())}.json") - with open(sp, "w", encoding="utf-8") as f: - json.dump(summary, f, ensure_ascii=False, indent=2) - print("SUMMARY", json.dumps(summary, ensure_ascii=False)) - print("WROTE", sp) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/skip_prio_ab.py b/scripts/skip_prio_ab.py deleted file mode 100644 index 7c0fc96..0000000 --- a/scripts/skip_prio_ab.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""High vs RealTime 多轮 A/B,输出对比。结束后设为 --final 指定档。""" -import json, os, subprocess, sys, time - -ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) -LAB = os.path.join(ROOT, "lab", "skip_meter") - -def ecat_pid(): - out = subprocess.check_output( - ["powershell","-NoProfile","-Command","(Get-Process ecat_native).Id"], - text=True).strip() - return int(out.splitlines()[0]) - -def set_prio(level): - p = ecat_pid() - subprocess.check_output( - ["powershell","-NoProfile","-Command", - f"$p=Get-Process -Id {p}; $p.PriorityClass='{level}'; $p.PriorityClass"], - text=True) - return level - -def meter(tag, secs=12): - r = 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 row(level, data, n): - return { - "round": n, "level": level, - "late_per_s": data["late_per_s"], "skip_per_s": data["skip_per_s"], - "send_stall_frac": data["send_stall_frac"], "rx_stall_frac": data["rx_stall_frac"], - "achieved_hz": data["achieved_hz"], "n_late": data["n_late"], - "send_p90": (data.get("window_cyc_us") or {}).get("send",{}).get("p90"), - "work_p90": (data.get("window_cyc_us") or {}).get("work",{}).get("p90"), - } - -def main(): - final = "RealTime" - if len(sys.argv) > 1: - final = sys.argv[1] - rows = [] - # A-B-A-B - seq = ["High","RealTime","High","RealTime"] - for i, lv in enumerate(seq, 1): - set_prio(lv) - time.sleep(1.0) - data = meter(f"ab_{lv}_{i}", 12) - r = row(lv, data, i) - rows.append(r) - print(r) - set_prio(final) - print("FINAL", final, "pid", ecat_pid()) - # summary means - by = {} - for r in rows: - by.setdefault(r["level"], []).append(r) - means = {} - for lv, rs in by.items(): - means[lv] = { - "n": len(rs), - "late_per_s_mean": round(sum(x["late_per_s"] for x in rs)/len(rs), 3), - "skip_per_s_mean": round(sum(x["skip_per_s"] for x in rs)/len(rs), 3), - "send_p90_mean": round(sum(x["send_p90"] or 0 for x in rs)/len(rs), 1), - "work_p90_mean": round(sum(x["work_p90"] or 0 for x in rs)/len(rs), 1), - } - print("MEANS", json.dumps(means, ensure_ascii=False, indent=2)) - path = os.path.join(LAB, f"summary_ab_prio_{int(time.time())}.json") - json.dump({"rows": rows, "means": means, "final": final}, open(path,"w",encoding="utf-8"), - ensure_ascii=False, indent=2) - print("WROTE", path) - -if __name__ == "__main__": - main() diff --git a/scripts/skip_prio_experiment.py b/scripts/skip_prio_experiment.py deleted file mode 100644 index dcd7500..0000000 --- a/scripts/skip_prio_experiment.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""进程优先级对照:不高/RealTime,各采 skip rate。不改网卡、不碰固件。""" -import json, os, subprocess, sys, time, urllib.request - -ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) -LAB = os.path.join(ROOT, "lab", "skip_meter") -BASE = "http://127.0.0.1:8090" - -def post(path, body=None): - data = json.dumps(body or {}).encode() - req = urllib.request.Request(BASE+path, data=data, headers={"Content-Type":"application/json"}) - with urllib.request.urlopen(req, timeout=5) as r: - return json.loads(r.read().decode()) - -def pid(): - return post("/api/native/health") # health has no pid; use process name - # fallback below - -def ecat_pid(): - out = subprocess.check_output( - ["powershell","-NoProfile","-Command", - "(Get-Process ecat_native).Id"], - text=True).strip() - return int(out.splitlines()[0]) - -def set_prio(level): - p = ecat_pid() - cmd = f"$p=Get-Process -Id {p}; $p.PriorityClass='{level}'; $p.PriorityClass" - return subprocess.check_output(["powershell","-NoProfile","-Command",cmd], text=True).strip() - -def meter(tag, secs=16): - r = 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")]) - data = json.load(open(os.path.join(LAB, files[-1]), encoding="utf-8")) if files else None - return data, r.stdout - -def main(): - p = ecat_pid() - print("ecat pid", p) - cur = subprocess.check_output( - ["powershell","-NoProfile","-Command", f"(Get-Process -Id {p}).PriorityClass"], - text=True).strip() - print("current PriorityClass", cur) - - results = [] - for level in ("High", "RealTime"): - print(f"\n=== set {level} ===") - try: - print(set_prio(level)) - except Exception as e: - print("set fail", e) - continue - time.sleep(1.5) - data, out = meter(f"prio_{level}", 16) - if data: - row = {"level": level, "late_per_s": data["late_per_s"], "skip_per_s": data["skip_per_s"], - "late_frac_pct": data["late_frac_pct"], "send_stall_frac": data["send_stall_frac"], - "rx_stall_frac": data["rx_stall_frac"], "achieved_hz": data["achieved_hz"], - "n_late": data["n_late"]} - results.append(row) - print("ROW", row) - print(out[-800:] if out else "") - - # restore High (safe default; pdo thread already TIME_CRITICAL) - try: - print("restore High") - print(set_prio("High")) - except Exception as e: - print("restore fail", e) - - path = os.path.join(LAB, f"summary_prio_{int(time.time())}.json") - json.dump({"results": results, "original": cur}, open(path,"w",encoding="utf-8"), ensure_ascii=False, indent=2) - print("WROTE", path) - -if __name__ == "__main__": - main() diff --git a/scripts/skip_quiet_periods.py b/scripts/skip_quiet_periods.py deleted file mode 100644 index 1c0dc99..0000000 --- a/scripts/skip_quiet_periods.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 -"""静默多档标定:1ms / 2ms / 4ms,每档先停环再设周期再开环,避免混拍。""" -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): - 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=8).read().decode()) - -def set_prio(level="RealTime"): - p = int(subprocess.check_output(["powershell","-NoProfile","-Command","(Get-Process ecat_native).Id"], text=True).strip()) - subprocess.check_output(["powershell","-NoProfile","-Command", - f"$p=Get-Process -Id {p}; $p.PriorityClass='{level}'; $p.PriorityClass"], text=True) - return p - -def set_period(ms): - post("/api/motor/stop") - time.sleep(0.2) - r = post("/api/motor/period", {"period_ms": ms}) - time.sleep(0.2) - post("/api/motor/start") - time.sleep(0.8) - return r - -def meter(tag, secs=18): - r = 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(): - print("set RealTime") - print("pid", set_prio("RealTime")) - rows = [] - for ms in (1.0, 2.0, 4.0): - print(f"\n=== period {ms} ms ===") - print(set_period(ms)) - time.sleep(2.0) # settle - data = meter(f"quiet_{ms:g}ms", 18) - row = { - "period_ms": ms, - "late_per_s": data["late_per_s"], - "skip_per_s": data["skip_per_s"], - "late_frac_pct": data["late_frac_pct"], - "achieved_hz": data["achieved_hz"], - "n_late": data["n_late"], - "send_stall_frac": data["send_stall_frac"], - "rx_stall_frac": data["rx_stall_frac"], - "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"], - } - rows.append(row) - print(row) - - # restore 1ms as user default - print("\nrestore period 1.0") - print(set_period(1.0)) - path = os.path.join(LAB, f"summary_quiet_periods_{int(time.time())}.json") - json.dump(rows, open(path,"w",encoding="utf-8"), ensure_ascii=False, indent=2) - print("WROTE", path) - print(json.dumps(rows, ensure_ascii=False, indent=2)) - -if __name__ == "__main__": - main() diff --git a/scripts/skip_rate_meter.py b/scripts/skip_rate_meter.py deleted file mode 100644 index 373b7d6..0000000 --- a/scripts/skip_rate_meter.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""跳拍率标定:固定时长采 /api/motor/dbg,输出可对比 JSON。 - -主判据: late_per_s, skip_per_s, send_stall_frac, rx_stall_frac -用法: - python scripts/skip_rate_meter.py --tag base --secs 20 -""" -from __future__ import annotations - -import argparse -import json -import os -import time -import urllib.request - -BASE = os.environ.get("ECAT_BASE", "http://127.0.0.1:8090") -OUTDIR = os.path.join(os.path.dirname(__file__), "..", "lab", "skip_meter") - - -def post(path: str, body=None, timeout=5.0): - data = json.dumps(body or {}).encode() - req = urllib.request.Request( - BASE + path, data=data, headers={"Content-Type": "application/json"} - ) - with urllib.request.urlopen(req, timeout=timeout) as r: - return json.loads(r.read().decode()) - - -def main() -> int: - ap = argparse.ArgumentParser() - ap.add_argument("--tag", default="sample") - ap.add_argument("--secs", type=float, default=20.0) - ap.add_argument("--hz", type=float, default=5.0) - ap.add_argument("--out", default=None) - args = ap.parse_args() - - st0 = post("/api/motor/state") - period = float(st0.get("period_ms") or 1.0) - t0 = time.time() - pulses = [] - last_t = None - n_poll = 0 - # dbg t0 uses now_s() (QPC-based), NOT unix time. Track by last-seen t0 only. - while time.time() - t0 < args.secs: - dbg = post("/api/motor/dbg") - n_poll += 1 - cyc = dbg.get("cyc") or [] - if last_t is not None: - new = [r for r in cyc if r[0] > last_t] - else: - # first poll: adopt the whole ring's last timestamp as cursor; - # count only k>0 from now on via subsequent polls. Also count - # k>0 already in ring as "pre-existing" (not in window rate). - new = [] - if cyc: - last_t = cyc[-1][0] - elif last_t is None: - last_t = None - for r in new: - if len(r) < 8: - continue - k = int(r[5]) - if k <= 0: - continue - send_us = (r[2] - r[1]) * 1e6 - rx_us = (r[3] - r[2]) * 1e6 - pulses.append( - { - "t0": r[0], - "k": k, - "send_us": send_us, - "rx_us": rx_us, - "work_us": (r[4] - r[0]) * 1e6, - "sd": float(r[6]) if len(r) > 6 else None, - "rxc": int(r[7]) if len(r) > 7 else None, - "kind": ( - "send" - if send_us >= 500 - else ("rx" if rx_us >= 500 else "other") - ), - } - ) - time.sleep(max(0.0, 1.0 / args.hz)) - - st1 = post("/api/motor/state") - wall = time.time() - t0 - n_late = len(pulses) - n_skip = sum(p["k"] for p in pulses) - by = {"send": 0, "rx": 0, "other": 0} - for p in pulses: - by[p["kind"]] += 1 - - # approximate expected beats in window - # use cycles delta when available - cyc0 = st0.get("cycles") or 0 - cyc1 = st1.get("cycles") or 0 - dcyc = max(0, cyc1 - cyc0) - - cu = st1.get("cyc_us") or {} - result = { - "tag": args.tag, - "ts": time.time(), - "wall_s": round(wall, 3), - "period_ms": period, - "pcore_mask": st1.get("pcore_mask"), - "wkc": st1.get("wkc"), - "spin": st1.get("spin"), - "cycles_delta": dcyc, - "achieved_hz": round(dcyc / wall, 2) if wall > 0 else None, - "late_per_s": round(n_late / wall, 3) if wall else None, - "skip_per_s": round(n_skip / wall, 3) if wall else None, - "late_frac_pct": round(100.0 * n_late / dcyc, 4) if dcyc else None, - "skip_frac_pct": round(100.0 * n_skip / dcyc, 4) if dcyc else None, - "send_stall_frac": round(by["send"] / n_late, 4) if n_late else None, - "rx_stall_frac": round(by["rx"] / n_late, 4) if n_late else None, - "n_late": n_late, - "n_skip": n_skip, - "by_kind": by, - "window_cyc_us": cu, - "n_poll": n_poll, - "pulses_sample": pulses[:40], - } - - os.makedirs(OUTDIR, exist_ok=True) - out = args.out or os.path.join(OUTDIR, f"{args.tag}_{int(time.time())}.json") - with open(out, "w", encoding="utf-8") as f: - json.dump(result, f, ensure_ascii=False, indent=2) - print(json.dumps({k: result[k] for k in result if k != "pulses_sample"}, - ensure_ascii=False, indent=2)) - print(f"WROTE {out}") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/skip_restart_native.py b/scripts/skip_restart_native.py deleted file mode 100644 index ec7c7ea..0000000 --- a/scripts/skip_restart_native.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/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() diff --git a/scripts/sweep_ecat_rate.py b/scripts/sweep_ecat_rate.py deleted file mode 100644 index f0f5904..0000000 --- a/scripts/sweep_ecat_rate.py +++ /dev/null @@ -1,335 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""PDO 周期扫描:一次跑完多档,同时记「速度达成 / 故障率 / CPU 占用」三条曲线。 - -与 tools/ecat/rate_probe.py 的区别:rate_probe 只量速度达成率,本脚本把故障率与 -CPU 一并采齐并落成机读 CSV,供 plot_ecat_sweep.py 画曲线。速率达成部分的 -"cycles 与时刻同取同舍"纪律直接沿用 rate_probe(它注释里记过一次把 99.2% 误报 -成 96% 的实踩教训:分子取末次 cycles、分母却在循环退出后才算,dt 多出一次 sleep)。 - -必须绕开的四件事(均由 Phase 0 现场标定确认,不是照抄文档): - 1 u16 回绕:bridge.drop 在 4000/s 下 16s 绕一圈,首尾相减必错 → 高频逐次累加, - 且单次增量 >32768 视为绕圈、该点作废并计数暴露采样率不足。 - 2 rx_torn / pdo_reentry 是**饱和**计数(到 65535 停),不能按回绕算 → 走 - delta_saturate,封顶即标注"下界"而不是报精确值。 - 3 周期能被 localStorage 劫持:任何开着的电机页标签在 ensureStarted 时会把总线 - 周期拽回存量值(transport-ecat.js:227-228)→ 每档下发后回读 period_ms 确认 - 真的生效,不一致就把该档标记为被劫持。 - 4 λ(CAN 到帧率)取 u32 的 head.global_seq,不取 u16 的 bridge.rx_count。 - 本次实测到关键分界:1ms/0.5ms 档 λ=4000/s、GD32 负载 44%/77%;0.25ms/0.125ms - 档 GD32 顶到 99.5%/99.3%,λ 塌到 149/s —— 从站主循环被 PDO 填充吃光、CAN 接收 - 停摆。而主站达成率反而仍很高(0.125ms 实测 7780Hz),所以瓶颈在从站不在主站: - 只看达成率会把这条线整个看漏。 - -用法: - python scripts/sweep_ecat_rate.py # 默认六档 × 60s - python scripts/sweep_ecat_rate.py --periods 0.25,0.5,1 --dwell 30 - python scripts/sweep_ecat_rate.py --restore 1 --out lab/ecat_sweep.csv - python scripts/sweep_ecat_rate.py --dry-run # 只走一遍不下发周期 -结束后一定把周期恢复到脚本启动前的原值(含 Ctrl-C / 异常)。 -""" -import argparse -import csv -import os -import signal -import sys -import time - -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) -from calibrate_ecat_metrics import (cadence_rings, find_native_pid, - firmware_identity, ensure_loop) -from ecat_sweep_common import delta_saturate, delta_wrap, percentile, post - -DEFAULT_PERIODS = "0.125,0.25,0.5,1,2,4" -COLS = ["period_ms", "set_period_ms", "hijacked", "native_pid", "pid_stable", - "measured_hz", "achieve_pct", "can_lambda_per_s", - "drop_per_s", "drop_pct_of_lambda", "drop_untrusted", "lambda_u16_crosscheck", - "rxb_count_mode_per_cycle", "rxb_lost_max", "rxb_pending_max", - "skip_per_s", "skip_delta", "motors_online", "online_frac", "motor_refresh_per_s", - "torn_per_s", "torn_cum", "torn_capped", - "reentry_per_s", "reentry_cum", "reentry_capped", "wkc", "expected_wkc", "wr", - "cyc_p50_us", "cyc_p90_us", "cyc_max_us", "cyc_duty_pct", - "gd32_load_pct", "gd32_load_samples", "gd32_load_stale", - "host_cpu_1core_pct", "host_cpu_total_pct", - "logical_cpus", "samples", "errors", "dwell_s", "note"] - - -def sample_period(url, ms, dwell, hz, substr, autostart, dry): - """在给定周期档上采 dwell 秒,返回一行 CSV 记录。""" - note = [] - pid0, perr = find_native_pid(url) - st0 = post(url, "/api/motor/state") - if not (st0.get("ok") and st0.get("cycles")): - st0, how = ensure_loop(url, autostart, substr) - note.append("起循环:" + how) - orig = st0.get("period_ms") - - if dry: - return {"period_ms": ms, "note": "dry-run 未下发"} - - r = post(url, "/api/motor/period", {"period_ms": ms}) - if not r.get("ok"): - return {"period_ms": ms, "note": "档位被拒:" + str(r.get("log"))[:80]} - - # 等周期真正生效(热调是异步落到循环线程的) - eff, stable_iters = None, 0 - t_deadline = time.perf_counter() + 3.0 - while time.perf_counter() < t_deadline: - time.sleep(0.2) - eff = (post(url, "/api/motor/state")).get("period_ms") - if eff is not None and abs(float(eff) - float(ms)) < 1e-6: - stable_iters += 1 - if stable_iters >= 3: - break - else: - stable_iters = 0 - hijacked = "no" - if eff is None or abs(float(eff) - float(ms)) >= 1e-6: - hijacked = "YES" - note.append(f"下发 {ms}ms 但生效 {eff}ms(疑 localStorage 劫持或有他端在设)") - - interval = 1.0 / hz - cyc_hz, drop_rate, rx_rate, gseq_rate, loads, hc1, hct = [], [], [], [], [], [], [] - cyc_p50, cyc_p90, cyc_max = [], [], [] - untrusted = torn_sum = reentry_sum = err = iters = load_stale = 0 - torn_cap = reentry_cap = False - torn_prev = re_prev = None - torn_cum = re_cum = None - node_seq = {} # node -> (t, seq):上一拍的槽 seq,用于算电机数据刷新率 - online_hits = online_tot = 0 - refresh_rates = [] - prev = prev_t = None - wr0 = (post(url, "/api/motor/state")).get("wr") - skip0 = (post(url, "/api/motor/dbg")).get("skip_total") - proc = None - try: - import psutil - if pid0: - proc = psutil.Process(pid0) - proc.cpu_percent(None) # 建立差分基线 - except Exception as e: - note.append(f"psutil 不可用({e.__class__.__name__}),主机 CPU 缺") - - t_end = time.perf_counter() + dwell - while time.perf_counter() < t_end: - tc = time.perf_counter() - try: - s = post(url, "/api/motor/state") - L = post(url, "/api/app/load") - except Exception: - err += 1 - time.sleep(interval) - continue - iters += 1 - b = s.get("bridge") or {} - cur = {"cycles": s.get("cycles"), "drop": b.get("drop"), "rx": b.get("rx_count"), - "gseq": (s.get("head") or {}).get("global_seq")} - if prev is not None and tc > prev_t: - dt = tc - prev_t - if cur["cycles"] is not None and prev["cycles"] is not None: - h = (cur["cycles"] - prev["cycles"]) / dt - if 0 < h < 1e6: - cyc_hz.append(h) - if cur["drop"] is not None and prev["drop"] is not None: - d = delta_wrap(prev["drop"], cur["drop"], 16) - if d < 32768: - drop_rate.append(d / dt) - else: - untrusted += 1 # 已绕圈:该点作废,只计数不臆造速率 - # λ 以 u32 的 head.global_seq 为准。u16 的 bridge.rx_count/drop 在快档 - # 会失真(实测 0.25ms 档真值 149/s,两路 u16 与 u32 虽一致但增量已贴近 - # 截断区),留作交叉核对:两者显著背离即说明该档 u16 不可信。 - if cur["gseq"] is not None and prev["gseq"] is not None: - d = delta_wrap(prev["gseq"], cur["gseq"], 32) - if 0 <= d < (1 << 31): - gseq_rate.append(d / dt) - if cur["rx"] is not None and prev["rx"] is not None: - d = delta_wrap(prev["rx"], cur["rx"], 32) - if d < (1 << 31): - rx_rate.append(d / dt) - tn, rn = L.get("rx_torn"), L.get("pdo_reentry") - if tn is not None: - if torn_prev is not None and not torn_cap: - dd, capped = delta_saturate(torn_prev, tn) - torn_sum += dd - torn_cap |= capped - torn_prev = tn - if rn is not None: - if re_prev is not None and not reentry_cap: - dd, capped = delta_saturate(re_prev, rn) - reentry_sum += dd - reentry_cap |= capped - re_prev = rn - # 从站停报时 load_perm 会保留最后一次的值(实测:从站楔死后 age_ms=214000 - # 而 load_perm 仍报 992)。不加 age 闸就会把残值当实测写进结论。 - if (L.get("valid") and L.get("load_perm") is not None - and (L.get("age_ms") if L.get("age_ms") is not None else 1e9) < 1000): - loads.append(L["load_perm"] / 10.0) - else: - load_stale += 1 - cu = (s.get("cyc_us") or {}).get("total") or {} - if cu.get("p50") is not None: - cyc_p50.append(cu["p50"]) - if cu.get("p90") is not None: - cyc_p90.append(cu["p90"]) - if cu.get("max") is not None: - cyc_max.append(cu["max"]) - if proc is not None: - try: - c = proc.cpu_percent(None) - hc1.append(c) - hct.append(c / max(1, os.cpu_count() or 1)) - except Exception: - pass - # 电机在线标志与数据刷新率:快档塌陷必须能区分"从站算不过来"与"电机掉线" - # —— 二者在 λ/seq 上长得一模一样,只有 online 标志能把它们切开。 - for m in (s.get("motors") or []): - nd = m.get("node") - if nd is None: - continue - online_tot += 1 - online_hits += 1 if m.get("online") else 0 - sq = m.get("seq") - if sq is None: - continue - pr = node_seq.get(nd) - if pr and tc > pr[0]: - d = delta_wrap(pr[1], sq, 32) - if d < (1 << 31): - refresh_rates.append(d / (tc - pr[0])) - node_seq[nd] = (tc, sq) - prev, prev_t = cur, tc - time.sleep(max(0.0, interval - (time.perf_counter() - tc))) - - dur = max(1e-6, time.perf_counter() - (t_end - dwell)) - stf = post(url, "/api/motor/state") - tend = post(url, "/api/app/load") - pid1, _ = find_native_pid(url) - skip1 = (post(url, "/api/motor/dbg")).get("skip_total") - cad = cadence_rings(url) - torn_cum, re_cum = tend.get("rx_torn"), tend.get("pdo_reentry") - lam = percentile(gseq_rate, 0.5) or 0 # u32 gseq 口径,主用 - lam_u16 = percentile(rx_rate, 0.5) or 0 # u16 rx_count 口径,仅作交叉核对 - dropr = percentile(drop_rate, 0.5) or 0 - hz_meas = percentile(cyc_hz, 0.5) or 0 - eff_ms = float(stf.get("period_ms") or ms) - cad_hist = cad.get("count_hist") or {} - mode_cnt = max(cad_hist, key=lambda k: cad_hist[k]) if cad_hist else None - rec = { - "period_ms": ms, "set_period_ms": eff_ms, "hijacked": hijacked, - "native_pid": pid0, "pid_stable": "yes" if pid0 == pid1 else "NO", - "measured_hz": round(hz_meas, 1), - "achieve_pct": round(hz_meas / (1000.0 / eff_ms) * 100, 1) if eff_ms else None, - "can_lambda_per_s": round(lam, 1), "lambda_u16_crosscheck": round(lam_u16, 1), - "drop_per_s": round(dropr, 1), - "drop_pct_of_lambda": round(dropr / lam * 100, 2) if lam else None, - "drop_untrusted": untrusted, - "rxb_count_mode_per_cycle": mode_cnt, - "rxb_lost_max": cad.get("lost_max"), "rxb_pending_max": cad.get("pending_max"), - "motors_online": sum(1 for m in (stf.get("motors") or []) if m.get("online")), - "online_frac": round(online_hits / online_tot, 3) if online_tot else None, - "motor_refresh_per_s": round(percentile(refresh_rates, 0.5) or 0, 1), - "skip_per_s": round(((skip1 or 0) - (skip0 or 0)) / dur, 2), - "skip_delta": (skip1 or 0) - (skip0 or 0), - "torn_per_s": round(torn_sum / dur, 3), - "torn_cum": torn_cum, "torn_capped": "yes" if torn_cap else "no", - "reentry_per_s": round(reentry_sum / dur, 3), - "reentry_cum": re_cum, "reentry_capped": "yes" if reentry_cap else "no", - "wkc": stf.get("wkc"), "expected_wkc": stf.get("expected_wkc"), - "wr": f"{wr0}->{stf.get('wr')}", - "cyc_p50_us": round(percentile(cyc_p50, 0.5) or 0, 1), - "cyc_p90_us": round(percentile(cyc_p90, 0.9) or 0, 1), - "cyc_max_us": round(max(cyc_max), 1) if cyc_max else None, - "cyc_duty_pct": round((percentile(cyc_p50, 0.5) or 0) / (eff_ms * 1000) * 100, 1), - "gd32_load_pct": round(percentile(loads, 0.5) or 0, 1) if loads else None, - "gd32_load_samples": len(loads), "gd32_load_stale": load_stale, - "host_cpu_1core_pct": round(percentile(hc1, 0.5) or 0, 1), - "host_cpu_total_pct": round(percentile(hct, 0.5) or 0, 2), - "logical_cpus": os.cpu_count(), "samples": iters, "errors": err, - "dwell_s": round(dur, 1), "note": "; ".join(note)} - return rec - - -def main(): - ap = argparse.ArgumentParser(description="ECAT 周期-故障率-CPU 扫描") - ap.add_argument("--url", default="http://127.0.0.1:8090") - ap.add_argument("--periods", default=DEFAULT_PERIODS, help="逗号分隔周期(ms)") - ap.add_argument("--dwell", type=float, default=60.0, help="每档采样秒数") - ap.add_argument("--hz", type=float, default=10.0, - help="HTTP 采样频率;u16 drop 在 4000/s 下 16s 绕一圈,须够高") - ap.add_argument("--restore", type=float, default=None, help="测完恢复到的周期(ms)") - ap.add_argument("--substr", default="I210") - ap.add_argument("--no-autostart", action="store_true") - ap.add_argument("--out", default=os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "lab", "ecat_rate_sweep.csv")) - ap.add_argument("--dry-run", action="store_true") - a = ap.parse_args() - - st = post(a.url, "/api/motor/state") - if not (st.get("ok") and st.get("cycles")) and a.no_autostart: - print("循环没跑且 --no-autostart:", st.get("log")) - return 2 - orig = st.get("period_ms") - pid0, _ = find_native_pid(a.url) - periods = [float(x) for x in a.periods.split(",")] - print(f"服务 {a.url} · native PID {pid0} · 原周期 {orig}ms · 档位 {periods} · " - f"每档 {a.dwell}s @ {a.hz:g}Hz") - fw = firmware_identity() - print(f"固件台账 {str(fw.get('ledger'))[:16]}… 匹配={fw.get('match') or fw.get('error') or '无'}") - print("提示:确认没有任何电机页标签开着,否则周期会被 localStorage 拽回去\n") - - rows, interrupted = [], False - signal.signal(signal.SIGINT, lambda *_: None) - try: - for ms in periods: - try: - rec = sample_period(a.url, ms, a.dwell, a.hz, a.substr, - not a.no_autostart, a.dry_run) - except Exception as e: - print(f"{ms}ms 档异常: {type(e).__name__}: {e}") - break - rows.append(rec) - if rec.get("note") == "dry-run 未下发": - print(f"[dry-run] {ms}ms 将下发,未执行") - continue - print(f"{ms:>7}ms 生效={rec['set_period_ms']} 劫持={rec['hijacked']} " - f"{rec['measured_hz']}Hz/{rec['achieve_pct']}% λ={rec['can_lambda_per_s']} " - f"drop={rec['drop_per_s']}/s({rec['drop_pct_of_lambda']}%) " - f"skip={rec['skip_per_s']}/s GD32={rec['gd32_load_pct']}% " - f"host={rec['host_cpu_1core_pct']}% wkc={rec['wkc']}/{rec['expected_wkc']}") - if rec["note"]: - print(f" note: {rec['note']}") - if rec["pid_stable"] == "NO": - print(" !! native PID 变了,该档及之后数据作废,停止扫描") - break - if rec["wkc"] != rec["expected_wkc"]: - print(" !! wkc 不达预期(从站丢帧/掉 OP 前兆),停止继续加压") - break - except KeyboardInterrupt: - interrupted = True - print("\n收到 Ctrl-C,正在恢复原周期…") - finally: - restore = a.restore if a.restore is not None else orig - if restore is not None and not a.dry_run: - try: - post(a.url, "/api/motor/period", {"period_ms": float(restore)}) - back = (post(a.url, "/api/motor/state")).get("period_ms") - print(f"\n已恢复周期 -> 设定 {restore}ms,实际回读 {back}ms") - except Exception as e: - print(f"\n!! 恢复周期失败({e}),请手动确认总线周期") - - if not rows or a.dry_run: - return 0 - os.makedirs(os.path.dirname(os.path.abspath(a.out)), exist_ok=True) - with open(a.out, "w", newline="", encoding="utf-8") as f: - w = csv.DictWriter(f, fieldnames=COLS, extrasaction="ignore") - w.writeheader() - w.writerows(rows) - print(f"扫描数据 {len(rows)} 档 -> {a.out}") - if interrupted: - print("(本次为中断后的部分数据)") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/test_ecat_dir_worktree.mjs b/scripts/test_ecat_dir_worktree.mjs deleted file mode 100644 index a4b5e1f..0000000 --- a/scripts/test_ecat_dir_worktree.mjs +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 从 git worktree 启动 esp32_server 时,ECAT_DIR 必须指回主检出的 - * tools/ecat,而不是 .../ESP32/.worktrees/ecat。 - * 复刻 _resolve_ecat_dir 的纯路径逻辑(无 DOM/无网络)。 - */ -import assert from "node:assert/strict"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -/** 与 esp32_server._resolve_ecat_dir 同构(JS 侧只测路径形状)。 */ -function resolveEcatDir(baseDir) { - let d = path.resolve(baseDir); - const parent = path.dirname(d); - if (path.basename(parent) === ".worktrees") { - d = path.dirname(parent); - } - return path.normalize(path.join(d, "..", "ecat")); -} - -let failed = 0; -function check(name, fn) { - try { - fn(); - console.log(" ✓", name); - } catch (e) { - failed++; - console.log(" ✗", name, "—", e.message); - } -} - -console.log("[worktree ECAT_DIR]"); - -check("主检出 BASE_DIR → 兄弟 tools/ecat", () => { - const base = path.resolve("G:/Robot_v2/DCU/tools/ESP32"); - const got = resolveEcatDir(base); - assert.equal(got, path.resolve("G:/Robot_v2/DCU/tools/ecat"), got); -}); - -check("worktree BASE_DIR → 仍指主检出的 tools/ecat", () => { - const base = path.resolve("G:/Robot_v2/DCU/tools/ESP32/.worktrees/device-link"); - const got = resolveEcatDir(base); - assert.equal(got, path.resolve("G:/Robot_v2/DCU/tools/ecat"), got); -}); - -check("嵌套 slug 也走 .worktrees 父级", () => { - const base = path.resolve("G:/x/tools/ESP32/.worktrees/foo-bar"); - const got = resolveEcatDir(base); - assert.equal(got, path.resolve("G:/x/tools/ecat"), got); -}); - -/* BASE_DIR 恒为 esp32_server.py 所在目录;普通主检出已在第一项覆盖。 */ - -if (failed) { - console.log(`失败 ${failed} 项`); - process.exit(1); -} -console.log("通过 3 项,失败 0 项"); diff --git a/scripts/test_motor_pair_n2_n3.py b/scripts/test_motor_pair_n2_n3.py deleted file mode 100644 index c9a81c2..0000000 --- a/scripts/test_motor_pair_n2_n3.py +++ /dev/null @@ -1,1132 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -"""N2/N3 对接电机诊断发令(ECAT)。 - -前提:用户保证机构处于安全状态(无障碍、可运动)。 -默认参数偏保守:span=200°,vel=80°/s,行程 hold 短,全程可 Ctrl+C 急停。 - -子命令: - status 只读:链路 / 状态 / CONFIG - align 下发统一 CONFIG(mode/limitV/PID)+ 双机 CMD - openloop 开环速度对比:双机同 v,采 速度/电流/积分 - stop 巡航后同时刹停,测停车滞后与 ∫(v2+v3) - trip 位置行程(mode3):同向或镜像,采全程积分 - full 依次跑 align → openloop → stop → trip,并汇总 JSON - stopnow 立即发速度=0(急停) - -示例: - python scripts/test_motor_pair_n2_n3.py status - python scripts/test_motor_pair_n2_n3.py align --limit-v 22.0 --pid 100 - python scripts/test_motor_pair_n2_n3.py stop --vel 80 --cruise 1.2 - python scripts/test_motor_pair_n2_n3.py full --mode-mirror --span 200 - python scripts/test_motor_pair_n2_n3.py stopnow -""" -from __future__ import annotations - -import argparse -import csv -import http.client -import json -import os -import struct -import sys -import threading -import time -from collections import defaultdict - -HOST, PORT = "127.0.0.1", 8090 -NODES = (2, 3) -LAB = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "lab") -PERIOD = 0.25 # ms,以 motor/state 为准 - -_tls = threading.local() -_lock = threading.Lock() -_stop_flag = threading.Event() -_rows: list = [] -_seen: dict = {} - - -# ───────────────────────── HTTP / ECAT ───────────────────────── - -def post(path: str, body: bytes | None = None, timeout: float = 8.0) -> dict: - if body is None: - body = b"{}" - c = getattr(_tls, "c", None) - if c is None: - c = _tls.c = http.client.HTTPConnection(HOST, PORT, timeout=timeout) - for _ in range(2): - try: - c.request("POST", path, body=body, - headers={"Content-Type": "application/json"}) - return json.loads(c.getresponse().read().decode("utf-8", "replace")) - except Exception: - try: - c.close() - except Exception: - pass - _tls.c = c = http.client.HTTPConnection(HOST, PORT, timeout=timeout) - raise RuntimeError(f"POST {path} failed") - - -def ensure_ecat() -> tuple[float, dict]: - st = post("/api/motor/state") - period = st.get("period_ms") or PERIOD - wkc = str(st.get("wkc") or "") - motors = st.get("motors") or [] - if wkc not in ("3", "3/3") or len(motors) < 2: - print("ECAT 未就绪 wkc=%s motors=%d → motor/start" % (wkc, len(motors))) - r = post("/api/motor/start", - json.dumps({"period_ms": 0.25, "dc_cycle_us": 250}).encode(), - timeout=35.0) - print(" start:", json.dumps(r, ensure_ascii=False)[:240]) - time.sleep(0.4) - st = post("/api/motor/state") - period = st.get("period_ms") or PERIOD - return period, st - - -# ───────────────────────── CMD 编码 ───────────────────────── - -# ───────────────── 安全:已机械对接,硬联锁 ───────────────── -# 规则(不可绕过,除非命令行 --i-understand-coupled-risk 且仍受硬限位约束): -# 1) 只允许 mode3 + 有限角度目标,两轴目标必须互为相反数(镜像)。 -# 2) 禁止 mode2 开环持续转(对接后会一直拧)。 -# 3) 速度只表示「走向目标的速率」,符号必须 = sign(target - current)。 -# 4) 发令前若 |a2+a3| 过大(错位)→ 拒绝运动,要求人工回零。 -# 5) 硬限位:|ang|、目标、单次行程超限 → 拒绝。 -# 6) 运行中看门狗:一台已停(|v|<8)另一台仍 |v|>40 超过 300ms → 双机急停。 -FORCE_MIRROR = True -MAX_ABS_ANGLE = 2500.0 # 编码器绝对角限(多圈保护) -MAX_STROKE = 1200.0 # 单次目标相对当前位置最大行程 -MAX_ASUM_ERR = 40.0 # |a2+a3| 超过则判定错位,禁止运动 -STOP_VEL_EPS = 8.0 -SPIN_VEL_WARN = 40.0 -ASYMMETRY_MS = 300.0 - - -def _read_pair(): - snap = snap_motors() - a2 = (snap.get(2) or {}).get("ang") - v2 = (snap.get(2) or {}).get("vel") - a3 = (snap.get(3) or {}).get("ang") - v3 = (snap.get(3) or {}).get("vel") - return a2, v2, a3, v3, snap - - -def safety_precheck_motion(target2, target3, speed, force=False): - """运动前硬检查。返回 (ok, msg, items)。items 为带符号速度的 CMD。""" - a2, v2, a3, v3, snap = _read_pair() - if a2 is None or a3 is None: - return False, "N2/N3 状态缺失,拒绝运动", None - if abs(a2) > MAX_ABS_ANGLE or abs(a3) > MAX_ABS_ANGLE: - return False, ("角度超硬限位 a2=%.1f a3=%.1f(限±%.0f)——请人工回位" - % (a2, a3, MAX_ABS_ANGLE)), None - if abs(target2) > MAX_ABS_ANGLE or abs(target3) > MAX_ABS_ANGLE: - return False, "目标超硬限位", None - if abs(target2 + target3) > 1.0: - return False, "目标必须镜像:target2+target3=0(得到 %s/%s)" % (target2, target3), None - if abs(target2 - a2) > MAX_STROKE or abs(target3 - a3) > MAX_STROKE: - return False, ("单次行程过大 Δ2=%.1f Δ3=%.1f(限±%.0f)" - % (target2 - a2, target3 - a3, MAX_STROKE)), None - asum = a2 + a3 - if abs(asum) > MAX_ASUM_ERR and not force: - return False, ("错位过大 a2+a3=%.1f°(a2=%.1f a3=%.1f)。" - "禁止电控继续跑,请先人工把机构回到对称位(建议 a2≈-a3,和≈0)再测。" - % (asum, a2, a3)), None - # 方向:必须朝向各自目标 - v2s = 0.0 if abs(target2 - a2) < 0.5 else (speed if target2 > a2 else -speed) - v3s = 0.0 if abs(target3 - a3) < 0.5 else (speed if target3 > a3 else -speed) - # 对接刚性镜像:位移方向应相反。若当前接近对称却要求同向位移 → 拒绝 - if abs(asum) <= MAX_ASUM_ERR: - d2, d3 = target2 - a2, target3 - a3 - if abs(d2) > 1 and abs(d3) > 1 and d2 * d3 > 0: - return False, ("拒绝:镜像机构不允许同向位移 Δ2=%.1f Δ3=%.1f" - % (d2, d3)), None - # 速度也应反号 - if abs(v2s) > 0.5 and abs(v3s) > 0.5 and v2s * v3s > 0: - return False, "拒绝:速度同号 v2=%s v3=%s" % (v2s, v3s), None - items = [(2, float(v2s), float(target2)), (3, float(v3s), float(target3))] - return True, ("precheck ok a2=%.2f a3=%.2f asum=%.2f cmd=%s" - % (a2, a3, asum, items)), items - - -def _check_pair(items, allow_same=False): - """双机指令必须:角度目标镜像;速度与目标方向一致且反号。""" - if not FORCE_MIRROR: - return - d = {int(n): (float(v), float(a)) for n, v, a in items} - if 2 not in d or 3 not in d: - return - v2, t2 = d[2] - v3, t3 = d[3] - if abs(t2 + t3) > 1.0 and (abs(t2) > 1 or abs(t3) > 1): - raise RuntimeError("SAFETY: 角度目标必须镜像 t2=%s t3=%s" % (t2, t3)) - if abs(v2) > 0.5 and abs(v3) > 0.5 and v2 * v3 > 0: - raise RuntimeError("SAFETY: 速度禁止同号 v2=%s v3=%s(会拧线)" % (v2, v3)) - a2, cur_v2, a3, cur_v3, _ = _read_pair() - if a2 is not None and a3 is not None: - if abs(a2) > MAX_ABS_ANGLE or abs(a3) > MAX_ABS_ANGLE: - raise RuntimeError("SAFETY: 编码器超限 a2=%.1f a3=%.1f" % (a2, a3)) - if abs(v2) > 0.5 and abs(t2 - a2) > 0.5: - if v2 * (t2 - a2) < 0: - raise RuntimeError("SAFETY: N2 速度方向与目标相反 v=%s a=%.1f t=%.1f" - % (v2, a2, t2)) - if abs(v3) > 0.5 and abs(t3 - a3) > 0.5: - if v3 * (t3 - a3) < 0: - raise RuntimeError("SAFETY: N3 速度方向与目标相反 v=%s a=%.1f t=%.1f" - % (v3, a3, t3)) - - -def _watchdog_stop(reason): - print("!! WATCHDOG STOP:", reason) - for _ in range(6): - try: - post("/api/motor/send", json.dumps({"frames": [ - {"ch": 2, "id": hex((2 << 16) | 0x0200), "dlc": 32, - "data": pack_cmd(mode=3, voltage=24, velocity=0, - angle=(_read_pair()[0] or 0), pid=(100, 100, 100))}, - {"ch": 2, "id": hex((3 << 16) | 0x0200), "dlc": 32, - "data": pack_cmd(mode=3, voltage=24, velocity=0, - angle=(_read_pair()[2] or 0), pid=(100, 100, 100))}, - ]}).encode()) - except Exception: - pass - time.sleep(0.03) - a2, v2, a3, v3, _ = _read_pair() - print(" after stop a2=%s v2=%s a3=%s v3=%s" % (a2, v2, a3, v3)) - - -def pack_cmd(mode=3, voltage=24.0, current=0.0, velocity=100.0, - angle=0.0, pid=(100, 100, 100)) -> str: - """与 pages/motor.html buildCmdBytes 一致(DLC32)。""" - b = bytearray(32) - b[0] = int(mode) & 0xFF - struct.pack_into(" str: - """CONFIG 帧与 CMD 同布局(回声/写参数用)。""" - return pack_cmd(mode=mode, voltage=limit_v, current=current, - velocity=velocity, angle=angle, pid=(0, 0, 0)) - - -def burst_cmds(items, mode=3, voltage=24.0, pid=(100, 100, 100), - allow_same_dir=False): - """items: [(node, velocity, angle)] 一次 POST 同一 seq 沿。""" - _check_pair(items, allow_same=allow_same_dir) - frames = [] - for n, vel, ang in items: - frames.append({ - "ch": 2, - "id": hex((int(n) << 16) | 0x0200), - "dlc": 32, - "data": pack_cmd(mode=mode, voltage=voltage, velocity=vel, - angle=ang, pid=pid), - }) - return post("/api/motor/send", json.dumps({"frames": frames}).encode()) - - -def send_config(cfg: dict, nodes=NODES): - """下发统一 CONFIG。cfg: {mode, limit_v, velocity, angle}""" - frames = [] - for n in nodes: - frames.append({ - "ch": 2, - "id": hex((int(n) << 16) | 0x0200), - "dlc": 32, - "data": pack_config(mode=cfg.get("mode", 3), - limit_v=cfg.get("limit_v", 22.0), - velocity=cfg.get("velocity", 100.0), - angle=cfg.get("angle", 0.0)), - }) - return post("/api/motor/send", json.dumps({"frames": frames}).encode()) - - -# ───────────────────────── 状态 ───────────────────────── - -def decode_status(raw: bytes): - if len(raw) < 21: - return None - ang = struct.unpack_from("= 13 else None, - "ucur": struct.unpack_from("= 6 else None, - "rt_cur": struct.unpack_from("= 25 else 0.0, - "motT": struct.unpack_from("= 4 else None, - "b10": raw[10] if len(raw) > 10 else 0, - } - return d - - -def snap_motors() -> dict: - out = {} - st = post("/api/motor/state") - for m in st.get("motors") or []: - n = m.get("node") - if n not in NODES: - continue - dec = decode_status(bytes.fromhex(m.get("data") or "")) - if dec: - dec["online"] = bool(m.get("online")) - dec["seq"] = m.get("seq") - out[n] = dec - out["_cycles"] = st.get("cycles") - out["_period_ms"] = st.get("period_ms") - return out - - -def read_configs_from_rx() -> dict: - cfgs = {} - try: - rx = post("/api/motor/rx") - except Exception: - return cfgs - for f in (rx.get("frames") or rx.get("parsed") or []): - if not isinstance(f, dict): - continue - fid = f.get("id", 0) - try: - if isinstance(fid, str): - fid = int(fid, 0) - except Exception: - continue - if (fid & 0xFFFF) != 0x0101: - continue - n = (fid >> 16) & 0xFF - if n not in NODES: - continue - try: - raw = bytes.fromhex(f.get("data") or "") - except Exception: - continue - if len(raw) >= 21: - cfgs[n] = { - "mode": raw[0], - "limit_V": struct.unpack_from("= s[-1]["cyc"]: - return s[-1]["ang"], s[-1]["vel"] - lo, hi = 0, len(s) - 1 - while lo + 1 < hi: - mid = (lo + hi) // 2 - if s[mid]["cyc"] <= cyc: - lo = mid - else: - hi = mid - c0, a0, v0 = s[lo]["cyc"], s[lo]["ang"], s[lo]["vel"] - c1, a1, v1 = s[hi]["cyc"], s[hi]["ang"], s[hi]["vel"] - if c1 == c0: - return a1, v1 - f = (cyc - c0) / (c1 - c0) - return a0 + (a1 - a0) * f, v0 + (v1 - v0) * f - - out = [] - for r in s2: - a3, v3 = interp(3, r["cyc"]) - out.append((r["cyc"], r["wall"], r["ang"], r["vel"], a3, v3, - r["vel"] + v3, r["ang"] + a3, r["rt_cur"])) - return out - - -def trapz_vel(series, period_ms, key_v2=3, key_v3=5): - """series 对齐列表 → (I2, I3, Isum) 全程梯形积分。""" - I2 = I3 = 0.0 - for i in range(1, len(series)): - dt = (series[i][0] - series[i - 1][0]) * period_ms / 1000.0 - if dt < 0: - dt = 0 - I2 += 0.5 * (series[i][key_v2] + series[i - 1][key_v2]) * dt - I3 += 0.5 * (series[i][key_v3] + series[i - 1][key_v3]) * dt - return I2, I3, I2 + I3 - - -def write_series_csv(path, series, meta=None): - with open(path, "w", newline="", encoding="utf-8") as fp: - w = csv.writer(fp) - w.writerow(["cycles", "wall", "a2", "v2", "a3", "v3", "vsum", "asum", "rt_cur2"]) - for row in series: - w.writerow(row) - if meta: - fp.write("# meta " + json.dumps(meta, ensure_ascii=False) + "\n") - - -def analyze_pair(series, period_ms, label=""): - if len(series) < 40: - return {"label": label, "error": "too_few_samples", "n": len(series)} - mov = [r for r in series if abs(r[3]) > 15 or abs(r[5]) > 15] - cr = [r for r in series if abs(r[3]) > 80 and abs(r[5]) > 80] - I2, I3, Isum = trapz_vel(series, period_ms) - out = { - "label": label, - "n": len(series), - "I2": I2, "I3": I3, "I_sum_v2_plus_v3": Isum, - "asum_start_end": [series[0][7], series[-1][7]], - "asum_delta": series[-1][7] - series[0][7], - "a2_start_end": [series[0][2], series[-1][2]], - "a3_start_end": [series[0][4], series[-1][4]], - } - if cr: - out["cruise_mean_abs_v2"] = sum(abs(r[3]) for r in cr) / len(cr) - out["cruise_mean_abs_v3"] = sum(abs(r[5]) for r in cr) / len(cr) - out["cruise_mean_vsum"] = sum(r[6] for r in cr) / len(cr) - out["cruise_rms_vsum"] = (sum(r[6] ** 2 for r in cr) / len(cr)) ** 0.5 - if mov: - out["mov_mean_abs_v2"] = sum(abs(r[3]) for r in mov) / len(mov) - out["mov_mean_abs_v3"] = sum(abs(r[5]) for r in mov) / len(mov) - out["mov_rms_vsum"] = (sum(r[6] ** 2 for r in mov) / len(mov)) ** 0.5 - out["mov_max_abs_vsum"] = max(abs(r[6]) for r in mov) - return out - - -# ───────────────────────── 连续发令 ───────────────────────── - -def drive(items, duration, rate=25.0, mode=3, voltage=24.0, pid=(100, 100, 100), - allow_same_dir=False): - """连续发令 + 看门狗。已对接:仅 mode3 + 镜像角度目标 + 低速优先。""" - if mode != 3: - raise RuntimeError("SAFETY: 已对接仅允许 mode3,禁止 mode=%s" % mode) - _check_pair(items, allow_same=allow_same_dir) - t_end = time.perf_counter() + duration - dt = 1.0 / max(rate, 1.0) - n_ok = 0 - last_err = None - spin_t0 = None - while time.perf_counter() < t_end: - t0 = time.perf_counter() - try: - burst_cmds(items, mode=mode, voltage=voltage, pid=pid, - allow_same_dir=allow_same_dir) - n_ok += 1 - except Exception as e: - last_err = str(e) - print(" send err:", e) - if "SAFETY" in str(e): - _watchdog_stop(str(e)) - break - try: - a2, v2, a3, v3, _ = _read_pair() - if v2 is not None and v3 is not None: - s2, s3 = abs(v2) < STOP_VEL_EPS, abs(v3) < STOP_VEL_EPS - spin = abs(v2) > SPIN_VEL_WARN or abs(v3) > SPIN_VEL_WARN - if (s2 != s3) and spin: - if spin_t0 is None: - spin_t0 = time.perf_counter() - elif (time.perf_counter() - spin_t0) * 1000.0 >= ASYMMETRY_MS: - _watchdog_stop("一台已停另一台仍在转 v2=%.1f v3=%.1f" % (v2, v3)) - break - else: - spin_t0 = None - if a2 is not None and a3 is not None and ( - abs(a2) > MAX_ABS_ANGLE or abs(a3) > MAX_ABS_ANGLE): - _watchdog_stop("角度超限 a2=%.1f a3=%.1f" % (a2, a3)) - break - except Exception: - pass - pad = dt - (time.perf_counter() - t0) - if pad > 0: - time.sleep(pad) - return n_ok, last_err - - -def hold_position(pid=(100, 100, 100), voltage=24.0, rate=20.0, seconds=0.4, - targets=None): - snap = snap_motors() - if targets is None: - targets = [] - for n in NODES: - if n in snap: - targets.append((n, 0.0, snap[n]["ang"])) - else: - targets.append((n, 0.0, 0.0)) - return drive(targets, seconds, rate=rate, mode=3, voltage=voltage, pid=pid) - - -# ───────────────────────── 子命令 ───────────────────────── - -def cmd_status(_args): - period, st = ensure_ecat() - print("链路 wkc=%s period=%sms cycles=%s motors=%s" - % (st.get("wkc"), period, st.get("cycles"), - [(m.get("node"), m.get("online"), m.get("seq")) - for m in st.get("motors") or []])) - print("\n--- live STATUS ---") - snap = snap_motors() - for n in NODES: - d = snap.get(n) - if not d: - continue - print(" N%s ang=%.3f vel=%.2f pwr=%s motT=%s b10=%s online=%s" - % (n, d["ang"], d["vel"], d["pwr"], d["motT"], d["b10"], d["online"])) - print("\n--- CONFIG from /api/motor/rx ---") - cfgs = read_configs_from_rx() - if not cfgs: - print(" (rx 暂无 0x0101 CONFIG,可先发一帧或等电机上报)") - for n, c in sorted(cfgs.items()): - print(" N%s %s" % (n, c)) - return 0 - - -def cmd_align(args): - period, st = ensure_ecat() - print("align: limitV=%.2f PID=%s mode=%s" % (args.limit_v, args.pid, args.mode)) - print("before CONFIG:", json.dumps(read_configs_from_rx(), ensure_ascii=False)) - r = send_config({"mode": args.mode, "limit_v": args.limit_v, - "velocity": args.vel, "angle": 0.0}) - print("config send:", r) - time.sleep(0.15) - # 用统一参数发 CMD(速度 0,保持) - snap = snap_motors() - items = [] - for n in NODES: - ang = snap.get(n, {}).get("ang", 0.0) - items.append((n, 0.0, ang)) - for _ in range(int(args.hold * 25)): - burst_cmds(items, mode=args.mode, voltage=args.limit_v + 2, - pid=tuple(args.pid)) - time.sleep(0.04) - print("after CONFIG:", json.dumps(read_configs_from_rx(), ensure_ascii=False)) - print("live:", json.dumps( - {str(n): snap_motors().get(n) for n in NODES if snap_motors().get(n)}, - ensure_ascii=False)[:400]) - return 0 - - -def cmd_openloop(args): - """已对接:禁止 mode2 开环。只做短时 mode3 镜像微动(±30°)测响应。""" - period, _ = ensure_ecat() - print("SAFETY: 对接后禁止 open-loop mode2。改为 mode3 镜像小行程 ±20° @ vel≤50") - speed = min(abs(args.vel), 50.0) - span = 20.0 - a2, _, a3, _, _ = _read_pair() - if a2 is None or a3 is None: - print("无状态") - return 2 - # 在当前位置附近做镜像微动:目标 = 当前 ± 小量,并强制 target2=-target3 - # 仅当当前已接近对称时允许 - if abs(a2 + a3) > MAX_ASUM_ERR: - print("拒绝:a2+a3=%.1f 错位过大,先人工回位" % (a2 + a3)) - return 2 - t2 = span if a2 < 0 else -span - t3 = -t2 - # 若当前位置离目标太近/太远则改用以 0 为中心 - t2, t3 = span, -span - ok, msg, items = safety_precheck_motion(t2, t3, speed) - print(msg) - if not ok: - return 2 - ths = start_poll(args.threads) - t0 = time.time() - drive(items, args.cruise, args.rate, mode=3, voltage=args.voltage, - pid=tuple(args.pid)) - # 回到对称位附近 0/0 或停在当前位置 - ok2, msg2, items2 = safety_precheck_motion(0.0, 0.0, speed) - print(msg2) - if ok2: - drive(items2, args.settle, args.rate, mode=3, voltage=args.voltage, - pid=tuple(args.pid)) - t1 = time.time() - stop_poll(ths) - series = aligned_series() - period = post("/api/motor/state").get("period_ms") or period - tag = "micro_mirror" - csv_path = os.path.join(LAB, "pair_test_%s.csv" % tag) - meta = {"test": "micro_mirror_mode3", "span": span, "vel": speed, - "t0": t0, "t1": t1} - write_series_csv(csv_path, series, meta) - an = analyze_pair(series, period, label=tag) - an["csv"] = csv_path - an["command"] = meta - jpath = os.path.join(LAB, "pair_test_%s.json" % tag) - with open(jpath, "w", encoding="utf-8") as fp: - json.dump(an, fp, ensure_ascii=False, indent=2) - print(json.dumps(an, ensure_ascii=False, indent=2)) - print("CSV", csv_path, "JSON", jpath) - return 0 - - -def cmd_stop(args): - """镜像 mode3:短行程后停在目标,禁止开环刹停测法。""" - period, _ = ensure_ecat() - speed = min(abs(args.vel), 80.0) - print("stop-test:对接机构仅允许 mode3 镜像行程后自然停靠(禁止 mode2)") - a2, _, a3, _, _ = _read_pair() - if a2 is None: - return 2 - # 从对称位附近:先到 ±span,再回 0(行程用 stop.hold) - span = min(args.span if hasattr(args, "span") else 150.0, 200.0) - ok, msg, items = safety_precheck_motion(span, -span, speed) - print("leg1", msg) - if not ok: - return 2 - ths = start_poll(args.threads) - t0 = time.time() - drive(items, args.cruise + 1.5, args.rate, mode=3, - voltage=args.voltage, pid=tuple(args.pid)) - t_cmd_stop = time.time() - ok2, msg2, items2 = safety_precheck_motion(0.0, 0.0, speed) - print("leg2→0", msg2) - if ok2: - drive(items2, args.hold + 1.5, args.rate, mode=3, - voltage=args.voltage, pid=tuple(args.pid)) - t1 = time.time() - stop_poll(ths) - series = aligned_series() - period = post("/api/motor/state").get("period_ms") or period - tag = "stop_mirror_mode3" - csv_path = os.path.join(LAB, "pair_test_%s.csv" % tag) - meta = {"test": "stop_mirror_mode3", "span": span, "vel": speed, - "t0": t0, "t_mid": t_cmd_stop, "t1": t1} - write_series_csv(csv_path, series, meta) - an = analyze_pair(series, period, label=tag) - an["csv"] = csv_path - an["command"] = meta - an["live_end"] = {str(n): snap_motors().get(n) for n in NODES if snap_motors().get(n)} - jpath = os.path.join(LAB, "pair_test_%s.json" % tag) - with open(jpath, "w", encoding="utf-8") as fp: - json.dump(an, fp, ensure_ascii=False, indent=2) - print(json.dumps(an, ensure_ascii=False, indent=2)) - print("CSV", csv_path, "JSON", jpath) - return 0 - hold_position(seconds=0.35, pid=tuple(args.pid), voltage=args.voltage) - ths = start_poll(args.threads) - t0 = time.time() - print("cruise...") - n1, e1 = drive(cruise_items, args.cruise, args.rate, mode=2, - voltage=args.voltage, pid=tuple(args.pid)) - t_cmd_stop = time.time() - print("STOP → 0") - n2, e2 = drive(stop_items, args.hold, args.rate, mode=2, - voltage=args.voltage, pid=tuple(args.pid)) - t1 = time.time() - stop_poll(ths) - series = aligned_series() - period = post("/api/motor/state").get("period_ms") or period - csv_path = os.path.join(LAB, "pair_test_%s.csv" % tag) - meta = {"test": "stop", "vel": vel, "mirror": args.mirror, - "t0": t0, "t_stop_cmd": t_cmd_stop, "t1": t1, - "sends_cruise": n1, "sends_stop": n2, "err": [e1, e2]} - write_series_csv(csv_path, series, meta) - - # 停止沿分析:以发 STOP 时刻为界 - if len(series) < 20: - print("samples too few") - return 2 - # 找 wall 接近 t_cmd_stop 的索引 - idx_stop = 0 - for i, row in enumerate(series): - if row[1] >= t_cmd_stop: - idx_stop = max(0, i - 1) - break - pre = series[:idx_stop + 1] - post_s = series[idx_stop:] - - def win_stats(seg): - if len(seg) < 5: - return None - v2 = [r[3] for r in seg] - v3 = [r[5] for r in seg] - vs = [r[6] for r in seg] - I2, I3, Isum = trapz_vel(seg, period) - return { - "n": len(seg), - "mean_v2": sum(v2) / len(v2), - "mean_v3": sum(v3) / len(v3), - "mean_abs_v2": sum(abs(x) for x in v2) / len(v2), - "mean_abs_v3": sum(abs(x) for x in v3) / len(v3), - "rms_vsum": (sum(x * x for x in vs) / len(vs)) ** 0.5, - "max_abs_vsum": max(abs(x) for x in vs), - "I2": I2, "I3": I3, "Isum": Isum, - } - - # 各轴 |v| 穿越 20 的时刻 - def cross(seg, node_i, thr=20.0, falling=True): - vi = 3 if node_i == 2 else 5 - for i in range(1, len(seg)): - v0, v1 = abs(seg[i - 1][vi]), abs(seg[i][vi]) - if falling and v0 >= thr > v1: - return seg[i - 1][0] + (v0 - thr) / (v0 - v1 + 1e-12) * (seg[i][0] - seg[i - 1][0]) - if (not falling) and v0 < thr <= v1: - return seg[i - 1][0] + (thr - v0) / (v1 - v0 + 1e-12) * (seg[i][0] - seg[i - 1][0]) - return None - - c2 = cross(post_s, 2) - c3 = cross(post_s, 3) - off_delta_ms = None - if c2 is not None and c3 is not None: - off_delta_ms = (c3 - c2) * period - - an = analyze_pair(series, period, label=tag) - an.update({ - "csv": csv_path, - "command": meta, - "pre_stop": win_stats(pre), - "post_stop": win_stats(post_s), - "off_cross20_ms_n3_minus_n2": off_delta_ms, - "verdict_hint": { - "cruise": "比 pre_stop 的 mean_abs_v2/v3 谁大", - "stop": "比 post_stop 的 max_abs_vsum 与 off_delta_ms;Δ>0 表示 N3 晚停", - "integral": "post_stop.Isum 绝对值 = 停车段被顶开的积分", - }, - }) - jpath = os.path.join(LAB, "pair_test_%s.json" % tag) - with open(jpath, "w", encoding="utf-8") as fp: - json.dump(an, fp, ensure_ascii=False, indent=2) - print(json.dumps(an, ensure_ascii=False, indent=2)) - print("CSV", csv_path, "JSON", jpath) - return 0 - - -def cmd_trip(args): - """mode3 镜像行程:n2=+span, n3=-span(已对接强制镜像)。""" - period, _ = ensure_ecat() - span = abs(args.span) - pos = [(2, span), (3, -span)] - zero = [(2, 0.0), (3, 0.0)] - tag = "trip_mirror" - print("trip %s span=±%.1f vel=%.1f hold=%.2f(强制镜像)" - % (tag, span, args.vel, args.hold)) - snap = snap_motors() - a2 = snap.get(2, {}).get("ang", 0.0) - # 选择更远的端点作为第一目标 - if abs(a2 - span) < abs(a2 - 0) and abs(a2) > span * 0.5: - first, second = zero, pos - elif abs(a2) < span * 0.5: - first, second = pos, zero - else: - first, second = zero, pos - - ths = start_poll(args.threads) - t0 = time.time() - print("leg1 →", first) - snap = snap_motors() - t2t, t3t = first[0][1], first[1][1] - drive(_mirror_cmd_items(t2t, t3t, abs(args.vel), snap), args.hold, - args.rate, mode=3, voltage=args.voltage, pid=tuple(args.pid)) - t_mid = time.time() - print("leg2 →", second) - snap = snap_motors() - t2t, t3t = second[0][1], second[1][1] - drive(_mirror_cmd_items(t2t, t3t, abs(args.vel), snap), args.hold, - args.rate, mode=3, voltage=args.voltage, pid=tuple(args.pid)) - t1 = time.time() - stop_poll(ths) - series = aligned_series() - period = post("/api/motor/state").get("period_ms") or period - csv_path = os.path.join(LAB, "pair_test_%s.csv" % tag) - meta = {"test": "trip_mirror", "span": span, "vel": args.vel, - "t0": t0, "t_mid": t_mid, "t1": t1, "coupled": True} - write_series_csv(csv_path, series, meta) - an = analyze_pair(series, period, label=tag) - an["csv"] = csv_path - an["command"] = meta - an["live_end"] = {str(n): snap_motors().get(n) for n in NODES if snap_motors().get(n)} - jpath = os.path.join(LAB, "pair_test_%s.json" % tag) - with open(jpath, "w", encoding="utf-8") as fp: - json.dump(an, fp, ensure_ascii=False, indent=2) - print(json.dumps(an, ensure_ascii=False, indent=2)) - print("CSV", csv_path, "JSON", jpath) - return 0 - - -def _mirror_cmd_items(target2, target3, speed, snap=None): - """仅通过 safety_precheck 生成 CMD;禁止把速度强行改成相反符号。""" - ok, msg, items = safety_precheck_motion(target2, target3, abs(speed)) - print(" precheck:", msg) - if not ok: - raise RuntimeError("SAFETY: " + msg) - return items - - -def cmd_lowspeed(args): - """低速镜像试控:先只读检查,再极小行程。错位大则拒绝一切运动。""" - period, st = ensure_ecat() - a2, v2, a3, v3, snap = _read_pair() - asum = None - if a2 is not None and a3 is not None: - asum = a2 + a3 - print("\n======== 低速试控前置检查 ========") - print("a2=%s a3=%s a2+a3=%s v2=%s v3=%s" % (a2, a3, asum, v2, v3)) - print("硬限位: |ang|≤%.0f |a2+a3|≤%.0f 禁止 mode2 禁止同向" % - (MAX_ABS_ANGLE, MAX_ASUM_ERR)) - if a2 is None or a3 is None: - print("结论: 状态缺失 → 不发运动指令") - return 2 - if abs(a2) > MAX_ABS_ANGLE or abs(a3) > MAX_ABS_ANGLE: - print("结论: 编码器超限 → 不发运动指令。请人工把机构回到对称位。") - return 2 - if asum is not None and abs(asum) > MAX_ASUM_ERR: - print("结论: 错位 a2+a3=%.1f° 过大 → 拒绝电控运动。" % asum) - print("请先人工盘车/松耦合,使 a2≈-a3(和接近 0),再跑 lows peed。") - return 2 - speed = min(abs(args.vel), 20.0) # 低速硬顶 20°/s - span = min(abs(args.span), 15.0) # 小行程硬顶 15° - t2, t3 = span, -span - if abs(t2 - a2) > 3 * span: - # 当前离目标太远则以当前对称附近为中心:目标 = round 到对称 - t2 = span if a2 < 0 else -span - t3 = -t2 - ok, msg, items = safety_precheck_motion(t2, t3, speed) - print("试控目标 ±%.1f° @ %.1f°/s" % (span, speed)) - print(msg) - if not ok: - print("结论: precheck 失败,不发令") - return 2 - print("发令 items:", items) - ths = start_poll(args.threads) - t0 = time.time() - n_ok, err = drive(items, max(args.hold, 2.0), rate=10.0, mode=3, - voltage=args.voltage, pid=tuple(args.pid)) - print("行程发送完成 n_ok=%s err=%s" % (n_ok, err)) - # 回 0(若仍安全) - ok2, msg2, items2 = safety_precheck_motion(0.0, 0.0, speed) - print("回零:", msg2) - if ok2: - drive(items2, max(args.hold, 2.0), rate=10.0, mode=3, - voltage=args.voltage, pid=tuple(args.pid)) - else: - # 保持当前位置 - a2, _, a3, _, _ = _read_pair() - if a2 is not None and a3 is not None: - try: - drive([(2, 0.0, a2), (3, 0.0, a3)], 0.6, rate=10.0, mode=3, - voltage=args.voltage, pid=tuple(args.pid)) - except Exception as e: - print("hold err", e) - t1 = time.time() - stop_poll(ths) - a2e, v2e, a3e, v3e, _ = _read_pair() - print("结束: a2=%s v2=%s a3=%s v3=%s asum=%s" % - (a2e, v2e, a3e, v3e, (None if a2e is None or a3e is None else a2e + a3e))) - series = aligned_series() - period = post("/api/motor/state").get("period_ms") or period - csv_path = os.path.join(LAB, "pair_test_lowspeed.csv") - meta = {"test": "lowspeed", "vel": speed, "span": span, - "t0": t0, "t1": t1, "items": items} - write_series_csv(csv_path, series, meta) - an = analyze_pair(series, period, label="lowspeed") - an["csv"] = csv_path - an["command"] = meta - an["end"] = {"a2": a2e, "v2": v2e, "a3": a3e, "v3": v3e, - "asum": None if a2e is None or a3e is None else a2e + a3e} - jpath = os.path.join(LAB, "pair_test_lowspeed.json") - with open(jpath, "w", encoding="utf-8") as fp: - json.dump(an, fp, ensure_ascii=False, indent=2) - print(json.dumps(an, ensure_ascii=False, indent=2)[:800]) - print("CSV", csv_path, "JSON", jpath) - return 0 - - -def cmd_fairtrip(args): - """公平行程:先双机 mode3 停到 0(镜像速度),再同时去 +span/-span。""" - period, _ = ensure_ecat() - span = abs(args.span) - print("fairtrip: settle@0 → mirror ±%.0f @ vel=%.1f hold=%.2f(强制镜像)" - % (span, args.vel, args.hold)) - - ths = start_poll(args.threads) - t0 = time.time() - print("1) 同步到 0(速度按镜像指向 0)") - items0 = _mirror_cmd_items(0.0, 0.0, abs(args.vel)) - print(" cmd", items0) - drive(items0, args.settle, - args.rate, mode=3, voltage=args.voltage, pid=tuple(args.pid)) - snap = snap_motors() - print(" at0:", {n: (snap.get(n) or {}).get("ang") for n in NODES}) - t_go = time.time() - print("2) 同时镜像 ±span") - items1 = _mirror_cmd_items(span, -span, abs(args.vel), snap) - print(" cmd", items1) - drive(items1, args.hold, - args.rate, mode=3, voltage=args.voltage, pid=tuple(args.pid)) - t1 = time.time() - stop_poll(ths) - snap = snap_motors() - print(" end:", {n: (snap.get(n) or {}).get("ang") for n in NODES}, - {n: (snap.get(n) or {}).get("vel") for n in NODES}) - - series = aligned_series() - period = post("/api/motor/state").get("period_ms") or period - csv_path = os.path.join(LAB, "pair_test_fairtrip.csv") - meta = {"test": "fairtrip", "span": span, "vel": args.vel, - "t0": t0, "t_go": t_go, "t1": t1} - write_series_csv(csv_path, series, meta) - - # 只分析 t_go 之后的行程 - seg = [r for r in series if r[1] >= t_go] - an = analyze_pair(seg, period, label="fairtrip") - an["csv"] = csv_path - an["command"] = meta - an["live_end"] = {str(n): snap.get(n) for n in NODES if snap.get(n)} - - # 到达时刻:|ang - target| < 3 且 |v| < 10 - def arrive(node, target): - vi_ang = 2 if node == 2 else 4 - vi_vel = 3 if node == 5 else 5 - vi_vel = 3 if node == 2 else 5 - for r in seg: - if abs(r[vi_ang] - target) < 3.0 and abs(r[vi_vel]) < 10.0: - return r[1] - return None - t2 = arrive(2, span) - t3 = arrive(3, -span) - an["arrive_wall"] = {"n2": t2, "n3": t3} - if t2 and t3: - an["arrive_delta_ms_n3_minus_n2"] = (t3 - t2) * 1000.0 - - # 行程中段 20%-80% 的 vsum / 巡航 - if len(seg) > 50: - w = seg[len(seg) // 5: max(len(seg) - len(seg) // 5, len(seg) // 5 + 2)] - I2, I3, Isum = trapz_vel(w, period) - an["mid_stroke"] = { - "n": len(w), - "mean_abs_v2": sum(abs(r[3]) for r in w) / len(w), - "mean_abs_v3": sum(abs(r[5]) for r in w) / len(w), - "mean_vsum": sum(r[6] for r in w) / len(w), - "rms_vsum": (sum(r[6] ** 2 for r in w) / len(w)) ** 0.5, - "max_abs_vsum": max(abs(r[6]) for r in w), - "I2": I2, "I3": I3, "Isum": Isum, - } - jpath = os.path.join(LAB, "pair_test_fairtrip.json") - with open(jpath, "w", encoding="utf-8") as fp: - json.dump(an, fp, ensure_ascii=False, indent=2) - print(json.dumps(an, ensure_ascii=False, indent=2)) - print("CSV", csv_path, "JSON", jpath) - return 0 - - -def cmd_stopnow(_args): - print("STOP NOW") - for _ in range(8): - try: - burst_cmds([(2, 0.0, 0.0), (3, 0.0, 0.0)], mode=2) - except Exception as e: - print(" ", e) - time.sleep(0.03) - print("live", json.dumps( - {str(n): snap_motors().get(n) for n in NODES if snap_motors().get(n)}, - ensure_ascii=False)[:300]) - return 0 - - -def cmd_full(args): - """对接安全套件:仅镜像 openloop / stop / fairtrip(禁止同向)。""" - results = {} - print("\n======== FULL-MIRROR: 1/4 align ========") - cmd_align(args) - - print("\n======== FULL-MIRROR: 2/4 openloop mirror ========") - cmd_openloop(args) - p_ol = os.path.join(LAB, "pair_test_openloop_mirror_vel%s.json" % abs(args.vel)) - if os.path.exists(p_ol): - results["openloop_mirror"] = json.loads(open(p_ol, encoding="utf-8").read()) - - print("\n======== FULL-MIRROR: 3/4 stop mirror ========") - cmd_stop(args) - p_st = os.path.join(LAB, "pair_test_stop_mirror.json") - if os.path.exists(p_st): - results["stop_mirror"] = json.loads(open(p_st, encoding="utf-8").read()) - - print("\n======== FULL-MIRROR: 4/4 fairtrip mirror ========") - cmd_fairtrip(args) - p_ft = os.path.join(LAB, "pair_test_fairtrip.json") - if os.path.exists(p_ft): - results["fairtrip"] = json.loads(open(p_ft, encoding="utf-8").read()) - - out = os.path.join(LAB, "pair_test_FULL_mirror_summary.json") - slim = {"coupled": True, "rule": "mirror_only_v2=-v3", "results": {}} - for k, v in results.items(): - slim["results"][k] = {kk: v.get(kk) for kk in ( - "label", "cruise_mean_abs_v2", "cruise_mean_abs_v3", - "cruise_mean_vsum", "mov_rms_vsum", "mov_max_abs_vsum", - "I_sum_v2_plus_v3", "asum_delta", "arrive_delta_ms_n3_minus_n2", - "off_cross20_ms_n3_minus_n2", "mid_stroke", "pre_stop", "post_stop", - ) if kk in v} - with open(out, "w", encoding="utf-8") as fp: - json.dump(slim, fp, ensure_ascii=False, indent=2) - print(json.dumps(slim, ensure_ascii=False, indent=2)) - print("summary ->", out) - return 0 - - -def _who_slower(an): - v2 = an.get("cruise_mean_abs_v2") - v3 = an.get("cruise_mean_abs_v3") - if not v2 or not v3: - return None - if abs(v2 - v3) < 1.0: - return "almost_equal" - return "N2_slower" if v2 < v3 else "N3_slower" - - -# ───────────────────────── CLI ───────────────────────── - -def build_parser(): - p = argparse.ArgumentParser( - description="N2/N3 对接电机诊断发令(ECAT)— 请确认机构安全") - p.add_argument("--pid", type=int, nargs=3, default=[100, 100, 100], - help="PID current/vel/angle P,默认 100 100 100") - p.add_argument("--voltage", type=float, default=24.0) - p.add_argument("--rate", type=float, default=25.0, help="发令频率 Hz") - p.add_argument("--threads", type=int, default=6) - sub = p.add_subparsers(dest="cmd", required=True) - - sub.add_parser("status", help="只读状态与 CONFIG") - slowp = sub.add_parser("lowspeed", help="低速镜像试控(错位大则拒绝)") - slowp.add_argument("--vel", type=float, default=15.0) - slowp.add_argument("--span", type=float, default=10.0) - slowp.add_argument("--hold", type=float, default=2.5) - - sp = sub.add_parser("align", help="统一 CONFIG 并保持") - sp.add_argument("--limit-v", type=float, default=22.0) - sp.add_argument("--vel", type=float, default=100.0) - sp.add_argument("--mode", type=int, default=3) - sp.add_argument("--hold", type=float, default=0.8) - - so = sub.add_parser("openloop", help="开环速度对比 mode2") - so.add_argument("--vel", type=float, default=80.0) - so.add_argument("--cruise", type=float, default=1.5) - so.add_argument("--settle", type=float, default=0.6) - - ss = sub.add_parser("stop", help="巡航→同时刹停") - ss.add_argument("--vel", type=float, default=80.0) - ss.add_argument("--cruise", type=float, default=1.2) - ss.add_argument("--hold", type=float, default=0.8) - ss.add_argument("--mirror", action="store_true", help="v2=-v3 镜像巡航") - - st = sub.add_parser("trip", help="mode3 位置行程") - st.add_argument("--span", type=float, default=200.0) - st.add_argument("--vel", type=float, default=80.0) - st.add_argument("--hold", type=float, default=2.5) - st.add_argument("--mirror", action="store_true") - - sfair = sub.add_parser("fairtrip", help="先到0再同时±span,公平对比") - sfair.add_argument("--span", type=float, default=400.0) - sfair.add_argument("--vel", type=float, default=200.0) - sfair.add_argument("--hold", type=float, default=2.5) - sfair.add_argument("--settle", type=float, default=2.0) - - sub.add_parser("stopnow", help="急停速度=0") - - sf = sub.add_parser("full", help="align+openloop+stop+trip 全套") - sf.add_argument("--vel", type=float, default=80.0) - sf.add_argument("--cruise", type=float, default=1.2) - sf.add_argument("--hold", type=float, default=2.2) - sf.add_argument("--settle", type=float, default=0.6) - sf.add_argument("--span", type=float, default=200.0) - sf.add_argument("--limit-v", type=float, default=22.0) - sf.add_argument("--mode", type=int, default=3) - sf.add_argument("--mirror", action="store_true") - return p - - -def main(argv=None): - args = build_parser().parse_args(argv) - print("⚠ 请确认电机2/3机构处于安全状态;Ctrl+C 后请手动 stopnow") - print("target:", args.cmd, "pid", args.pid) - handlers = { - "status": cmd_status, - "lowspeed": cmd_lowspeed, - "align": cmd_align, - "openloop": cmd_openloop, - "stop": cmd_stop, - "trip": cmd_trip, - "fairtrip": cmd_fairtrip, - "stopnow": cmd_stopnow, - "full": cmd_full, - } - try: - return handlers[args.cmd](args) - except KeyboardInterrupt: - print("\n!! 中断 → 急停") - try: - cmd_stopnow(args) - except Exception: - pass - return 130 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/test_pram_batch_sim.py b/scripts/test_pram_batch_sim.py deleted file mode 100644 index ceccf28..0000000 --- a/scripts/test_pram_batch_sim.py +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/env python3 -"""test_pram_batch_sim.py - 主机侧仿真:验证 pramRead/pramWrite 批量化(1.6.0) -与旧实现(逐 DWORD 轮询)逐字节等价。 - -模型依据 LAN9252 DS00001909A §12.11.3/12.11.4: - - PRAM FIFO 16 deep x 32bit;AVAIL_CNT(CMD bits12:8)=可连续读/写的 DWORD 数 - - 读引擎按 DWORD 对齐向下取源:第 i 个 FIFO DWORD = 源地址 aligned(addr)+4i 起 4 字节; - 线性有效窗口 = [0, len),即 DWORD i 的字节 k 有效 iff 0 <= 4i+k-lead < len - - 写引擎按同一线性窗口落盘,主站推满一个 DWORD 计一次 - - 引擎以有限速率搬运(rate),主站 respect AVAIL_CNT 就不会溢 FIFO/读空洞 - -对全部 (addr 对齐 x len 1..70/96/384/640 x 引擎节奏) 断言: -新旧算法产出逐字节一致,且主站从不超出 AVAIL_CNT(引擎内 assert)。 -""" -import random - -FIFO_DW = 16 # FIFO 深度 16 DWORD -WINDOW_DW = 8 # 别名窗口一程 8 DWORD (0x00-0x1C / 0x20-0x3C) -PRAM_BASE = 0x1000 -PRAM_SIZE = 0x1000 - - -def byte_valid(i, k, lead, ln): - """DWORD i 的字节 k 是否落在线性有效窗口 [0, ln) 内。""" - return 0 <= 4 * i + k - lead < ln - - -class ReadEngine: - def __init__(self, mem, addr, ln, rate): - self.mem = mem - self.lead = addr & 3 - self.ln = ln - self.rate = max(1, rate) - self.total_dw = (ln + self.lead + 3) // 4 - self.start = (addr & ~3) - PRAM_BASE # mem 内偏移 - self.pushed = 0 - self.popped = 0 - self.data = [] - - def pump(self): - n = 0 - while (self.pushed - self.popped < FIFO_DW and self.pushed < self.total_dw - and n < self.rate): - i = self.pushed - dw = bytearray(4) - for k in range(4): - if byte_valid(i, k, self.lead, self.ln): - dw[k] = self.mem[self.start + 4 * i + k] - self.data.append(bytes(dw)) - self.pushed += 1 - n += 1 - return n - - def avail_cnt(self): - return self.pushed - self.popped - - def pop(self): - assert self.popped < self.pushed, "主站弹出超出 AVAIL_CNT!" - dw = self.data[self.popped] - self.popped += 1 - return dw - - -def pram_read_old(eng, addr, ln, poll_budget=2000): - out = bytearray() - done = 0 - polls = 0 - while done < ln: - while eng.avail_cnt() == 0: - polls += 1 - if polls > poll_budget: - return bytes(out), False - eng.pump() - dw = eng.pop() - lead = (addr & 3) if done == 0 else 0 - take = min(ln - done, 4 - lead) - out += dw[lead:lead + take] - done += take - eng.pump() - return bytes(out), True - - -def pram_read_new(eng, addr, ln, poll_budget=2000): - out = bytearray() - done = 0 - polls = 0 - while done < ln: - while eng.avail_cnt() == 0: - polls += 1 - if polls > poll_budget: - return bytes(out), False - eng.pump() - batch = eng.avail_cnt() - if batch == 0 or batch > WINDOW_DW: - batch = 1 - lead0 = (addr & 3) if done == 0 else 0 - need_dw = (ln - done + lead0 + 3) // 4 - batch = min(batch, need_dw) - for _ in range(batch): - dw = eng.pop() - lead = (addr & 3) if done == 0 else 0 - take = min(ln - done, 4 - lead) - out += dw[lead:lead + take] - done += take - eng.pump() - return bytes(out), True - - -class WriteEngine: - def __init__(self, addr, ln, rate): - self.lead = addr & 3 - self.ln = ln - self.rate = max(1, rate) - self.total_dw = (ln + self.lead + 3) // 4 - self.start = (addr & ~3) - PRAM_BASE - self.fifo = [] - self.consumed_dw = 0 - self.mem = bytearray(PRAM_SIZE) - - def pump(self): - n = 0 - while self.fifo and n < self.rate: - dw = self.fifo.pop(0) - i = self.consumed_dw - for k in range(4): - if byte_valid(i, k, self.lead, self.ln): - self.mem[self.start + 4 * i + k] = dw[k] - self.consumed_dw += 1 - n += 1 - return n - - def space_cnt(self): - return FIFO_DW - len(self.fifo) - - def push(self, dw): - assert len(self.fifo) < FIFO_DW, "主站写溢出 FIFO!" - self.fifo.append(bytes(dw)) - - -def _write_common(eng, addr, buf, poll_budget, batched): - ln = len(buf) - done = 0 - polls = 0 - while done < ln: - while eng.space_cnt() == 0: - polls += 1 - if polls > poll_budget: - return False - eng.pump() - n_iter = 1 - if batched: - batch = eng.space_cnt() - if batch == 0 or batch > WINDOW_DW: - batch = 1 - lead0 = (addr & 3) if done == 0 else 0 - need_dw = (ln - done + lead0 + 3) // 4 - n_iter = min(batch, need_dw) - for _ in range(n_iter): - lead = (addr & 3) if done == 0 else 0 - take = min(ln - done, 4 - lead) - dw = bytearray(4) - for i in range(take): - dw[lead + i] = buf[done + i] - eng.push(dw) - done += take - eng.pump() - budget = poll_budget - while eng.consumed_dw < eng.total_dw and budget: - eng.pump() - budget -= 1 - return eng.consumed_dw == eng.total_dw - - -def pram_write_old(eng, addr, buf, poll_budget=2000): - return _write_common(eng, addr, buf, poll_budget, batched=False) - - -def pram_write_new(eng, addr, buf, poll_budget=2000): - return _write_common(eng, addr, buf, poll_budget, batched=True) - - -def run(): - random.seed(20260905) - cases = 0 - lens = list(range(1, 71)) + [96, 384, 640] # 覆盖 SM2(384B)/SM3(640B) 实际长度 - for align in range(4): - for ln in lens: - for rate in (1, 3, 16, 64): - src = bytes(random.randrange(1, 256) for _ in range(ln)) - base = random.randrange(PRAM_SIZE - ln - 8) - addr = PRAM_BASE + base + align - mem = bytearray(PRAM_SIZE) - mem[base + align: base + align + ln] = src - # 读等价:新旧都须完整取回 src - e1 = ReadEngine(mem, addr, ln, rate); e1.pump() - r_old, ok1 = pram_read_old(e1, addr, ln) - e2 = ReadEngine(mem, addr, ln, rate); e2.pump() - r_new, ok2 = pram_read_new(e2, addr, ln) - assert ok1 and ok2, f"read 未完成 align={align} len={ln} rate={rate}" - assert r_old == src, f"read 旧实现不符 align={align} len={ln} rate={rate}" - assert r_new == src, f"read 新实现不符 align={align} len={ln} rate={rate}" - # 写等价:新旧都须把 src 落到同一片 PRAM - w1 = WriteEngine(addr, ln, rate); w1.pump() - assert pram_write_old(w1, addr, src), f"write 未完成 {align}/{ln}/{rate}" - w2 = WriteEngine(addr, ln, rate); w2.pump() - assert pram_write_new(w2, addr, src), f"write 未完成 {align}/{ln}/{rate}" - assert bytes(w1.mem[base + align: base + align + ln]) == src, \ - f"write 旧不符 {align}/{ln}/{rate}" - assert bytes(w2.mem[base + align: base + align + ln]) == src, \ - f"write 新不符 {align}/{ln}/{rate}" - cases += 1 - print(f"OK: {cases} 组 (对齐x长度x引擎节奏) 新旧算法逐字节等价") - # 统计新实现的轮询次数收益(代表性场景:SM3 640B DWORD 对齐,引擎速率 16) - for rate in (1, 3, 16): - src = bytes(random.randrange(1, 256) for _ in range(640)) - e = ReadEngine(bytearray(PRAM_SIZE), PRAM_BASE, 640, rate); e.pump() - _, p1 = _count_polls(e, 640) - print(f" 640B@rate{rate}: 新算法主站外圈轮询 {p1} 次") - - -def _count_polls(eng, ln): - done = 0 - polls = 0 - while done < ln: - while eng.avail_cnt() == 0: - polls += 1 - eng.pump() - batch = min(eng.avail_cnt(), WINDOW_DW) - lead0 = 0 if done == 0 else 0 - batch = min(batch, (ln - done + lead0 + 3) // 4) - for _ in range(batch): - eng.pop() - done += 4 - return None, polls - - -if __name__ == "__main__": - run()