Adds gps_map_panel.html/css/js — standalone dashboard panel:
- Leaflet.js + OpenStreetMap with dark CSS filter (matches dashboard theme)
- Heading-aware SVG robot marker (orange arrow shows direction of travel)
- Orange breadcrumb trail polyline (up to 2000 pts, CLEAR button)
- FOLLOW mode auto-pan; drag map to switch to FREE mode
- Sidebar: speed (km/h, color-coded), altitude, heading compass rose,
fix status (0=NO FIX…4=RTK), fix count, lat/lon, trail log
- Exponential backoff auto-reconnect (2s→30s cap)
- Stale detection at 5s for fix + velocity badges
Subscribes via rosbridge to:
saltybot/gps/fix std_msgs/String JSON — {lat, lon, alt, stat, t}
saltybot/gps/vel std_msgs/String JSON — {spd, hdg, t}
index.html: new GPS MAP card (🛰️, #709) before CAN MONITOR
dashboard.js: gpsWatch subscription + 'gps' panel entry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
313 lines
11 KiB
HTML
313 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
|
<title>Saltybot — Dashboard</title>
|
|
<link rel="stylesheet" href="dashboard.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/roslib@1.3.0/build/roslib.min.js"></script>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- ══════════════════════════════════════════════════════ TOP BAR ══ -->
|
|
<div id="topbar">
|
|
<div id="topbar-left">
|
|
<div class="logo">⚡ SALTYBOT</div>
|
|
<div id="robot-name">ROBOT-01</div>
|
|
</div>
|
|
|
|
<div id="topbar-status">
|
|
<!-- Battery -->
|
|
<div class="stat-block" id="stat-battery">
|
|
<div class="stat-icon">🔋</div>
|
|
<div class="stat-body">
|
|
<div class="stat-label">BATTERY</div>
|
|
<div class="stat-val" id="val-battery">—</div>
|
|
</div>
|
|
<div class="batt-bar"><div id="batt-fill"></div></div>
|
|
</div>
|
|
|
|
<div class="stat-sep"></div>
|
|
|
|
<!-- Safety -->
|
|
<div class="stat-block">
|
|
<div class="stat-icon">🛡️</div>
|
|
<div class="stat-body">
|
|
<div class="stat-label">SAFETY</div>
|
|
<div class="stat-val" id="val-safety">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-sep"></div>
|
|
|
|
<!-- E-stop -->
|
|
<div class="stat-block">
|
|
<div class="stat-icon">⛔</div>
|
|
<div class="stat-body">
|
|
<div class="stat-label">E-STOP</div>
|
|
<div class="stat-val" id="val-estop" style="color:#6b7280">OFF</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-sep"></div>
|
|
|
|
<!-- Drive mode -->
|
|
<div class="stat-block">
|
|
<div class="stat-icon">🚗</div>
|
|
<div class="stat-body">
|
|
<div class="stat-label">MODE</div>
|
|
<div class="stat-val" id="val-mode">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-sep"></div>
|
|
|
|
<!-- Uptime -->
|
|
<div class="stat-block">
|
|
<div class="stat-icon">⏱</div>
|
|
<div class="stat-body">
|
|
<div class="stat-label">UPTIME</div>
|
|
<div class="stat-val" id="val-uptime">—</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="topbar-right">
|
|
<div id="conn-dot"></div>
|
|
<input id="ws-input" type="text" value="ws://localhost:9090" placeholder="ws://robot-ip:9090" />
|
|
<button id="btn-connect" class="hbtn">CONNECT</button>
|
|
<span id="conn-label">Not connected</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ══════════════════════════════════════════════ PANEL GRID ══ -->
|
|
<div id="main">
|
|
|
|
<!-- Auto-detect bar -->
|
|
<div id="detect-bar">
|
|
<span id="detect-status">🔍 Auto-detecting rosbridge…</span>
|
|
<div id="detect-dots"></div>
|
|
</div>
|
|
|
|
<!-- Panel cards grid -->
|
|
<div id="grid">
|
|
|
|
<a class="panel-card" href="map_panel.html" data-panel="map">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#22c55e">🗺️</div>
|
|
<div>
|
|
<div class="card-title">MAP VIEW</div>
|
|
<div class="card-sub">#587</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-map"></div>
|
|
</div>
|
|
<div class="card-desc">RPLIDAR scan overlay · robot position · UWB anchors · safety zones · breadcrumb trail</div>
|
|
<div class="card-topics">
|
|
<code>/saltybot/pose/fused</code>
|
|
<code>/scan</code>
|
|
<code>/saltybot/safety_zone/status</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-map">OFFLINE</span>
|
|
<span class="card-msg" id="msg-map">No data</span>
|
|
</div>
|
|
</a>
|
|
|
|
<a class="panel-card" href="gamepad_panel.html" data-panel="gamepad">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#06b6d4">🎮</div>
|
|
<div>
|
|
<div class="card-title">GAMEPAD TELEOP</div>
|
|
<div class="card-sub">#598</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-gamepad"></div>
|
|
</div>
|
|
<div class="card-desc">Web Gamepad API · virtual dual sticks · WASD keyboard · speed limits · deadzone config</div>
|
|
<div class="card-topics">
|
|
<code>/cmd_vel</code>
|
|
<code>geometry_msgs/Twist</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-gamepad">OFFLINE</span>
|
|
<span class="card-msg" id="msg-gamepad">No data</span>
|
|
</div>
|
|
</a>
|
|
|
|
<a class="panel-card" href="diagnostics_panel.html" data-panel="diag">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#f59e0b">📊</div>
|
|
<div>
|
|
<div class="card-title">DIAGNOSTICS</div>
|
|
<div class="card-sub">#562</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-diag"></div>
|
|
</div>
|
|
<div class="card-desc">Battery · CPU/GPU/motor temps · RAM/disk · WiFi signal · ROS2 node health</div>
|
|
<div class="card-topics">
|
|
<code>/diagnostics</code>
|
|
<code>diagnostic_msgs/DiagnosticArray</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-diag">OFFLINE</span>
|
|
<span class="card-msg" id="msg-diag">No data</span>
|
|
</div>
|
|
</a>
|
|
|
|
<a class="panel-card" href="event_log_panel.html" data-panel="events">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#a78bfa">📋</div>
|
|
<div>
|
|
<div class="card-title">EVENT LOG</div>
|
|
<div class="card-sub">#576</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-events"></div>
|
|
</div>
|
|
<div class="card-desc">Filterable real-time feed · severity badges · node filter · text search · CSV export</div>
|
|
<div class="card-topics">
|
|
<code>/rosout</code>
|
|
<code>/saltybot/events</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-events">OFFLINE</span>
|
|
<span class="card-msg" id="msg-events">No data</span>
|
|
</div>
|
|
</a>
|
|
|
|
<a class="panel-card" href="settings_panel.html" data-panel="settings">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#9ca3af">⚙️</div>
|
|
<div>
|
|
<div class="card-title">SETTINGS</div>
|
|
<div class="card-sub">#614</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-settings" style="background:#374151"></div>
|
|
</div>
|
|
<div class="card-desc">PID tuning · speed limits · safety thresholds · sensor toggles · named presets</div>
|
|
<div class="card-topics">
|
|
<code>rcl_interfaces/srv/GetParameters</code>
|
|
<code>SetParameters</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-settings" style="color:#6b7280">CONFIG</span>
|
|
<span class="card-msg" id="msg-settings">Parameter service</span>
|
|
</div>
|
|
</a>
|
|
|
|
<a class="panel-card" href="gps_map_panel.html" data-panel="gps">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#22c55e">🛰️</div>
|
|
<div>
|
|
<div class="card-title">GPS MAP</div>
|
|
<div class="card-sub">#709</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-gps"></div>
|
|
</div>
|
|
<div class="card-desc">Live robot position · breadcrumb trail · speed · heading compass · fix quality</div>
|
|
<div class="card-topics">
|
|
<code>saltybot/gps/fix</code>
|
|
<code>saltybot/gps/vel</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-gps">OFFLINE</span>
|
|
<span class="card-msg" id="msg-gps">No data</span>
|
|
</div>
|
|
</a>
|
|
|
|
<a class="panel-card" href="can_monitor_panel.html" data-panel="can">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#06b6d4">📡</div>
|
|
<div>
|
|
<div class="card-title">CAN MONITOR</div>
|
|
<div class="card-sub">#681</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-can"></div>
|
|
</div>
|
|
<div class="card-desc">VESC L/R RPM · current · temps · voltage · IMU pitch/roll/yaw · balance PID · barometer</div>
|
|
<div class="card-topics">
|
|
<code>/vesc/left/state</code>
|
|
<code>/vesc/right/state</code>
|
|
<code>/saltybot/imu</code>
|
|
<code>/saltybot/balance_state</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-can">OFFLINE</span>
|
|
<span class="card-msg" id="msg-can">No data</span>
|
|
</div>
|
|
</a>
|
|
|
|
<a class="panel-card" href="gimbal_panel.html" data-panel="gimbal">
|
|
<div class="card-header">
|
|
<div class="card-icon" style="color:#f97316">🎥</div>
|
|
<div>
|
|
<div class="card-title">GIMBAL</div>
|
|
<div class="card-sub">#551</div>
|
|
</div>
|
|
<div class="card-dot" id="dot-gimbal"></div>
|
|
</div>
|
|
<div class="card-desc">Pan/tilt joystick · live camera feed · preset positions · person-tracking toggle</div>
|
|
<div class="card-topics">
|
|
<code>/gimbal/cmd</code>
|
|
<code>/gimbal/state</code>
|
|
</div>
|
|
<div class="card-footer">
|
|
<span class="card-status" id="status-gimbal">OFFLINE</span>
|
|
<span class="card-msg" id="msg-gimbal">No data</span>
|
|
</div>
|
|
</a>
|
|
|
|
</div><!-- /#grid -->
|
|
|
|
<!-- Connection info strip -->
|
|
<div id="info-strip">
|
|
<div class="info-item">
|
|
<span class="info-lbl">ROSBRIDGE</span>
|
|
<span class="info-val" id="info-ws">—</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-lbl">MSG RATE</span>
|
|
<span class="info-val" id="info-rate">—</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-lbl">LATENCY</span>
|
|
<span class="info-val" id="info-latency">—</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-lbl">ROBOT IP</span>
|
|
<span class="info-val" id="info-ip">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /#main -->
|
|
|
|
<!-- ══════════════════════════════════════════════ BOTTOM BAR ══ -->
|
|
<div id="bottombar">
|
|
|
|
<div id="bottombar-left">
|
|
<button id="btn-estop" class="estop-btn">⛔ E-STOP</button>
|
|
<button id="btn-resume" class="resume-btn" style="display:none">▶ RESUME</button>
|
|
</div>
|
|
|
|
<div id="bottombar-center">
|
|
<span class="bb-lbl">DRIVE MODE</span>
|
|
<div id="mode-btns">
|
|
<button class="mode-btn active" data-mode="MANUAL">MANUAL</button>
|
|
<button class="mode-btn" data-mode="AUTO">AUTO</button>
|
|
<button class="mode-btn" data-mode="FOLLOW">FOLLOW</button>
|
|
<button class="mode-btn" data-mode="DOCK">DOCK</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="bottombar-right">
|
|
<div id="uptime-display">
|
|
<span class="bb-lbl">SESSION</span>
|
|
<span id="session-time">00:00:00</span>
|
|
</div>
|
|
<div style="color:#374151;font-size:9px">issue #630</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script src="dashboard.js"></script>
|
|
</body>
|
|
</html>
|