# 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_.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 ``` 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 (→ ToDo 3) | | 4a — InfoServer auf Express umgestellt | appRobotDriver | ✅ erledigt | | 4b — UI: Robot.json + History in index.html | 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 writeRobotJson(data) → Promise<{ snapshotFile }> // legt robot_.json an + pruning listHistory() → Promise<{ filename, day, timestamp }[]> readSnapshot(ts) → Promise 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) ✅ `InfoServer.js` wurde auf Express umgestellt. Registrierung mit einer Zeile: ```js robotConfigService.register(app, { apiKey: options.apiKey }); ``` ### Schritt 4 — Driver liest Armlängen aus robot.json ✅ Ausgelagert nach `robot/RobotConfig.js` (→ ToDo 3). Liest beim Start synchron `data/robot/robot.json`, leitet l1/l2/l3 aus `links.*.skeleton.to` ab (nicht mehr aus `size`): ```js // links.Arm1.skeleton.to[1] → l1 (Math.abs) // links.Arm2.skeleton.to[1] → l2 (Math.abs) // links.Ellbow.skeleton.to[0] → l3 ``` Fallback auf Defaults wenn Datei fehlt. Controller-IPs/-Ports/-Achsen ebenfalls aus `robot.json` (keine hardcodierten Werte mehr in `startRobot.js`). ### Schritt 4a — InfoServer auf Express umgestellt ✅ `server/InfoServer.js` nutzt jetzt Express statt rohem `https.createServer`. Ermöglicht saubere Router-Registrierung und ist Grundlage für alle weiteren API-Endpunkte. ### Schritt 4b — UI: Robot.json + History in index.html ✅ Zwei neue Panels im InfoServer-Frontend (`public/index.html` + `app.js`): - **Robot.json** — zeigt aktuelle `robot.json` mit aufklappbaren Top-Level-Abschnitten (`
`). Aktualisiert sich automatisch, solange kein Snapshot ausgewählt ist. Label (`_label`-Feld) wird oben angezeigt. - **Robot.json History** — listet alle Snapshots aus `GET /api/robot/history`. Klick auf Eintrag lädt diesen Snapshot in das Robot.json-Panel. Klick auf „aktuell" schaltet zurück auf Live-Ansicht. ### 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)