271 lines
8.8 KiB
Markdown
271 lines
8.8 KiB
Markdown
# robot.json – Zugriff via appRobotDriver
|
||
|
||
> **Status: umgesetzt** (2026-06-17) — `server/robotConfig.js` ist aktiv.
|
||
> Dieses Dokument beschreibt Entwurf und Implementierung. Der Implementierungsplan
|
||
> (Schritte 1–3) ist vollständig abgearbeitet.
|
||
|
||
---
|
||
|
||
## Verhalten je Env-Variable
|
||
|
||
| Variable | nicht gesetzt | gesetzt |
|
||
|----------|--------------|---------|
|
||
| `ROBOT_URL` | Kein Driver-Kontakt; alle Lese-/Schreibvorgänge direkt auf die lokale Datei | `fetchRobot()` liest von `GET {ROBOT_URL}/api/robot/config`; `pushRobot()` schreibt nach `POST {ROBOT_URL}/api/robot/config` |
|
||
| `ROBOT_JSON` | Standardpfad `scripts/robot_1781069752019.json` | Angegebener Pfad wird als lokale Cache-Datei verwendet |
|
||
|
||
Beide Variablen nicht gesetzt = **reiner Lokal-Modus**, identisch zum Verhalten vor dem Umbau.
|
||
|
||
---
|
||
|
||
## Ehemaliger Ist-Zustand (vor 2026-06-17)
|
||
|
||
`appRobotHoming` las und schrieb die Roboter-Konfiguration direkt aus einer
|
||
lokalen Datei:
|
||
|
||
```
|
||
ROBOT_JSON = process.env.ROBOT_JSON
|
||
|| 'scripts/robot_1781069752019.json'
|
||
```
|
||
|
||
Die Python-Skripte erhielten den Dateipfad als CLI-Argument (`-robot`, `--robot`).
|
||
Alle Kalibrierungs-Endpoints schrieben ebenfalls in diese Datei.
|
||
|
||
**Problem:** Der appRobotDriver besitzt die maßgebliche Konfiguration — nicht das
|
||
Homing-System. Nach einem Neustart könnten Konfiguration und Driver auseinanderlaufen.
|
||
|
||
---
|
||
|
||
## Ziel-Zustand
|
||
|
||
```
|
||
Startup GET {ROBOT_URL}/api/robot/config → robot.json laden
|
||
Kalibrierung schreiben → lokal anpassen → POST {ROBOT_URL}/api/robot/config
|
||
Python-Skripte → weiterhin lokale Datei (Cache) (unverändert)
|
||
```
|
||
|
||
`appRobotDriver` ist die **Single Source of Truth**.
|
||
`appRobotHoming` hält eine **lokale Kopie** (Cache-Datei) nur für die Dauer eines
|
||
Laufs — Python-Skripte müssen nicht angepasst werden.
|
||
|
||
---
|
||
|
||
## appRobotDriver API (Platzhalter)
|
||
|
||
Die genaue API ist noch zu klären. Annahmen:
|
||
|
||
| Aktion | Endpoint | Body / Antwort |
|
||
|--------|----------|----------------|
|
||
| Konfiguration lesen | `GET {ROBOT_URL}/api/robot/config` | → JSON (robot.json-Inhalt) |
|
||
| Konfiguration schreiben | `POST {ROBOT_URL}/api/robot/config` | Body: JSON (robot.json-Inhalt), → `{ ok: true }` |
|
||
|
||
Sobald die echten Endpoints bekannt sind, diese Tabelle und die Implementierung
|
||
(`server/robotConfig.js`) entsprechend anpassen.
|
||
|
||
---
|
||
|
||
## Implementierungsplan
|
||
|
||
### Schritt 1 — `server/robotConfig.js` (neu)
|
||
|
||
Kapselt den gesamten robot.json-Zugriff. `server.js` und `editRobot.js` importieren
|
||
nur noch diese Funktionen — kein direktes `fsPromises.readFile` / `writeFile` mehr.
|
||
|
||
```javascript
|
||
// server/robotConfig.js (ESM)
|
||
import fsPromises from 'fs/promises';
|
||
import path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
|
||
const ROBOT_URL = process.env.ROBOT_URL || '';
|
||
// Lokale Cache-Datei: bleibt als Fallback und für Python-Skripte
|
||
const ROBOT_JSON = process.env.ROBOT_JSON
|
||
|| path.join(__dirname, '..', 'scripts', 'robot_1781069752019.json');
|
||
|
||
/**
|
||
* Lädt robot.json.
|
||
* Reihenfolge: (1) ROBOT_URL/api/robot/config, (2) lokale Datei als Fallback.
|
||
* Schreibt das Ergebnis immer in die lokale Cache-Datei (für Python-Skripte).
|
||
*/
|
||
export async function fetchRobot() {
|
||
if (ROBOT_URL) {
|
||
const res = await fetch(new URL('/api/robot/config', ROBOT_URL));
|
||
if (!res.ok) throw new Error(`Driver ${res.status}: ${await res.text()}`);
|
||
const data = await res.json();
|
||
// Cache für Python-Skripte aktualisieren
|
||
await fsPromises.writeFile(ROBOT_JSON, JSON.stringify(data, null, 2), 'utf8');
|
||
return data;
|
||
}
|
||
// Fallback: lokale Datei (Entwicklung ohne Driver)
|
||
return JSON.parse(await fsPromises.readFile(ROBOT_JSON, 'utf8'));
|
||
}
|
||
|
||
/**
|
||
* Speichert robot.json.
|
||
* Schreibt immer in lokale Cache-Datei; sendet zusätzlich an Driver wenn konfiguriert.
|
||
*/
|
||
export async function pushRobot(data) {
|
||
await fsPromises.writeFile(ROBOT_JSON, JSON.stringify(data, null, 2), 'utf8');
|
||
if (ROBOT_URL) {
|
||
const res = await fetch(new URL('/api/robot/config', ROBOT_URL), {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(data),
|
||
});
|
||
if (!res.ok) throw new Error(`Driver ${res.status}: ${await res.text()}`);
|
||
}
|
||
}
|
||
|
||
/** Pfad zur lokalen Cache-Datei – wird an Python-Skripte als -robot-Argument übergeben. */
|
||
export const robotCachePath = ROBOT_JSON;
|
||
```
|
||
|
||
---
|
||
|
||
### Schritt 2 — `server/editRobot.js` anpassen
|
||
|
||
`readRobot()` und `writeRobot()` sind die einzigen I/O-Primitiven in `editRobot.js`.
|
||
Sie müssen auf `fetchRobot()` / `pushRobot()` umgestellt werden.
|
||
|
||
**Aktuell:**
|
||
```javascript
|
||
async function readRobot(robotPath) {
|
||
return JSON.parse(await fsPromises.readFile(robotPath, 'utf8'));
|
||
}
|
||
async function writeRobot(robotPath, data) {
|
||
await fsPromises.writeFile(robotPath, JSON.stringify(data, null, 2), 'utf8');
|
||
}
|
||
```
|
||
|
||
**Neu:**
|
||
```javascript
|
||
import { fetchRobot, pushRobot } from './robotConfig.js';
|
||
|
||
async function readRobot(_robotPath) { // _robotPath ignoriert – Quelle ist Driver
|
||
return fetchRobot();
|
||
}
|
||
async function writeRobot(_robotPath, data) {
|
||
return pushRobot(data);
|
||
}
|
||
```
|
||
|
||
Alle exportierten Funktionen (`assignByZRange`, `setArmMarkerSpin`, `adoptXAxis`,
|
||
`setJointOriginYZ`, …) bleiben **unverändert** — sie rufen intern `readRobot` /
|
||
`writeRobot` auf.
|
||
|
||
> Der `robotPath`-Parameter bleibt in den Signaturen erhalten (Kompatibilität),
|
||
> wird aber ignoriert. Alternativ: alle Aufrufer in `server.js` bereinigen und
|
||
> Parameter entfernen (Folgeschritt).
|
||
|
||
---
|
||
|
||
### Schritt 3 — `server/server.js` anpassen
|
||
|
||
#### 3a — Python-Skripte erhalten weiterhin die Cache-Datei
|
||
|
||
```javascript
|
||
// Vorher:
|
||
import { ROBOT_JSON } from './config.js'; // oder const direkt
|
||
// '-robot', ROBOT_JSON
|
||
|
||
// Nachher:
|
||
import { robotCachePath } from './robotConfig.js';
|
||
// '-robot', robotCachePath
|
||
```
|
||
|
||
Die Pipeline (`runBoardPipeline`, `runHoming`) fetcht robot.json **einmal vor dem
|
||
Lauf** via `fetchRobot()`, um den Cache zu aktualisieren:
|
||
|
||
```javascript
|
||
import { fetchRobot, robotCachePath } from './robotConfig.js';
|
||
|
||
async function runBoardPipeline(runDir, send, refSet) {
|
||
// Cache aktualisieren bevor Python startet
|
||
await fetchRobot();
|
||
|
||
// Python-Skripte erhalten robotCachePath wie bisher
|
||
const script1Args = [..., '-robot', robotCachePath, ...];
|
||
// …
|
||
}
|
||
```
|
||
|
||
#### 3b — `GET /api/robot` liest via `fetchRobot()`
|
||
|
||
```javascript
|
||
// Vorher:
|
||
app.get('/api/robot', async (req, res) => {
|
||
const robot = JSON.parse(await fsPromises.readFile(ROBOT_JSON, 'utf8'));
|
||
return res.json(robot);
|
||
});
|
||
|
||
// Nachher:
|
||
import { fetchRobot } from './robotConfig.js';
|
||
|
||
app.get('/api/robot', async (req, res) => {
|
||
try {
|
||
const robot = await fetchRobot();
|
||
return res.json(robot);
|
||
} catch (err) {
|
||
return res.status(502).json({ error: `Driver nicht erreichbar: ${err.message}` });
|
||
}
|
||
});
|
||
```
|
||
|
||
#### 3c — Kalibrierungs-Endpoints: kein Änderungsbedarf
|
||
|
||
Da `editRobot.js` intern `readRobot` / `writeRobot` verwendet und diese umgestellt
|
||
werden (Schritt 2), propagieren sich alle Kalibrierungs-Schreibvorgänge automatisch
|
||
zum Driver. Kein Änderungsbedarf in den einzelnen Endpoints.
|
||
|
||
---
|
||
|
||
## Startup-Verhalten
|
||
|
||
Beim Start von `server.js` einmalig robot.json laden und cachen:
|
||
|
||
```javascript
|
||
// server.js – nach HTTPS-Server-Start
|
||
try {
|
||
await fetchRobot();
|
||
console.log('✅ robot.json vom Driver geladen und gecacht.');
|
||
} catch (err) {
|
||
console.warn(`⚠ Driver nicht erreichbar – nutze lokale Datei: ${err.message}`);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Fallback-Verhalten
|
||
|
||
| Szenario | Verhalten |
|
||
|----------|-----------|
|
||
| `ROBOT_URL` nicht gesetzt | Nur lokale Datei — Entwicklungsmodus, Driver nicht nötig |
|
||
| Driver beim Start nicht erreichbar | Warnung, lokale Cache-Datei wird verwendet |
|
||
| Driver während Lauf nicht erreichbar | `pushRobot()` wirft Fehler → Kalibrierungs-Endpoint antwortet 502 |
|
||
| Python-Skript schlägt fehl | Kein push nötig (Python schreibt nicht in robot.json) |
|
||
|
||
---
|
||
|
||
## Datei-Übersicht nach Umbau
|
||
|
||
| Datei | Rolle |
|
||
|-------|-------|
|
||
| `server/robotConfig.js` *(neu)* | `fetchRobot()`, `pushRobot()`, `robotCachePath` |
|
||
| `server/editRobot.js` | `readRobot` / `writeRobot` delegieren an `robotConfig.js` |
|
||
| `server/server.js` | importiert `robotCachePath` statt lokalem `ROBOT_JSON`; ruft `fetchRobot()` vor Pipelines |
|
||
| `scripts/robot_1781069752019.json` | Bleibt als lokale Cache-Datei; **nicht** mehr primäre Quelle der Wahrheit |
|
||
|
||
---
|
||
|
||
## Status: Umgesetzt (2026-06-17)
|
||
|
||
`server/robotConfig.js` erstellt. `server/editRobot.js` und `server/server.js` angepasst.
|
||
|
||
## Offene Fragen
|
||
|
||
- [ ] Genaue Endpoints des appRobotDriver für GET / POST robot.json bestätigen (aktuell: `/api/robot/config`)
|
||
- [ ] Soll der Driver eine Versions-/Konflikterkennung haben (z.B. ETag / `updatedAt`)?
|
||
- [ ] `pushRobot()` bei Driver-Fehler: aktuell hard fail → Kalibrierungs-Endpoint antwortet 502
|
||
- [ ] Authentifizierung zwischen appRobotHoming und appRobotDriver nötig?
|