Files
DCU_DesktopController/pages/index.html
T
cbb beeae8a382 chore(hygiene): P3 报告归档 docs/reports + scripts 收敛 + lab 清理 + CSS/README 约定
正式 scripts 仅保留 README 自测清单;根目录报告迁入 docs/reports;
lab 采样 dump 出库;CSS 注明 btn/toast 所有权并 bump ?v=。
2026-09-21 02:26:42 +08:00

585 lines
35 KiB
HTML

<!--
index.html - ESP32 OTA 涓�户鎺у埗鍙颁富椤甸潰锛圖CU Mission Control 路 缇庡寲鐗堬級
鏀�寔 WiFi OTA锛圗SP32 鏈�満 + GD32 涓�户锛夊拰 BLE OTA 鍙岄€氶亾鍥轰欢鍗囩骇銆?
閰嶅�鑴氭湰锛歛pp.js (WiFi OTA), ble_ota.js (BLE OTA)
瀛愮郴缁熼厤鑹诧細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 涓�户鎺у埗鍙?/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=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 class="header-bar">
<div class="header-brand">
<span class="brand-logo" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="6" y="6" width="12" height="12" rx="2.5"/>
<rect x="9.5" y="9.5" width="5" height="5" rx="1" fill="currentColor" stroke="none" opacity=".85"/>
<path d="M9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3"/>
</svg>
</span>
<div class="brand-text">
<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" 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">璁惧�ID: <b class="pill-val" id="deviceId">-</b></span>
</div>
</header>
<!-- ====== MAIN LAYOUT ====== -->
<div class="main-layout">
<!-- ====== LEFT: workflow ====== -->
<div>
<!-- 1. Connection -->
<div class="card card--ble">
<div class="card-head"><span class="ico">馃敆</span> 璁惧�杩炴帴</div>
<div class="card-body">
<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">
<label class="form-label" for="espIp">ESP32 IP 鍦板潃</label>
<div class="input-group">
<input class="input" type="text" id="espIp" value="192.168.31.127" placeholder="杈撳叆 IP 鍦板潃">
<button id="testConnBtn" class="btn btn-outline" data-action="test-connection">娴嬭瘯</button>
</div>
</div>
</div>
<!-- BLE 妯″紡锛歭aunch 椋庢牸杩炴帴鎸夐挳锛坈heckbox 椹卞姩锛氬嬀閫?宸茶繛鎺ワ級 -->
<div id="bleModeCards" style="display:none;">
<div class="ble-launch-wrap">
<label class="ble-launch-btn" for="bleConnectToggle" data-ble-state="idle">
<input id="bleConnectToggle" type="checkbox">
<div class="back">
<div class="spot-shadow"></div>
<div class="mid">
<div class="spot-main"></div>
<div class="ble-dial">
<div class="spot-light"></div>
<div class="center">
<div class="icon">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#bleIconGlow)">
<path d="M7 7 L17 17 L12 22 V2 L17 7 L7 17"
stroke="#BBE7E9" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round" fill="none"
shape-rendering="geometricPrecision"/>
</g>
<defs>
<filter id="bleIconGlow" x="-50%" y="-50%" width="200%" height="200%"
filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="bg"/>
<feColorMatrix in="SourceAlpha" type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="a"/>
<feOffset/>
<feGaussianBlur stdDeviation="2"/>
<feComposite in2="a" operator="out"/>
<feColorMatrix type="matrix"
values="0 0 0 0 0.37 0 0 0 0 0.66 0 0 0 0 1 0 0 0 0.88 0"/>
<feBlend mode="normal" in2="bg" result="g"/>
<feBlend mode="normal" in="SourceGraphic" in2="g" result="shape"/>
</filter>
</defs>
</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>
<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:3">.</span>
<span style="--n:4">.</span>
<span style="--n:5">.</span>
</div>
<div class="text3">杩炴帴涓?..</div>
</div>
</div>
</div>
</div>
</div>
</label>
<div class="ble-launch-hint">鐐瑰嚮鎸夐挳杩炴帴 / 鏂�紑钃濈墮璁惧�</div>
</div>
</div>
<!-- EtherCAT 妯″紡锛欰PP 鈫?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="鐐瑰嚮閲嶆柊鎺㈡祴瀵圭�瑙掕壊锛圓PP / BOOT锛?>
<span class="jump-role-label">褰撳墠瑙掕壊 路 鐐瑰嚮鎺㈡祴</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>
</div>
<button class="btn btn-primary btn-block jump-btn-primary" id="bootEcatRebootBtn"
data-action="boot-ecat-reboot"
title="鎸夊�绔�韩浠借嚜鍔ㄩ€夋柟鍚戯細APP鈫?x2502 杩?BOOT锛汢OOT鈫?x2503 璺冲洖 APP">璁╁簲鐢ㄥ洖寮曞�绋嬪簭</button>
</div>
<!-- 杩炴帴鏂囨�鍙�湪椤舵爮 #pillConnTxt 鏄剧ず锛屾�澶勪笉鍐嶉噸澶?-->
</div>
</div>
<!-- ====== Shared Target + Firmware + Actions ====== -->
<div class="card card--gd32">
<div class="card-head"><span class="ico">馃幆</span> 鍗囩骇鐩�爣銆佸浐浠舵枃浠朵笌鎵ц�鎿嶄綔</div>
<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-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">
<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>
</div>
<input type="file" id="fileInputEsp32" accept=".bin" style="display:none;">
<div class="file-info" id="fileInfoEsp32">
<div class="file-info-row">
<span style="font-weight:600;">馃搫 <span id="fileNameEsp32">-</span></span>
<button class="btn btn-ghost btn-sm" data-action="clear-shared-firmware" data-arg="esp32">娓呴櫎</button>
</div>
<div class="file-info-grid">
<div class="file-meta-item"><span>绫诲瀷</span><b id="fileTypeEsp32">-</b></div>
<div class="file-meta-item"><span>澶у皬</span><b id="fileSizeEsp32">-</b></div>
</div>
<div class="file-meta-item" style="margin-top:6px;">
<span>SHA-256</span>
<b id="fileSha256Esp32" style="font-size:10px;word-break:break-all;font-family:monospace;">-</b>
</div>
</div>
</div>
<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>
</div>
<input type="file" id="fileInputGd32" accept=".hex,.bin" style="display:none;">
<div class="file-info" id="fileInfoGd32">
<div class="file-info-row">
<span style="font-weight:600;">馃搫 <span id="fileNameGd32">-</span></span>
<button class="btn btn-ghost btn-sm" data-action="clear-shared-firmware" data-arg="gd32">娓呴櫎</button>
</div>
<div class="file-info-grid">
<div class="file-meta-item"><span>绫诲瀷</span><b id="fileTypeGd32">-</b></div>
<div class="file-meta-item"><span>澶у皬</span><b id="fileSizeGd32">-</b></div>
</div>
<div class="file-meta-item" style="margin-top:6px;">
<span>SHA-256</span>
<b id="fileSha256Gd32" style="font-size:10px;word-break:break-all;font-family:monospace;">-</b>
</div>
</div>
</div>
<div id="bleUploadPane" style="display:none;">
<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>
</div>
<input id="firmwareFile" type="file" accept=".bin,.hex,application/octet-stream" style="display:none;">
<div class="file-info" id="fileInfo">
<div class="file-info-row">
<span style="font-weight:600;">馃搫 <span id="fileName">-</span></span>
<button id="clearFileBtn" class="btn btn-outline btn-sm" data-action="ble-clear-file">娓呴櫎</button>
</div>
<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>
<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>
<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
</button>
</div>
<div id="wifiGd32ActionPane" style="display:none;">
<button class="btn btn-success btn-block" id="uploadGd32Btn" data-action="start-upload" data-arg="gd32" disabled>
GD32 OTA
</button>
</div>
<div class="row-2" id="bleActionPane" style="display:none;">
<button id="autoFlashBtn" class="btn btn-primary" disabled>涓€閿�埛鍐?/button>
<button id="abortBtn" class="btn btn-danger" disabled>涓��浠诲姟</button>
</div>
<!-- EtherCAT 鐩寸亴闈㈡澘锛氬彧鍦ㄣ€岃�澶囪繛鎺?= EtherCAT銆嶆椂鏄剧ず锛坅pplySharedModeUi 鎺у埗锛夈€?
鍥轰欢澶嶇敤鍚屼竴涓?#fileInputGd32 涓庡悓涓€鏉?processFile 杞�崲/鏍¢獙锛?
鎵€浠ヨ繖閲屼笉鍑虹幇绗�簩浠?HEX 瑙f瀽鍣�紱"鑳戒笉鑳界亴"鍙�敱 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锛圵iFi/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">
<div class="progress-track"><div class="progress-fill" id="progressFill"></div></div>
<div class="progress-text">
<span id="progressTxt">绛夊緟鎿嶄綔...</span>
<span id="progressPct">0%</span>
</div>
</div>
<div style="height:14px"></div>
<!-- ESP32 OTA stages -->
<div class="stage-flow" id="esp32Stages" style="display:none;">
<div class="stage-node" id="es1"><div class="stage-dot">1</div><div class="stage-label">鍥轰欢涓婁紶</div><div class="stage-detail" id="esd1"></div></div>
<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="es6"><div class="stage-dot">6</div><div class="stage-label">鍚�姩瀹屾垚</div><div class="stage-detail" id="esd6"></div></div>
</div>
<!-- BLE OTA stages -->
<div class="stage-flow" id="bleStages" style="display:none;">
<div class="stage-node" id="bs1"><div class="stage-dot">1</div><div class="stage-label">BLE 杩炴帴</div><div class="stage-detail" id="bsd1"></div></div>
<div class="stage-node" id="bs2"><div class="stage-dot">2</div><div class="stage-label">GATT 鍑嗗�</div><div class="stage-detail" id="bsd2"></div></div>
<div class="stage-node" id="bs3"><div class="stage-dot">3</div><div class="stage-label">鏂囦欢鍑嗗�</div><div class="stage-detail" id="bsd3"></div></div>
<div class="stage-node" id="bs4"><div class="stage-dot">4</div><div class="stage-label">鏁版嵁鍐欏叆</div><div class="stage-detail" id="bsd4"></div></div>
<div class="stage-node" id="bs5"><div class="stage-dot">5</div><div class="stage-label" id="bsl5">鏍¢獙姣斿�</div><div class="stage-detail" id="bsd5"></div></div>
<div class="stage-node" id="bs6"><div class="stage-dot">6</div><div class="stage-label" id="bsl6">瀹屾垚瑙﹀彂</div><div class="stage-detail" id="bsd6"></div></div>
</div>
<!-- GD32 OTA stages -->
<div class="stage-flow" id="gd32Stages">
<div class="stage-node" id="s1"><div class="stage-dot">1</div><div class="stage-label">璁よ瘉鎻℃墜</div><div class="stage-detail" id="sd1"></div></div>
<div class="stage-node" id="s2"><div class="stage-dot">2</div><div class="stage-label">浼犺緭鍗忓晢</div><div class="stage-detail" id="sd2"></div></div>
<div class="stage-node" id="s3"><div class="stage-dot">3</div><div class="stage-label">鏁版嵁杞�彂</div><div class="stage-detail" id="sd3"></div></div>
<div class="stage-node" id="s4"><div class="stage-dot">4</div><div class="stage-label">鏀跺熬楠岃瘉</div><div class="stage-detail" id="sd4"></div></div>
<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>
</div>
<div class="stage-verify-info" id="stageVerifyInfo" style="display:none;">
<div class="svi-row"><span class="svi-key">鍓嶇� SHA-256</span><span class="svi-val" id="sviFrontSha">-</span></div>
<div class="svi-row"><span class="svi-key">璁惧� SHA-256</span><span class="svi-val" id="sviDeviceSha">-</span></div>
<div class="svi-row"><span class="svi-key">鏍¢獙缁撴灉</span><span class="svi-val" id="sviResult">-</span></div>
</div>
<div style="margin-top:12px;">
<button class="btn btn-outline btn-block" data-action="restart-esp32">閲嶅惎 ESP32</button>
</div>
</div>
</div>
</div>
<!-- ====== RIGHT: status + tools ====== -->
<div>
<!-- Primary status -->
<div class="card card--status">
<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>
<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>
<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>
</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="boot-module-list" id="bootModuleList" aria-label="鍚�姩妯″潡鍒楄〃"></div>
</div>
</div>
</div>
<!-- Secondary metrics -->
<div class="card card--mem">
<div class="card-head"><span class="ico">馃搳</span> 杩愯�鎸囨爣</div>
<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 娈?锛?s 绐楀彛">--</div></div>
<div class="metric"><div class="k">杈撳叆 VCC</div><div class="v" id="mVcc" title="PA6 鍒嗗帇鍙嶆帹锛歏CC鈥?7k鈥擜DC鈥?.7k鈥擥ND">--</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>
</div>
</div>
</div>
<div class="card card--mem" style="display:none">
<div class="card-head">
<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">鍒锋柊鏃堕棿</span><b id="sStatusUpdated">--</b></div>
</div>
</div>
</div>
<!-- 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 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">
<div class="log-toolbar">
<div class="log-filters">
<button class="log-filter active" onclick="setLogFilter('all', this)">鍏ㄩ儴</button>
<button class="log-filter" onclick="setLogFilter('E', this)">閿欒�</button>
<button class="log-filter" onclick="setLogFilter('W', this)">璀﹀憡</button>
<button class="log-filter" onclick="setLogFilter('I', this)">淇℃伅</button>
</div>
<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>
<div class="log-foot">
<span>鍏?<span id="logCount">0</span> 鏉?/span>
<button class="btn btn-ghost btn-sm" onclick="clearLogs()">娓呴櫎鏃ュ織</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 鍙充晶婊戝嚭锛氬爢涓庝换鍔℃爤锛堥粯璁ら湶鍑轰竴鏉★紝鎮�仠灞曞紑锛?-->
<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>
</div>
<div class="sys-drawer-panel card card--mem">
<div class="card-head sys-drawer-head">
<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">
<div class="status-section-title">CPU 鍗犵敤</div>
<div class="heap-group cpu-group">
<div class="heap-group-title">ESP32 Runtime <span style="font-weight:400;color:var(--text-dim);font-size:10px;">(FreeRTOS)</span></div>
<div class="heap-bar-row">
<span class="heap-bar-text" id="cpuUsageText">绛夊緟閲囨牱...</span>
<span class="heap-bar-track"><span class="heap-bar-fill cpu-fill" id="cpuUsageFill"></span></span>
<span class="heap-bar-pct" id="cpuUsagePct">--</span>
</div>
<div class="heap-detail-row">
<span>绌洪棽: <b id="cpuIdlePct">--</b></span>
<span>绐楀彛: <b id="cpuSampleMs">--</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>
<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-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="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>
<span class="mem-seg mem-seg-used" id="memSegUsed"></span>
<span class="mem-seg mem-seg-free" id="memSegFree"></span>
</span>
<span class="heap-bar-pct" id="memSramPct">--</span>
</div>
<div class="heap-legend">
<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>
</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>
</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-bar-row">
<span class="heap-bar-text" id="memInternalUsage">--</span>
<span class="heap-bar-track"><span class="heap-bar-fill" id="memInternalFill"></span></span>
<span class="heap-bar-pct" id="memInternalPct">--</span>
</div>
<div class="heap-detail-row">
<span>绌洪棽: <b id="mInternalHeap">--</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>鍒嗛厤鍧楁暟: <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>
<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>
</div>
<div class="status-subfold-body">
<pre class="status-json" id="statusJson">--</pre>
</div>
</div>
</div>
</div>
</aside>
<!-- Toast -->
<div class="toast" id="toast">
<span class="toast-ico" id="toastIco">鉁?/span>
<span id="toastMsg">鎿嶄綔鎴愬姛</span>
</div>
<!-- app 绐楀彛绉熺害锛氭湰椤典細鑷�姩鎷夎捣 EtherCAT PDO 寰�幆锛圙D32 璐熻浇锛夛紝
蹇呴』鎸佹湁绉熺害锛屽惁鍒欐湇鍔$�鍦ㄧ�绾﹀綊闆跺�闄愭湡鍚庝細鎶婂惊鐜�張鍋滄帀銆?
涓?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 璁剧疆锛屾�澶勮烦杩囦笉瑕嗙洊銆?-->
<script type="module">
setInterval(function() {
var btn = document.querySelector('.ble-launch-btn');
if (!btn || !btn.offsetParent) return;
var st = btn.getAttribute('data-ble-state');
if (st === 'connecting') return;
var toggle = document.getElementById('bleConnectToggle');
if (!toggle) return;
var expected = toggle.checked ? 'connected' : 'idle';
if (st !== expected) btn.setAttribute('data-ble-state', expected);
}, 500);
</script>
</body>
</html>