Files
appRobotFileservice/doc/draft_filehandeling.md
chk b68bdfa9b4 Initiales Projekt-Skelett appRobotFileservice
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>
2026-06-14 10:12:41 +02:00

13 KiB

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. 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 (receiveFC, ContainsFilesCommand, removeStringFromFile, toPiMultiple, der ;!-Cursor) und wird in 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 <id> Programm aktiv setzen PUT /active
FSave <name> 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 <n> 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.


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) 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 ;<epoch> (Aufnahme-Zeitstempel),
    • die Cursor-Zeile trägt zusätzlich ein !: ;<epoch>!.
    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 DateipfadeGCodeFiles/ 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