From 1899a49f4d07e476180f281c255a24f94a91406d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sun, 24 May 2026 10:43:57 +0200 Subject: [PATCH] GPS im Admin und offline verbesserungen --- ExoMy_Software-master/gui/admin.css | 357 ----------------- ExoMy_Software-master/gui/admin.html | 553 +++++++++++++++++++++++++- ExoMy_Software-master/gui/app.css | 173 ++++++-- ExoMy_Software-master/gui/index.html | 193 +++++++-- ExoMy_Software-master/src/gps_node.py | 170 +++++++- 5 files changed, 1019 insertions(+), 427 deletions(-) delete mode 100644 ExoMy_Software-master/gui/admin.css diff --git a/ExoMy_Software-master/gui/admin.css b/ExoMy_Software-master/gui/admin.css deleted file mode 100644 index 7ad6c6d..0000000 --- a/ExoMy_Software-master/gui/admin.css +++ /dev/null @@ -1,357 +0,0 @@ -:root { - --bg: #05080F; - --pri: #0088FF; - --sec: #FF8C00; - --ok: #00ff91; - --txt: rgba(0,255,145,.8); - --txt2: rgba(0,210,130,.38); - --mono: "Consolas","Cascadia Code","Cascadia Mono","Courier New",monospace; -} -* { margin:0; padding:0; box-sizing:border-box; } -html { height:100%; } -body { - font-family:var(--mono); - background-color:var(--bg); - background-image: - linear-gradient(rgba(0,255,145,.04) 1px, transparent 1px), - linear-gradient(90deg, rgba(0,255,145,.04) 1px, transparent 1px); - background-size:32px 32px; - color:var(--txt); - display:flex; - flex-direction:column; - height:100%; - overflow:hidden; -} - -/* ── HEADER ─────────────────────────────────────── */ -header { - height:52px; - background:linear-gradient(135deg, #060D1A 0%, #0C1C38 100%); - border-bottom:2px solid var(--pri); - box-shadow:0 2px 24px rgba(0,136,255,.28); - display:flex; - align-items:center; - padding:0 16px; - gap:14px; - flex-shrink:0; - z-index:10; -} -.hdr-icon { width:32px; height:32px; flex-shrink:0; } -.hdr-title { font-weight:700; font-size:14px; color:#fff; letter-spacing:5px; text-transform:uppercase; white-space:nowrap; } -.hdr-title em { font-style:normal; color:var(--pri); } -.hdr-div { width:1px; height:28px; background:rgba(255,255,255,.12); flex-shrink:0; } -.hdr-badge { font-size:7px; color:var(--pri); border:1px solid rgba(0,136,255,.5); padding:2px 8px; letter-spacing:2.5px; text-transform:uppercase; white-space:nowrap; } -.hdr-gap { flex:1; } -.hdr-status { font-size:8px; color:rgba(0,255,145,.55); letter-spacing:2px; text-transform:uppercase; white-space:nowrap; } -.hdr-back { color:rgba(0,255,145,.45); text-decoration:none; font-size:8px; letter-spacing:2px; text-transform:uppercase; } -.hdr-back:hover { color:rgba(0,255,145,.8); } -.hdr-clk { display:flex; flex-direction:column; align-items:flex-end; } -.hdr-clk-lbl { font-size:7px; color:rgba(255,255,255,.35); letter-spacing:2px; text-transform:uppercase; } -.hdr-clk-val { font-weight:700; font-size:14px; color:#fff; letter-spacing:3px; line-height:1.1; } - -/* ── PANEL ──────────────────────────────────────── */ -.panel { - position:relative; - background: - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 0/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 0/1px 14px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 0/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 0/1px 14px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 100%/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 100%/1px 14px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 100%/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 100%/1px 14px no-repeat, - #070D18; - border:1px solid rgba(0,255,145,.1); - box-shadow:0 0 24px rgba(0,0,0,.5), inset 0 0 40px rgba(0,255,145,.018); -} -.p-hdr { - display:flex; align-items:center; gap:7px; - padding:7px 12px; - border-bottom:1px solid rgba(0,255,145,.08); - background:linear-gradient(90deg, rgba(0,255,145,.05) 0%, transparent 100%); -} -.p-dot { width:5px; height:5px; border-radius:50%; background:var(--ok); box-shadow:0 0 6px var(--ok); flex-shrink:0; } -.p-lbl { font-size:8px; letter-spacing:3px; color:rgba(0,255,145,.7); text-transform:uppercase; font-weight:bold; } -.p-tag { margin-left:auto; font-size:7px; color:rgba(0,255,145,.25); letter-spacing:1px; } - -/* ── STATUS ROWS ────────────────────────────────── */ -.sr { display:flex; align-items:center; padding:4px 10px; gap:7px; border-bottom:1px solid rgba(0,255,145,.04); transition:background .1s; } -.sr:last-child { border-bottom:none; } -.sr:hover { background:rgba(0,255,145,.02); } -.sr-led { width:6px; height:6px; border-radius:50%; flex-shrink:0; border:1px solid rgba(0,255,145,.15); background:#0a1408; } -.sr-led.led-ok { background:#00ff91; border-color:#00ff91; box-shadow:0 0 5px #00ff91, 0 0 12px rgba(0,255,145,.55); } -.sr-led.led-warn { background:#FF8C00; border-color:#FF8C00; box-shadow:0 0 5px #FF8C00, 0 0 12px rgba(255,140,0,.5); } -.sr-led.led-blue { background:#0088FF; border-color:#0088FF; box-shadow:0 0 5px #0088FF, 0 0 12px rgba(0,136,255,.5); } -.sr-led.led-off { background:#0d1208; border-color:rgba(0,255,145,.08); box-shadow:none; } -.sr-key { font-size:7px; letter-spacing:2px; color:rgba(0,210,130,.4); text-transform:uppercase; min-width:60px; flex-shrink:0; } -.sr-val { font-size:9px; font-weight:700; letter-spacing:.8px; margin-left:auto; color:rgba(0,255,145,.85); text-align:right; } - -/* ── SERVICE STATE ──────────────────────────────── */ -.state_active { color:#00ff91; } -.state_inactive { color:#FF3333; } -.state_unknown { color:#FF8C00; } -@keyframes statusBlink { 0%,100%{opacity:1} 50%{opacity:.1} } - -/* ── SECTION DIVIDER ────────────────────────────── */ -.ss-hdr { - display:flex; align-items:center; gap:7px; - padding:5px 10px 4px; - background:rgba(0,255,145,.035); - border-top:1px solid rgba(0,255,145,.1); - border-bottom:1px solid rgba(0,255,145,.05); -} -.ss-hdr-line { flex:1; height:1px; background:repeating-linear-gradient(90deg, rgba(0,255,145,.22) 0, rgba(0,255,145,.22) 3px, transparent 3px, transparent 6px); } -.ss-hdr-txt { font-size:6px; letter-spacing:4px; color:rgba(0,255,145,.45); text-transform:uppercase; } - -/* ── GAUGE ──────────────────────────────────────── */ -.sr-gauge { padding-bottom:6px; } -.gauge { flex:1; height:8px; background:rgba(0,255,145,.04); border:1px solid rgba(0,255,145,.14); position:relative; overflow:hidden; } -.gauge::after { content:""; position:absolute; inset:0; background:repeating-linear-gradient(90deg, transparent 0, transparent 8px, rgba(4,8,16,.88) 8px, rgba(4,8,16,.88) 9px); z-index:2; pointer-events:none; } -.gauge-fill { height:100%; width:0%; position:relative; z-index:1; transition:width .6s cubic-bezier(.25,.46,.45,.94); } -.gauge-temp .gauge-fill { background:linear-gradient(90deg, rgba(180,60,0,.7), rgba(255,160,0,.9)); box-shadow:2px 0 14px rgba(255,140,0,.45); } -.gauge-num { font-size:8px; font-weight:700; min-width:52px; text-align:right; letter-spacing:.5px; color:rgba(255,160,0,.8); } - -/* ── MAIN LAYOUT ────────────────────────────────── */ -.admin-main { - flex:1; min-height:0; overflow-y:auto; - display:flex; flex-direction:column; gap:12px; - padding:12px; width:100%; -} -.admin-col { display:flex; flex-direction:column; gap:12px; } - -/* ── RESPONSIVE ─────────────────────────────────── */ -@media (min-width:640px) { - .admin-main { flex-direction:row; overflow:hidden; max-width:1100px; margin:0 auto; } - .admin-col { flex:1; min-width:0; overflow-y:auto; min-height:0; } -} -@media (min-width:1024px) { - .admin-main { padding:16px; gap:16px; } -} -@media (max-width:479px) { - .hdr-badge, .hdr-clk { display:none; } -} - -/* ── ACTION BUTTONS ─────────────────────────────── */ -.act-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; padding:10px 12px; } -.act-btn { - font-family:var(--mono); font-size:8px; letter-spacing:1.5px; text-transform:uppercase; - padding:10px 8px; border:1px solid rgba(0,255,145,.2); background:rgba(0,255,145,.03); - color:rgba(0,255,145,.6); cursor:pointer; transition:all .15s; -} -.act-btn:hover { background:rgba(0,255,145,.08); color:var(--ok); border-color:rgba(0,255,145,.5); } -.act-btn.warm { border-color:rgba(255,140,0,.35); color:rgba(255,140,0,.7); background:rgba(255,140,0,.04); } -.act-btn.warm:hover { background:rgba(255,140,0,.12); color:#FF8C00; border-color:rgba(255,140,0,.7); } -.act-btn.danger { border-color:rgba(255,51,51,.35); color:rgba(255,51,51,.7); background:rgba(255,51,51,.04); } -.act-btn.danger:hover { background:rgba(255,51,51,.12); color:#FF3333; border-color:rgba(255,51,51,.7); } -.act-btn.full { grid-column:span 2; } -.act-btn.subtle { border-color:rgba(0,255,145,.1); color:rgba(0,255,145,.3); } -.act-btn.subtle:hover { color:rgba(0,255,145,.6); border-color:rgba(0,255,145,.35); } - -/* ── LINK CARD ──────────────────────────────────── */ -.link-card { display:flex; align-items:center; gap:12px; padding:10px 12px; } -.link-card-text strong { display:block; font-size:9px; letter-spacing:1.5px; color:rgba(0,255,145,.8); margin-bottom:3px; text-transform:uppercase; } -.link-card-text p { font-size:8px; color:rgba(0,210,130,.45); letter-spacing:.5px; } -.link-card-action { margin-left:auto; flex-shrink:0; } -.link-btn { - font-family:var(--mono); font-size:8px; letter-spacing:1.5px; text-transform:uppercase; - padding:8px 14px; border:1px solid rgba(0,136,255,.4); background:rgba(0,136,255,.06); - color:rgba(0,136,255,.8); cursor:pointer; text-decoration:none; display:inline-block; transition:all .15s; -} -.link-btn:hover { background:rgba(0,136,255,.14); color:#55AAFF; border-color:rgba(0,136,255,.7); } - -/* ── DROPDOWN CONTROL ───────────────────────────── */ -.dropdown-row { display:flex; align-items:center; gap:10px; padding:10px 12px; } -.dropdown-row label { font-size:7px; letter-spacing:2px; color:rgba(0,210,130,.4); text-transform:uppercase; min-width:40px; } -.ctrl-select { - font-family:var(--mono); font-size:8px; letter-spacing:1px; - color:rgba(0,255,145,.8); background:#050A14; - border:1px solid rgba(0,255,145,.2); padding:6px 8px; - cursor:pointer; outline:none; flex:1; -} -.ctrl-select:focus { border-color:rgba(0,255,145,.5); } -.ctrl-status { font-size:7px; letter-spacing:1.5px; color:rgba(0,255,145,.45); white-space:nowrap; min-width:50px; text-align:right; } - -.act-btn.is_active { - background: rgba(0,136,255,.16); - color: #55aaff; - border-color: rgba(0,136,255,.55); - box-shadow: 0 0 14px rgba(0,136,255,.18); -} - -.admin-section-grid_map { - grid-template-columns: minmax(0, 1.7fr) minmax(280px, 0.85fr); -} - -.admin-col_map_main, -.admin-col_map_side { - min-width: 0; -} - -.map-panel { - display: flex; - flex-direction: column; - min-height: 620px; -} - -.map-toolbar { - display: flex; - gap: 8px; - padding: 10px 12px 0; - flex-wrap: wrap; -} - -.map-copy { - padding: 10px 12px; -} - -.map-stage { - position: relative; - flex: 1; - min-height: 460px; - margin: 0 12px 12px; - border: 1px solid rgba(0,255,145,.12); - background: - radial-gradient(circle at 20% 20%, rgba(0,136,255,.12), transparent 34%), - radial-gradient(circle at 78% 24%, rgba(0,255,145,.08), transparent 28%), - #04101a; - overflow: hidden; -} - -.rover-map, -.rover-map-fallback { - position: absolute; - inset: 0; -} - -.rover-map-fallback { - display: flex; - align-items: center; - justify-content: center; - text-align: center; - padding: 20px; - color: rgba(0,255,145,.65); - font-size: 12px; - letter-spacing: .6px; - background: rgba(4, 16, 26, .92); -} - -.map-waypoint-list { - display: flex; - flex-direction: column; - gap: 8px; - padding: 10px 12px 12px; -} - -.map-waypoint-empty { - padding: 12px; - border: 1px dashed rgba(0,255,145,.14); - color: rgba(0,255,145,.42); - font-size: 8px; - letter-spacing: 1px; - text-transform: uppercase; - text-align: center; -} - -.map-waypoint-item { - display: flex; - align-items: center; - gap: 10px; - padding: 10px; - border: 1px solid rgba(0,255,145,.1); - background: rgba(0,255,145,.03); -} - -.map-waypoint-index { - min-width: 42px; - font-size: 8px; - color: rgba(0,255,145,.5); - letter-spacing: 1.4px; - text-transform: uppercase; -} - -.map-waypoint-coords { - display: flex; - flex-direction: column; - gap: 3px; - min-width: 0; -} - -.map-waypoint-coords strong, -.map-waypoint-coords span { - font-size: 10px; - letter-spacing: .5px; - color: rgba(0,255,145,.82); -} - -.leaflet-container { - font-family: var(--mono); - background: #04101a; -} - -.leaflet-control-attribution { - background: rgba(4, 16, 26, .82); - color: rgba(0,255,145,.58); -} - -.leaflet-control-attribution a { - color: rgba(85,170,255,.85); -} - -.leaflet-popup-content-wrapper, -.leaflet-popup-tip { - background: #07111c; - color: rgba(0,255,145,.82); -} - -@media (max-width: 1024px) { - .admin-section-grid_map { - grid-template-columns: 1fr; - } - - .map-panel { - min-height: 0; - } - - .map-stage { - min-height: 360px; - } -} - -/* ── AUTH OVERLAY ───────────────────────────────── */ -.auth-overlay { - position:fixed; inset:0; background:rgba(5,8,15,.93); - display:flex; align-items:center; justify-content:center; - z-index:1000; backdrop-filter:blur(4px); -} -.auth-box { - width:320px; display:flex; flex-direction:column; gap:14px; padding:28px 24px; - background: - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 0/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 0/1px 14px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 0/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 0/1px 14px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 100%/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 0 100%/1px 14px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 100%/14px 1px no-repeat, - linear-gradient(rgba(0,255,145,.55),rgba(0,255,145,.55)) 100% 100%/1px 14px no-repeat, - #070D18; - border:1px solid rgba(0,255,145,.12); - box-shadow:0 0 60px rgba(0,0,0,.8), 0 0 40px rgba(0,136,255,.08); -} -.auth-eyebrow { font-size:7px; letter-spacing:3px; color:rgba(0,210,130,.5); text-transform:uppercase; } -.auth-title { font-size:20px; font-weight:700; color:#fff; letter-spacing:4px; text-transform:uppercase; } -.auth-sub { font-size:8px; color:rgba(0,255,145,.35); letter-spacing:1px; } -.auth-input { - font-family:var(--mono); font-size:16px; background:#050A14; - border:1px solid rgba(0,255,145,.2); color:var(--ok); - padding:10px 12px; outline:none; letter-spacing:6px; width:100%; -} -.auth-input:focus { border-color:rgba(0,255,145,.5); } -.auth-error { font-size:8px; color:#FF3333; letter-spacing:1px; min-height:12px; } -.auth-submit { - font-family:var(--mono); font-size:9px; letter-spacing:2px; text-transform:uppercase; - padding:11px; border:1px solid rgba(0,136,255,.5); background:rgba(0,136,255,.1); - color:rgba(0,136,255,.9); cursor:pointer; transition:all .15s; width:100%; -} -.auth-submit:hover { background:rgba(0,136,255,.22); color:#55AAFF; } diff --git a/ExoMy_Software-master/gui/admin.html b/ExoMy_Software-master/gui/admin.html index 8feeb6b..95842b1 100644 --- a/ExoMy_Software-master/gui/admin.html +++ b/ExoMy_Software-master/gui/admin.html @@ -12,17 +12,26 @@
- - - - - - - - - - - + CUNO ADMIN
@@ -30,6 +39,8 @@
Lade...
+
Prüfe…
+
← Ground Control
@@ -37,12 +48,17 @@
--:--:--
+
+
@@ -357,6 +373,64 @@
+
+
+
+
+
+
+ GNSS-Satelliten + Himmelskarte +
+
+ Diese Ansicht zeigt die vom Empfänger gemeldeten Satelliten im Himmelsplot. Außen ist der Horizont, die Mitte ist der Zenit. +
+
+ + +
+
+
+ +
+
+
+
+ Fix-Diagnose + GNSS-Diagnose +
+
StatusOffline
+
Fix-Qualität--
+
Frame--
+
Breite--
+
Länge--
+
Höhe--
+
Geschwindigkeit--
+
Kurs--
+
UTC-Datum--
+
UTC-Zeit--
+
Fix-Alter--
+
+ +
+
+
+ Genauigkeit + Qualität +
+
Positionsgenauigkeit--
+
Horizontale Genauigkeit--
+
Vertikale Genauigkeit--
+
Für Fix genutzt--
+
Im Himmelsplot--
+
+ Für eine gute Fix-Qualität sind niedrige DOP-Werte und hohe, gleichmäßig verteilte Satelliten sinnvoll. Der Himmelsplot zeigt nur Satelliten, die der Empfänger per GSV mit Richtung und Höhe meldet. +
+
+
+
+
+
@@ -479,6 +553,15 @@ var motorCommandsTopic = null; var gpsFixTopic = null; var gpsStatusTopic = null; + var gpsDiagnosticsTopic = null; + var pageConnection = { + apiOk: false, + apiLastAt: 0, + rosOk: false, + gpsLastAt: 0, + lastHealthyAt: 0, + state: 'offline' + }; var isMotorTestRunning = false; var isContainerActionRunning = false; @@ -560,6 +643,29 @@ hasCenteredOnFix: false, suppressMapClickUntil: 0 }; + var gpsDiagnosticsState = { + connected: false, + frameId: 'gps', + valid: false, + fixQuality: 0, + satellitesUsed: 0, + satellitesVisible: 0, + latitude: null, + longitude: null, + altitudeM: null, + hdop: null, + pdop: null, + vdop: null, + speedMps: null, + speedKmh: null, + trackDeg: null, + utcDate: null, + utcTime: null, + lastFixAgeS: null, + satellites: [], + selectedSatellitePrn: null, + plottedSatellites: [] + }; function submitPassword() { var input = document.getElementById('auth_input'); @@ -578,6 +684,129 @@ if (el) el.textContent = text; } + function clearAdminApiDisplay() { + setText('info_ips', '--'); + setText('info_wifi', '--'); + setText('info_temp', '--'); + setText('info_uptime', '--'); + setText('info_disk', '--'); + setUndervoltageState('info_undervoltage', '--', 'unknown'); + setServiceState('service_camera', '-'); + setServiceState('service_admin_api', '-'); + setServiceState('service_exomy', '-'); + var bar = document.getElementById('bar-temp-adm'); + if (bar) bar.style.width = '0%'; + } + + function clearAdminRosDisplay() { + roverMapState.hasFix = false; + roverMapState.roverLat = null; + roverMapState.roverLon = null; + roverMapState.roverAlt = null; + roverMapState.accuracyMeters = null; + roverMapState.lastFixAt = null; + roverMapState.gpsStatusText = 'Offline'; + roverMapState.satellites = '--'; + updateMapTelemetryPanel(); + + setDriveEstimatorState('offline', 'state_inactive'); + setText('drive_estimator_speed', '--'); + setText('drive_estimator_distance', '--'); + setText('drive_estimator_pulse', '--'); + setText('drive_estimator_rpm', '--'); + setText('drive_estimator_mode', '--'); + setText('drive_estimator_direction', '--'); + gpsDiagnosticsState.connected = false; + gpsDiagnosticsState.valid = false; + gpsDiagnosticsState.fixQuality = 0; + gpsDiagnosticsState.satellitesUsed = 0; + gpsDiagnosticsState.satellitesVisible = 0; + gpsDiagnosticsState.latitude = null; + gpsDiagnosticsState.longitude = null; + gpsDiagnosticsState.altitudeM = null; + gpsDiagnosticsState.hdop = null; + gpsDiagnosticsState.pdop = null; + gpsDiagnosticsState.vdop = null; + gpsDiagnosticsState.speedMps = null; + gpsDiagnosticsState.speedKmh = null; + gpsDiagnosticsState.trackDeg = null; + gpsDiagnosticsState.utcDate = null; + gpsDiagnosticsState.utcTime = null; + gpsDiagnosticsState.lastFixAgeS = null; + gpsDiagnosticsState.satellites = []; + updateGpsDiagnosticsPanel(); + } + + function formatConnectionTime(timestamp) { + if (!timestamp) return '--:--:--'; + return new Date(timestamp).toLocaleTimeString('de-DE', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); + } + + function updatePageConnectionState() { + var now = Date.now(); + var apiFresh = pageConnection.apiOk && (now - pageConnection.apiLastAt) < 20000; + var rosFresh = !!pageConnection.rosOk; + var gpsFresh = pageConnection.gpsLastAt && (now - pageConnection.gpsLastAt) < 15000; + var state = 'offline'; + var title = 'Offline'; + var details = []; + + if (apiFresh && rosFresh) { + state = gpsFresh || !pageConnection.gpsLastAt ? 'online' : 'degraded'; + title = state === 'online' ? 'Online' : 'Eingeschränkt'; + } else if (apiFresh || rosFresh) { + state = 'degraded'; + title = 'Eingeschränkt'; + } + + if (!apiFresh) details.push('Admin-API nicht erreichbar'); + if (!rosFresh) details.push('ROSBridge getrennt'); + if (rosFresh && pageConnection.gpsLastAt && !gpsFresh) details.push('GPS-Daten veraltet'); + if (!details.length) details.push('Alle Live-Dienste antworten'); + if (state !== 'online' && pageConnection.lastHealthyAt) { + details.push('letzter Kontakt ' + formatConnectionTime(pageConnection.lastHealthyAt)); + } + + pageConnection.state = state; + document.body.classList.toggle('page-connection-online', state === 'online'); + document.body.classList.toggle('page-connection-degraded', state === 'degraded'); + document.body.classList.toggle('page-connection-offline', state === 'offline'); + setText('page-connection-label', title); + setText('connection-banner-title', title); + setText('connection-banner-detail', details.join(' | ')); + + if (!apiFresh) clearAdminApiDisplay(); + if (!rosFresh) clearAdminRosDisplay(); + + var banner = document.getElementById('connection-banner'); + if (banner) banner.hidden = state === 'online'; + } + + function markAdminApiConnection(ok) { + pageConnection.apiOk = !!ok; + if (ok) { + pageConnection.apiLastAt = Date.now(); + pageConnection.lastHealthyAt = pageConnection.apiLastAt; + } + updatePageConnectionState(); + } + + function markAdminRosConnection(ok) { + pageConnection.rosOk = !!ok; + if (ok) pageConnection.lastHealthyAt = Date.now(); + updatePageConnectionState(); + } + + function markAdminGpsFresh() { + pageConnection.gpsLastAt = Date.now(); + if (pageConnection.rosOk) pageConnection.lastHealthyAt = pageConnection.gpsLastAt; + updatePageConnectionState(); + } + function setMotorTestStatus(text) { setAdminStatus(text); } @@ -587,26 +816,32 @@ var systemTab = document.getElementById('admin_tab_system'); var driveTab = document.getElementById('admin_tab_drive'); var mapTab = document.getElementById('admin_tab_map'); + var gpsTab = document.getElementById('admin_tab_gps'); var motorTab = document.getElementById('admin_tab_motor'); var systemPanel = document.getElementById('admin_panel_system'); var drivePanel = document.getElementById('admin_panel_drive'); var mapPanel = document.getElementById('admin_panel_map'); + var gpsPanel = document.getElementById('admin_panel_gps'); var motorPanel = document.getElementById('admin_panel_motor'); var isSystem = sectionName === 'system'; var isDrive = sectionName === 'drive'; var isMap = sectionName === 'map'; + var isGps = sectionName === 'gps'; var isMotor = sectionName === 'motor'; systemTab.classList.toggle('is_active', isSystem); driveTab.classList.toggle('is_active', isDrive); mapTab.classList.toggle('is_active', isMap); + gpsTab.classList.toggle('is_active', isGps); motorTab.classList.toggle('is_active', isMotor); systemTab.setAttribute('aria-selected', isSystem ? 'true' : 'false'); driveTab.setAttribute('aria-selected', isDrive ? 'true' : 'false'); mapTab.setAttribute('aria-selected', isMap ? 'true' : 'false'); + gpsTab.setAttribute('aria-selected', isGps ? 'true' : 'false'); motorTab.setAttribute('aria-selected', isMotor ? 'true' : 'false'); systemPanel.classList.toggle('is_active', isSystem); drivePanel.classList.toggle('is_active', isDrive); mapPanel.classList.toggle('is_active', isMap); + gpsPanel.classList.toggle('is_active', isGps); motorPanel.classList.toggle('is_active', isMotor); window.location.hash = isSystem ? '' : ('#' + sectionName); if (isMap) { @@ -615,6 +850,9 @@ if (roverMapState.map) roverMapState.map.invalidateSize(); }, 80); } + if (isGps) { + window.setTimeout(drawGpsSkyPlot, 80); + } if (isMotor) fetchMotorTestStatus(); } @@ -665,14 +903,14 @@ }); } - function formatCoordinate(value) { + function formatCoordinate(value, digits) { if (typeof value !== 'number' || isNaN(value)) return '--'; - return formatNumber(value, 5); + return formatNumber(value, typeof digits === 'number' ? digits : 5); } - function formatAltitude(value) { + function formatAltitude(value, digits) { if (typeof value !== 'number' || isNaN(value)) return '--'; - return formatNumber(value, 1) + ' m'; + return formatNumber(value, typeof digits === 'number' ? digits : 1) + ' m'; } function formatAccuracy(value) { @@ -680,6 +918,26 @@ return formatNumber(value, 1) + ' m'; } + function formatDop(value) { + if (typeof value !== 'number' || isNaN(value) || value <= 0) return '--'; + return formatNumber(value, 2); + } + + function formatDegrees(value) { + if (typeof value !== 'number' || isNaN(value)) return '--'; + return formatNumber(value, 1) + '°'; + } + + function formatSpeedKmh(value) { + if (typeof value !== 'number' || isNaN(value)) return '--'; + return formatNumber(value, 1) + ' km/h'; + } + + function formatFixAge(value) { + if (typeof value !== 'number' || isNaN(value)) return '--'; + return formatNumber(value, 1) + ' s'; + } + function formatFixTime(date) { if (!date) return '--'; return date.toLocaleTimeString('de-DE', { @@ -689,6 +947,241 @@ }); } + function fixQualityLabel(value) { + var labels = { + 0: 'Kein Fix', + 1: 'GPS Fix', + 2: 'DGPS Fix', + 4: 'RTK Fixed', + 5: 'RTK Float' + }; + return labels[value] || String(value || 0); + } + + function describeDopQuality(value) { + if (typeof value !== 'number' || isNaN(value) || value <= 0) { + return { text: '--', stateClass: 'state_unknown' }; + } + if (value < 1) { + return { text: formatNumber(value, 2) + ' sehr gut', stateClass: 'state_active' }; + } + if (value < 2) { + return { text: formatNumber(value, 2) + ' gut', stateClass: 'state_active' }; + } + if (value < 5) { + return { text: formatNumber(value, 2) + ' brauchbar', stateClass: 'state_unknown' }; + } + if (value < 10) { + return { text: formatNumber(value, 2) + ' kritisch', stateClass: 'state_inactive' }; + } + return { text: formatNumber(value, 2) + ' schlecht', stateClass: 'state_inactive' }; + } + + function setGpsQualityState(id, value) { + var node = document.getElementById(id); + if (!node) return; + var quality = describeDopQuality(value); + node.textContent = quality.text; + node.classList.remove('state_active', 'state_inactive', 'state_unknown'); + node.classList.add(quality.stateClass); + } + + function updateGpsDiagnosticsPanel() { + setText('gps_diag_status', gpsDiagnosticsState.connected ? (gpsDiagnosticsState.valid ? 'Fix aktiv' : 'Empfänger online') : 'Offline'); + setText('gps_diag_fix_quality', fixQualityLabel(gpsDiagnosticsState.fixQuality)); + setText('gps_diag_frame', gpsDiagnosticsState.frameId || '--'); + setText('gps_diag_lat', formatCoordinate(gpsDiagnosticsState.latitude, 8)); + setText('gps_diag_lon', formatCoordinate(gpsDiagnosticsState.longitude, 8)); + setText('gps_diag_alt', formatAltitude(gpsDiagnosticsState.altitudeM, 4)); + setText('gps_diag_speed', formatSpeedKmh(gpsDiagnosticsState.speedKmh)); + setText('gps_diag_track', formatDegrees(gpsDiagnosticsState.trackDeg)); + setText('gps_diag_utc_date', gpsDiagnosticsState.utcDate || '--'); + setText('gps_diag_utc_time', gpsDiagnosticsState.utcTime || '--'); + setText('gps_diag_fix_age', formatFixAge(gpsDiagnosticsState.lastFixAgeS)); + setGpsQualityState('gps_diag_pdop', gpsDiagnosticsState.pdop); + setGpsQualityState('gps_diag_hdop', gpsDiagnosticsState.hdop); + setGpsQualityState('gps_diag_vdop', gpsDiagnosticsState.vdop); + setText('gps_diag_sat_used', gpsDiagnosticsState.satellitesUsed); + setText('gps_diag_sat_visible', gpsDiagnosticsState.satellitesVisible); + drawGpsSkyPlot(); + } + + function getSelectedSatellite() { + if (!gpsDiagnosticsState.satellites.length) return null; + var selected = gpsDiagnosticsState.satellites.find(function(item) { + return item.prn === gpsDiagnosticsState.selectedSatellitePrn; + }); + if (selected) return selected; + gpsDiagnosticsState.selectedSatellitePrn = gpsDiagnosticsState.satellites[0].prn || null; + return gpsDiagnosticsState.satellites[0]; + } + + function hideGpsSatelliteTooltip() { + var node = document.getElementById('gps_satellite_tooltip'); + if (!node) return; + node.hidden = true; + } + + function renderGpsSatelliteTooltip(leftPx, topPx) { + var node = document.getElementById('gps_satellite_tooltip'); + if (!node) return; + var selected = getSelectedSatellite(); + if (!selected) { + node.hidden = true; + return; + } + + node.innerHTML = + '
' + (selected.prn || '--') + '
' + + '
System' + (selected.constellation || '--') + '
' + + '
Richtung' + (typeof selected.azimuth === 'number' ? selected.azimuth + '°' : '--') + '
' + + '
Höhe' + (typeof selected.elevation === 'number' ? selected.elevation + '°' : '--') + '
' + + '
Signal' + (typeof selected.snr === 'number' ? selected.snr + ' dB' : '--') + '
' + + '
Qualität' + (typeof selected.snr === 'number' ? (selected.snr >= 35 ? 'sehr gut' : (selected.snr >= 20 ? 'gut' : 'schwach')) : 'unbekannt') + '
'; + node.style.left = leftPx + 'px'; + node.style.top = topPx + 'px'; + node.hidden = false; + } + + function drawGpsSkyPlot() { + var canvas = document.getElementById('gps_sky_canvas'); + if (!canvas || !canvas.getContext) return; + + var ctx = canvas.getContext('2d'); + var width = canvas.width; + var height = canvas.height; + var cx = width / 2; + var cy = height / 2; + var radius = Math.min(width, height) * 0.42; + + ctx.clearRect(0, 0, width, height); + ctx.fillStyle = '#07111c'; + ctx.fillRect(0, 0, width, height); + + ctx.strokeStyle = 'rgba(0,255,145,.18)'; + ctx.lineWidth = 1; + [radius, radius * 0.66, radius * 0.33].forEach(function(ring) { + ctx.beginPath(); + ctx.arc(cx, cy, ring, 0, Math.PI * 2); + ctx.stroke(); + }); + + ctx.beginPath(); + ctx.moveTo(cx, cy - radius); + ctx.lineTo(cx, cy + radius); + ctx.moveTo(cx - radius, cy); + ctx.lineTo(cx + radius, cy); + ctx.stroke(); + + ctx.fillStyle = 'rgba(190,255,225,.84)'; + ctx.font = '16px Consolas'; + ctx.textAlign = 'center'; + ctx.fillText('N', cx, cy - radius - 12); + ctx.fillText('S', cx, cy + radius + 24); + ctx.fillText('W', cx - radius - 18, cy + 6); + ctx.fillText('E', cx + radius + 18, cy + 6); + + ctx.fillStyle = 'rgba(150,255,210,.5)'; + ctx.font = '12px Consolas'; + ctx.fillText('30°', cx, cy - radius * 0.33 + 4); + ctx.fillText('60°', cx, cy - radius * 0.66 + 4); + ctx.fillText('90°', cx, cy + 4); + + gpsDiagnosticsState.plottedSatellites = []; + gpsDiagnosticsState.satellites.forEach(function(item) { + if (typeof item.azimuth !== 'number' || typeof item.elevation !== 'number') return; + var satRadius = radius * (1 - Math.max(0, Math.min(90, item.elevation)) / 90); + var angle = (item.azimuth - 90) * Math.PI / 180.0; + var x = cx + Math.cos(angle) * satRadius; + var y = cy + Math.sin(angle) * satRadius; + var snr = typeof item.snr === 'number' ? item.snr : 0; + var isSelected = item.prn === gpsDiagnosticsState.selectedSatellitePrn; + + ctx.beginPath(); + ctx.fillStyle = snr >= 35 ? '#00ff91' : (snr >= 20 ? '#ffb300' : '#ff6b6b'); + ctx.arc(x, y, isSelected ? 13 : 10, 0, Math.PI * 2); + ctx.fill(); + + ctx.strokeStyle = isSelected ? '#ffffff' : 'rgba(5,17,29,.9)'; + ctx.lineWidth = isSelected ? 3 : 2; + ctx.stroke(); + + ctx.fillStyle = '#041017'; + ctx.font = 'bold 10px Consolas'; + ctx.textAlign = 'center'; + ctx.fillText(String(item.prn || '?'), x, y + 3); + + gpsDiagnosticsState.plottedSatellites.push({ + prn: item.prn, + x: x, + y: y, + radius: isSelected ? 13 : 10 + }); + }); + } + + function onGpsSkyCanvasClick(event) { + var canvas = document.getElementById('gps_sky_canvas'); + if (!canvas) return; + var rect = canvas.getBoundingClientRect(); + var scaleX = canvas.width / rect.width; + var scaleY = canvas.height / rect.height; + var x = (event.clientX - rect.left) * scaleX; + var y = (event.clientY - rect.top) * scaleY; + var selectedPrn = null; + + gpsDiagnosticsState.plottedSatellites.forEach(function(item) { + var dx = x - item.x; + var dy = y - item.y; + if ((dx * dx + dy * dy) <= (item.radius + 6) * (item.radius + 6)) { + selectedPrn = item.prn; + } + }); + + if (!selectedPrn) { + hideGpsSatelliteTooltip(); + return; + } + gpsDiagnosticsState.selectedSatellitePrn = selectedPrn; + drawGpsSkyPlot(); + var tooltipLeft = Math.min(rect.width - 18, (event.clientX - rect.left) + 18); + var tooltipTop = Math.max(18, (event.clientY - rect.top) - 18); + renderGpsSatelliteTooltip(tooltipLeft, tooltipTop); + } + + function handleGpsDiagnostics(message) { + if (!message || !message.data) return; + try { + var data = JSON.parse(message.data); + gpsDiagnosticsState.connected = !!data.connected; + gpsDiagnosticsState.frameId = data.frame_id || 'gps'; + gpsDiagnosticsState.valid = !!data.valid; + gpsDiagnosticsState.fixQuality = Number(data.fix_quality || 0); + gpsDiagnosticsState.satellitesUsed = Number(data.satellites_used || 0); + gpsDiagnosticsState.satellitesVisible = Number(data.satellites_visible || 0); + gpsDiagnosticsState.latitude = typeof data.latitude === 'number' ? data.latitude : null; + gpsDiagnosticsState.longitude = typeof data.longitude === 'number' ? data.longitude : null; + gpsDiagnosticsState.altitudeM = typeof data.altitude_m === 'number' ? data.altitude_m : null; + gpsDiagnosticsState.hdop = typeof data.hdop === 'number' ? data.hdop : null; + gpsDiagnosticsState.pdop = typeof data.pdop === 'number' ? data.pdop : null; + gpsDiagnosticsState.vdop = typeof data.vdop === 'number' ? data.vdop : null; + gpsDiagnosticsState.speedMps = typeof data.speed_mps === 'number' ? data.speed_mps : null; + gpsDiagnosticsState.speedKmh = typeof data.speed_kmh === 'number' ? data.speed_kmh : null; + gpsDiagnosticsState.trackDeg = typeof data.track_deg === 'number' ? data.track_deg : null; + gpsDiagnosticsState.utcDate = data.utc_date || null; + gpsDiagnosticsState.utcTime = data.utc_time || null; + gpsDiagnosticsState.lastFixAgeS = typeof data.last_fix_age_s === 'number' ? data.last_fix_age_s : null; + gpsDiagnosticsState.satellites = Array.isArray(data.satellites) ? data.satellites : []; + if (gpsDiagnosticsState.selectedSatellitePrn) { + var stillExists = gpsDiagnosticsState.satellites.some(function(item) { + return item.prn === gpsDiagnosticsState.selectedSatellitePrn; + }); + if (!stillExists) gpsDiagnosticsState.selectedSatellitePrn = null; + } + updateGpsDiagnosticsPanel(); + } catch (error) {} + } + function updateMapTelemetryPanel() { setText('map_gps_status', roverMapState.gpsStatusText || 'Offline'); setText('map_gps_satellites', roverMapState.satellites); @@ -1113,6 +1606,7 @@ } function updateGpsStatusSummary(text) { + markAdminGpsFresh(); roverMapState.gpsStatusText = text || 'Offline'; var satMatch = String(roverMapState.gpsStatusText).match(/(\d+)\s*Sat/i); roverMapState.satellites = satMatch ? satMatch[1] : '--'; @@ -1121,6 +1615,7 @@ function handleGpsFix(message) { if (!message || !message.status) return; + markAdminGpsFresh(); roverMapState.hasFix = Number(message.status.status) >= 0; if (!roverMapState.hasFix) { @@ -1450,6 +1945,15 @@ } function renderDriveEstimator() { + if (!pageConnection.rosOk) { + setText('drive_estimator_speed', '--'); + setText('drive_estimator_distance', '--'); + setText('drive_estimator_pulse', '--'); + setText('drive_estimator_rpm', '--'); + setText('drive_estimator_mode', '--'); + setText('drive_estimator_direction', '--'); + return; + } updateDriveEstimatorModel(); setText('drive_estimator_speed', formatSpeedCms(driveEstimator.speedMps, 1)); setText('drive_estimator_distance', formatNumber(driveEstimator.distanceM, 2)); @@ -1572,9 +2076,11 @@ }); ros.on('connection', function() { + markAdminRosConnection(true); setDriveEstimatorState('verbunden', 'state_active'); }); ros.on('close', function() { + markAdminRosConnection(false); setDriveEstimatorState('getrennt', 'state_inactive'); ros = null; roverCommandTopic = null; @@ -1582,6 +2088,7 @@ window.setTimeout(connectDriveEstimatorRos, 3000); }); ros.on('error', function() { + markAdminRosConnection(false); setDriveEstimatorState('Fehler', 'state_unknown'); }); @@ -1621,6 +2128,13 @@ gpsStatusTopic.subscribe(function(message) { updateGpsStatusSummary(message.data); }); + + gpsDiagnosticsTopic = new ROSLIB.Topic({ + ros: ros, + name: '/gps/diagnostics_json', + messageType: 'std_msgs/String' + }); + gpsDiagnosticsTopic.subscribe(handleGpsDiagnostics); } function temperatureToWidth(value) { @@ -1649,8 +2163,10 @@ try { var response = await fetch(adminApiBase + '/api/status'); if (!response.ok) throw new Error('status ' + response.status); + markAdminApiConnection(true); renderStatus(await response.json()); } catch (e) { + markAdminApiConnection(false); setAdminStatus('API nicht erreichbar'); } } @@ -2116,18 +2632,23 @@ window.addEventListener('load', function() { var input = document.getElementById('auth_input'); + var gpsCanvas = document.getElementById('gps_sky_canvas'); input.focus(); input.addEventListener('keydown', function(e) { if (e.key === 'Enter') submitPassword(); }); + if (gpsCanvas) gpsCanvas.addEventListener('click', onGpsSkyCanvasClick); if (window.location.hash === '#motor') { switchAdminSection('motor'); } else if (window.location.hash === '#drive') { switchAdminSection('drive'); + } else if (window.location.hash === '#gps') { + switchAdminSection('gps'); } else if (window.location.hash === '#map') { switchAdminSection('map'); } updateClock(); + updatePageConnectionState(); window.setInterval(updateClock, 1000); fetchStatus(); fetchDelay(); @@ -2140,11 +2661,13 @@ renderWaypointList(); applyWaypointPlanningState(); renderDriveEstimator(); + updateGpsDiagnosticsPanel(); window.setInterval(fetchStatus, 10000); window.setInterval(fetchDelay, 5000); window.setInterval(fetchSpeedLimit, 5000); window.setInterval(fetchMotorTestStatus, 4000); window.setInterval(renderDriveEstimator, 250); + window.setInterval(updatePageConnectionState, 1000); }); window.addEventListener('beforeunload', function(event) { diff --git a/ExoMy_Software-master/gui/app.css b/ExoMy_Software-master/gui/app.css index 53639f4..41fd846 100644 --- a/ExoMy_Software-master/gui/app.css +++ b/ExoMy_Software-master/gui/app.css @@ -184,6 +184,15 @@ header { } #stat-panel { flex-shrink: 0; } +#joy-panel.is-disabled { + opacity: .55; +} +#joy-panel.is-disabled .joy-base, +#joy-panel.is-disabled .dpad-btn, +#joy-panel.is-disabled .dbtn, +#joy-panel.is-disabled .mtbtn { + cursor: not-allowed; +} .drive-btns { display: flex; gap: 8px; padding: 10px 12px; } .dbtn { @@ -298,6 +307,9 @@ header { letter-spacing: calc(2px * var(--cam-scale)); z-index: 10; } +.c-rec.is-offline { + color: rgba(170,255,220,.72); +} .c-rec-dot { width: calc(6px * var(--cam-scale)); height: calc(6px * var(--cam-scale)); @@ -305,6 +317,10 @@ header { background: #FF3333; animation: blink 1.1s step-end infinite; } +.c-rec.is-offline .c-rec-dot { + background: rgba(170,255,220,.72); + animation: none; +} @keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: 0; } } .c-tl, .c-tr, .c-bl, .c-br { position: absolute; @@ -324,34 +340,59 @@ header { left: 50%; bottom: calc(13px * var(--cam-scale)); transform: translateX(-50%); - display: flex; - align-items: center; - gap: calc(8px * var(--cam-scale)); z-index: 10; pointer-events: none; -} -.c-bm-box { - display: flex; - align-items: baseline; - gap: calc(5px * var(--cam-scale)); - padding: calc(6px * var(--cam-scale)) calc(10px * var(--cam-scale)); - background: rgba(3, 10, 14, .72); - border: 1px solid rgba(0,255,145,.18); - box-shadow: inset 0 0 12px rgba(0,255,145,.04), 0 0 16px rgba(0,0,0,.18); - backdrop-filter: blur(2px); -} -.c-bm-lbl, -.c-bm-unit { - font-size: calc(8px * var(--cam-scale)); color: rgba(0,255,145,.72); - letter-spacing: calc(1.2px * var(--cam-scale)); - text-transform: uppercase; -} -.c-bm-box strong { - font-size: calc(14px * var(--cam-scale)); - line-height: 1; - color: rgba(245,255,250,.98); + font-size: calc(9px * var(--cam-scale)); letter-spacing: calc(1px * var(--cam-scale)); + line-height: 1.9; + text-align: center; + white-space: nowrap; +} +.connection-banner{ + display:flex; + align-items:center; + gap:12px; + min-height:36px; + padding:8px 16px; + border-bottom:1px solid rgba(255,179,0,.2); + background:linear-gradient(90deg, rgba(255,179,0,.14) 0%, rgba(255,140,0,.06) 60%, transparent 100%); + color:rgba(255,235,190,.92); + font-size:11px; + letter-spacing:.4px; +} +.connection-banner strong{ + font-size:10px; + letter-spacing:2px; + text-transform:uppercase; + color:#fff3cf; +} +body.page-connection-offline .connection-banner{ + border-bottom-color:rgba(255,107,107,.3); + background:linear-gradient(90deg, rgba(255,107,107,.18) 0%, rgba(120,12,12,.12) 60%, transparent 100%); + color:rgba(255,220,220,.94); +} +body.page-connection-offline .mission-ok{ + color:#ffb0b0; +} +body.page-connection-offline .pulse-dot{ + background:#ff6b6b; + box-shadow:0 0 0 0 rgba(255,107,107,.5); +} +body.page-connection-degraded .mission-ok{ + color:#ffd37a; +} +body.page-connection-degraded .pulse-dot{ + background:var(--warn); + box-shadow:0 0 0 0 rgba(255,179,0,.5); +} +body.page-connection-offline .main, +body.page-connection-offline .admin-shell{ + opacity:.74; +} +body.page-connection-degraded .main, +body.page-connection-degraded .admin-shell{ + opacity:.9; } /* ── STAT PANEL ─────────────────────────────────────── */ .c-track { @@ -1186,13 +1227,23 @@ body.page-admin .drive-calibration-button{ body.page-admin .admin-section-grid_map{ grid-template-columns:minmax(0, 1.7fr) minmax(280px, .85fr); } +body.page-admin .admin-section-grid_gps{ + grid-template-columns:minmax(0, 1.45fr) minmax(300px, .95fr); +} body.page-admin .admin-col_map_main, body.page-admin .admin-col_map_side{ min-width:0; } +body.page-admin .admin-col_gps_main, +body.page-admin .admin-col_gps_side{ min-width:0; } body.page-admin .map-panel{ display:flex; flex-direction:column; min-height:620px; } +body.page-admin .gps-panel{ + display:flex; + flex-direction:column; + min-height:620px; +} body.page-admin .map-toolbar{ display:flex; gap:8px; @@ -1230,6 +1281,78 @@ body.page-admin .map-route-storage-input{ body.page-admin .map-route-storage-input:focus{ border-color:rgba(0,255,145,.5); } +body.page-admin .gps-copy{ + font-size:10px; + color:rgba(205,255,235,.86); + letter-spacing:.4px; + line-height:1.6; + padding:10px 12px; + border-bottom:1px solid rgba(0,255,145,.05); +} +body.page-admin .gps-copy_compact{ + border-bottom:none; +} +body.page-admin .gps-sky-stage{ + padding:18px; + position:relative; +} +body.page-admin .gps-sky-canvas{ + display:block; + width:100%; + max-width:560px; + aspect-ratio:1 / 1; + margin:0 auto; + border:1px solid rgba(0,255,145,.12); + background:radial-gradient(circle at 50% 50%, rgba(0,255,145,.04) 0%, rgba(4,16,26,.96) 70%); + cursor:pointer; +} +body.page-admin .gps-satellite-tooltip{ + position:absolute; + z-index:20; + min-width:180px; + max-width:220px; + padding:10px 12px; + border:1px solid rgba(0,255,145,.18); + background:rgba(4,16,26,.96); + box-shadow:0 10px 28px rgba(0,0,0,.34), inset 0 0 14px rgba(0,255,145,.04); + transform:translate(-50%, -100%); + pointer-events:none; +} +body.page-admin .gps-satellite-tooltip-title{ + margin-bottom:8px; + color:rgba(245,255,250,.98); + font-size:11px; + font-weight:700; + letter-spacing:.5px; +} +body.page-admin .gps-satellite-tooltip-row{ + display:flex; + align-items:baseline; + justify-content:space-between; + gap:10px; +} +body.page-admin .gps-satellite-tooltip-row + .gps-satellite-tooltip-row{ + margin-top:5px; +} +body.page-admin .gps-satellite-tooltip-row span{ + color:rgba(170,255,220,.72); + font-size:8px; + letter-spacing:1px; + text-transform:uppercase; +} +body.page-admin .gps-satellite-tooltip-row strong{ + color:rgba(245,255,250,.98); + font-size:10px; + letter-spacing:.4px; +} +body.page-admin .gps-satellite-empty{ + padding:14px 10px; + border:1px solid rgba(0,255,145,.08); + background:rgba(0,255,145,.025); + color:rgba(175,255,220,.7); + font-size:10px; + letter-spacing:.4px; +} body.page-admin .map-stage{ position:relative; flex:1; @@ -1402,7 +1525,9 @@ body.page-admin .leaflet-popup-tip{ body.page-admin .drive-calibration-toolbar{ flex-direction:column; align-items:stretch; } body.page-admin .drive-calibration-grid{ grid-template-columns:1fr; } body.page-admin .admin-section-grid_map{ grid-template-columns:1fr; } + body.page-admin .admin-section-grid_gps{ grid-template-columns:1fr; } body.page-admin .map-panel{ min-height:0; } + body.page-admin .gps-panel{ min-height:0; } body.page-admin .map-stage{ min-height:360px; } body.page-admin .map-route-storage, body.page-admin .map-route-storage_secondary{ grid-template-columns:1fr; } diff --git a/ExoMy_Software-master/gui/index.html b/ExoMy_Software-master/gui/index.html index 8d4c542..e901f46 100644 --- a/ExoMy_Software-master/gui/index.html +++ b/ExoMy_Software-master/gui/index.html @@ -9,17 +9,26 @@
- - - - - - - - - - - + EXOMY GROUND CTRL @@ -28,7 +37,7 @@
Admin
-
Bereit
+
Cuno offline
@@ -54,7 +63,7 @@
Kamerabild vom ExoMy -
LIVE
+
LIVE
CAM: Verbinde…
MODE: Ackermann @@ -74,16 +83,8 @@ STREAM: 8083
-
- V - 0,0 - cm/s -
-
- S - 0,00 - m -
+ V: 0,0 cm/s
+ S: 0,00 m