feat: 添加局域网设备发现功能和链路胶囊优化

- 实现局域网设备发现面板,支持探测ESP32中继板并自动切换端点
- 添加POST /api/lan/probe接口用于设备探测,返回三态判决结果
- 实现快扫和全段扫描两种模式,支持种子地址优先探测
- 添加端点唯一写手机制,防止多处同时修改设备IP
- 优化链路胶囊UI显示WiFi/EtherCAT/BLE连接状态和设备名称
- 修复EtherCAT链路OTA刷写期间PDO循环暂停和AL状态处理问题
- 调整PDO周期设置范围为0.125-4ms,支持亚毫秒显示为微秒
- 添加ESP32 WiFi使能开关控件样式和数据来源角标显示
This commit is contained in:
cbb
2026-10-13 00:48:34 +08:00
parent 514070b36d
commit eac3717f9a
41 changed files with 4229 additions and 934 deletions
+30 -5
View File
@@ -14,7 +14,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 调试控制台 — DCU</title>
<link rel="icon" href="data:,">
<link rel="stylesheet" href="css/base.css?v=857f83a5">
<link rel="stylesheet" href="css/base.css?v=a7b6c32b">
<!-- 外壳也要 .btn/.pill/.input 这些共享 chrome:只在 base.css 里写弹窗,
控件一律用 layout.css 的既有类,避免顶栏那颗按钮退回浏览器默认样式 -->
<link rel="stylesheet" href="css/layout.css?v=20260925n">
<style>
html, body { height: 100%; margin: 0; }
body {
@@ -34,6 +37,8 @@
border-right: 1px solid var(--border);
font-weight: 600; font-size: .86rem; color: var(--text-dim);
letter-spacing: .02em;
/* 右侧加了设备 pill + 发现按钮后空间变紧:不换行也不被挤扁 */
flex-shrink: 0; white-space: nowrap;
}
.tabbar .brand .dot { width: 9px; height: 9px; border-radius: 50%;
background: var(--esp32); box-shadow: 0 0 8px var(--esp32); }
@@ -42,8 +47,9 @@
color: var(--text-dim); font-size: .84rem; font-family: inherit;
padding: 0 16px; display: flex; align-items: center; gap: 7px;
border-bottom: 2px solid transparent; transition: color .12s, background .12s;
white-space: nowrap;
min-width: 0; /* 窄窗口时由页签让位(标签省略号),设备入口不被挤出 */
}
.tab .lbl { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tab:hover { color: var(--text); background: var(--bg-hover); }
.tab.active { color: var(--text); border-bottom-color: var(--esp32); background: var(--bg-card); }
.tab .ico { font-size: .95rem; opacity: .9; }
@@ -51,7 +57,14 @@
.tab.active .accent { opacity: 1; }
.tabbar .spacer { flex: 1 1 auto; }
.tabbar .hint { display: flex; align-items: center; color: var(--text-label);
font-size: .72rem; padding: 0 8px; }
font-size: .72rem; padding: 0 8px; white-space: nowrap; flex-shrink: 0; }
.tabbar .lan-slot { flex-shrink: 0; }
/* 窄窗口(APP 拖窄 / 1024 级屏幕):先让最不值钱的刷新提示让位、页签收紧,
设备入口不能被挤出可视区——body 是 overflow:hidden,挤出去就点不到了。 */
@media (max-width: 1280px) {
.tabbar .hint { display: none; }
.tab { padding: 0 11px; }
}
/* 多 iframe 宿主:占满剩余高度;子帧绝对定位铺满。
隐藏用 visibility 而非 display:none —— 少触发引擎对不可见文档的定时器/绘制压制,
@@ -73,6 +86,14 @@
<div class="brand"><span class="dot"></span>ESP32 调试控制台</div>
<!-- 标签由 TABS 数组渲染 -->
<div class="spacer"></div>
<!-- 设备端点:用各页同款 .pill 组件(online/offline/processing 状态类),
文本由 js/common/lan-picker.js 从 Owner 快照刷,本文件不自己判活 -->
<div class="lan-slot">
<span class="pill offline" id="lanPill" title="当前设备端点">
<span class="indicator"></span><span class="pill-val" id="lanPillTxt">未连接</span>
</span>
<button class="btn btn-outline btn-sm" id="lanScanBtn" title="扫描局域网设备 / 选择连接哪一台">🔍 发现</button>
</div>
<div class="hint">⟳ 刷新当前页 · F5</div>
</nav>
<!-- 外壳标记:注入脚本用 #viewHost 判定「标签外壳」;各功能页是其子 iframe.page-frame -->
@@ -163,7 +184,8 @@
b.className = 'tab'; b.type = 'button';
b.setAttribute('data-page', t.page);
b.innerHTML = '<span class="accent" style="background:'+t.color+'"></span>'
+ '<span class="ico">'+t.ico+'</span>' + t.label;
+ '<span class="ico">'+t.ico+'</span>'
+ '<span class="lbl">' + t.label + '</span>';
b.onclick = function(){ go(t.page); };
bar.insertBefore(b, spacer);
});
@@ -181,6 +203,9 @@
<script src="js/common/app-lease.js"></script>
<!-- DeviceLink Owner:全链路健康轮询只在本外壳跑一次,各 iframe 订阅 -->
<script>window.__dcuDeviceLinkOwner = true;</script>
<script type="module" src="js/common/device-link.js?v=20260922a"></script>
<script type="module" src="js/common/device-link.js?v=20261008b"></script>
<!-- 设备发现面板(外壳是宿主:顶栏 pill + 🔍 + 覆盖层都在这里) -->
<div id="lanMount"></div>
<script type="module" src="js/common/lan-picker.js?v=20261008b"></script>
</body>
</html>