# ToDo 6b — File-Handling > **✅ Erledigt / abgelöst:** Das File-Handling wird in das eigenständige Projekt > **`appRobotFileservice`** ausgelagert (Driver als Gateway, FCodes als Pass-through). > Die hier beschriebenen Detailprobleme werden **dort** gelöst: Cursor als In-Memory- > Index (Paket 2), explizite Grad↔Radian-Umrechnung im Fileservice (Paket 3), > Fehler-Envelope (Paket 4), asynchrones IO (Paket 5). Konzept & Schnittstelle: > [`fileserviceAPI.md`](fileserviceAPI.md). > Die folgende Analyse bleibt als Umsetzungs-Vorlage für *jenes* Projekt erhalten. ## Ist-Zustand `GCode.receiveFC()` implementiert nur einen Bruchteil der erkannten Befehle: | Befehl | Erkannt | Implementiert | Anmerkung | |---|---|---|---| | `FPoint` | ✅ | ✅ | Speichert aktuelle Position | | `FPlus` | ✅ | ✅ | Cursor vorwärts | | `FMinus` | ✅ | ✅ | Cursor rückwärts | | `FShow` | ✅ | ✅ | Gibt Dateiinhalt zurück | | `FFirst` | ✅ | ❌ | Cursor auf erste Zeile | | `FLast` | ✅ | ❌ | Cursor auf letzte Zeile | | `FList` | ✅ | ❌ | Listet verfügbare G-Code-Dateien | | `FLoad ` | ✅ | ❌ | Lädt eine andere Datei als aktive | | `FSave ` | ✅ | ❌ | Speichert aktive Datei unter neuem Namen | | `FClear` | ✅ | ❌ | Leert die aktive Log-Datei | | `M20` | ✅ | ❌ | List SD/Files | | `M23` | ✅ | ❌ | Select file | | `M28` | ✅ | ❌ | Start write | | `M29` | ✅ | ❌ | Stop write | Zusätzliche strukturelle Probleme: - **Cursor lebt in der Datei** (`';!'`-Marker): jedes `FPlus`/`FMinus` liest und schreibt die ganze Datei neu - **Synchrones File-IO** (`readFileSync`/`writeFileSync`) blockiert den Node.js-Event-Loop - **Kein Fehler-Feedback** an den WebSocket-Client — Fehler gehen nur auf `console.error` - **Aktive Datei ist statisch**: `static fileName = "GCodeFiles/log.gcode"` — `FLoad` kann das nicht ändern, weil es nicht implementiert ist - **`toPiMultiple()`** ist eine String-Manipulation aus der Vor-Parser-Ära, inkonsistent mit `GCodeParser` - **`FPoint` hardcodiert `f1000`** — sollte `robot.feedrate` verwenden (auch in ToDo_6a) - **Kein Directory-Listing für `FList`** — welche Dateien sind gültig? --- ## Konzept Die gesamte Datei-Logik wird in eine eigene Klasse `GCodeFileManager` ausgelagert (gemäß ToDo_4). Diese ToDo beschreibt die **inhaltliche Umsetzung** — unabhängig davon, ob sie noch in `GCode.js` oder schon in `GCodeFileManager` landet. Der **Cursor** wird aus der Datei in den Speicher verlagert: `currentLineIndex` als Instanzvariable, die nur beim Laden einer Datei initialisiert wird. Das spart permanentes Neu-Schreiben der Datei. --- ## Pakete ### Paket 1: Fehlende Befehle implementieren - [ ] **`FFirst`** — `currentLineIndex` auf 0 setzen, erste Zeile anfahren - [ ] **`FLast`** — `currentLineIndex` auf letzte Zeile setzen, anfahren - [ ] **`FClear`** — aktive Log-Datei leeren, `currentLineIndex` auf 0 - [ ] **`FList`** — Inhalte von `GCodeFiles/` auflesen, Liste zurückgeben ``` XYZ__FList__XYZ log.gcode log_2025_10_04.gcode ... ``` - [ ] **`FLoad `** — benannte Datei aus `GCodeFiles/` laden, `currentLineIndex` auf den `;!`-Marker setzen (Rückwärtskompatibilität), oder auf 0 falls kein Marker - [ ] **`FSave `** — aktiven Puffer unter `GCodeFiles/` speichern - [ ] `M20`/`M23`/`M28`/`M29` — Marlin-kompatible Aliases für `FList`/`FLoad`/`FSave`/Stop, oder explizit als „nicht unterstützt" mit Fehlermeldung antworten ### Paket 2: Cursor in den Speicher verlagern Aktuell wird der Cursor als `';!'` direkt in die Datei geschrieben. Das zwingt bei jedem `FPlus`/`FMinus` die gesamte Datei neu zu schreiben. - [ ] `currentLineIndex` als Instanzvariable einführen (startet bei 0 oder am `';!'`-Marker beim Laden) - [ ] `FPlus`/`FMinus` nur noch `currentLineIndex` ändern, Datei bleibt unverändert - [ ] `';!'`-Marker beim **Speichern** (`FSave`) optional mitschreiben (für externe Kompatibilität) - [ ] Beim Laden einer bestehenden Datei: Zeile mit `';!'` suchen → `currentLineIndex` setzen, dann `';!'`-Marker aus der Datei entfernen (einmalige Migration) ### Paket 3: `toPiMultiple` durch Parser-Integration ersetzen Gespeicherte Dateien enthalten Winkel in Grad (`a45.00`), der Parser erwartet Zahlenwerte ohne Umrechnungspflicht. Die Konversion muss explizit und testbar sein. - [ ] Beim Lesen einer Zeile aus der Datei: erkennen, ob Winkel in Grad oder Rad vorliegen - Marker-Ansatz: `FPoint` könnte einen Einheiten-Kommentar schreiben (`;unit=deg`) - Oder: Konvention festlegen und dokumentieren (Dateien immer in Grad, Anzeige in Grad) - [ ] `toPiMultiple()` durch eine klar benannte Funktion `degreesToRadians(gCodeString)` ersetzen, die intern `GCodeParser.parse()` nutzt statt raw-String-Manipulation - [ ] `FPoint` sollte die Einheit konsistent speichern — Grad ist sinnvoller für Lesbarkeit ### Paket 4: Fehler-Feedback und Validierung - [ ] Bei Dateifehlern (nicht gefunden, Leserechte) → strukturierte Fehlermeldung an den WebSocket-Client zurückgeben (gemäß Fehler-Envelope aus ToDo_5) - [ ] `FLoad` validiert, dass die Zieldatei existiert und mindestens eine gültige G-Code-Zeile enthält - [ ] Dateinamen in `FLoad`/`FSave` sanitizen: keine `../`-Pfade, nur `GCodeFiles/`-Verzeichnis ### Paket 5: Asynchrones File-IO (optional, später) Alle aktuellen `readFileSync`/`writeFileSync`-Aufrufe blockieren den Event-Loop. Bei kleinen Dateien (< 1 MB) ist das tolerierbar. Bei größeren Programmen oder häufigem `FPoint`-Schreiben wird es spürbar. - [ ] `FPoint`-Schreiben auf `fs.appendFile` (async) umstellen — Cursor ist im Speicher, kein Lesen nötig - [ ] `FShow`/`FList` auf `fs.readFile` (async) umstellen - [ ] `FLoad`/`FSave` async ## Betroffene Dateien - `robot/GCode.js` (oder künftig `robot/GCodeFileManager.js` nach ToDo_4) - `server/InputWS.js` — Fehler-Routing für File-Befehle - `test/GCode.FileOperation.test.js` — stark erweitern