Alles verbessert

This commit is contained in:
2026-05-21 19:30:33 +02:00
parent 5dccc8985e
commit 33b71416ff
19 changed files with 855 additions and 229 deletions
+155 -57
View File
@@ -187,6 +187,12 @@ header {
color: #fff;
box-shadow: 0 0 24px rgba(0,136,255,.45), inset 0 0 16px rgba(255,255,255,.1);
}
@keyframes mode-flash {
0% { box-shadow: 0 0 0px rgba(0,255,145,0), background-color: var(--pri); }
30% { box-shadow: 0 0 28px rgba(0,255,145,.9), background-color: #00ff91; }
100% { box-shadow: 0 0 24px rgba(0,136,255,.45), background-color: var(--pri); }
}
.dbtn.flash { animation: mode-flash .5s ease-out forwards; }
#cam-panel { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.cam-port {
@@ -205,19 +211,6 @@ header {
background: #05070d;
z-index: 1;
}
.c-scanlines {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 5;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 3px,
rgba(0,0,0,.09) 3px,
rgba(0,0,0,.09) 4px
);
}
.c-vignette {
position: absolute;
inset: 0;
@@ -230,42 +223,19 @@ header {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50px;
height: 50px;
width: 180px;
height: 180px;
z-index: 10;
pointer-events: none;
}
.c-xhair::before {
content: "";
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: rgba(0,255,145,.45);
transform: translateY(-50%);
@keyframes xh-spin { to { transform: rotate(360deg); } }
@keyframes xh-pulse {
0% { transform: scale(1); opacity: .7; }
100% { transform: scale(2.8); opacity: 0; }
}
.c-xhair::after {
content: "";
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 1px;
background: rgba(0,255,145,.45);
transform: translateX(-50%);
}
.xc {
position: absolute;
width: 9px;
height: 9px;
border-color: rgba(0,255,145,.7);
border-style: solid;
border-width: 0;
}
.xc.tl { top: 0; left: 0; border-top-width: 1.5px; border-left-width: 1.5px; }
.xc.tr { top: 0; right: 0; border-top-width: 1.5px; border-right-width: 1.5px; }
.xc.bl { bottom: 0; left: 0; border-bottom-width: 1.5px; border-left-width: 1.5px; }
.xc.br { bottom: 0; right: 0; border-bottom-width: 1.5px; border-right-width: 1.5px; }
.xh-rot { animation: xh-spin 14s linear infinite; transform-box: fill-box; transform-origin: center; }
.xh-rot-rev { animation: xh-spin 8s linear infinite reverse; transform-box: fill-box; transform-origin: center; }
.xh-pulse { animation: xh-pulse 2.5s ease-out infinite; transform-box: fill-box; transform-origin: center; }
.c-rec {
position: absolute;
top: 13px;
@@ -328,11 +298,20 @@ header {
.sval.g { color: var(--ok); }
.sval.o { color: var(--sec); }
.sval.b { color: var(--pri); }
.sval.alert {
color: #D50000;
animation: uv-blink 0.9s step-end infinite;
}
.sval.warntext { color: var(--sec); }
.sval-conn { display: flex; align-items: center; gap: 5px; color: var(--ok); font-size: 10px; }
.sbar-wrap { display: flex; align-items: center; gap: 8px; }
.sbar { width: 52px; height: 3px; background: rgba(0,136,255,.12); border-radius: 1px; overflow: hidden; }
.sbar-i { height: 100%; border-radius: 1px; background: var(--pri); }
.sbar-i.w { background: var(--sec); }
@keyframes uv-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.2; }
}
#joy-panel .p-hdr { padding: 14px 12px; }
.mode-toggle { display: flex; gap: 2px; margin-left: auto; }
@@ -563,10 +542,41 @@ header {
STREAM: 8081
</div>
<div class="c-xhair">
<div class="xc tl"></div><div class="xc tr"></div>
<div class="xc bl"></div><div class="xc br"></div>
<svg viewBox="-90 -90 180 180" xmlns="http://www.w3.org/2000/svg" width="180" height="180">
<!-- äußerer rotierender Strichelring -->
<circle class="xh-rot" cx="0" cy="0" r="82" fill="none" stroke="rgba(0,255,145,.2)" stroke-width="1" stroke-dasharray="4 10"/>
<!-- mittlerer Ring, Gegenrichtung -->
<circle class="xh-rot-rev" cx="0" cy="0" r="60" fill="none" stroke="rgba(0,255,145,.15)" stroke-width=".75" stroke-dasharray="2 16"/>
<!-- innerer fester Ring -->
<circle cx="0" cy="0" r="16" fill="none" stroke="rgba(0,255,145,.3)" stroke-width=".75"/>
<!-- Kreuzlinien mit Lücke in der Mitte -->
<line x1="-86" y1="0" x2="-19" y2="0" stroke="rgba(0,255,145,.6)" stroke-width="1"/>
<line x1="19" y1="0" x2="86" y2="0" stroke="rgba(0,255,145,.6)" stroke-width="1"/>
<line x1="0" y1="-86" x2="0" y2="-19" stroke="rgba(0,255,145,.6)" stroke-width="1"/>
<line x1="0" y1="19" x2="0" y2="86" stroke="rgba(0,255,145,.6)" stroke-width="1"/>
<!-- Skalenstriche bei r=60 -->
<line x1="-60" y1="-6" x2="-60" y2="6" stroke="rgba(0,255,145,.45)" stroke-width=".75"/>
<line x1="60" y1="-6" x2="60" y2="6" stroke="rgba(0,255,145,.45)" stroke-width=".75"/>
<line x1="-6" y1="-60" x2="6" y2="-60" stroke="rgba(0,255,145,.45)" stroke-width=".75"/>
<line x1="-6" y1="60" x2="6" y2="60" stroke="rgba(0,255,145,.45)" stroke-width=".75"/>
<!-- Skalenstriche bei r=38 -->
<line x1="-38" y1="-3.5" x2="-38" y2="3.5" stroke="rgba(0,255,145,.3)" stroke-width=".5"/>
<line x1="38" y1="-3.5" x2="38" y2="3.5" stroke="rgba(0,255,145,.3)" stroke-width=".5"/>
<line x1="-3.5" y1="-38" x2="3.5" y2="-38" stroke="rgba(0,255,145,.3)" stroke-width=".5"/>
<line x1="-3.5" y1="38" x2="3.5" y2="38" stroke="rgba(0,255,145,.3)" stroke-width=".5"/>
<!-- Eckklammern -->
<path d="M-74,-58 L-74,-74 L-58,-74" fill="none" stroke="rgba(0,255,145,.75)" stroke-width="1.5"/>
<path d="M58,-74 L74,-74 L74,-58" fill="none" stroke="rgba(0,255,145,.75)" stroke-width="1.5"/>
<path d="M-74,58 L-74,74 L-58,74" fill="none" stroke="rgba(0,255,145,.75)" stroke-width="1.5"/>
<path d="M58,74 L74,74 L74,58" fill="none" stroke="rgba(0,255,145,.75)" stroke-width="1.5"/>
<!-- pulsierender Ring -->
<circle class="xh-pulse" cx="0" cy="0" r="6" fill="none" stroke="rgba(0,255,145,.7)" stroke-width="1"/>
<!-- Mittelpunkt -->
<circle cx="0" cy="0" r="2.5" fill="rgba(0,255,145,.9)"/>
<circle cx="0" cy="0" r="1" fill="rgba(255,255,255,.7)"/>
</svg>
</div>
<div class="c-scanlines"></div>
<div class="c-vignette"></div>
</div>
</div>
@@ -633,6 +643,10 @@ header {
<span class="slbl">IP Adressen</span>
<span class="sval b" id="stat-ips">--</span>
</div>
<div class="srow warn">
<span class="slbl">Unterspannung</span>
<span class="sval b" id="stat-undervoltage">--</span>
</div>
<div class="srow info">
<span class="slbl">Uptime Pi</span>
<span class="sval b" id="stat-uptime">--</span>
@@ -704,6 +718,7 @@ header {
var hostUrl = window.location.hostname;
var ros = null;
var joyListener = null;
var roverCommandListener = null;
var publishTimer = null;
var statusTimer = null;
var axes = [0, 0, 0, 0, 0, 0];
@@ -771,6 +786,45 @@ function updateModeDisplay() {
setText("stat-mode", label);
}
function applyDriveMode(mode, flash) {
var changed = mode !== driveMode;
driveMode = mode;
document.querySelectorAll(".dbtn[data-mode]").forEach(function (btn) {
var isActive = btn.dataset.mode === mode;
btn.classList.toggle("active", isActive);
if (isActive && changed && flash) {
btn.classList.remove("flash");
void btn.offsetWidth;
btn.classList.add("flash");
}
});
updateModeDisplay();
updateControlLocks();
}
function mirrorJoyThumb(x, y) {
if (dragging || inputMode !== "joystick") return;
var data = joyMetrics();
var dx = x * data.maxD;
var dy = -y * data.maxD;
var thumb = document.getElementById("jthumb");
thumb.style.transition = "left .05s, top .05s";
thumb.style.left = "calc(50% + " + dx + "px)";
thumb.style.top = "calc(50% + " + dy + "px)";
thumb.style.transform = "translate(-50%,-50%)";
updateAxesDisplay(x, y);
}
function locomotionModeToUi(modeValue) {
if (modeValue === 2) {
return "turn-on-point";
}
if (modeValue === 3) {
return "crab";
}
return "ackermann";
}
function updateMotorDisplay() {
setText("stat-motors", motorsEnabled ? "Aktiv" : "Aus");
}
@@ -787,11 +841,35 @@ function setRosStatus(text) {
setText("mission-state", text === "Verbunden" ? "Nominal" : text);
}
function updateUndervoltageDisplay(text, state) {
var el = document.getElementById("stat-undervoltage");
if (!el) {
return;
}
el.textContent = text || "--";
el.classList.remove("b", "g", "o", "alert", "warntext");
if (state === "active") {
el.classList.add("alert");
return;
}
if (state === "past") {
el.classList.add("warntext");
return;
}
if (state === "clear") {
el.classList.add("g");
return;
}
el.classList.add("b");
}
function updateServiceState(payload) {
var system = payload.system || {};
var services = payload.services || {};
setText("stat-wifi", system.wifi || "unbekannt");
setText("stat-ips", system.ips || "unbekannt");
updateUndervoltageDisplay(system.undervoltage || "unbekannt", system.undervoltage_state || "unknown");
setText("stat-uptime", system.uptime || "unbekannt");
setText("stat-disk", system.disk_free || "unbekannt");
setText("val-cpu", system.cpu_usage || "--");
@@ -832,10 +910,10 @@ function publishJoy() {
}
function startPublishing() {
publishJoy();
if (publishTimer) {
clearInterval(publishTimer);
return;
}
publishJoy();
publishTimer = window.setInterval(publishJoy, 50);
}
@@ -868,12 +946,7 @@ function pulseButton(buttonIndex) {
}
function selectDriveMode(mode, buttonIndex) {
driveMode = mode;
document.querySelectorAll(".dbtn[data-mode]").forEach(function (btn) {
btn.classList.toggle("active", btn.dataset.mode === mode);
});
updateModeDisplay();
updateControlLocks();
applyDriveMode(mode);
pulseButton(buttonIndex);
}
@@ -940,6 +1013,7 @@ function moveThumb(clientX, clientY) {
var nx = dx / data.maxD;
var ny = -dy / data.maxD;
setAxes(nx, ny);
publishJoy();
startPublishing();
}
@@ -1013,6 +1087,30 @@ window.addEventListener("load", function () {
messageType: "sensor_msgs/Joy"
});
roverCommandListener = new ROSLIB.Topic({
ros: ros,
name: "/rover_command",
messageType: "exomy/RoverCommand"
});
roverCommandListener.subscribe(function (message) {
applyDriveMode(locomotionModeToUi(message.locomotion_mode), true);
motorsEnabled = !!message.motors_enabled;
updateMotorDisplay();
});
var joySubscriber = new ROSLIB.Topic({
ros: ros,
name: "/joy",
messageType: "sensor_msgs/Joy"
});
joySubscriber.subscribe(function (message) {
if (message.header.frame_id === "webgui") return;
var x = (message.axes[0] || 0);
var y = -(message.axes[1] || 0);
mirrorJoyThumb(x, y);
});
document.querySelectorAll(".dbtn[data-mode]").forEach(function (button) {
button.addEventListener("click", function () {
selectDriveMode(button.dataset.mode, parseInt(button.dataset.button, 10));