173 lines
7.3 KiB
Markdown
173 lines
7.3 KiB
Markdown
# Roadmap: robot.json als zentraler Service
|
||
|
||
## Ist-Zustand (Problem)
|
||
|
||
`robot.json` existiert aktuell an mindestens zwei unabhängigen Orten:
|
||
|
||
| App | Pfad | Rolle |
|
||
|-----|------|-------|
|
||
| `appRobotHoming` | `scripts/robot_<ts>.json` (Env: `ROBOT_JSON`) | Lesen + Schreiben (editRobot.js) |
|
||
| `appRobotRendering` | `data/robot/robot.json` | Lesen (Rendering) |
|
||
| `appRobotDriver` | — | Armlängen hardcodiert (`250, 264, 100`) |
|
||
|
||
Es gibt keine Versionierung, keinen Single Point of Truth, und wenn Homing die Geometrie kalibriert, bekommen Driver und Rendering davon nichts mit.
|
||
|
||
---
|
||
|
||
## Ziel-Architektur
|
||
|
||
`appRobotDriver` ist das **Zentrum des Systems** (er steuert die Hardware). Er besitzt daher auch die `robot.json` und stellt sie über einen REST-Endpunkt bereit. Alle anderen Apps lesen/schreiben ausschließlich über diesen Endpunkt.
|
||
|
||
```
|
||
appRobotDriver
|
||
└── data/robot/
|
||
├── robot.json ← aktueller Stand (Single Source of Truth)
|
||
├── robot_20260101_143522.json ← Snapshot vor jeder Änderung
|
||
└── robot_20260115_091010.json
|
||
|
||
appRobotHoming ──GET/PUT──► appRobotDriver :2098/api/robot
|
||
appRobotRendering ──GET──────► appRobotDriver :2098/api/robot
|
||
appRobotSimulation ──GET────► appRobotDriver :2098/api/robot
|
||
```
|
||
|
||
Der InfoServer (Port 2098) bekommt die neuen Endpunkte. Der WebSocket-Server (Port 2095) bleibt unverändert.
|
||
|
||
---
|
||
|
||
## API-Design
|
||
|
||
Alle Endpunkte unter dem bereits laufenden InfoServer auf Port 2098.
|
||
|
||
```
|
||
GET /api/robot → robot.json als JSON (kein Auth nötig)
|
||
PUT /api/robot → ersetzt robot.json, legt vorher Snapshot an (Auth nötig)
|
||
GET /api/robot/history → Liste aller Snapshots [{ filename, timestamp }]
|
||
GET /api/robot/history/:ts → einen bestimmten Snapshot abrufen
|
||
```
|
||
|
||
### Sicherheit (trivial, da Netz sicher)
|
||
|
||
PUT-Requests brauchen einen statischen API-Key als HTTP-Header:
|
||
|
||
```
|
||
Authorization: Bearer <ROBOT_API_KEY>
|
||
```
|
||
|
||
Key wird per Umgebungsvariable konfiguriert (`ROBOT_API_KEY`). Fehlt die Variable, wird ein zufälliger Key generiert und beim Start geloggt. Für GET braucht es keinen Key — Lesen ist überall erlaubt.
|
||
|
||
Das ist die einzige Absicherung. Kein JWT, keine Sessions, kein Rate-Limiting — Netz ist sicher, und ein versehentlicher Schreib-Request aus einem Browser soll trotzdem nicht funktionieren.
|
||
|
||
---
|
||
|
||
## Status
|
||
|
||
| Schritt | Bereich | Status |
|
||
|---------|---------|--------|
|
||
| 1 — Datei anlegen | appRobotDriver | ✅ erledigt |
|
||
| 2 — RobotConfigService | appRobotDriver | ✅ erledigt |
|
||
| 3 — Registrierung InfoServer | appRobotDriver | ✅ erledigt |
|
||
| 4 — Driver liest Armlängen | appRobotDriver | ✅ erledigt |
|
||
| 5 — appRobotHoming umstellen | appRobotHoming | ⬜ offen |
|
||
| 6 — appRobotRendering umstellen | appRobotRendering | ⬜ offen |
|
||
| 7 — Aufräumen | alle Repos | ⬜ offen |
|
||
|
||
Die Schritte 1–4 betreffen ausschließlich `appRobotDriver` und sind vollständig umgesetzt.
|
||
Schritte 5–7 betreffen `appRobotHoming` und `appRobotRendering` — separate Tickets/Sessions.
|
||
|
||
---
|
||
|
||
## Umsetzungsschritte
|
||
|
||
### Schritt 1 — Datei anlegen (appRobotDriver) ✅
|
||
|
||
- Verzeichnis `data/robot/` anlegen
|
||
- Default-`robot.json` wird vom Nutzer geliefert und dort abgelegt
|
||
- `.gitignore`-Eintrag für `data/robot/robot_*.json` (Snapshots gehören nicht ins Repo, `robot.json` selbst schon)
|
||
- Driver startet mit Defaults (`l1: 250, l2: 264, l3: 100`) wenn Datei fehlt, loggt Warnung
|
||
|
||
### Schritt 2 — RobotConfigService (appRobotDriver) ✅
|
||
|
||
Neue, **in sich geschlossene** Datei `server/RobotConfigService.js`. Sie hat keine Abhängigkeiten auf andere Teile des Drivers und kann in jeden Express- oder `https.createServer`-basierten Server mit einer Zeile eingehängt werden:
|
||
|
||
```js
|
||
const robotConfigService = require('./server/RobotConfigService');
|
||
robotConfigService.register(app, { apiKey: process.env.ROBOT_API_KEY });
|
||
// fertig — alle /api/robot*-Routen sind registriert
|
||
```
|
||
|
||
Das Modul kapselt intern:
|
||
|
||
```
|
||
readRobotJson() → Promise<object>
|
||
writeRobotJson(data) → Promise<{ snapshotFile }> // legt robot_<ts>.json an + pruning
|
||
listHistory() → Promise<{ filename, day, timestamp }[]>
|
||
readSnapshot(ts) → Promise<object>
|
||
pruneSnapshots() → löscht überschüssige Snapshots (s. Regel unten)
|
||
```
|
||
|
||
**Snapshot-Pruning-Regel:** Pro Tag maximal 100 Snapshots. Sind es mehr, wird nur der neueste des Tages behalten. Diese Bereinigung läuft automatisch nach jedem Schreibvorgang.
|
||
|
||
Timestamp-Format: `YYYYMMDD_HHmmss` (konsistent mit appRobotHoming).
|
||
|
||
**API-Key:** Wird per Option übergeben. Fehlt der Key (undefined), generiert das Modul beim ersten Start einen zufälligen Key, loggt ihn einmalig und speichert ihn in `data/robot/.apikey` (nicht im Repo). So funktioniert es ohne Konfiguration, ist aber trotzdem nicht offen.
|
||
|
||
### Schritt 3 — Registrierung in InfoServer.js (appRobotDriver) ✅
|
||
|
||
Eine Zeile in `server/InfoServer.js` am Anfang der Request-Handler:
|
||
|
||
```js
|
||
robotConfigService.register(httpsServer, { apiKey });
|
||
```
|
||
|
||
Da `InfoServer.js` kein Express nutzt (rohes `https.createServer`), bekommt `RobotConfigService` intern einen minimalen Router, der url-Matching selbst macht — oder `InfoServer.js` wird auf Express umgestellt (kleiner Schritt, bringt mehr Flexibilität für spätere Endpunkte).
|
||
|
||
### Schritt 4 — Driver liest Armlängen aus robot.json ✅
|
||
|
||
`startRobot.js` liest beim Start arm-lengths aus `data/robot/robot.json`.
|
||
Fallback auf `{ l1: 250, l2: 264, l3: 100 }` mit Log-Warnung wenn Datei fehlt oder Keys fehlen.
|
||
|
||
```js
|
||
// links.Arm1.size[1] → l1
|
||
// links.Arm2.size[1] → l2
|
||
// links.Ellbow.size[0] → l3
|
||
```
|
||
|
||
### Schritt 5 — appRobotHoming auf Driver-API umstellen ⬜
|
||
|
||
`server/server.js` in appRobotHoming:
|
||
|
||
- `ROBOT_JSON`-Env-Variable durch `ROBOT_DRIVER_URL` ersetzen (z.B. `https://appRobotDriver:2098`)
|
||
- Neue Hilfsfunktionen `fetchRobotJson()` / `pushRobotJson(data)` (HTTP GET/PUT mit API-Key)
|
||
- Alle `fsPromises.readFile(ROBOT_JSON)`-Aufrufe durch `await fetchRobotJson()` ersetzen
|
||
- Nach jeder editRobot.js-Transformation: `await pushRobotJson(updated)`
|
||
- `editRobot.js` bleibt unverändert (pure Transformationen, kein File-IO)
|
||
|
||
### Schritt 6 — appRobotRendering auf Driver-API umstellen ⬜
|
||
|
||
`robot.json` wird einmalig beim Start vom Driver geholt und gecacht. Cache wird per `GET /api/robot` aktualisierbar. Env-Variable `ROBOT_DRIVER_URL`.
|
||
|
||
### Schritt 7 — Aufräumen ⬜
|
||
|
||
- Lokale `robot.json`-Kopien in appRobotHoming und appRobotRendering entfernen
|
||
- `.gitignore`-Einträge in den betroffenen Repos anpassen
|
||
- `ROBOT_JSON`-Env-Variable aus docker-compose-Dateien entfernen, `ROBOT_DRIVER_URL` hinzufügen
|
||
|
||
---
|
||
|
||
## Entschiedene Punkte
|
||
|
||
| Frage | Entscheidung |
|
||
|-------|-------------|
|
||
| Driver-Start ohne robot.json | Mit Defaults starten + Warnung loggen |
|
||
| Snapshot-Limit | Max. 100 pro Tag; danach pro Tag nur die letzte Version behalten |
|
||
| Caching in Clients | Ja, erlaubt — Ziel ist API-only-Zugriff, kein direktes File-IO |
|
||
| Port InfoServer | 2098 bleibt; weitere Endpunkte werden dort angehängt |
|
||
|
||
---
|
||
|
||
## Nicht in dieser Roadmap
|
||
|
||
- Konflikte bei gleichzeitigen Schreibzugriffen (mutex) — vorerst nicht nötig, Homing ist der einzige Schreiber
|
||
- Diff-Anzeige zwischen Snapshots
|
||
- Rollback-Endpunkt (kann manuell über `GET /api/robot/history/:ts` + `PUT /api/robot` gemacht werden)
|