Routenplanung eingebaut

This commit is contained in:
2026-05-23 20:53:16 +02:00
parent b24996e67f
commit 91fb1fcf0b
6 changed files with 1877 additions and 0 deletions
+652
View File
@@ -5,7 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CUNO // Admin</title>
<link rel="stylesheet" href="./app.css?v=20260522104500">
<link rel="stylesheet" href="./vendor/leaflet/leaflet.css">
<script src="./vendor/roslib.min.js"></script>
<script src="./vendor/leaflet/leaflet.js"></script>
</head>
<body class="page-admin">
@@ -40,6 +42,7 @@
<div class="admin-section-tabs" role="tablist" aria-label="Admin-Bereiche">
<button id="admin_tab_system" class="admin-section-tab is_active" role="tab" aria-selected="true" onclick="switchAdminSection('system')">Admin</button>
<button id="admin_tab_drive" class="admin-section-tab" role="tab" aria-selected="false" onclick="switchAdminSection('drive')">Geschwindigkeit / Weg</button>
<button id="admin_tab_map" class="admin-section-tab" role="tab" aria-selected="false" onclick="switchAdminSection('map')">Karte</button>
<button id="admin_tab_motor" class="admin-section-tab" role="tab" aria-selected="false" onclick="switchAdminSection('motor')">Motorentest</button>
</div>
@@ -245,6 +248,115 @@
</div>
</section>
<section id="admin_panel_map" class="admin-section-panel" role="tabpanel">
<div class="admin-section-grid admin-section-grid_map">
<div class="admin-col admin-col_map_main">
<div class="panel map-panel">
<div class="p-hdr">
<div class="p-dot"></div>
<span class="p-lbl">Rover-Karte</span>
<span class="p-tag">Live Position</span>
</div>
<div class="map-toolbar">
<button id="map_plan_toggle" class="act-btn subtle is_active" onclick="toggleWaypointPlanning()">Route planen: an</button>
<button class="act-btn" onclick="addWaypointAtMapCenter()">Wegpunkt an Mitte setzen</button>
<button class="act-btn warm" onclick="centerMapOnRover()">Auf Rover zentrieren</button>
<button class="act-btn subtle" onclick="clearPlannedRoute()">Route löschen</button>
</div>
<div class="copy-text map-copy">
Die Karte zeigt den aktuellen GPS-Standort des Rovers. Ein Klick in die Karte setzt sofort einen Wegpunkt, solange <code>Route planen</code> aktiv ist. Die eigentliche Routenausführung binden wir später darauf auf.
</div>
<div class="map-stage">
<div id="rover_map" class="rover-map"></div>
<div id="rover_map_fallback" class="rover-map-fallback map-hidden" hidden>
Kartenbibliothek nicht geladen. GPS-Daten sind trotzdem unten sichtbar.
</div>
</div>
</div>
</div>
<div class="admin-col admin-col_map_side">
<div class="panel">
<div class="p-hdr">
<div class="p-dot"></div>
<span class="p-lbl">GPS</span>
<span class="p-tag">Telemetry</span>
</div>
<div class="sr">
<span class="sr-led led-ok"></span>
<span class="sr-key">Status</span>
<span id="map_gps_status" class="sr-val">Offline</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Sat</span>
<span id="map_gps_satellites" class="sr-val">--</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Breite</span>
<span id="map_gps_lat" class="sr-val">--</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Länge</span>
<span id="map_gps_lon" class="sr-val">--</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Höhe</span>
<span id="map_gps_alt" class="sr-val">--</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Genauigk.</span>
<span id="map_gps_accuracy" class="sr-val">--</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Letztes Fix</span>
<span id="map_gps_last_fix" class="sr-val">--</span>
</div>
</div>
<div class="panel">
<div class="p-hdr">
<div class="p-dot"></div>
<span class="p-lbl">Route</span>
<span class="p-tag">Planning Scaffold</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Planung</span>
<span id="map_route_mode" class="sr-val">aus</span>
</div>
<div class="sr">
<span class="sr-led led-blue"></span>
<span class="sr-key">Wegpunkte</span>
<span id="map_waypoint_count" class="sr-val">0</span>
</div>
<div class="copy-text map-copy">
Diese Wegpunkte sind aktuell nur lokal in der Admin-Seite. Sie dienen als Vorbereitung für die spätere echte Routenprogrammierung.
</div>
<div class="map-route-storage">
<label class="map-route-storage-label" for="map_route_name">Name</label>
<input id="map_route_name" class="map-route-storage-input" type="text" placeholder="z. B. Hofrunde">
<button class="act-btn" onclick="saveCurrentRoute()">Route speichern</button>
</div>
<div class="map-route-storage map-route-storage_secondary">
<label class="map-route-storage-label" for="map_route_select">Gespeichert</label>
<select id="map_route_select" class="ctrl-select" onchange="loadSelectedRoute()">
<option value="">Route wählen</option>
</select>
<button class="act-btn subtle" onclick="refreshRouteLibrary()">Neu laden</button>
<button class="act-btn warm" onclick="deleteSelectedRoute()">Route löschen</button>
</div>
<div id="map_waypoint_list" class="map-waypoint-list"></div>
</div>
</div>
</div>
</section>
<section id="admin_panel_motor" class="admin-section-panel motor-admin-tab" role="tabpanel">
<div class="admin-section-grid">
<div class="admin-col">
@@ -365,6 +477,8 @@
var ros = null;
var roverCommandTopic = null;
var motorCommandsTopic = null;
var gpsFixTopic = null;
var gpsStatusTopic = null;
var isMotorTestRunning = false;
var isContainerActionRunning = false;
@@ -412,6 +526,7 @@
calibrationPoints: [],
savedCalibrationPoints: []
};
var routeLibrary = [];
var driveCalibrationMeasurement = {
distanceM: 1.0,
activeIndex: null,
@@ -423,6 +538,28 @@
startThresholdPercent: 2,
stopThresholdPercent: 1
};
var roverMapState = {
map: null,
mapReady: false,
roverMarker: null,
roverAccuracyCircle: null,
roverTrackLine: null,
routeLine: null,
waypointMarkers: [],
waypointPlanning: true,
waypoints: [],
trackPoints: [],
roverLat: null,
roverLon: null,
roverAlt: null,
accuracyMeters: null,
hasFix: false,
gpsStatusText: 'Offline',
satellites: '--',
lastFixAt: null,
hasCenteredOnFix: false,
suppressMapClickUntil: 0
};
function submitPassword() {
var input = document.getElementById('auth_input');
@@ -449,22 +586,35 @@
activeAdminSection = sectionName;
var systemTab = document.getElementById('admin_tab_system');
var driveTab = document.getElementById('admin_tab_drive');
var mapTab = document.getElementById('admin_tab_map');
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 motorPanel = document.getElementById('admin_panel_motor');
var isSystem = sectionName === 'system';
var isDrive = sectionName === 'drive';
var isMap = sectionName === 'map';
var isMotor = sectionName === 'motor';
systemTab.classList.toggle('is_active', isSystem);
driveTab.classList.toggle('is_active', isDrive);
mapTab.classList.toggle('is_active', isMap);
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');
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);
motorPanel.classList.toggle('is_active', isMotor);
window.location.hash = isSystem ? '' : ('#' + sectionName);
if (isMap) {
initRoverMap();
window.setTimeout(function() {
if (roverMapState.map) roverMapState.map.invalidateSize();
}, 80);
}
if (isMotor) fetchMotorTestStatus();
}
@@ -515,6 +665,487 @@
});
}
function formatCoordinate(value) {
if (typeof value !== 'number' || isNaN(value)) return '--';
return formatNumber(value, 5);
}
function formatAltitude(value) {
if (typeof value !== 'number' || isNaN(value)) return '--';
return formatNumber(value, 1) + ' m';
}
function formatAccuracy(value) {
if (typeof value !== 'number' || isNaN(value) || value <= 0) return '--';
return formatNumber(value, 1) + ' m';
}
function formatFixTime(date) {
if (!date) return '--';
return date.toLocaleTimeString('de-DE', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
function updateMapTelemetryPanel() {
setText('map_gps_status', roverMapState.gpsStatusText || 'Offline');
setText('map_gps_satellites', roverMapState.satellites);
setText('map_gps_lat', formatCoordinate(roverMapState.roverLat));
setText('map_gps_lon', formatCoordinate(roverMapState.roverLon));
setText('map_gps_alt', formatAltitude(roverMapState.roverAlt));
setText('map_gps_accuracy', formatAccuracy(roverMapState.accuracyMeters));
setText('map_gps_last_fix', formatFixTime(roverMapState.lastFixAt));
setText('map_route_mode', roverMapState.waypointPlanning ? 'aktiv' : 'aus');
setText('map_waypoint_count', roverMapState.waypoints.length);
}
function renderRouteLibrary() {
var select = document.getElementById('map_route_select');
if (!select) return;
var current = select.value;
var options = ['<option value="">Route wählen</option>'];
routeLibrary.forEach(function(route) {
options.push('<option value="' + route.name.replace(/"/g, '&quot;') + '">' + route.name + '</option>');
});
select.innerHTML = options.join('');
var stillExists = routeLibrary.some(function(route) {
return route.name === current;
});
select.value = stillExists ? current : '';
}
function applyWaypointPlanningState() {
var button = document.getElementById('map_plan_toggle');
if (button) {
button.textContent = 'Route planen: ' + (roverMapState.waypointPlanning ? 'an' : 'aus');
button.classList.toggle('is_active', roverMapState.waypointPlanning);
}
var mapNode = document.getElementById('rover_map');
if (mapNode) {
mapNode.classList.toggle('is_planning', roverMapState.waypointPlanning);
}
updateMapTelemetryPanel();
}
function renderWaypointList() {
var list = document.getElementById('map_waypoint_list');
if (!list) return;
if (!roverMapState.waypoints.length) {
list.innerHTML = '<div class="map-waypoint-empty">Noch keine Wegpunkte gesetzt.</div>';
updateMapTelemetryPanel();
return;
}
var html = roverMapState.waypoints.map(function(point, index) {
return (
'<div class="map-waypoint-item">' +
'<div class="map-waypoint-index">WP ' + (index + 1) + '</div>' +
'<div class="map-waypoint-coords">' +
'<strong>' + formatCoordinate(point.lat) + '</strong>' +
'<span>' + formatCoordinate(point.lon) + '</span>' +
'</div>' +
'</div>'
);
});
list.innerHTML = html.join('');
updateMapTelemetryPanel();
}
function createWaypointIcon(index) {
return L.divIcon({
className: 'map-waypoint-icon',
html:
'<div class="map-waypoint-stack">' +
'<div class="map-waypoint-label">WP ' + (index + 1) + '</div>' +
'<div class="map-waypoint-dot"></div>' +
'</div>',
iconSize: [58, 42],
iconAnchor: [29, 34]
});
}
function buildRouteCoordinates() {
var coords = [];
if (roverMapState.hasFix && typeof roverMapState.roverLat === 'number' && typeof roverMapState.roverLon === 'number') {
coords.push([roverMapState.roverLat, roverMapState.roverLon]);
}
roverMapState.waypoints.forEach(function(point) {
coords.push([point.lat, point.lon]);
});
return coords;
}
function renderPlannedRoute() {
if (!roverMapState.mapReady || !roverMapState.routeLine) return;
roverMapState.routeLine.setLatLngs(buildRouteCoordinates());
renderWaypointList();
}
function getWaypointInsertIndexFromRouteClick(latlng) {
if (!roverMapState.waypoints.length) return 0;
var clickLat = Number(latlng.lat);
var clickLon = Number(latlng.lng);
var bestIndex = roverMapState.waypoints.length;
var bestDistance = Number.POSITIVE_INFINITY;
for (var i = 0; i < roverMapState.waypoints.length - 1; i += 1) {
var start = roverMapState.waypoints[i];
var end = roverMapState.waypoints[i + 1];
var ax = Number(start.lon);
var ay = Number(start.lat);
var bx = Number(end.lon);
var by = Number(end.lat);
var px = clickLon;
var py = clickLat;
var abx = bx - ax;
var aby = by - ay;
var abLen2 = abx * abx + aby * aby;
var t = 0;
if (abLen2 > 0) {
t = ((px - ax) * abx + (py - ay) * aby) / abLen2;
t = Math.max(0, Math.min(1, t));
}
var projX = ax + abx * t;
var projY = ay + aby * t;
var dx = px - projX;
var dy = py - projY;
var distance = dx * dx + dy * dy;
if (distance < bestDistance) {
bestDistance = distance;
bestIndex = i + 1;
}
}
return bestIndex;
}
function clearWaypointMarkers() {
roverMapState.waypointMarkers.forEach(function(marker) {
roverMapState.map.removeLayer(marker);
});
roverMapState.waypointMarkers = [];
}
function buildWaypointPopupContent(index, lat, lon) {
return (
'<div class="map-waypoint-popup">' +
'<div class="map-waypoint-popup-title">Wegpunkt ' + (index + 1) + '</div>' +
'<div class="map-waypoint-popup-coords">' + formatCoordinate(lat) + ', ' + formatCoordinate(lon) + '</div>' +
'<button class="map-waypoint-delete" onclick="removeWaypoint(' + index + ')">Wegpunkt löschen</button>' +
'</div>'
);
}
function renderWaypointMarkers() {
if (!roverMapState.mapReady) return;
clearWaypointMarkers();
roverMapState.waypoints.forEach(function(point, index) {
var marker = L.marker([point.lat, point.lon], {
title: 'Wegpunkt ' + (index + 1),
icon: createWaypointIcon(index),
draggable: true
}).addTo(roverMapState.map);
marker.bindPopup(buildWaypointPopupContent(index, point.lat, point.lon));
marker.on('drag', function(event) {
var latlng = event.target.getLatLng();
roverMapState.waypoints[index].lat = Number(latlng.lat);
roverMapState.waypoints[index].lon = Number(latlng.lng);
renderPlannedRoute();
});
marker.on('dragend', function(event) {
var latlng = event.target.getLatLng();
roverMapState.waypoints[index].lat = Number(latlng.lat);
roverMapState.waypoints[index].lon = Number(latlng.lng);
event.target.setPopupContent(buildWaypointPopupContent(index, latlng.lat, latlng.lng));
renderWaypointList();
renderPlannedRoute();
});
roverMapState.waypointMarkers.push(marker);
});
renderPlannedRoute();
}
function addWaypointFromMap(latlng) {
roverMapState.waypoints.push({
lat: Number(latlng.lat),
lon: Number(latlng.lng)
});
renderWaypointMarkers();
}
function insertWaypointAtIndex(index, latlng) {
var insertIndex = Math.max(0, Math.min(roverMapState.waypoints.length, Number(index)));
roverMapState.waypoints.splice(insertIndex, 0, {
lat: Number(latlng.lat),
lon: Number(latlng.lng)
});
renderWaypointMarkers();
}
function addWaypointAtMapCenter() {
if (!roverMapState.mapReady || !roverMapState.map) return;
addWaypointFromMap(roverMapState.map.getCenter());
}
function removeWaypoint(index) {
if (index < 0 || index >= roverMapState.waypoints.length) return;
roverMapState.waypoints.splice(index, 1);
renderWaypointMarkers();
}
async function refreshRouteLibrary() {
try {
var response = await fetch(adminApiBase + '/api/routes');
var data = await response.json();
if (!response.ok) throw new Error(data.status || ('status ' + response.status));
routeLibrary = Array.isArray(data.library && data.library.routes) ? data.library.routes : [];
renderRouteLibrary();
} catch (error) {
setAdminStatus(error.message || 'Routen konnten nicht geladen werden');
}
}
function loadSelectedRoute() {
var select = document.getElementById('map_route_select');
if (!select || !select.value) return;
var route = routeLibrary.find(function(item) {
return item.name === select.value;
});
if (!route) return;
roverMapState.waypoints = (route.waypoints || []).map(function(point) {
return {
lat: Number(point.lat),
lon: Number(point.lon)
};
});
var nameInput = document.getElementById('map_route_name');
if (nameInput) nameInput.value = route.name;
renderWaypointMarkers();
setAdminStatus('Route geladen');
}
async function saveCurrentRoute() {
var nameInput = document.getElementById('map_route_name');
var routeName = nameInput ? String(nameInput.value || '').trim() : '';
if (!routeName) {
alert('Bitte einen Routennamen eingeben.');
return;
}
if (!roverMapState.waypoints.length) {
alert('Die Route hat noch keine Wegpunkte.');
return;
}
try {
var response = await fetch(adminApiBase + '/api/routes/save', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
name: routeName,
waypoints: roverMapState.waypoints
})
});
var data = await response.json();
if (!response.ok) throw new Error(data.status || ('status ' + response.status));
routeLibrary = Array.isArray(data.library && data.library.routes) ? data.library.routes : routeLibrary;
renderRouteLibrary();
var select = document.getElementById('map_route_select');
if (select) select.value = routeName;
setAdminStatus(data.status || 'Route gespeichert');
} catch (error) {
alert(error.message || 'Die Route konnte nicht gespeichert werden.');
}
}
async function deleteSelectedRoute() {
var select = document.getElementById('map_route_select');
if (!select || !select.value) {
alert('Bitte zuerst eine gespeicherte Route auswählen.');
return;
}
if (!window.confirm('Diese gespeicherte Route wirklich löschen?')) return;
try {
var response = await fetch(adminApiBase + '/api/routes/delete', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name: select.value})
});
var data = await response.json();
if (!response.ok) throw new Error(data.status || ('status ' + response.status));
routeLibrary = Array.isArray(data.library && data.library.routes) ? data.library.routes : [];
renderRouteLibrary();
setAdminStatus(data.status || 'Route gelöscht');
} catch (error) {
alert(error.message || 'Die Route konnte nicht gelöscht werden.');
}
}
function toggleWaypointPlanning() {
roverMapState.waypointPlanning = !roverMapState.waypointPlanning;
applyWaypointPlanningState();
}
function clearPlannedRoute() {
roverMapState.waypoints = [];
renderWaypointMarkers();
}
function centerMapOnRover() {
if (!roverMapState.mapReady || !roverMapState.hasFix) return;
roverMapState.map.setView([roverMapState.roverLat, roverMapState.roverLon], Math.max(roverMapState.map.getZoom(), 17));
}
function initRoverMap() {
if (roverMapState.mapReady) return;
var fallback = document.getElementById('rover_map_fallback');
var mapNode = document.getElementById('rover_map');
if (!mapNode) return;
if (typeof L === 'undefined') {
return;
}
roverMapState.map = L.map('rover_map', {
zoomControl: true,
attributionControl: true,
maxZoom: 22
}).setView([50.2984, 11.0480], 16);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 22,
maxNativeZoom: 19,
attribution: '&copy; OpenStreetMap'
}).addTo(roverMapState.map);
roverMapState.roverTrackLine = L.polyline([], {
color: '#00ff91',
weight: 3,
opacity: 0.7,
interactive: false
}).addTo(roverMapState.map);
roverMapState.routeLine = L.polyline([], {
color: '#0088FF',
weight: 3,
opacity: 0.85,
interactive: true
}).addTo(roverMapState.map);
roverMapState.routeLine.on('click', function(event) {
roverMapState.suppressMapClickUntil = Date.now() + 250;
if (event.originalEvent) {
if (typeof event.originalEvent.preventDefault === 'function') event.originalEvent.preventDefault();
if (typeof event.originalEvent.stopPropagation === 'function') event.originalEvent.stopPropagation();
}
if (!roverMapState.waypointPlanning) return;
if (roverMapState.waypoints.length < 2) {
addWaypointFromMap(event.latlng);
return;
}
insertWaypointAtIndex(getWaypointInsertIndexFromRouteClick(event.latlng), event.latlng);
});
roverMapState.roverAccuracyCircle = L.circle([50.2984, 11.0480], {
radius: 0,
color: '#00ff91',
weight: 1,
opacity: 0.7,
fillColor: '#00ff91',
fillOpacity: 0.1,
interactive: false
}).addTo(roverMapState.map);
roverMapState.roverMarker = L.circleMarker([50.2984, 11.0480], {
radius: 7,
color: '#05111d',
weight: 2,
fillColor: '#FF8C00',
fillOpacity: 1,
interactive: false
}).addTo(roverMapState.map);
roverMapState.map.on('click', function(event) {
if (Date.now() < roverMapState.suppressMapClickUntil) return;
if (!roverMapState.waypointPlanning) return;
addWaypointFromMap(event.latlng);
});
roverMapState.mapReady = true;
applyWaypointPlanningState();
renderWaypointMarkers();
updateMapTelemetryPanel();
}
function updateRoverPositionOnMap() {
if (!roverMapState.mapReady || !roverMapState.hasFix) return;
var latlng = [roverMapState.roverLat, roverMapState.roverLon];
roverMapState.roverMarker.setLatLng(latlng);
roverMapState.roverMarker.bindPopup(
'Rover<br>' +
formatCoordinate(roverMapState.roverLat) + ', ' + formatCoordinate(roverMapState.roverLon)
);
var accuracy = roverMapState.accuracyMeters || 0;
roverMapState.roverAccuracyCircle.setLatLng(latlng);
roverMapState.roverAccuracyCircle.setRadius(accuracy);
roverMapState.trackPoints.push(latlng);
if (roverMapState.trackPoints.length > 60) roverMapState.trackPoints.shift();
roverMapState.roverTrackLine.setLatLngs(roverMapState.trackPoints);
if (!roverMapState.hasCenteredOnFix) {
roverMapState.map.setView(latlng, 17);
roverMapState.hasCenteredOnFix = true;
}
renderPlannedRoute();
}
function updateGpsStatusSummary(text) {
roverMapState.gpsStatusText = text || 'Offline';
var satMatch = String(roverMapState.gpsStatusText).match(/(\d+)\s*Sat/i);
roverMapState.satellites = satMatch ? satMatch[1] : '--';
updateMapTelemetryPanel();
}
function handleGpsFix(message) {
if (!message || !message.status) return;
roverMapState.hasFix = Number(message.status.status) >= 0;
if (!roverMapState.hasFix) {
roverMapState.roverLat = null;
roverMapState.roverLon = null;
roverMapState.roverAlt = null;
roverMapState.accuracyMeters = null;
updateMapTelemetryPanel();
renderPlannedRoute();
return;
}
roverMapState.roverLat = Number(message.latitude);
roverMapState.roverLon = Number(message.longitude);
roverMapState.roverAlt = Number(message.altitude);
roverMapState.lastFixAt = new Date();
var covariance = Array.isArray(message.position_covariance) ? Number(message.position_covariance[0]) : 0;
roverMapState.accuracyMeters = covariance > 0 ? Math.sqrt(covariance) : null;
updateMapTelemetryPanel();
initRoverMap();
updateRoverPositionOnMap();
}
function formatSpeedCms(speedMps, digits) {
return formatNumber(Number(speedMps || 0) * 100, digits);
}
@@ -974,6 +1605,22 @@
driveEstimator.motorSpeeds = Array.isArray(message.motor_speeds) ? message.motor_speeds.map(Number) : [0, 0, 0, 0, 0, 0];
renderDriveEstimator();
});
gpsFixTopic = new ROSLIB.Topic({
ros: ros,
name: '/fix',
messageType: 'sensor_msgs/NavSatFix'
});
gpsFixTopic.subscribe(handleGpsFix);
gpsStatusTopic = new ROSLIB.Topic({
ros: ros,
name: '/gps/status_text',
messageType: 'std_msgs/String'
});
gpsStatusTopic.subscribe(function(message) {
updateGpsStatusSummary(message.data);
});
}
function temperatureToWidth(value) {
@@ -1477,6 +2124,8 @@
switchAdminSection('motor');
} else if (window.location.hash === '#drive') {
switchAdminSection('drive');
} else if (window.location.hash === '#map') {
switchAdminSection('map');
}
updateClock();
window.setInterval(updateClock, 1000);
@@ -1486,7 +2135,10 @@
fetchMotorTestStatus();
fetchDriveEstimatorConfig();
loadDriveEstimatorCalibration();
refreshRouteLibrary();
connectDriveEstimatorRos();
renderWaypointList();
applyWaypointPlanningState();
renderDriveEstimator();
window.setInterval(fetchStatus, 10000);
window.setInterval(fetchDelay, 5000);