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 ProjektappRobotFileserviceausgelagert. Schnittstelle:draft_filehandeling_API.md. Verhältnis zu ToDos: ersetzt den Driver-internenGCodeFileManager-Ansatz ausdoc/ToDo_4_GCode.mdunddoc/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 —
toPiMultipleentfä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 ;1759566118Damit ist die
.gcodeohne Sidecar vollständig (Bewegung + Zeitstempel + Cursor). - jede Zeile endet mit
-
.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 (ersetztstatic 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 jedemFPlusdie 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
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).FPoint-Pose: Der Driver muss die Live-Pose inkl. Greifere(und φ/θ/ψ) mitliefern. Heute setztgetM114ehart auf0.0— sonst geht die Greiferstellung beim Aufnehmen verloren.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— appRobotFileservice-SchnittstelleToDo_4_GCode.md·ToDo_6b_FileHandling.md— abgelöst/gelöstToDo_5_API.md/API.md— Routing & Fehler-Enveloperobot/GCode.js·server/InputWS.js·server/InfoServer.js