GPS im Admin und offline verbesserungen
This commit is contained in:
@@ -9,17 +9,26 @@
|
||||
</head>
|
||||
<body class="page-index">
|
||||
<header>
|
||||
<svg class="hdr-icon" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="7" y="11" width="18" height="10" rx="1" fill="#0088FF" opacity=".85"/>
|
||||
<rect x="11" y="7" width="10" height="4" rx="1" fill="#0088FF" opacity=".5"/>
|
||||
<circle cx="9" cy="23" r="3.5" fill="none" stroke="#0088FF" stroke-width="1.5"/>
|
||||
<circle cx="23" cy="23" r="3.5" fill="none" stroke="#0088FF" stroke-width="1.5"/>
|
||||
<circle cx="9" cy="23" r="1.3" fill="#0088FF"/>
|
||||
<circle cx="23" cy="23" r="1.3" fill="#0088FF"/>
|
||||
<rect x="22" y="8" width="4" height="3" rx=".5" fill="#FF6B00" opacity=".9"/>
|
||||
<line x1="16" y1="7" x2="16" y2="3" stroke="#0088FF" stroke-width="1.5"/>
|
||||
<circle cx="16" cy="2.5" r="1" fill="#0088FF"/>
|
||||
<rect x="3" y="14" width="4" height="2" rx="1" fill="#0088FF" opacity=".6"/>
|
||||
<svg class="hdr-icon" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<ellipse cx="14" cy="51" rx="6" ry="7" fill="#FFB21F"/>
|
||||
<ellipse cx="28" cy="50" rx="6" ry="8" fill="#FFB21F"/>
|
||||
<ellipse cx="43" cy="50" rx="6" ry="8" fill="#FFB21F"/>
|
||||
<ellipse cx="55" cy="49" rx="5" ry="7" fill="#FFB21F"/>
|
||||
<path d="M11 36L23 30H50L58 32L47 37H14L11 36Z" fill="#1F43C9"/>
|
||||
<rect x="24" y="14" width="7" height="23" rx="3.5" fill="#1B120F"/>
|
||||
<path d="M18 38L25 36L41 36L51 40L51 46L42 49L38 59L31 59L29 50L19 47L18 38Z" fill="#1B120F"/>
|
||||
<rect x="21" y="38" width="5" height="10" rx="2.5" fill="#1B120F"/>
|
||||
<rect x="43" y="39" width="5" height="11" rx="2.5" fill="#1B120F"/>
|
||||
<path d="M15 41L9 44L8 49" stroke="#1B120F" stroke-width="4" stroke-linecap="round"/>
|
||||
<path d="M49 41L57 43L57 47" stroke="#1B120F" stroke-width="4" stroke-linecap="round"/>
|
||||
<path d="M14 16C14 12 17 9 22 9H32C38 9 41 12 41 16C41 22 36 26 28 26H20C16 26 14 23 14 16Z" fill="#FFB21F"/>
|
||||
<circle cx="21" cy="15.5" r="3.2" fill="white"/>
|
||||
<circle cx="30.5" cy="14.5" r="3.6" fill="white"/>
|
||||
<circle cx="21.5" cy="17" r="1.8" fill="#000"/>
|
||||
<circle cx="30.8" cy="16" r="2.1" fill="#000"/>
|
||||
<path d="M25 18C25.8 19.2 27.2 19.2 28 18V20.2C28 21.8 26.8 22.7 25.5 22.7C24.2 22.7 23 21.8 23 20.2V18C23.6 18.8 24.4 19.1 25 18Z" fill="#1B120F"/>
|
||||
<path d="M12 12C13.5 9.2 17.8 8.2 21 10" stroke="#1B120F" stroke-width="2.8" stroke-linecap="round"/>
|
||||
<path d="M29 7.5C31.8 6.2 35.6 7.1 37.6 9.7" stroke="#1B120F" stroke-width="2.8" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
<span class="hdr-title">EXO<em>MY</em> GROUND CTRL</span>
|
||||
@@ -28,7 +37,7 @@
|
||||
<div class="hdr-gap"></div>
|
||||
<a class="admin-link" href="./admin.html">Admin</a>
|
||||
<div class="hdr-div"></div>
|
||||
<div class="mission-ok"><div class="pulse-dot"></div><span id="mission-state">Bereit</span></div>
|
||||
<div class="mission-ok"><div class="pulse-dot"></div><span id="cuno-connection-status">Cuno offline</span></div>
|
||||
<div class="hdr-div"></div>
|
||||
<div class="hdr-clocks">
|
||||
<div class="hdr-clk">
|
||||
@@ -54,7 +63,7 @@
|
||||
<div class="cam-port">
|
||||
<div class="cam-frame" id="video_frame">
|
||||
<img id="video_output" alt="Kamerabild vom ExoMy" />
|
||||
<div class="c-rec"><div class="c-rec-dot"></div>LIVE</div>
|
||||
<div class="c-rec" id="cam-live-indicator"><div class="c-rec-dot"></div><span id="cam-live-text">LIVE</span></div>
|
||||
<div class="c-tl">
|
||||
CAM: <span id="cam-state">Verbinde…</span><br>
|
||||
MODE: <span id="overlay-mode">Ackermann</span>
|
||||
@@ -74,16 +83,8 @@
|
||||
STREAM: 8083
|
||||
</div>
|
||||
<div class="c-bm">
|
||||
<div class="c-bm-box">
|
||||
<span class="c-bm-lbl">V</span>
|
||||
<strong id="overlay-speed">0,0</strong>
|
||||
<span class="c-bm-unit">cm/s</span>
|
||||
</div>
|
||||
<div class="c-bm-box">
|
||||
<span class="c-bm-lbl">S</span>
|
||||
<strong id="overlay-distance">0,00</strong>
|
||||
<span class="c-bm-unit">m</span>
|
||||
</div>
|
||||
V: <span id="overlay-speed">0,0</span> cm/s<br>
|
||||
S: <span id="overlay-distance">0,00</span> m
|
||||
</div>
|
||||
<svg class="c-track" id="track-overlay" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
|
||||
<path id="track-center" class="track-center" d=""></path>
|
||||
@@ -310,6 +311,13 @@ var trajectoryOverlay = {
|
||||
steering: 90,
|
||||
velocity: 0
|
||||
};
|
||||
var pageConnection = {
|
||||
apiOk: false,
|
||||
apiLastAt: 0,
|
||||
rosOk: false,
|
||||
gpsLastAt: 0,
|
||||
state: "offline"
|
||||
};
|
||||
|
||||
function setText(id, text) {
|
||||
var el = document.getElementById(id);
|
||||
@@ -445,6 +453,11 @@ function updateDriveEstimatorModel() {
|
||||
}
|
||||
|
||||
function renderDriveEstimatorOverlay() {
|
||||
if (!pageConnection.rosOk) {
|
||||
setText("overlay-speed", "--");
|
||||
setText("overlay-distance", "--");
|
||||
return;
|
||||
}
|
||||
updateDriveEstimatorModel();
|
||||
setText("overlay-speed", formatSpeedCms(driveEstimator.speedMps, 1));
|
||||
setText("overlay-distance", formatNumber(driveEstimator.distanceM, 2));
|
||||
@@ -592,6 +605,7 @@ function updateAxesDisplay(x, y) {
|
||||
|
||||
function updateGpsOverlayFromFix(message) {
|
||||
if (!message) return;
|
||||
markGpsFresh();
|
||||
|
||||
var hasFix = message.status && message.status.status >= 0;
|
||||
if (!hasFix) {
|
||||
@@ -606,6 +620,7 @@ function updateGpsOverlayFromFix(message) {
|
||||
|
||||
function updateGpsOverlayFromStatus(text) {
|
||||
var value = String(text || "Offline");
|
||||
markGpsFresh();
|
||||
setText("gps-state", value);
|
||||
|
||||
var satMatch = value.match(/(\d+)\s*Sat/i);
|
||||
@@ -675,7 +690,96 @@ function updateInputDisplay() {
|
||||
function setRosStatus(text) {
|
||||
setText("status", text);
|
||||
setText("ros-state", text);
|
||||
setText("mission-state", text === "Verbunden" ? "Nominal" : text);
|
||||
}
|
||||
|
||||
function controlsAreBlocked() {
|
||||
return pageConnection.state === "offline";
|
||||
}
|
||||
|
||||
function updateControlAvailability() {
|
||||
var panel = document.getElementById("joy-panel");
|
||||
if (panel) {
|
||||
panel.classList.toggle("is-disabled", controlsAreBlocked());
|
||||
}
|
||||
}
|
||||
|
||||
function clearSystemStatusDisplay() {
|
||||
setText("stat-wifi", "--");
|
||||
setText("stat-ips", "--");
|
||||
updateUndervoltageDisplay("--");
|
||||
setText("stat-uptime", "--");
|
||||
setText("stat-disk", "--");
|
||||
setText("val-cpu", "--");
|
||||
setText("val-temp", "--");
|
||||
setText("val-mem", "--");
|
||||
document.getElementById("bar-cpu").style.width = "0%";
|
||||
document.getElementById("bar-mem").style.width = "0%";
|
||||
document.getElementById("bar-temp").style.width = "0%";
|
||||
}
|
||||
|
||||
function clearRosLiveDisplay() {
|
||||
setRosStatus("Offline");
|
||||
setText("cam-state", "Offline");
|
||||
setText("cam-host", "--");
|
||||
setText("cam-live-text", "OFFLINE");
|
||||
var liveIndicator = document.getElementById("cam-live-indicator");
|
||||
if (liveIndicator) {
|
||||
liveIndicator.classList.add("is-offline");
|
||||
}
|
||||
setText("gps-state", "Offline");
|
||||
setText("gps-sats", "--");
|
||||
setText("gps-lat", "--");
|
||||
setText("gps-lon", "--");
|
||||
setText("overlay-speed", "--");
|
||||
setText("overlay-distance", "--");
|
||||
}
|
||||
|
||||
function setPageConnectionState() {
|
||||
var now = Date.now();
|
||||
var apiFresh = pageConnection.apiOk && (now - pageConnection.apiLastAt) < 15000;
|
||||
var rosFresh = !!pageConnection.rosOk;
|
||||
var gpsFresh = pageConnection.gpsLastAt && (now - pageConnection.gpsLastAt) < 12000;
|
||||
var state = "offline";
|
||||
var cunoTitle = "Cuno offline";
|
||||
|
||||
if (apiFresh && rosFresh) {
|
||||
state = gpsFresh || !pageConnection.gpsLastAt ? "online" : "degraded";
|
||||
} else if (apiFresh || rosFresh) {
|
||||
state = "degraded";
|
||||
}
|
||||
|
||||
cunoTitle = state === "online" ? "Cuno online" : "Cuno offline";
|
||||
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("cuno-connection-status", cunoTitle);
|
||||
updateControlAvailability();
|
||||
|
||||
if (!apiFresh) {
|
||||
clearSystemStatusDisplay();
|
||||
}
|
||||
if (!rosFresh) {
|
||||
clearRosLiveDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
function markApiConnection(ok) {
|
||||
pageConnection.apiOk = !!ok;
|
||||
if (ok) {
|
||||
pageConnection.apiLastAt = Date.now();
|
||||
}
|
||||
setPageConnectionState();
|
||||
}
|
||||
|
||||
function markRosConnection(ok) {
|
||||
pageConnection.rosOk = !!ok;
|
||||
setPageConnectionState();
|
||||
}
|
||||
|
||||
function markGpsFresh() {
|
||||
pageConnection.gpsLastAt = Date.now();
|
||||
setPageConnectionState();
|
||||
}
|
||||
|
||||
function updateUndervoltageDisplay(text, state) {
|
||||
@@ -727,14 +831,16 @@ function fetchSystemStatus() {
|
||||
}
|
||||
return response.json();
|
||||
}).then(function (payload) {
|
||||
markApiConnection(true);
|
||||
updateServiceState(payload);
|
||||
}).catch(function () {
|
||||
markApiConnection(false);
|
||||
setText("stat-wifi", "nicht erreichbar");
|
||||
});
|
||||
}
|
||||
|
||||
function publishJoy() {
|
||||
if (!joyListener) {
|
||||
if (!joyListener || controlsAreBlocked()) {
|
||||
return;
|
||||
}
|
||||
joyListener.publish(new ROSLIB.Message({
|
||||
@@ -772,6 +878,9 @@ function resetAxes() {
|
||||
}
|
||||
|
||||
function pulseButton(buttonIndex) {
|
||||
if (controlsAreBlocked()) {
|
||||
return;
|
||||
}
|
||||
axes = [0, 0, 0, 0, 0, 0];
|
||||
updateAxesDisplay(0, 0);
|
||||
buttons[buttonIndex] = 1;
|
||||
@@ -783,11 +892,17 @@ function pulseButton(buttonIndex) {
|
||||
}
|
||||
|
||||
function selectDriveMode(mode, buttonIndex) {
|
||||
if (controlsAreBlocked()) {
|
||||
return;
|
||||
}
|
||||
applyDriveMode(mode);
|
||||
pulseButton(buttonIndex);
|
||||
}
|
||||
|
||||
function toggleMotors() {
|
||||
if (controlsAreBlocked()) {
|
||||
return;
|
||||
}
|
||||
motorsEnabled = !motorsEnabled;
|
||||
updateMotorDisplay();
|
||||
pulseButton(9);
|
||||
@@ -831,6 +946,9 @@ function joyMetrics() {
|
||||
}
|
||||
|
||||
function moveThumb(clientX, clientY) {
|
||||
if (controlsAreBlocked()) {
|
||||
return;
|
||||
}
|
||||
var data = joyMetrics();
|
||||
var dx = clientX - data.cx;
|
||||
var dy = clientY - data.cy;
|
||||
@@ -865,6 +983,9 @@ function resetThumb() {
|
||||
}
|
||||
|
||||
function setDpadPressed(button, x, y) {
|
||||
if (controlsAreBlocked()) {
|
||||
return;
|
||||
}
|
||||
document.querySelectorAll(".dpad-btn").forEach(function (btn) {
|
||||
btn.classList.remove("pressed");
|
||||
});
|
||||
@@ -959,10 +1080,12 @@ window.addEventListener("load", function () {
|
||||
updateClocks();
|
||||
window.setInterval(updateClocks, 1000);
|
||||
fetchSystemStatus();
|
||||
setPageConnectionState();
|
||||
fetchDriveEstimatorData();
|
||||
renderDriveEstimatorOverlay();
|
||||
renderTrajectoryOverlay();
|
||||
statusTimer = window.setInterval(fetchSystemStatus, 5000);
|
||||
window.setInterval(setPageConnectionState, 1000);
|
||||
window.setInterval(renderDriveEstimatorOverlay, 250);
|
||||
window.setInterval(renderTrajectoryOverlay, 250);
|
||||
|
||||
@@ -970,10 +1093,21 @@ window.addEventListener("load", function () {
|
||||
video.src = "//" + hostUrl + ":8083/stream.mjpg";
|
||||
video.addEventListener("load", function () {
|
||||
setText("cam-state", "Online");
|
||||
setText("cam-host", hostUrl);
|
||||
setText("cam-live-text", "LIVE");
|
||||
var liveIndicator = document.getElementById("cam-live-indicator");
|
||||
if (liveIndicator) {
|
||||
liveIndicator.classList.remove("is-offline");
|
||||
}
|
||||
updateCameraFrameLayout();
|
||||
});
|
||||
video.addEventListener("error", function () {
|
||||
setText("cam-state", "Fehler");
|
||||
setText("cam-live-text", "OFFLINE");
|
||||
var liveIndicator = document.getElementById("cam-live-indicator");
|
||||
if (liveIndicator) {
|
||||
liveIndicator.classList.add("is-offline");
|
||||
}
|
||||
});
|
||||
updateCameraFrameLayout();
|
||||
window.addEventListener("resize", updateCameraFrameLayout);
|
||||
@@ -1006,14 +1140,17 @@ window.addEventListener("load", function () {
|
||||
});
|
||||
|
||||
ros.on("connection", function () {
|
||||
markRosConnection(true);
|
||||
setRosStatus("Verbunden");
|
||||
});
|
||||
|
||||
ros.on("error", function () {
|
||||
markRosConnection(false);
|
||||
setRosStatus("Fehler");
|
||||
});
|
||||
|
||||
ros.on("close", function () {
|
||||
markRosConnection(false);
|
||||
setRosStatus("Getrennt");
|
||||
});
|
||||
|
||||
@@ -1074,6 +1211,7 @@ window.addEventListener("load", function () {
|
||||
messageType: "sensor_msgs/Joy"
|
||||
});
|
||||
joySubscriber.subscribe(function (message) {
|
||||
markRosConnection(true);
|
||||
if (message.header.frame_id === "webgui") return;
|
||||
var x = (message.axes[0] || 0);
|
||||
var y = -(message.axes[1] || 0);
|
||||
@@ -1089,6 +1227,7 @@ window.addEventListener("load", function () {
|
||||
document.getElementById("motor-toggle").addEventListener("click", toggleMotors);
|
||||
|
||||
document.getElementById("mt-joy").addEventListener("click", function () {
|
||||
if (controlsAreBlocked()) return;
|
||||
inputMode = "joystick";
|
||||
updateInputDisplay();
|
||||
document.getElementById("mt-joy").classList.add("active");
|
||||
@@ -1100,6 +1239,7 @@ window.addEventListener("load", function () {
|
||||
});
|
||||
|
||||
document.getElementById("mt-dpad").addEventListener("click", function () {
|
||||
if (controlsAreBlocked()) return;
|
||||
inputMode = "dpad";
|
||||
updateInputDisplay();
|
||||
document.getElementById("mt-dpad").classList.add("active");
|
||||
@@ -1112,6 +1252,7 @@ window.addEventListener("load", function () {
|
||||
|
||||
var joyBase = document.getElementById("jbase");
|
||||
joyBase.addEventListener("mousedown", function (event) {
|
||||
if (controlsAreBlocked()) return;
|
||||
dragging = true;
|
||||
moveThumb(event.clientX, event.clientY);
|
||||
event.preventDefault();
|
||||
@@ -1128,6 +1269,7 @@ window.addEventListener("load", function () {
|
||||
}
|
||||
});
|
||||
joyBase.addEventListener("touchstart", function (event) {
|
||||
if (controlsAreBlocked()) return;
|
||||
dragging = true;
|
||||
moveThumb(event.touches[0].clientX, event.touches[0].clientY);
|
||||
event.preventDefault();
|
||||
@@ -1147,6 +1289,7 @@ window.addEventListener("load", function () {
|
||||
|
||||
document.querySelectorAll(".dpad-btn").forEach(function (button) {
|
||||
function activate(event) {
|
||||
if (controlsAreBlocked()) return;
|
||||
var x = parseFloat(button.dataset.x);
|
||||
var y = parseFloat(button.dataset.y);
|
||||
setDpadPressed(button, x, y);
|
||||
|
||||
Reference in New Issue
Block a user