Kleine Arbeiten
This commit is contained in:
253
doc/draft_filehandeling_API.md
Normal file
253
doc/draft_filehandeling_API.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user