Fileservice
This commit is contained in:
10
doc/API.md
10
doc/API.md
@@ -22,16 +22,14 @@ Anfrager) und **Broadcasts** (an alle verbundenen Clients).
|
||||
| `Ping` | Heartbeat, wird geloggt | `Ping` | **nur Anfrager** (gezielt) |
|
||||
| `M114` | Statusabfrage | Positions-JSON (siehe unten) | **nur Anfrager** (gezielt) |
|
||||
| G-Code (`G1`, `G90`, `G91`, `G28`, `M1`, `M92`, …) | Bewegung/Zustandsänderung | aktuelles Positions-JSON | **alle Clients** (Broadcast) |
|
||||
| Datei-Befehle (`FShow`, `FList`, `FPoint`, `FPlus`, `FMinus`, `FLoad`, `FSave`, `FClear`, `M20/23/28/29`) | Datei-/Log-Verwaltung | Befehlsergebnis | **alle Clients** (Broadcast) |
|
||||
| FCodes (`FShow`, `FList`, `FPoint`, `FPlus`, `FMinus`, `FFirst`, `FLast`, `FGoto`, `FLoad`, `FSave`, `FClear`, `FPlay`, `FStop`) | Weiterleitung → `appRobotFileservice` via `robot/FCodeClient.js`; Stepping-Ergebnis (Radian-Zeile) wird lokal ausgeführt | Daten-JSON oder Positions-JSON | **alle Clients** (Broadcast) |
|
||||
| alles andere | – | Fehler-Envelope | **nur Anfrager** (gezielt) |
|
||||
|
||||
**Begründung der Trennung:** Eine Bewegung ändert die Roboterposition — das ist ein
|
||||
Status-Update, das jeder Client (z. B. die Simulation) sehen soll → Broadcast. Eine
|
||||
reine Abfrage (`Ping`, `M114`) ist eine direkte Antwort an den Anfrager → gezielt.
|
||||
|
||||
> **Hinweis:** Feinere Zielsteuerung der Datei-Befehle (z. B. `FShow` als
|
||||
> Anfrager-only-Antwort) sowie `FFirst`/`FLast` gehören zur Datei-Verwaltung in
|
||||
> **ToDo 4** und bleiben hier bewusst unverändert.
|
||||
FCodes (Datei-Befehle) werden durch den Driver als Gateway weitergereicht —
|
||||
Steuerungen brauchen keine direkte Verbindung zur `appRobotFileservice`.
|
||||
|
||||
### Positions-JSON (`M114` / Broadcast nach Bewegung)
|
||||
|
||||
@@ -57,7 +55,7 @@ Bei unbekannter Eingabe oder Verarbeitungsfehler erhält **nur der Anfrager**:
|
||||
|--------|-----------|
|
||||
| `UNKNOWN_COMMAND` | Eingabe passt auf keinen bekannten Befehl |
|
||||
| `GCODE_ERROR` | Fehler beim Parsen/Ausführen eines G-Code-Befehls |
|
||||
| `FILE_ERROR` | Fehler bei einem Datei-Befehl |
|
||||
| `FILE_ERROR` | Fehler bei einem FCode-Befehl (von `appRobotFileservice` weitergereicht) |
|
||||
|
||||
Erfolgs-Antworten (`Ping`, Positions-JSON) bleiben aus Kompatibilitätsgründen im
|
||||
bisherigen Rohformat; das Envelope gilt nur für Fehler.
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
> (`GCodeFileManager`) gebaut, sondern in das eigenständige Projekt
|
||||
> **`appRobotFileservice`** ausgelagert und über FCodes durch den Driver
|
||||
> weitergereicht. Im Driver bleibt nur ein dünner Proxy. Konzept & Schnittstelle:
|
||||
> [`draft_filehandeling.md`](draft_filehandeling.md) ·
|
||||
> [`draft_filehandeling_API.md`](draft_filehandeling_API.md).
|
||||
> [`fileserviceAPI.md`](fileserviceAPI.md).
|
||||
> Die folgenden Punkte sind als Vorlage für die Umsetzung *dort* zu lesen.
|
||||
|
||||
## Ziel der Verbesserung
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
> 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:
|
||||
> [`draft_filehandeling.md`](draft_filehandeling.md) ·
|
||||
> [`draft_filehandeling_API.md`](draft_filehandeling_API.md).
|
||||
> [`fileserviceAPI.md`](fileserviceAPI.md).
|
||||
> Die folgende Analyse bleibt als Umsetzungs-Vorlage für *jenes* Projekt erhalten.
|
||||
|
||||
## Ist-Zustand
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
# 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 <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`](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 `;<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
|
||||
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)
|
||||
@@ -1,253 +0,0 @@
|
||||
# Draft — `appRobotFileservice` API
|
||||
|
||||
> **Status:** Entwurf. Schnittstelle des ausgelagerten Programm-Handlings
|
||||
> (`appRobotFileservice`). Konzept & Rollenteilung:
|
||||
> [`draft_filehandeling.md`](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 `e` in **Radian**, `x/y/z` in
|
||||
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 (analog `ROBOT_API_KEY`).
|
||||
|
||||
---
|
||||
|
||||
## 2. Datenmodell
|
||||
|
||||
### Program (Metadaten, aus dem `.json`-Sidecar)
|
||||
```json
|
||||
{ "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)
|
||||
```json
|
||||
{ "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 }
|
||||
```
|
||||
> `currentLine` ist **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`:
|
||||
```json
|
||||
{ "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`
|
||||
```json
|
||||
{ "programs": [ { "id": "log", "name": "log", "lineCount": 36 }, … ] }
|
||||
```
|
||||
|
||||
### `GET /programs/{id}` ← `FShow`
|
||||
Inhalt + Metadaten für die Anzeige — in **Grad**, wie gespeichert (lesbar):
|
||||
```json
|
||||
{ "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`
|
||||
```jsonc
|
||||
{ "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`
|
||||
```json
|
||||
{ "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 }`
|
||||
```json
|
||||
{ "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.
|
||||
```json
|
||||
{ "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`):
|
||||
```json
|
||||
{ "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`
|
||||
```jsonc
|
||||
{ "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/next` lädt das nächste Programm (`PUT /active`) und startet `play`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Optionaler Event-Kanal (WebSocket)
|
||||
|
||||
Für eine Live-UI der appRobotFileservice (Fortschritt) ohne Polling:
|
||||
```json
|
||||
{ "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`) |
|
||||
|
||||
```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`](draft_filehandeling.md) — Konzept, Gateway-Rolle, Einheiten, Storage
|
||||
- [`API.md`](API.md) — bestehende Driver-Endpunkte (`/api/position`, WS `:2095`)
|
||||
- [`ToDo_6b_FileHandling.md`](ToDo_6b_FileHandling.md) — gelöste Detailprobleme
|
||||
211
doc/fileserviceAPI.md
Normal file
211
doc/fileserviceAPI.md
Normal file
@@ -0,0 +1,211 @@
|
||||
# `appRobotFileservice` — REST API
|
||||
|
||||
> **Einziger Consumer ist der Driver** (`appRobotDriver`). Steuerungen sprechen die
|
||||
> appRobotFileservice nie direkt an, sondern schicken **FCodes** an den Driver, der
|
||||
> sie hierher weiterreicht (`robot/FCodeClient.js`). 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.
|
||||
- **Basis-URL:** `http://appRobot_Fileservice:2100/api` (Container-intern) · `http://thinkcentre.local:2100/api` (lokal)
|
||||
- **Identität:** Programme über **`id`/Name** — **nie über Dateipfade**. Storage
|
||||
(`.gcode` + `.json`-Sidecar) ist intern gekapselt.
|
||||
- **Einheiten am Wire:** **driver-nativ** (φ/θ/ψ und `e` in **Radian**, `x/y/z` in
|
||||
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.
|
||||
- **Auth:** `Bearer <FILE_API_KEY>` für schreibende Operationen.
|
||||
|
||||
---
|
||||
|
||||
## 2. Datenmodell
|
||||
|
||||
### Program (Metadaten, aus dem `.json`-Sidecar)
|
||||
```json
|
||||
{ "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)
|
||||
```json
|
||||
{ "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 }
|
||||
```
|
||||
> `currentLine` ist **driver-nativ (Radian)** und kommentarfrei — direkt ausführbar.
|
||||
> Gespeichert wird in **Grad** mit Zeitstempel-Kommentar im Kommentarfeld (`;`).
|
||||
|
||||
### Pose (vom Driver beim `FPoint` mitgeschickt)
|
||||
```json
|
||||
{ "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 (`robot/FCodeClient.js`) übersetzt die FCodes der Steuerungen in diese Endpoints:
|
||||
|
||||
| FCode | Endpoint | Antwort an Steuerung (über Driver) |
|
||||
|---|---|---|
|
||||
| `FList` | `GET /programs` | Liste (Broadcast) |
|
||||
| `FShow [id]` | `GET /programs/{id}` | Inhalt in **Grad** (Broadcast) |
|
||||
| `FLoad <id>` | `PUT /active` | ActiveState (Broadcast) |
|
||||
| `FSave <name>` | `POST /programs` | id (Broadcast) |
|
||||
| `FClear` | `POST /active/clear` | ActiveState (Broadcast) |
|
||||
| `FPoint` | `POST /active/points` | Bestätigung (Broadcast) |
|
||||
| `FPlus` | `POST /active/next` | Bewegung → Driver führt aus → **Pose-Broadcast** |
|
||||
| `FMinus` | `POST /active/prev` | Bewegung → Driver führt aus → **Pose-Broadcast** |
|
||||
| `FFirst` | `POST /active/first` | Bewegung → Driver führt aus → **Pose-Broadcast** |
|
||||
| `FLast` | `POST /active/last` | Bewegung → Driver führt aus → **Pose-Broadcast** |
|
||||
| `FGoto <n>` | `POST /active/goto` | Bewegung → Driver führt aus → **Pose-Broadcast** |
|
||||
| `FPlay` / `FStop` | `POST /active/play` / `/stop` | Status (Broadcast) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Endpoints — Programm-Verwaltung
|
||||
|
||||
### `GET /api/programs` ← `FList`
|
||||
```json
|
||||
{ "programs": [ { "id": "log", "name": "log", "lineCount": 36 }, … ] }
|
||||
```
|
||||
|
||||
### `GET /api/programs/{id}` ← `FShow`
|
||||
Inhalt + Metadaten — in **Grad**, wie gespeichert (lesbar):
|
||||
```json
|
||||
{ "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!" ] }
|
||||
```
|
||||
> `;<epoch>` = Aufnahme-Zeitstempel; abschließendes `!` = Cursor-Zeile.
|
||||
|
||||
### `POST /api/programs` ← `FSave`
|
||||
```jsonc
|
||||
{ "name": "Demo C", "fromActive": true } // aus aktivem Puffer
|
||||
// oder expliziter Inhalt (in Grad):
|
||||
{ "name": "Demo C", "lines": ["G90 G1 x0 y300 … a90.00 …"], "angleUnit": "deg" }
|
||||
```
|
||||
→ `201 { "id": "demo_c", "lineCount": 12 }`
|
||||
|
||||
### `PUT /api/programs/{id}` · `DELETE /api/programs/{id}`
|
||||
Inhalt ersetzen / umbenennen · löschen (jeweils `.gcode` **und** `.json`).
|
||||
|
||||
---
|
||||
|
||||
## 5. Endpoints — Aktives Programm & Cursor
|
||||
|
||||
### `GET /api/active`
|
||||
Aktuellen `ActiveState` lesen (inkl. `currentLine` in Radian).
|
||||
|
||||
### `PUT /api/active` ← `FLoad`
|
||||
```json
|
||||
{ "id": "besteck_spuelmaschine" }
|
||||
```
|
||||
→ `ActiveState`. Nicht-existierendes Programm wird **leer angelegt** (für Teaching).
|
||||
|
||||
### `POST /api/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 /api/active/next` · `/prev` · `/first` · `/last` · `/goto` `{ "index": 7 }`
|
||||
```json
|
||||
{ "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`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Endpoints — Teaching / Aufnahme
|
||||
|
||||
### `POST /api/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.
|
||||
```json
|
||||
{ "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 /api/active/lines`
|
||||
Rohe Zeile(n) anhängen/einfügen (z. B. Pause `G4`):
|
||||
```json
|
||||
{ "line": "G4 P0.5", "atIndex": 8 }
|
||||
```
|
||||
|
||||
### `PUT /api/active/lines/{index}` · `DELETE /api/active/lines/{index}`
|
||||
Einzelne Zeile ersetzen / löschen.
|
||||
|
||||
---
|
||||
|
||||
## 7. Endpoints — Playback
|
||||
|
||||
### `POST /api/active/play` ← `FPlay`
|
||||
```jsonc
|
||||
{ "mode": "run", "fromStart": false }
|
||||
```
|
||||
|
||||
### `POST /api/active/stop` ← `FStop`
|
||||
|
||||
---
|
||||
|
||||
## 8. Fehler-Envelope
|
||||
|
||||
Konsistent mit dem Driver (`doc/ToDo_5_API.md`): `{ type, code, message, input }`.
|
||||
Der Driver reicht Fileservice-Fehler (`FILE_ERROR`) 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`) |
|
||||
|
||||
```json
|
||||
{ "type": "error", "code": "PROGRAM_NOT_FOUND", "message": "no program 'demo_x'", "input": "demo_x" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Beispiel-Flows
|
||||
|
||||
### Teaching-Session (Joystick → Aufnahme)
|
||||
```
|
||||
Steuerung → Driver: FLoad demo_c → Driver: PUT /api/active {id:"demo_c"}
|
||||
Steuerung → Driver: G1 … (Arm bewegen, lokal — Fileservice unbeteiligt)
|
||||
Steuerung → Driver: FPoint → Driver hängt Live-Pose an,
|
||||
POST /api/active/points { pose, feedrate }
|
||||
… weitere Punkte …
|
||||
Steuerung → Driver: FSave "Demo C" → Driver: POST /api/programs {name,fromActive:true}
|
||||
```
|
||||
|
||||
### Playback-Session (schrittweise)
|
||||
```
|
||||
Steuerung → Driver: FList → GET /api/programs
|
||||
Steuerung → Driver: FLoad demo_c → PUT /api/active
|
||||
Steuerung → Driver: FFirst → POST /api/active/first → {line (Radian)}
|
||||
Driver: receiveGCode(line) → Bewegung
|
||||
Driver: Pose-Broadcast an alle WS-Clients
|
||||
Steuerung → Driver: FPlus … / FPlay
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Verweise
|
||||
|
||||
- [`API.md`](API.md) — Driver-Endpunkte (`/api/position`, WS `:2095`)
|
||||
- [`robot/FCodeClient.js`](../robot/FCodeClient.js) — Gateway-Implementierung im Driver
|
||||
- [`ToDo_6b_FileHandling.md`](ToDo_6b_FileHandling.md) — gelöste Detailprobleme
|
||||
- `appRobotFileservice/README.md` — Konzept, Einheiten, Dateiformat, Konfiguration
|
||||
Reference in New Issue
Block a user