feat(server): app 窗口租约, 全部窗口关闭自动停 EtherCAT 循环

PDO 循环起来后 native 侧一直跑, 其 --parent-pid 自清理只覆盖"本服务拉起
的实例死掉"; 浏览器模式关窗后本服务还在、窗口模式复用现成 ecat_native
(parent 是别人)这两种常见离开方式都够不着 —— 从站卡在 OP、自旋节拍白占
一个核。现由每个 app 页面与页面服务器的 WebSocket 租约兜底:

- esp32_server.py: 新增 GET /ws/app-lease(手写 RFC6455 握手 + 帧泵)。
  连接计数归零后隔 3s GRACE 调后端 /api/op/stop 停循环退回 INIT; 断开
  感知靠 TCP FIN / Close 帧回显而非心跳(后台标签页节流不误停); 代理
  透传时 _note_ecat_loop 记账"循环是否本服务跑起来的", 只停自己确认过
  的; /api/shutdown 与 serve_forever 退出路径同步补停(定时器随进程死亡)。
- js/common/app-lease.js: 租约客户端, 3~30s 退避重连, file:// 静默跳过。
- 接入 tabs.html(外壳持有, 内部切标签不断连)与 pages/motor.html(单独
  打开时页面自持)。
- 测试: scripts/test_app_lease.py(裸 TCP 全路径, 假 _ecat_post) +
  scripts/test_app_lease_e2e.mjs(真 Edge headless + 假后端 8091, 不碰
  真总线)。README 补设计动机与要点。
This commit is contained in:
cbb
2026-09-05 19:58:25 +08:00
parent c189a8563e
commit 99fb9bcb4a
7 changed files with 636 additions and 0 deletions
+3
View File
@@ -127,5 +127,8 @@
go(init, false);
})();
</script>
<!-- app 窗口租约: 本外壳窗口活着期间保持与本地服务的连接,
全部窗口关闭后服务端自动停 EtherCAT PDO 循环(见 js/common/app-lease.js) -->
<script src="js/common/app-lease.js"></script>
</body>
</html>