Ausgelagertes Programm-/File-Handling (vormals GCode.receiveFC im appRobotDriver, ToDo_4 / ToDo_6b). Express-Service mit .gcode + .json-Storage, aktivem Programm + Cursor, Teaching (FPoint) und Playback. Speicherung in Grad, driver-nativ (Radian) zum Driver. Konzept/API unter doc/draft_filehandeling*.md. Tests: jest (13 gruen). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.4 KiB
Draft — appRobotFileservice API
Status: Entwurf. Schnittstelle des ausgelagerten Programm-Handlings (
appRobotFileservice). Konzept & Rollenteilung:draft_filehandeling.md.Einziger Consumer ist der Driver (
appRobotDriver). Steuerungen sprechen die appRobotFileservice nie direkt an, sondern schicken FCodes an den Driver, der sie hierher weiterreicht. Die appRobotFileservice ist passiv und driver-agnostisch: sie ruft den Driver nie an, kennt weder dessen URL noch dessen Pose. (Eine eigene Visualisierungs-UI darf direkt zugreifen — sie ist keine Steuerung.)
1. Überblick
- Transport: HTTP/REST + JSON. Optional ein WebSocket-Event-Kanal (Abschnitt 8).
- Basis-URL (Vorschlag):
https://<host>:2100/api - Identität: Programme über
id/Name — nie über Dateipfade. Storage (.gcode+.json-Sidecar) ist intern gekapselt. - Einheiten am Wire: driver-nativ (φ/θ/ψ und
ein Radian,x/y/zin mm) — exakt die G-Code-Strings, die der Driver ausführt. Gespeichert wird in Grad (standardnahe.gcode); die appRobotFileservice rechnet an ihrer Storage-Grenze um (Konzept §7). - Auth:
Bearer <FILE_API_KEY>für schreibende Operationen (analogROBOT_API_KEY).
2. Datenmodell
Program (Metadaten, aus dem .json-Sidecar)
{ "id": "besteck_spuelmaschine", "name": "Besteck Spülmaschine",
"lineCount": 12, "angleUnit": "deg",
"createdAt": "2025-10-04T10:25:00Z", "updatedAt": "2025-10-04T10:41:00Z" }
ActiveState (aktives Programm + Cursor — Single Source of Truth)
{ "programId": "besteck_spuelmaschine", "cursor": 4, "lineCount": 12,
"currentLine": "G90 G1 x310 y444 z0.5 a1.5708 b-1.5708 c0 e0.12 f1000",
"playing": false, "version": 7 }
currentLineist driver-nativ (Radian) und kommentarfrei — direkt ausführbar. Gespeichert wird in Grad mit Zeitstempel-Kommentar (draft_filehandeling.md§8).
Pose (vom Driver beim FPoint mitgeschickt)
Native Radian-Werte inkl. Greifer e:
{ "pose": { "x": 0, "y": 300, "z": 0, "a": 1.5708, "b": -1.5708, "c": 0, "e": 0.12 },
"feedrate": 1000 }
3. FCode ↔ Endpoint-Mapping
Der Driver übersetzt die FCodes der Steuerungen in diese Endpoints:
| FCode | Endpoint | Antwort an Steuerung (über Driver) |
|---|---|---|
FList |
GET /programs |
Liste (gezielt) |
FShow [id] |
GET /programs/{id} |
Inhalt in Grad (gezielt) |
FLoad <id> |
PUT /active |
ActiveState (gezielt) |
FSave <name> |
POST /programs |
id (gezielt) |
FClear |
POST /active/clear |
ActiveState (gezielt) |
FPoint |
POST /active/points |
Bestätigung (gezielt) |
FPlus |
POST /active/next |
Bewegung → Pose-Broadcast |
FMinus |
POST /active/prev |
Bewegung → Pose-Broadcast |
FFirst |
POST /active/first |
Bewegung → Pose-Broadcast |
FLast |
POST /active/last |
Bewegung → Pose-Broadcast |
FGoto <n> |
POST /active/goto |
Bewegung → Pose-Broadcast |
FPlay / FStop |
POST /active/play / /stop |
Status |
4. Endpoints — Programm-Verwaltung
GET /programs ← FList
{ "programs": [ { "id": "log", "name": "log", "lineCount": 36 }, … ] }
GET /programs/{id} ← FShow
Inhalt + Metadaten für die Anzeige — in Grad, wie gespeichert (lesbar):
{ "id": "besteck_spuelmaschine", "displayUnit": "deg",
"lines": [ "G90 G1 x0 y614 z0 a-90.00 b90.00 c0.00 e0 f1000 ;1759566014",
"G90 G1 x0 y300 z0 a90.00 b-90.00 c0.00 e0 f1000 ;1759566052!" ] }
Kommentar
;<epoch>= Aufnahme-Zeitstempel; ein abschließendes!markiert die Cursor-Zeile.
POST /programs ← FSave
{ "name": "Demo C", "fromActive": true } // aus aktivem Puffer
// oder expliziter Inhalt (in Grad, wie eine .gcode):
{ "name": "Demo C", "lines": ["G90 G1 x0 y300 … a90.00 …"], "angleUnit": "deg" }
→ 201 { "id": "demo_c", "lineCount": 12 } (legt demo_c.gcode + demo_c.json an)
PUT /programs/{id} · DELETE /programs/{id}
Inhalt ersetzen / umbenennen · löschen (jeweils .gcode und .json).
5. Endpoints — Aktives Programm & Cursor
GET /active
Aktuellen ActiveState lesen.
PUT /active ← FLoad
{ "id": "besteck_spuelmaschine" }
→ ActiveState. Validierung: existiert, ≥1 gültige Zeile (sonst EMPTY_PROGRAM).
POST /active/clear ← FClear
Aktives Programm leeren, Cursor → 0.
Stepping — next · prev · first · last · goto
Bewegt den Cursor und gibt die driver-native, ausführbare Zeile (Radian) zurück. Der Driver führt sie selbst aus — der Fileservice pusht nichts.
POST /active/next · /prev · /first · /last · /goto { "index": 7 }
{ "cursor": 5, "line": "G90 G1 x310 y444 z30.5 a1.5708 b-1.5708 c0 e0.12 f1000" }
Grenzen: next am Ende / prev am Anfang → CURSOR_OUT_OF_RANGE (optional wrap).
6. Endpoints — Teaching / Aufnahme
POST /active/points ← FPoint
Der Driver schickt die aktuelle Pose mit (native Radian-Werte). Die
appRobotFileservice rechnet nach Grad um, formatiert die Zeile (Feedrate,
Zeitstempel als Kommentar ;<epoch>) und hängt sie an.
{ "pose": { "x": 0, "y": 300, "z": 0, "a": 1.5708, "b": -1.5708, "c": 0, "e": 0.12 },
"feedrate": 1000 }
→ 201 { "index": 12, "line": "G90 G1 x0 y300 z0 a90.00 b-90.00 c0.00 e6.88 f1000 ;1759566014" }
POST /active/lines
Rohe Zeile(n) anhängen/einfügen (z. B. Pause G4):
{ "line": "G4 P0.5", "atIndex": 8 }
PUT /active/lines/{index} · DELETE /active/lines/{index}
Einzelne Zeile ersetzen / löschen (Editieren der Aufnahme).
7. Endpoints — Playback (kontinuierlich)
POST /active/play ← FPlay
{ "mode": "run", "fromStart": false } // "run" = bis Ende/Stop; "step" = eine Zeile
Die appRobotFileservice liefert die Zeilen getaktet zurück bzw. meldet Fortschritt
über den Event-Kanal (§8); ausgeführt werden sie vom Driver. POST /active/stop
hält an.
„Nächste File" (Playlist über mehrere Programme) baut darauf auf:
POST /playlist/nextlädt das nächste Programm (PUT /active) und startetplay.
8. Optionaler Event-Kanal (WebSocket)
Für eine Live-UI der appRobotFileservice (Fortschritt) ohne Polling:
{ "event": "cursorMoved", "cursor": 5, "line": "G90 G1 … a1.5708 …" }
{ "event": "activeChanged", "programId": "demo_c", "lineCount": 12 }
{ "event": "playStopped", "cursor": 9, "reason": "end" }
(Die Roboter-Pose-Updates laufen weiterhin über den Driver-WS-Broadcast — der
Fileservice kennt die Pose nur, soweit der Driver sie beim FPoint mitgibt.)
9. Fehler-Envelope
Konsistent mit dem Driver (doc/ToDo_5_API.md): { type, code, message, input }.
Der Driver reicht Fileservice-Fehler unverändert an die Steuerung zurück.
code |
Bedeutung |
|---|---|
PROGRAM_NOT_FOUND |
{id} existiert nicht |
INVALID_NAME |
unzulässiger Name (kein Pfad) |
EMPTY_PROGRAM |
FLoad auf Programm ohne gültige Zeile |
CURSOR_OUT_OF_RANGE |
next/prev/goto über die Grenzen |
NO_ACTIVE_PROGRAM |
Aktion erfordert geladenes Programm |
FILE_ERROR |
Storage-Fehler (.gcode/.json) |
{ "type": "error", "code": "PROGRAM_NOT_FOUND", "message": "no program 'demo_x'", "input": "demo_x" }
10. Durchgereichte Payloads
FShow/FList können größere Antworten erzeugen, die der Driver nur durchreicht.
Die appRobotFileservice hält sie akzeptabel klein (Paginierung, Übersichtsform),
sodass der Weg über den Driver unkritisch bleibt.
11. Konfiguration
Die appRobotFileservice braucht keinen Driver-Zugang (kein DRIVER_BASE_URL).
| Variable | Zweck | Beispiel |
|---|---|---|
FILE_SERVICE_PORT |
Port | 2100 |
STORAGE_DIR |
Verzeichnis für .gcode + .json |
./GCodeFiles |
FILE_EXT |
gcode oder ngc |
gcode |
STORE_ANGLE_UNIT |
Speichereinheit der Winkel | deg |
FILE_API_KEY |
Bearer-Token (Schreiben) | — |
12. Beispiel-Flows (durch den Driver)
Teaching-Session (Joystick → Aufnahme)
Steuerung → Driver: FLoad demo_c → Driver: PUT /active {id:"demo_c"}
Steuerung → Driver: G1 …/$J= (Arm bewegen, lokal — Fileservice unbeteiligt)
Steuerung → Driver: FPoint → Driver hängt Live-Pose an,
POST /active/points { pose, feedrate }
… weitere Punkte …
Steuerung → Driver: FSave "Demo C" → Driver: POST /programs {name,fromActive:true}
→ demo_c.gcode + demo_c.json
Playback-Session (Datei → Roboter, schrittweise)
Steuerung → Driver: FList → GET /programs (Auswahl)
Steuerung → Driver: FLoad demo_c → PUT /active
Steuerung → Driver: FFirst → POST /active/first → {line (Radian)}
Driver: receiveGCode(line) → Bewegung
Driver: Pose-Broadcast an alle UIs
Steuerung → Driver: FPlus … / FPlay
13. Verweise
draft_filehandeling.md— Konzept, Gateway-Rolle, Einheiten, StorageAPI.md— bestehende Driver-Endpunkte (/api/position, WS:2095)ToDo_6b_FileHandling.md— gelöste Detailprobleme