Fileservice

This commit is contained in:
chk
2026-06-14 11:18:46 +02:00
parent 319fae944a
commit a807732b58
16 changed files with 822 additions and 678 deletions

211
doc/fileserviceAPI.md Normal file
View 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