chore: 解决 stash pop 与 device-link 合并冲突并收回本地 WIP

保留 ConnectionHub(deviceLink 导入/ECAT 自启/pmHold 幂等)与主线
WIP(断线清指标、AUTO_POLL=false、boot_ecat 等)。
This commit is contained in:
cbb
2026-09-16 12:31:39 +08:00
parent 0c772f5924
commit e98950cde2
12 changed files with 1026 additions and 248 deletions
+81 -12
View File
@@ -13,7 +13,7 @@
<!-- 空 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/layout.css?v=targettabs20260914g">
<link rel="stylesheet" href="../css/components.css?v=a2c3d4e5">
<link rel="stylesheet" href="../css/can.css?v=c06f5688">
<link rel="stylesheet" href="../css/responsive.css?v=46449483">
@@ -59,9 +59,19 @@
<div class="card card--ble">
<div class="card-head"><span class="ico">🔗</span> 设备连接</div>
<div class="card-body">
<div style="display:flex;gap:6px;margin-bottom:14px;">
<button id="modeWifiBtn" class="btn btn-primary" style="flex:1;font-size:12px;padding:7px 10px;" data-action="set-console-mode" data-arg="wifi">WiFi OTA</button>
<button id="modeBleBtn" class="btn btn-outline" style="flex:1;font-size:12px;padding:7px 10px;" data-action="set-console-mode" data-arg="ble">BLE 蓝牙</button>
<div class="target-tab-row" id="modeTabRow">
<button id="modeWifiBtn" class="btn target-tab-btn btn-primary" data-action="set-console-mode" data-arg="wifi" type="button">
<span class="target-tab-ico" aria-hidden="true">🌐</span>
<span class="target-tab-txt">WiFi OTA</span>
</button>
<button id="modeBleBtn" class="btn target-tab-btn btn-outline" data-action="set-console-mode" data-arg="ble" type="button">
<span class="target-tab-ico" aria-hidden="true">📡</span>
<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-txt">EtherCAT</span>
</button>
</div>
<div id="wifiModeCards">
<div class="form-row">
@@ -135,6 +145,35 @@
<div class="ble-launch-hint">点击按钮连接 / 断开蓝牙设备</div>
</div>
</div>
<!-- EtherCAT 模式:这条链路没有 IP / 配对概念,"连接对象"就是本机 EtherCAT 主站。
总线的打开/关闭由主站侧(ecat_native)负责,本页只在下面几个读数里反映现状。 -->
<div id="ecatModeCards" style="display:none;">
<div class="ecat-mode-note">
主站经引导程序邮箱刷写 GD32 APP,<b>ESP32 离线不影响</b>
</div>
<div class="form-label">总线对面</div>
<div class="conn-indicator" id="bootEcatConn" style="margin-bottom:10px;">
<span class="light" id="bootEcatConnLight"></span>
<span id="bootEcatConnTxt">未探测</span>
</div>
<button class="btn btn-outline btn-block" id="bootEcatProbeBtn" data-action="boot-ecat-probe">探测对面是谁</button>
<button class="btn btn-outline btn-block" id="bootEcatRebootBtn" data-action="boot-ecat-reboot"
style="margin-top:8px;" title="写 APP 的 CoE 0x2502:00=1,让它复位进引导程序">让应用回引导程序</button>
<div class="file-info-grid" style="margin-top:10px;">
<div class="file-meta-item"><span>对面是谁</span><b id="bootEcatWho">--</b></div>
<div class="file-meta-item"><span>从站是否放行</span><b id="bootEcatArm">--</b></div>
</div>
<div class="file-info-grid" style="margin-top:8px;">
<div class="file-meta-item"><span>主站已缓存</span><b id="bootEcatSrv">0 B</b></div>
<div class="file-meta-item"><span>从站已写 flash</span><b id="bootEcatRecv">--</b></div>
</div>
<div class="file-meta-item" style="margin-top:8px;">
<span>设备名(0x1008)</span><b id="bootEcatName" style="word-break:break-all;">—</b>
</div>
<div class="file-meta-item">
<span>软件版本(0x100A)</span><b id="bootEcatSw">—</b>
</div>
</div>
<!-- 连接文案只在顶栏 #pillConnTxt 显示,此处不再重复 -->
</div>
</div>
@@ -143,9 +182,15 @@
<div class="card card--gd32">
<div class="card-head"><span class="ico">🎯</span> 升级目标、固件文件与执行操作</div>
<div class="card-body">
<div style="display:flex;gap:6px;margin-bottom:14px;">
<button id="targetGd32Tab" class="btn btn-outline" style="flex:1;font-size:12px;padding:7px 10px;" data-action="select-flash-target" data-arg="gd32">GD32</button>
<button id="targetEsp32Tab" class="btn btn-primary" style="flex:1;font-size:12px;padding:7px 10px;" data-action="select-flash-target" data-arg="esp32">ESP32</button>
<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-txt">GD32</span>
</button>
<button id="targetEsp32Tab" class="btn target-tab-btn btn-primary" data-action="select-flash-target" data-arg="esp32" type="button">
<span class="target-tab-ico" aria-hidden="true">📶</span>
<span class="target-tab-txt">ESP32</span>
</button>
</div>
<div style="border-top:1px solid var(--border);margin:0 -16px 14px;padding-top:14px;padding-left:16px;padding-right:16px;">
<div id="wifiEsp32UploadPane">
@@ -233,18 +278,28 @@
<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 头注)。 -->
<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"
data-action="boot-ecat-abort" style="margin-top:8px;">放弃本轮</button>
</div>
</div>
</div>
</div>
<!-- 4. Flash Progress + Stage Flow -->
<div class="card card--esp32">
<!-- 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-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>
<!-- Unified progress bar -->
<div class="progress-wrap show" id="progressWrap">
@@ -282,6 +337,16 @@
<div class="stage-node" id="s5"><div class="stage-dot">5</div><div class="stage-label">APP 启动</div><div class="stage-detail" id="sd5"></div></div>
<div class="stage-node" id="s6"><div class="stage-dot">6</div><div class="stage-label">OTA完成</div><div class="stage-detail" id="sd6"></div></div>
</div>
<!-- EtherCAT 刷写 stages -->
<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="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>
<div class="stage-node" id="ec7"><div class="stage-dot">7</div><div class="stage-label">刷写完成</div><div class="stage-detail" id="ecd7"></div></div>
</div>
<div class="stage-status-bar" id="stageStatusBar">
<span class="ssb-dot" id="stageStatusDot"></span>
<span class="ssb-text" id="stageStatusText">待机</span>
@@ -344,7 +409,7 @@
<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="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>
@@ -500,8 +565,12 @@
<span id="toastMsg">操作成功</span>
</div>
<script type="module" src="../js/app.js?v=20260905a"></script>
<script type="module" src="../js/ota/console-mode.js?v=20260901d"></script>
<!-- app 窗口租约:本页会自动拉起 EtherCAT PDO 循环(GD32 负载),
必须持有租约,否则服务端在租约归零宽限期后会把循环又停掉。
与 tabs.html / motor.html 并存时连接数>1,互不影响。 -->
<script src="../js/common/app-lease.js"></script>
<script type="module" src="../js/app.js?v=20260915a"></script>
<script type="module" src="../js/ota/console-mode.js?v=20260914b"></script>
<script type="module" src="../js/ble_ota.js?v=20260901d"></script>
<!-- 内联兜底:同步 BLE 按钮 data-ble-state 与 checkbox.checked
不依赖 ES 模块(子模块可能被浏览器缓存为旧版,不含 data-ble-state 逻辑)。