# Draft — File-Handling als externes Projekt `appRobotFileservice` (Driver als Gateway) > **Status:** Entwurf / Diskussionsgrundlage. > **Projekte:** Der **Driver** lebt in `appRobotDriver` (dieses Repo). Das gesamte > G-Code-**Programm**-Handling wird in das eigenständige Projekt > **`appRobotFileservice`** ausgelagert. Schnittstelle: > [`draft_filehandeling_API.md`](draft_filehandeling_API.md). > **Verhältnis zu ToDos:** ersetzt den Driver-internen `GCodeFileManager`-Ansatz aus > `doc/ToDo_4_GCode.md` und `doc/ToDo_6b_FileHandling.md`. > **Übergang darf hart sein** — keine Rückwärtskompatibilität nötig. --- ## 1. Motivation Heute lebt das Datei-Handling in [`robot/GCode.js`](../robot/GCode.js) (`receiveFC`, `ContainsFilesCommand`, `removeStringFromFile`, `toPiMultiple`, der `;!`-Cursor) und wird in [`server/InputWS.js`](../server/InputWS.js) gleichberechtigt neben den Bewegungs-Befehlen geroutet. Das vermischt zwei Verantwortungen: | | **Bewegung / Hardware** | **Programm-Verwaltung** | |---|---|---| | Aufgabe | eine G-Code-Zeile → Achsen bewegen | Programme speichern, anzeigen, durchblättern | | Zustand | Live-Pose des Roboters | Datei-Inhalte, Cursor, Listen | | Echtzeit | ja (Telnet/FluidNC) | nein (Storage-/UI-getrieben) | | Gehört zu | **`appRobotDriver`** | **`appRobotFileservice`** | --- ## 2. Leitprinzip — der Driver ist das einzige Front Door **Vorgabe:** Alle Steuerungen (Joystick, Tastatur, Bilderkennung, sensor-gesteuerte Programme …) kennen **nur den Driver**. Sie sprechen die appRobotFileservice **niemals direkt** an — nur indirekt, *durch den Driver hindurch*. ``` Steuerungen → Driver → appRobotFileservice (nur EINE Verbindung pro Steuerung: zum Driver) ``` Daraus folgt eine **einseitige Abhängigkeit**: ``` Steuerung ──kennt──► Driver ──kennt──► appRobotFileservice (Gateway) (passiver Storage-Dienst) • Der Driver hängt von der appRobotFileservice ab (ruft sie). • Die appRobotFileservice hängt von NICHTS ab — sie ruft den Driver nie an, kennt weder dessen URL noch dessen Pose. • Steuerungen brauchen KEINEN neuen Weg: sie reden weiter nur mit dem Driver. ``` > **Abgrenzung:** Gemeint sind **Steuerungen** (Echtzeit-Eingaben). Die > **Visualisierungs-/Verwaltungs-UI** der appRobotFileservice ist Teil *jenes* > Projekts und darf den Fileservice direkt ansprechen — sie ist keine Steuerung. --- ## 3. Befehls-Routing im Driver (der „Pass-through") Der Driver klassifiziert jede eingehende Nachricht und routet sie: ``` eingehende Nachricht am Driver (WS :2095 oder POST /api/gcode) │ ├─ Bewegung (G…, M1, M92, G92) → lokal ausführen → Pose broadcast ├─ Status (Ping, M114) → gezielt antworten ├─ FCode (FShow, FList, FPoint …) → an appRobotFileservice weiterreichen └─ sonst → Fehler-Envelope ``` ### FCodes — eine Befehlsfamilie wie die G-/M-Codes G-Code kennt `G1`, `G2`, `Gx` und `M1`, `M92`, … . Analog bilden die **FCodes** eine eigene Familie für Datei-/Programm-Befehle — **ohne Sonderzeichen**, einfach `F` + Wort: | FCode (Steuerung → Driver) | Bedeutung | Driver leitet weiter an | |---|---|---| | `FList` | Programme auflisten | `GET /programs` | | `FShow [id]` | Inhalt anzeigen | `GET /programs/{id}` | | `FLoad ` | Programm aktiv setzen | `PUT /active` | | `FSave ` | aktiven Puffer speichern | `POST /programs` | | `FClear` | aktives Programm leeren | `POST /active/clear` | | `FPoint` | **aktuelle Pose** aufnehmen | `POST /active/points` (Driver hängt Pose an) | | `FPlus` / `FMinus` | nächste / vorige Zeile | `POST /active/next` / `/prev` | | `FFirst` / `FLast` | an Anfang / Ende | `POST /active/first` / `/last` | | `FGoto ` | zu Zeile springen | `POST /active/goto` | | `FPlay` / `FStop` | durchlaufen / anhalten | `POST /active/play` / `/stop` | **Warum kein Sonderzeichen-Prefix nötig ist:** Eine Bewegungszeile beginnt mit `G` oder `M`; ein FCode mit `F`+Buchstabe. Das Feedrate-Wort `F1000` ist `F`+Ziffer und steht **nur innerhalb** einer `G`-Zeile, nie am Anfang. Der Router muss also nur **am Nachrichtenanfang** prüfen: `F` + Buchstabe → FCode. Damit ist die Familie kollisionsfrei — gegen die Lesbarkeit spricht nichts. `FFirst`/`FLast` werden dabei endlich umgesetzt (heute erkannt, aber nicht implementiert — vgl. ToDo_6b / Bug 2). Konkrete API: [`draft_filehandeling_API.md`](draft_filehandeling_API.md). --- ## 4. Zwei Datei-Welten — nur eine wandert aus | Welt | Beispiele | Verbleib | |---|---|---| | **Betriebs-Logs** | `logs/gcode_commands.log`, `logs/pings.log` | **bleibt im Driver** | | **G-Code-Programme** | `GCodeFiles/*.gcode` | **wird ausgelagert** (`appRobotFileservice`) | Die Logs betreffen den Hardware-/Verbindungsbetrieb und bleiben. Ausgelagert wird ausschließlich `GCodeFiles/` samt Cursor und FCodes. --- ## 5. Was bleibt im Driver, was wird ausgelagert | Heute (in [`robot/GCode.js`](../robot/GCode.js)) | Ziel | Anmerkung | |---|---|---| | `receiveGCode` / `containsCommand` / `receiveMCode` | **bleibt** | reine Bewegung | | `getM114` / `GET /api/position` | **bleibt** | Pose-Quelle für `FPoint` | | `logCommand` / `logPing` | **bleibt** | Betriebs-Logging | | Routing der FCodes | **bleibt als dünner Proxy** | neuer Gateway-Zweig in `InputWS` | | `receiveFC` (Programm-Logik) | **appRobotFileservice** | Verwaltung | | `static fileName`, `;!`-Cursor | **appRobotFileservice** (Cursor: In-Memory-Index, persistiert als `!`-Kommentar) | löst ToDo_6b Paket 2 | | `removeStringFromFile` | **entfällt** | nur für den `;!`-Hack nötig | | `toPiMultiple` (Grad→Radian) | **entfällt im Driver** → Umrechnung lebt im Fileservice | siehe §7 | | Zeilen-String-Bau in `FPoint` | **appRobotFileservice** | Zeilenformat ist Programm-Logik | Im Driver bleibt also: Bewegung, Pose, Logs — **plus ein dünner Proxy-Zweig**, der FCodes weiterreicht. Kein `GCodeFiles/`-IO, kein Cursor, **keine** Einheiten-Umrechnung. --- ## 6. Die zwei Kernabläufe ### 6a. Playback (Datei → Roboter) ``` Steuerung → Driver: FPlus Driver → Fileservice: POST /active/next (Cursor++) Fileservice → Driver: { line: "G90 G1 x310 y444 … a1.5708 …" } (driver-nativ, Radian) Driver: receiveGCode(line) → Achsen bewegen Driver: Pose-Broadcast an alle WS-Clients ``` Die appRobotFileservice liefert eine **fertig ausführbare, driver-native Zeile**; der Driver führt sie über seinen normalen `receiveGCode`-Pfad aus — *keine* Sonderbehandlung, *keine* Umrechnung. ### 6b. Teaching / Training (Roboter → Datei) — der robotik-spezifische Fall Der Arm wird **per Joystick** bewegt; G-Code ist hier **Ausgabe**. Entscheidend: Beim `FPoint` hat der **Driver die Live-Pose bereits lokal**. ``` Steuerung (Joystick) → Driver: G1 …/$J= (Arm bewegen, lokal) Steuerung → Driver: FPoint Driver: hängt die AKTUELLE Pose an (robot.x … robot.e, feedrate) Driver → Fileservice: POST /active/points { pose:{ x,y,z, a,b,c, e }, feedrate } Fileservice: Pose → Grad → als G-Code-Zeile persistieren, Cursor ans Ende Fileservice → Driver: { index, line } Driver → Steuerung: Bestätigung ``` Der Driver ist die Quelle der Wahrheit für die Pose und reicht sie beim Forwarden mit. Die appRobotFileservice muss den Driver dafür **nicht** anrufen. --- ## 7. Einheiten: Driver bleibt Radian, der Fileservice rechnet um Die Datei soll **wie Standard-G-Code aussehen** (Grad, `a-90.00`). Der Driver arbeitet intern und am G-Code-Eingang in **Radian** (Beleg: `receiveGCode` setzt `robot.phi = A` ohne Umrechnung). Beides ist vereinbar, ohne dass der Driver etwas umrechnen muss: | Achse | `.gcode`-Datei (Storage) | Wire Driver ↔ Fileservice | Driver intern | |---|---|---|---| | `x y z` | mm | mm | mm | | `a b c` (φ/θ/ψ) | **Grad** (`a-90.00`) | **Radian** | Radian | | `e` (Greifer) | **Grad** | **Radian** | Radian | | Umrechnung | — | **in der appRobotFileservice** | **keine** | - **Driver:** rechnet nie um — `toPiMultiple` **entfällt** ersatzlos (harter Übergang). - **appRobotFileservice:** konvertiert an ihrer **Storage-Grenze**: beim Lesen für Playback Grad→Radian, beim `FPoint`-Schreiben Radian→Grad. Damit liegt die Umrechnung an genau **einer** Stelle und ist testbar (löst ToDo_6b Paket 3). So bleibt die Datei standardnah und lesbar, der Hot-Path im Driver aber sauber. --- ## 8. Storage-Modell der appRobotFileservice: GCode-Datei + JSON-Sidecar Ziel: am Ende stehen **Dateien, die wie G-Code aussehen** (möglichst nah an einem Standard). Pro Programm: ``` GCodeFiles/ besteck_spuelmaschine.gcode ← das Programm, sieht aus wie Standard-G-Code (Grad) besteck_spuelmaschine.json ← Sidecar: Metadaten + Verwaltung ``` - **`.gcode`** (alternativ `.ngc`): standardnahe Bewegungszeilen, Winkel in **Grad**. Zeitstempel **und** Cursor stehen im **G-Code-Kommentarfeld** (`;…`) — so bleibt die Zeile standardkonform (Kommentare sind Teil des G-Code-Standards): - jede Zeile endet mit `;` (Aufnahme-Zeitstempel), - die **Cursor-Zeile** trägt zusätzlich ein `!`: `;!`. ``` G90 G1 x0 y300 z0 a90.00 b-90.00 c0.00 e0.00 f1000 ;1759566014 G90 G1 x310 y444 z0.5 a90.00 b-90.00 c0.00 e6.88 f1000 ;1759566112! G90 G1 x310 y444 z30.5 a90.00 b-90.00 c0.00 e6.88 f1000 ;1759566118 ``` Damit ist die `.gcode` **ohne Sidecar vollständig** (Bewegung + Zeitstempel + Cursor). - **`.json`-Sidecar** (Komfort/Verwaltung): Anzeigename, `createdAt`/`updatedAt`, `lineCount`, `angleUnit` (`"deg"`), optional benannte Labels (`"pick"`, `"place"` → Zeilenindex). Quelle der Wahrheit für Bewegung/Zeitstempel/Cursor bleibt die `.gcode`. Nach außen (API) werden Programme über **id/Name** angesprochen, **nie über Dateipfade** — `GCodeFiles/` und das Sidecar-Schema bleiben **intern** in der appRobotFileservice. Damit entfällt die `../`-Pfad-Problematik (ToDo_6b Paket 4) und ein späterer Wechsel des Storage bleibt unsichtbar. --- ## 9. Gemeinsamer Zustand: aktives Programm + Cursor (im Fileservice) Die appRobotFileservice hält genau einen **„aktives Programm + Cursor"**-Zustand als *Single Source of Truth*. Weil alle Steuerungen durch denselben Driver auf denselben Fileservice gehen, teilen sie automatisch denselben Cursor — `FPlus` vom Joystick und gleich darauf `FPlus` von der Bilderkennung sehen denselben Stand. - `aktivesProgramm` — id/Name (ersetzt `static fileName`). - `cursor` — während einer Session **Zeilenindex im Speicher** (schnelles Stepping ohne Neu-Schreiben). Beim Laden aus dem `!`-Kommentar gelesen, beim Speichern/ Entladen als `!` in die Cursor-Zeile zurückgeschrieben — so ist der Cursor persistiert, **ohne** bei jedem `FPlus` die ganze Datei neu zu schreiben (löst ToDo_6b Paket 2). --- ## 10. `/api/gcode` & WS — der Steuerungs-Kanal `POST /api/gcode` am Driver (optional, REST-Alternative zur WS) und die WS `:2095` sind der **Bewegungs-Eingang für alle Steuerungen**: - **Zugriff: alle Steuerungen** (Joystick, Tastatur, Bilderkennung, Sensorik). - **Nicht** die appRobotFileservice — sie pusht nie Bewegung an den Driver; der Driver führt Playback-Zeilen selbst aus (§6a). Der Fileservice braucht **keinen** Driver-Zugang. --- ## 11. Durchgereichte Payload-Größen Der Driver reicht bei `FShow`/`FList` ggf. größere Mengen durch (Datei-Inhalt, Listen). Das ist akzeptabel: die **appRobotFileservice** hält diese Antworten später klein (z. B. Paginierung, Kurz-/Übersichtsform), sodass der Durchreich-Weg über den Driver unkritisch bleibt. --- ## 12. Erforderliche kleine Driver-Ergänzungen 1. **`InputWS`-Router:** neuer Zweig „FCode am Anfang (`F`+Buchstabe) → an Fileservice forwarden, Antwort zurückreichen". Playback-Zeile lokal ausführen; Verwaltungs- Antworten gezielt an den Anfrager, Pose-ändernde Aktionen broadcasten (analog ToDo_5). 2. **`FPoint`-Pose:** Der Driver muss die **Live-Pose inkl. Greifer `e`** (und φ/θ/ψ) mitliefern. Heute setzt `getM114` `e` hart auf `0.0` — sonst geht die Greiferstellung beim Aufnehmen verloren. 3. **`POST /api/gcode`** (optional): REST-Bewegungs-Eingang für Steuerungen ohne WS. --- ## 13. Offene Fragen - **FCode-Namen:** bestehende Familie (`FPlus`/`FMinus` …) beibehalten oder einzelne umbenennen (`FNext`/`FPrev`)? — Empfehlung: bestehende behalten, neue ergänzen. - **Cursor-Persistenz:** als `!`-Kommentar in der `.gcode` (gewählt) — Häufigkeit des Zurückschreibens (sofort vs. debounced beim Entladen) noch offen. - **Sidecar-Umfang:** Metadaten + Labels (Cursor & Zeitstempel liegen in der `.gcode`). --- ## 14. Verweise - [`draft_filehandeling_API.md`](draft_filehandeling_API.md) — appRobotFileservice-Schnittstelle - [`ToDo_4_GCode.md`](ToDo_4_GCode.md) · [`ToDo_6b_FileHandling.md`](ToDo_6b_FileHandling.md) — abgelöst/gelöst - [`ToDo_5_API.md`](ToDo_5_API.md) / [`API.md`](API.md) — Routing & Fehler-Envelope - [`robot/GCode.js`](../robot/GCode.js) · [`server/InputWS.js`](../server/InputWS.js) · [`server/InfoServer.js`](../server/InfoServer.js)