变更点: 1. esp32_server.py - /api/motor/ui-state、/api/lab/ui-state:配置原子落盘 data/*.json(WebView2 清存储仍在) - _GuiApi.ecat_api/bridge_info:桌面壳经 GUI 进程裸 socket 直连 ecat_native,不经浏览器 HTTP 2. js/common/desktop-bridge.js(新) - 探测 pywebview.api(含 iframe top/parent),transport-ecat 优先走原生桥 3. connection.js + responsive.css + components.css - setQuietMode:离线且无传输时 html[data-dcu-calm=1] 掐常驻动画 - 进度条斜纹仅 .live 挂载,空闲不再按刷新率空转重绘 4. device-link.js - 先查 native/health,已健康跳过 motor/start,避免 loop_stop 重进 OP 假超时 - 周期强制 0.25ms + DC SYNC0 对齐;轮询自愈被配错的 DC/周期 5. motor/main.js + transport-ecat.js - 配置双写 localStorage + 磁盘 ui-state;pagehide/sendBeacon 刷盘;导入导出 - 探测代际 probeGen;ECAT 失败阈值 12;跟踪 bootActive/slaveAl/slaveOp 6. lab/spi_test.html、ota/boot_ecat.js、can_monitor 等跟随桥/静默态 7. .gitignore - 排除 scripts/_* 与 anal/collect/measure/trace 等调试脚本、lab 产物、data/ 运行态
66 lines
2.2 KiB
JavaScript
66 lines
2.2 KiB
JavaScript
/**
|
||
* desktop-bridge.js — 桌面壳(pywebview)原生桥探测与调用
|
||
*
|
||
* 形态约定:
|
||
* esp32_app.vbs → --window → WebView2:JS 可经 window.pywebview.api 调到
|
||
* GUI 进程 Python,由它直连 ecat_native(不经浏览器 fetch、不经页面服务子进程代理)。
|
||
* esp32_web.vbs / 浏览器标签:无 pywebview,本模块返回 null,调用方回落 HTTP。
|
||
*
|
||
* 电机页在 tabs.html 的 iframe 内:pywebview 只注入顶层窗口,同源 iframe
|
||
* 必须向上找 window.parent / window.top 上的 pywebview.api。
|
||
*/
|
||
"use strict";
|
||
|
||
function collectApi() {
|
||
const roots = [];
|
||
try { roots.push(window); } catch (_) {}
|
||
try {
|
||
if (window.parent && window.parent !== window) roots.push(window.parent);
|
||
} catch (_) {}
|
||
try {
|
||
if (window.top && window.top !== window) roots.push(window.top);
|
||
} catch (_) {}
|
||
for (const w of roots) {
|
||
try {
|
||
const api = w.pywebview && w.pywebview.api;
|
||
if (api && typeof api.ecat_api === "function") return api;
|
||
} catch (_) { /* 跨源 parent 会抛 */ }
|
||
}
|
||
return null;
|
||
}
|
||
|
||
let _api = null;
|
||
|
||
/** @description 取桌面桥 API;没有则 null(不缓存失败,pywebview 可能稍后注入)。 */
|
||
export function getDesktopEcatApi() {
|
||
if (_api && typeof _api.ecat_api === "function") return _api;
|
||
_api = collectApi();
|
||
return _api;
|
||
}
|
||
|
||
/** @description 是否跑在 pywebview 桌面壳里(有壳但桥方法未就绪时也可能 true)。 */
|
||
export function isDesktopShell() {
|
||
try {
|
||
return !!(window.pywebview ||
|
||
(window.parent && window.parent !== window && window.parent.pywebview) ||
|
||
(window.top && window.top !== window && window.top.pywebview));
|
||
} catch (_) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 桌面桥 POST。path 接受 "motor/send" / "/api/motor/send" / "/ecat/api/motor/send"。
|
||
* @returns {Promise<object>} 与 native 同构的 JSON
|
||
*/
|
||
export async function desktopEcatPost(path, body, timeoutMs) {
|
||
const api = getDesktopEcatApi();
|
||
if (!api) throw new Error("desktop bridge unavailable");
|
||
return api.ecat_api(path, body == null ? {} : body, timeoutMs || 4000);
|
||
}
|
||
|
||
/** @description 状态栏用:当前 ECAT 控制通道形态。 */
|
||
export function desktopBridgeLabel() {
|
||
return getDesktopEcatApi() ? "APP 原生桥" : "HTTP 代理";
|
||
}
|