V2
This commit is contained in:
+127
-88
@@ -3,117 +3,156 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>SUB Control</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<title>Sub Control</title>
|
||||
<link rel="stylesheet" href="styles.css?v=20260827-bridge-v5">
|
||||
</head>
|
||||
<body>
|
||||
<header class="window-bar">
|
||||
<div class="window-drag pywebview-drag-region"><span class="mini-mark">S</span><strong>SUB Control</strong><i id="model-file-name">Built-in vehicle</i></div>
|
||||
<div class="window-actions">
|
||||
<button id="window-min" aria-label="Minimize">—</button>
|
||||
<button id="window-max" aria-label="Maximize">□</button>
|
||||
<button id="window-close" class="window-close" aria-label="Close">×</button>
|
||||
</div>
|
||||
</header>
|
||||
<main class="app-shell">
|
||||
<aside class="vehicle-rail">
|
||||
<header class="brand"><span class="brand-mark">S</span><span>SUB CONTROL</span><small>03</small></header>
|
||||
<div class="eyebrow">LIVE VEHICLE</div>
|
||||
<div class="rov-stage" aria-label="Top view of three-thruster ROV">
|
||||
<div class="water-ring r1"></div><div class="water-ring r2"></div>
|
||||
<svg id="default-model" viewBox="0 0 260 310" role="img">
|
||||
<defs>
|
||||
<linearGradient id="body" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#566168"/><stop offset="1" stop-color="#22292d"/>
|
||||
</linearGradient>
|
||||
<filter id="shadow"><feDropShadow dx="0" dy="18" stdDeviation="13" flood-opacity=".45"/></filter>
|
||||
</defs>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="73" y="34" width="114" height="226" rx="55" fill="url(#body)" stroke="#707b81" stroke-width="2"/>
|
||||
<rect x="93" y="60" width="74" height="168" rx="35" fill="#30383c" stroke="#49545a"/>
|
||||
<circle cx="130" cy="145" r="28" fill="#7d898f" stroke="#a3adb1" stroke-width="3"/>
|
||||
<circle cx="130" cy="145" r="12" fill="#d9e0df"/>
|
||||
<path d="M78 98 48 80M182 98l30-18" stroke="#7e8a90" stroke-width="9" stroke-linecap="round"/>
|
||||
<g fill="#191f22" stroke="#7b878c" stroke-width="3">
|
||||
<ellipse cx="38" cy="74" rx="27" ry="34"/><ellipse cx="222" cy="74" rx="27" ry="34"/>
|
||||
</g>
|
||||
<g stroke="#59656a" stroke-width="2">
|
||||
<path d="M22 54l32 40M54 54 22 94M206 54l32 40M238 54l-32 40"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<canvas id="model-canvas" aria-label="Interactive STL vehicle model"></canvas>
|
||||
<div class="model-tools">
|
||||
<button id="load-model">Load STL</button>
|
||||
<input id="model-input" type="file" accept=".stl,model/stl,application/sla" hidden>
|
||||
|
||||
<!-- ── DECK (Connection, 3D Model, Battery, Deck Controls, Status) ── -->
|
||||
<aside class="sidebar-deck" aria-label="Vehicle Deck and Status">
|
||||
<div class="sidebar-drag-surface pywebview-drag-region" aria-hidden="true"></div>
|
||||
<!-- Connection Controls -->
|
||||
<div class="deck-connection">
|
||||
<div class="port-row">
|
||||
<select id="port" aria-label="Serial port">
|
||||
<option value="demo" selected>Demo Mode (no hardware)</option>
|
||||
</select>
|
||||
<button id="refresh" class="icon-button" title="Refresh ports" aria-label="Refresh ports">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 7v5h-5M4 17v-5h5M18.5 9A7 7 0 0 0 6.8 6.8L4 10m16 4-2.8 3.2A7 7 0 0 1 5.5 15"/></svg>
|
||||
</button>
|
||||
<button class="icon-button" data-open="link-modal" title="Link settings" aria-label="Link settings">
|
||||
<svg class="dots-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="5" cy="12" r="1.25"/><circle cx="12" cy="12" r="1.25"/><circle cx="19" cy="12" r="1.25"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<button id="connect" class="button connect-button" aria-label="Toggle connection">
|
||||
<span class="btn-spinner" aria-hidden="true"></span>
|
||||
<span class="btn-label">Connect</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Telemetry / Status Zone -->
|
||||
<section class="telemetry-zone" aria-label="Vehicle Telemetry and Status">
|
||||
<div class="deck-card model-card">
|
||||
<div class="rov-stage" aria-label="Top view of three-thruster ROV">
|
||||
<canvas id="model-canvas" aria-label="Interactive STL vehicle model"></canvas>
|
||||
<div id="model-status" class="model-status">Initializing…</div>
|
||||
<div class="model-tools">
|
||||
<button id="load-model">Load STL</button>
|
||||
<input id="model-input" type="file" accept=".stl,.STL" hidden>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vehicle-name"><small>VEHICLE</small><strong id="vehicle">No heartbeat</strong><span>SimpleROV-3 • differential drive</span></div>
|
||||
<div class="rail-bottom">
|
||||
<button class="battery-card ghost" data-open="battery-modal">
|
||||
<span><small>BATTERY</small><strong id="battery-title">—</strong></span><i id="battery-percent">Waiting</i>
|
||||
<b><em id="battery-fill"></em></b>
|
||||
</button>
|
||||
<div class="link-state"><span id="link-dot"></span><strong id="link">DISCONNECTED</strong></div>
|
||||
|
||||
<!-- Battery Status -->
|
||||
<button class="battery-card ghost" data-open="battery-modal">
|
||||
<span><small>Battery</small><strong id="battery-title">—</strong></span><i id="battery-percent">waiting</i>
|
||||
<b><em id="battery-fill"></em></b>
|
||||
</button>
|
||||
|
||||
<div class="deck-status-bar">
|
||||
<ol class="activity-history" aria-label="Last three events" aria-live="polite">
|
||||
<li id="activity-oldest" hidden></li>
|
||||
<li id="activity-previous" hidden></li>
|
||||
<li id="status-text">Not connected</li>
|
||||
</ol>
|
||||
<header class="activity-heading"><span>Recent activity</span><a id="logs" class="logs-link" title="Open session logs" aria-label="Open session logs">Logs</a></header>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</aside>
|
||||
|
||||
<section class="workspace">
|
||||
<header class="topbar">
|
||||
<div><div class="eyebrow">PILOT CONSOLE</div><h1>Drive</h1></div>
|
||||
<div class="connection">
|
||||
<select id="port" aria-label="Serial port"><option value="">Select COM port…</option></select>
|
||||
<button id="refresh" class="icon-button" title="Refresh ports">↻</button>
|
||||
<button class="icon-button" data-open="link-modal" title="Link settings">•••</button>
|
||||
<button id="connect" class="button connect-button">Connect</button>
|
||||
<!-- ── PRIMARY CONTROL ZONE ── -->
|
||||
<section class="controls-workspace" aria-label="Primary Vehicle Controls">
|
||||
<!-- Frameless Drag & Window Controls Header -->
|
||||
<div class="workspace-topbar">
|
||||
<div class="workspace-drag-area pywebview-drag-region"></div>
|
||||
<div class="window-actions">
|
||||
<button id="window-min" aria-label="Minimize" title="Minimize"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/></svg></button>
|
||||
<button id="window-max" aria-label="Maximize" title="Maximize"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="5" width="14" height="14" rx="1.5"/></svg></button>
|
||||
<button id="window-close" class="window-close" aria-label="Close" title="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg></button>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<section class="drive-grid">
|
||||
<section class="primary-control-zone" aria-label="Primary Control Zone">
|
||||
<!-- Reserved Engage / Disarm row -->
|
||||
<div class="engage-header-zone">
|
||||
<button id="engage" class="button subtle engage-button" aria-label="Engage" aria-describedby="control-status" title="Engage (F2)">
|
||||
<span class="engage-face">
|
||||
<span id="engage-label" class="engage-label">engage</span>
|
||||
<span class="engage-sheen-label" aria-hidden="true">engage</span>
|
||||
<span class="engage-progress-label" aria-hidden="true">engage</span>
|
||||
</span>
|
||||
</button>
|
||||
<p id="control-status" class="control-status" role="status" aria-live="polite">Connect to receive vehicle state.</p>
|
||||
</div>
|
||||
|
||||
<!-- Main Joysticks & Thrust Control Grid -->
|
||||
<div class="controls-grid">
|
||||
<!-- Steering / Throttle Joystick -->
|
||||
<article class="control-card steer-card">
|
||||
<header><div><small>STEER + THROTTLE</small><strong>Differential drive</strong></div><span class="key-hint">W A S D</span></header>
|
||||
<div id="joystick" class="joystick" touch-action="none">
|
||||
<div class="axis vertical-axis"></div><div class="axis horizontal-axis"></div>
|
||||
<span class="axis-label forward">FORWARD</span><span class="axis-label reverse">REVERSE</span>
|
||||
<span class="axis-label left">TURN LEFT</span><span class="axis-label right">TURN RIGHT</span>
|
||||
<div class="stick-shadow"></div><div id="stick" class="stick"><span></span></div>
|
||||
<header>
|
||||
<div><small>steer + throttle</small></div>
|
||||
<span class="key-hint">w a s d</span>
|
||||
</header>
|
||||
<div class="joystick-pad">
|
||||
<div id="joystick" class="joystick" touch-action="none">
|
||||
<canvas id="joystick-canvas" aria-label="Steering and throttle joystick; W A S D keys"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<footer><span><i class="dot surge"></i><b id="surge-readout">0</b>% surge</span><span><i class="dot yaw"></i><b id="yaw-readout">0</b>% turn</span></footer>
|
||||
<footer>
|
||||
<span class="steer-readout-left">surge <b id="surge-readout">—</b></span>
|
||||
<span class="steer-readout-right"><b id="yaw-readout">—</b> turn</span>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<!-- Vertical Thruster Slider -->
|
||||
<article class="control-card vertical-card">
|
||||
<header><div><small>VERTICAL</small><strong>Center thruster</strong></div><span class="key-hint">R / F</span></header>
|
||||
<div class="vertical-control">
|
||||
<span>RISE</span>
|
||||
<div id="heave" class="heave-track" touch-action="none"><div class="heave-center"></div><div id="heave-fill" class="heave-fill"></div><div id="heave-knob" class="heave-knob"></div></div>
|
||||
<span>DIVE</span>
|
||||
<header>
|
||||
<div><small>vertical</small></div>
|
||||
<span class="key-hint">r / f</span>
|
||||
</header>
|
||||
<div class="heave-pad">
|
||||
<div class="vertical-control">
|
||||
<span>rise</span>
|
||||
<div id="heave" class="heave-track" touch-action="none">
|
||||
<div class="heave-center"></div>
|
||||
<div id="heave-fill" class="heave-fill"></div>
|
||||
<div id="heave-knob" class="heave-knob"></div>
|
||||
</div>
|
||||
<span>dive</span>
|
||||
</div>
|
||||
</div>
|
||||
<footer><span><i class="dot depth"></i><b id="heave-readout">0</b>% vertical</span></footer>
|
||||
<footer>
|
||||
<span><b id="heave-readout">—</b> vertical</span>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
<div class="control-toolbar">
|
||||
<label class="output-limit">
|
||||
<span>Output limit <b id="gain-value">20%</b></span>
|
||||
<div class="range-pill">
|
||||
<button id="gain-down" type="button" aria-label="Decrease output limit">‹</button>
|
||||
<input id="gain" aria-label="Output limit" type="range" min="5" max="50" value="20">
|
||||
<button id="gain-up" type="button" aria-label="Increase output limit">›</button>
|
||||
</div>
|
||||
</label>
|
||||
<button class="button subtle config-btn" data-open="motors-modal">Motor setup</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="command-bar">
|
||||
<div class="commands">
|
||||
<button id="manual" class="button subtle">Manual</button>
|
||||
<button id="arm" class="button arm">Arm</button>
|
||||
<button id="disarm" class="button danger">Disarm</button>
|
||||
</div>
|
||||
<label class="output-limit"><span>Output limit <b id="gain-value">20%</b></span><input id="gain" type="range" min="5" max="50" value="20"></label>
|
||||
<button class="button subtle" data-open="motors-modal">Motor setup</button>
|
||||
<button id="logs" class="icon-button" title="Open session logs">⌁</button>
|
||||
</section>
|
||||
<div class="status-line"><span></span><p id="status">Ready. Select a USB port to begin.</p></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div id="toast" class="toast"></div>
|
||||
|
||||
<dialog id="arm-dialog" class="modal confirm-modal"><button class="modal-close" data-close>×</button><small>SAFETY CHECK</small><h2>Arm the vehicle?</h2><p>Remove propellers or securely restrain the ROV before enabling motor output.</p><div class="modal-actions"><button class="button subtle" data-close>Cancel</button><button id="confirm-arm" class="button arm">Confirm arm</button></div></dialog>
|
||||
<dialog id="link-modal" class="modal"><button class="modal-close" data-close>×</button><small>CONNECTION</small><h2>Link settings</h2><div class="form-grid two"><label>Baud rate<select id="baud"><option>9600</option><option>19200</option><option>38400</option><option selected>57600</option><option>115200</option></select></label><label>Control rate<select id="rate"><option>10</option><option selected>20</option></select></label></div><p class="note">20 Hz is recommended for smooth intermediate commands on this link.</p></dialog>
|
||||
<dialog id="battery-modal" class="modal"><button class="modal-close" data-close>×</button><small>POWER</small><h2>Battery settings</h2><form id="battery-form" class="form-grid three"><label>Cells (S)<input name="cells" type="number" min="1"></label><label>Capacity (mAh)<input name="capacity_mah" type="number" min="1"></label><label>C rating<input name="c_rating" type="number" min="1" step=".1"></label><label>Full V / cell<input name="full_v_per_cell" type="number" step=".01"></label><label>Empty V / cell<input name="empty_v_per_cell" type="number" step=".01"></label><label>Warning %<input name="warning_percent" type="number" min="0" max="100"></label><div class="modal-actions wide"><button type="button" class="button subtle" data-close>Cancel</button><button class="button connect-button">Save settings</button></div></form></dialog>
|
||||
<dialog id="motors-modal" class="modal motor-modal"><button class="modal-close" data-close>×</button><small>CONFIGURATION</small><h2>Motor outputs</h2><p class="note">Function and direction are read one parameter at a time to protect the low-bandwidth link.</p><div class="motor-head"><span>Output</span><span>Function</span><span>Direction</span><span></span></div><div id="motor-list" class="motor-list"></div></dialog>
|
||||
<!-- Modals -->
|
||||
<dialog id="link-modal" class="modal"><button class="modal-close" data-close>×</button><small>connection</small><h2>Link Settings</h2><div class="form-grid two"><label><span>Baud rate</span><select id="baud"><option>9600</option><option>19200</option><option>38400</option><option selected>57600</option><option>115200</option></select></label><label><span>Control rate</span><select id="rate"><option>10</option><option selected>20</option></select></label></div><div style="margin-top:14px;"><label style="display:flex;flex-direction:column;gap:6px;"><span style="font-size:11px;font-weight:500;color:var(--muted)">Custom Port / Endpoint</span><div style="display:flex;gap:8px;"><input id="custom-port-input" placeholder="e.g. COM3 or tcp:127.0.0.1:5760" style="flex:1;height:32px;padding:0 12px;font-size:11px;"><button type="button" id="custom-port-btn" class="button subtle" style="white-space:nowrap;">Use Port</button></div></label></div><p class="note" style="margin-top:14px;">20 Hz is recommended for smooth intermediate commands on this link.</p></dialog>
|
||||
<dialog id="battery-modal" class="modal"><button class="modal-close" data-close>×</button><small>Power</small><h2>Battery Settings</h2><form id="battery-form" class="form-grid three"><label><span>Cells (S)</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="cells" type="number" min="1" step="1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Capacity (mAh)</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="capacity_mah" type="number" min="1" step="50"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>C rating</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="c_rating" type="number" min="1" step=".1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Full V / cell</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="full_v_per_cell" type="number" step=".01"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Empty V / cell</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="empty_v_per_cell" type="number" step=".01"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><label><span>Warning %</span><div class="number-pill"><button type="button" class="num-step down" tabindex="-1">‹</button><input name="warning_percent" type="number" min="0" max="100" step="1"><button type="button" class="num-step up" tabindex="-1">›</button></div></label><div class="modal-actions wide"><button type="button" class="button subtle" data-close>Cancel</button><button class="button connect-button">Save Settings</button></div></form></dialog>
|
||||
<dialog id="motors-modal" class="modal motor-modal"><button class="modal-close" data-close>×</button><small>configuration</small><h2>Motor Outputs</h2><p class="note">Function and direction are read one parameter at a time to protect the low-bandwidth link.</p><div class="motor-head"><span>Output</span><span>Function</span><span>Direction</span><span></span></div><div id="motor-list" class="motor-list"></div></dialog>
|
||||
|
||||
<script src="model-viewer.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="model-viewer.js?v=20260826-controls-v2"></script>
|
||||
<script src="joystick-3d.js?v=20260827-polish-v3"></script>
|
||||
<script src="control-state.js?v=20260827-state-v1"></script>
|
||||
<script src="app.js?v=20260827-bridge-v6"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user