Admin seite umgebaut mit PW

This commit is contained in:
2026-05-21 20:35:30 +02:00
parent f53d51acb6
commit ea5521c815
4 changed files with 415 additions and 0 deletions
+336
View File
@@ -0,0 +1,336 @@
# CUNO – Admin-Dokumentation
Stand: 2026-05-21
---
## 1. Hardware & Betriebssystem
| Eigenschaft | Wert |
|---|---|
| Hardware | Raspberry Pi 4 Model B Rev 1.4 |
| Betriebssystem | Debian GNU/Linux 13 (Trixie) |
| Kernel | 6.12.75+rpt-rpi-v8 (64-bit ARM) |
| Python | 3.13.5 |
| Docker | 26.1.5 |
---
## 2. Raspberry Pi – Zugangsdaten
| Eigenschaft | Wert |
|---|---|
| Hostname | `ExoMyCuno` |
| Benutzer | `pi` |
| Passwort | `Sonneberg` |
| LAN-IP | `192.168.1.83` |
| WLAN-IP | `192.168.1.9` |
**SSH-Zugang:**
```
ssh pi@192.168.1.9
```
---
## 3. WLAN-Logik
Der Rover wählt automatisch das beste verfügbare Netzwerk. Ein Hintergrunddienst (`exomy-wifi-fallback`) prüft alle 20 Sekunden die Verbindung und schaltet bei Bedarf um.
### Prioritätenreihenfolge
| Priorität | SSID | Typ | Wann aktiv |
|---|---|---|---|
| 1 (höchste) | `eskimue.de` | Heimnetz | wenn im Heimnetz |
| 2 | `4pi` | Mobiler Hotspot | wenn unterwegs mit Handy |
| 3 (Fallback) | `CUNO` | Eigener AP des Rovers | wenn kein bekanntes WLAN erreichbar |
### Netzwerk-Passwörter
| SSID | Passwort |
|---|---|
| `4pi` | `st89Saf6H86n` |
| `CUNO` (Rover-AP) | `astr0cun042` |
### Verhalten im Detail
- Ist `eskimue.de` erreichbar → Verbindung wird hergestellt und gehalten.
- Ist `eskimue.de` nicht erreichbar, aber `4pi` sichtbar → Verbindung mit `4pi`.
- Ist keines der beiden Netze erreichbar → Rover öffnet den eigenen WLAN-Hotspot **CUNO**.
- Sobald ein besser priorisiertes Netz wieder verfügbar wird, schaltet der Dienst automatisch zurück.
### Eigener Access Point (CUNO)
Wenn der Rover als AP läuft, ist er unter einer festen IP erreichbar:
| Eigenschaft | Wert |
|---|---|
| SSID | `CUNO` |
| Passwort | `astr0cun042` |
| IP des Rovers | `192.168.50.1` |
| Sicherheit | WPA2-PSK |
Browser-Adresse im AP-Modus: `http://192.168.50.1:8000/`
---
## 4. Offene Ports und Dienste
Alle Dienste laufen auf dem **Host** (Raspberry Pi), nicht im Docker-Container.
| Port | Dienst | Beschreibung |
|---|---|---|
| `8000` | Web-GUI | Haupt-Steuerseite und Admin-Seite (Python HTTP-Server im Container) |
| `8081` | Kamerastream (Quelle) | Rohstream direkt von der Kamera, kein Delay. Nur intern genutzt. |
| `8082` | Admin-API | REST-API für Systemsteuerung (Neustart, Status, Delay). Kein Browser-Frontend. |
| `8083` | Video-Delay-Proxy | Kamerastream **mit** eingestellter Verzögerung. Wird von der Web-GUI gezeigt. |
| `9090` | ROSBridge WebSocket | Verbindung zwischen Browser und ROS im Container. |
### Kamera-Signalkette
```
Kamera (Hardware)
↓
libcamera_mjpeg_server → Port 8081 (Rohstream, kein Delay)
↓
video_delay_proxy → Port 8083 (mit Delay, 0–10 s einstellbar)
↓
Web-GUI (Browser)
```
Bei Delay = 0 s wird das Bild sofort weitergegeben (kein echtes Buffern).
---
## 5. Webseiten
### 4.1 Steuerung – `http://<IP>:8000/`
Die Haupt-Steuerseite des Rovers.
**Inhalt:**
- Kamerabild (live, mit Delay wenn eingestellt)
- Virtueller Joystick (Maussteuerung)
- Modus-Anzeige (Ackermann / Punkt-Drehen / Crabbing)
- Systemstatus-Panel (rechts): Verbindung, CPU, RAM, Temp, WLAN, Uptime, Speicher
**Besonderheiten:**
- Physischer Controller und Web-Joystick können gleichzeitig betrieben werden.
- Wenn die Web-GUI aktiv gesteuert hat, wird der physische Controller für 2 Sekunden ignoriert (Prioritätslogik).
- Moduswechsel vom physischen Controller werden in der Web-GUI sofort angezeigt.
- Die Joystick-Spiegelanzeige zeigt, welche Eingaben der physische Controller gerade sendet.
---
### 4.2 Admin-Seite – `http://<IP>:8000/admin.html`
Systemverwaltung des Rovers. Die Seite ist passwortgeschützt — bei jedem Aufruf wird das Passwort abgefragt.
| Eigenschaft | Wert |
|---|---|
| Passwort | `cuno` |
**Bereiche:**
#### Aktionen
| Schaltfläche | Funktion |
|---|---|
| Kameradienst neu starten | Startet `exomy-camera-stream.service` neu |
| ExoMy-Container neu starten | Startet Docker-Container `exomy_autostart` neu (ROS, Motoren, Joystick) |
| Raspberry Pi neu starten | Fährt den Pi neu hoch |
| Raspberry Pi herunterfahren | Fährt den Pi aus |
#### Dienststatus
Zeigt den Live-Status von Kamera, Admin-API und ExoMy-Container.
#### Motorentest
Link zur separaten Motortest-Seite (siehe 4.3).
#### Latenz-Simulation
Dropdown 0–10 Sekunden. Verzögert **gleichzeitig**:
- Steuerbefehle (sowohl Web-GUI als auch physischer Controller)
- Kamerabild
So lässt sich realitätsnahe Steuerung mit erhöhter Signallaufzeit simulieren (z. B. Mond-Delay ≈ 1 s hin + 1 s zurück → 2 s einstellen).
#### Systemstatus
| Anzeige | Quelle |
|---|---|
| WLAN-Status | nmcli |
| IP-Adressen | wlan0 |
| CPU-Temperatur | `/sys/class/thermal/thermal_zone0/temp` |
| Unterspannung | `vcgencmd get_throttled` |
| Uptime | `/proc/uptime` |
| Freier Speicherplatz | Root-Partition |
---
### 4.3 Motortest – `http://<IP>:8000/admin-motor-test.html`
Separate Testseite zum Kalibrieren und Prüfen einzelner Räder.
**Wichtig:** Der ExoMy-Container muss für den Motortest **gestoppt** sein (Schaltfläche auf der Seite). Nach dem Test kann er wieder gestartet werden.
**Funktionen:**
- Lenkung links / rechts je Rad testen
- Vorwärts / Rückwärts je Rad testen
- Servo-Mittenwerte (Lenkung) einstellen und speichern
- Antriebsneutralwert einstellen und speichern
---
## 7. Controller-Belegung (Logitech F710)
Der Controller muss im **D-Modus** (Schalter auf der Rückseite) betrieben werden.
| Taste / Achse | Funktion |
|---|---|
| **Linker Stick** | Fahren und Lenken |
| **A** | Ackermann-Modus (normales Kurvenfahren) |
| **X** | Punkt-Drehen (Rover dreht auf der Stelle) |
| **Y** | Crabbing (seitliche Fahrt, alle Räder gleich eingeschlagen) |
| **B** | (nicht belegt) |
| **START** | Motoren ein- / ausschalten (Toggle) |
| **SELECT** | (nicht belegt) |
### Fahrmodi im Detail
**Ackermann** (Standard):
Normales Kurvenfahren wie ein Auto. Die Vorder- und Hinterräder lenken gegenläufig für einen engen Wendekreis.
**Punkt-Drehen:**
Der Rover dreht sich auf der Stelle. Alle Räder stehen schräg, der Antrieb dreht links und rechts gegeneinander.
**Crabbing:**
Alle Räder werden auf den gleichen Winkel eingeschlagen. Der Rover fährt seitlich (wie eine Krabbe). Lenkrichtung folgt dem X-Achsen-Ausschlag des Sticks.
---
## 6. Docker-Container
### Container `exomy_autostart`
| Eigenschaft | Wert |
|---|---|
| Image | `exomy:latest` (lokal gebaut, basiert auf `ros:melodic`) |
| ROS-Version | ROS 1 Melodic (Ubuntu 18.04-Basis im Container) |
| Startet automatisch | ja (`--restart=always`) |
| Ports (Host → Container) | `8000→8000` (Web-GUI), `8080→8080` (alt, nicht aktiv genutzt), `9090→9090` (ROSBridge) |
### ROS-Knoten im Container
| Knoten | Funktion |
|---|---|
| `/delay_node` | Puffert `/joy` → `/joy_delayed` mit einstellbarer Verzögerung (0–10 s) |
| `/f710_joy_node` | Liest den Logitech F710 Controller und publiziert auf `/joy` |
| `/joystick_parser_node` | Übersetzt Joystick-Eingaben in Rover-Befehle (`/rover_command`) |
| `/robot_node` | Berechnet Rad-Winkel und -Geschwindigkeiten aus dem Rover-Befehl |
| `/motors` | Schreibt PWM-Werte auf den Servo-Controller (I²C) |
| `/rosbridge_websocket` | WebSocket-Brücke zwischen Browser und ROS (Port 9090) |
| `/rosapi_node` | Erlaubt dem Browser, ROS-Parameter zu lesen/schreiben |
### ROS-Signalfluss
```
Controller (USB)
↓
/f710_joy_node → /joy
↓
/delay_node → /joy_delayed
↓
Web-GUI (Browser) → /joy /joystick_parser_node
(frame_id="webgui") ↓
/rover_command
↓
/robot_node
↓
/motor_commands
↓
/motors (PWM → Servos)
```
### Nützliche Container-Befehle
```bash
# Container neu starten
docker restart exomy_autostart
# Shell im Container öffnen
docker exec -it exomy_autostart bash
# ROS-Knoten anzeigen
docker exec exomy_autostart bash -c 'source /opt/ros/melodic/setup.bash && rosnode list'
# Alle ROS-Topics anzeigen
docker exec exomy_autostart bash -c 'source /opt/ros/melodic/setup.bash && rostopic list'
# Delay-Parameter im ROS lesen/setzen
docker exec exomy_autostart bash -c 'source /opt/ros/melodic/setup.bash && rosparam get /delay_seconds'
docker exec exomy_autostart bash -c 'source /opt/ros/melodic/setup.bash && rosparam set /delay_seconds 2.0'
# Alten Image-Schrott aufräumen (dangling images)
docker image prune -f
```
---
## 8. Systemdienste (Übersicht)
Alle laufen auf dem Raspberry Pi als systemd-Dienste:
| Dienst | Beschreibung | Startet automatisch |
|---|---|---|
| `exomy-camera-stream` | MJPEG-Kamerastream auf Port 8081 | ja |
| `exomy-video-delay` | Delay-Proxy auf Port 8083 | ja |
| `exomy-admin-api` | REST-API auf Port 8082 | ja |
| `exomy-wifi-fallback` | WLAN-Priorisierung und AP-Fallback | ja |
Der **Docker-Container** `exomy_autostart` enthält ROS und startet ebenfalls automatisch (via `docker run --restart=always` oder manuell).
### Nützliche SSH-Befehle
```bash
# Dienststatus prüfen
systemctl status exomy-camera-stream
systemctl status exomy-video-delay
systemctl status exomy-admin-api
# Dienst neu starten
sudo systemctl restart exomy-camera-stream
# Container-Status
docker ps
# Container-Logs
docker logs exomy_autostart
# ROS-Topic live anzeigen (im Container)
docker exec exomy_autostart bash -c \
'source /opt/ros/melodic/setup.bash && rostopic echo /rover_command'
# Aktuellen Delay prüfen
cat /tmp/exomy_delay.txt
```
---
## 9. Latenz-Simulation – technischer Hintergrund
Die Verzögerung wirkt auf zwei Ebenen gleichzeitig:
1. **Steuerbefehle** (ROS-Ebene):
Der `delay_node` puffert alle Joystick-Nachrichten vom Topic `/joy` und gibt sie verzögert auf `/joy_delayed` aus. Der Joystick-Parser liest nur `/joy_delayed`.
2. **Kamerabild** (Host-Ebene):
Der `video_delay_proxy` puffert JPEG-Frames aus Port 8081 mit Timestamps. Beim Abruf wird der Frame geliefert, der zum Zeitpunkt `jetzt − Delay` aufgezeichnet wurde.
Der eingestellte Wert wird in `/tmp/exomy_delay.txt` gespeichert und von beiden Komponenten gleichzeitig gelesen. Änderungen über die Admin-Seite wirken sofort.
---
## 10. Bekannte Eigenheiten
- **Crabbing**: Funktioniert softwareseitig, mechanische Einstellung der Einzelräder ist noch nicht abgeschlossen.
- **Containerstart**: Niemals `sudo docker run ...` nutzen – das falsche Home-Verzeichnis wird gemountet.
- **Browser-Cache**: Nach GUI-Updates ggf. Shift+F5 (Hard Reload) im Browser nötig.
- **Port 8081 direkt**: Erreichbar für Debug-Zwecke, zeigt immer das Live-Bild ohne Delay.
@@ -35,6 +35,7 @@ then
rosparam load /root/exomy_ws/src/exomy/config/exomy.yaml
rosparam set /controller logitech-F710
rosparam set /delay_seconds 0.0
echo 0.0 > /tmp/exomy_delay.txt
/opt/ros/melodic/lib/rosbridge_server/rosbridge_websocket > /tmp/rosbridge.log 2>&1 &
ROSBRIDGE_PID=$!
+30
View File
@@ -138,8 +138,32 @@
</div>
</main>
<div id="auth_overlay" class="auth_overlay">
<div class="auth_box">
<p class="eyebrow" style="margin:0;">Admin-Zugang</p>
<h2 style="margin:0;">CUNO System</h2>
<p style="margin:0; font-size:0.88rem; opacity:.6;">Passwort eingeben um fortzufahren</p>
<input type="password" id="auth_input" class="auth_input" placeholder="••••••••" autocomplete="current-password">
<p id="auth_error" class="auth_error"></p>
<button class="button buttonX" onclick="submitPassword()">Entsperren</button>
</div>
</div>
<script type="text/javascript">
var adminApiBase = window.location.protocol + '//' + window.location.hostname + ':8082';
var AUTH_TOKEN = 'Y3Vubw==';
function submitPassword() {
var input = document.getElementById('auth_input');
var error = document.getElementById('auth_error');
if (btoa(input.value) === AUTH_TOKEN) {
document.getElementById('auth_overlay').remove();
} else {
error.textContent = 'Falsches Passwort';
input.value = '';
input.focus();
}
}
function setAdminStatus(text) {
var status = document.getElementById('admin_status');
@@ -307,6 +331,12 @@
}
window.addEventListener('load', function () {
var input = document.getElementById('auth_input');
input.focus();
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') submitPassword();
});
fetchStatus();
fetchDelay();
window.setInterval(fetchStatus, 10000);
+48
View File
@@ -464,6 +464,54 @@ h2 {
}
}
.auth_overlay {
position: fixed;
inset: 0;
background: #05080F;
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.auth_box {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 2.5rem 2rem;
border-radius: 12px;
border: 1px solid rgba(255,255,255,0.08);
background: rgba(255,255,255,0.04);
width: 100%;
max-width: 320px;
color: #e8f0f3;
}
.auth_input {
width: 100%;
box-sizing: border-box;
padding: 0.7rem 0.9rem;
border-radius: 6px;
border: 1px solid rgba(255,255,255,0.15);
background: rgba(0,0,0,0.35);
color: #e8f0f3;
font-size: 1rem;
letter-spacing: 0.1em;
outline: none;
transition: border-color 0.15s;
}
.auth_input:focus {
border-color: rgba(0,136,255,0.6);
}
.auth_error {
margin: 0;
min-height: 1.1em;
font-size: 0.82rem;
color: #e05555;
}
.admin-page {
color: #1b2730;
overflow-y: auto;