# appRobotFileservice Programm-/File-Handling-Service für den AppRobot. Speichert **G-Code-Programme** (`.gcode` + `.json`-Sidecar), hält das **aktive Programm + Cursor** und unterstützt **Teaching** (Pose aufnehmen), **Playback** (Programm abspielen) sowie einen **Browser-basierten Datei-Browser** auf Port 2100. ## Rolle in der Architektur ``` Steuerungen → appRobotDriver → appRobotFileservice ← Browser (Port 2100) (Joystick, …) (Gateway) (dieses Projekt) │ [Senden-Modus, lokales Netz] └──► appRobotDriver WS ``` - **Steuerungen** kennen nur den Driver. Datei-Befehle (**FCodes** wie `FList`, `FPoint`, `FPlus`) schickt die Steuerung an den Driver, der sie als REST-Aufrufe hierher weiterreicht. - **Browser** spricht direkt mit dem Fileservice (Port 2100). Im **Senden-Modus** sendet der Fileservice die G-Code-Zeile server-seitig an den Driver — der Browser verbindet sich nie direkt mit dem Driver. - **Playback**: dieser Service liefert die nächste Zeile **driver-nativ (Radian)** zurück; ausgeführt wird sie vom Driver. ## Einheiten (wichtig) - **Gespeichert** wird in **Grad** (Standard-G-Code, lesbar): `a/b/c/e` in Grad, `x/y/z` in mm. - **Am Wire / zum Driver** ist alles **driver-nativ**: `a/b/c/e` in **Radian**. - Die Umrechnung passiert **ausschließlich hier** (`src/gcode/units.js`). ## Dateiformat `.gcode` ist die **einzige verbindliche Positions-Abfolge** — reiner Standard-G-Code, Zeitstempel und Cursor-Marker stehen im Kommentarfeld: ``` 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! ``` - `;` = Aufnahme-Zeitstempel. - `;!` (Ausrufezeichen am Ende des Kommentars) = **Cursor-Marker**: zeigt die zuletzt angefahrene Zeile. Primäre Cursor-Quelle beim Lesen. Direkt im Texteditor sichtbar. - `.json` ist ein Sidecar mit Metadaten (Name, Zeiten, `lineCount`, `angleUnit`). `cursor` im Sidecar dient nur als Fallback für Altdateien ohne `;!`-Marker. ## Start ``` npm install npm start # http://localhost:2100 npm test # jest ``` HTTP (kein TLS) — der Service ist intern. Konfiguration via Env-Variablen (s. u.). ## Konfiguration (Env) | Variable | Default | Zweck | |---|---|---| | `FILE_SERVICE_PORT` | `2100` | Port | | `STORAGE_DIR` | `./GCodeFiles` | Wurzel-Verzeichnis für `.gcode` + `.json` | | `FILE_EXT` | `gcode` | `gcode` oder `ngc` | | `STORE_ANGLE_UNIT` | `deg` | Speichereinheit der Winkel | | `FILE_API_KEY` | – | Bearer-Token für Schreibzugriffe (fehlt → offen, Dev) | | `DRIVER_WS_URL` | – | WS-URL des appRobotDriver (z. B. `wss://appRobotDriver:2095`); leer → Senden-Modus deaktiviert | | `DRIVER_TIMEOUT_MS` | `10000` | Maximale Wartezeit auf Driver-Antwort (ms) | ## API (Kurzüberblick) ### Programme | Endpoint | Methode | Zweck | |---|---|---| | `GET /api/programs?dir=` | GET | Programme auflisten | | `POST /api/programs` | POST | Programm anlegen | | `DELETE /api/programs/:id?dir=` | DELETE | Programm löschen | | `GET /api/programs/:id/download` | GET | `.gcode`-Datei herunterladen | ### Ordner | Endpoint | Methode | Zweck | |---|---|---| | `GET /api/folders?dir=` | GET | Unterordner auflisten | | `POST /api/folders` | POST | Unterordner anlegen | | `DELETE /api/folders/:name?dir=` | DELETE | Unterordner (rekursiv) löschen | ### Aktives Programm | FCode (am Driver) | Endpoint | Hinweis | |---|---|---| | `FList` | `GET /api/programs` | | | `FLoad ` | `PUT /api/active` | `{ id, dir }` | | `FClear` | `POST /api/active/clear` | | | `FPoint` | `POST /api/active/points` | | | `FPlus` / `FMinus` | `POST /api/active/next` / `/prev` | `?execute=true` → auch an Driver senden | | `FFirst` / `FLast` | `POST /api/active/first` / `/last` | `?execute=true` → auch an Driver senden | | `FGoto ` | `POST /api/active/goto` | | | `FPlay` / `FStop` | `POST /api/active/play` / `/stop` | | | — | `DELETE /api/active/lines/:index` | Zeile löschen | ### Senden-Modus (`?execute=true`) `POST /api/active/next?execute=true` bewegt den Cursor UND sendet die G-Code-Zeile an den Driver (server-seitig). Der Endpoint wartet auf die Driver-Antwort: - **Erfolg** (M114-Broadcast) → HTTP 200 `{ cursor, line, driverPos }` - **Driver-Fehler** → HTTP 502 `{ error, message }` - **Timeout** → HTTP 504 ### Konfiguration | Endpoint | Zweck | |---|---| | `GET /api/health` | Liveness-Check | | `GET /api/config` | `{ driverConfigured: bool }` — ob `DRIVER_WS_URL` gesetzt ist | ## Projektstruktur ``` index.js Einstiegspunkt (startet den Server) src/ config.js Env-Konfiguration (inkl. driverWsUrl, driverTimeoutMs) server.js Express-App (createApp, /api/config, /api/health) errors.js Fehler-Envelope + Middleware auth.js Bearer-Auth (für Schreibzugriffe) driverClient.js WS-Client zum Driver (Senden-Modus) gcode/units.js Grad↔Radian, Zeilenformat, Cursor-Marker (;!) store/fileStore.js .gcode + .json Persistenz (mit dir-Support, max. 5 Ebenen) active/activeState.js Aktives Programm + Cursor (Singleton, ;!-Persistenz) routes/programs.js /api/programs* inkl. /download routes/active.js /api/active* inkl. ?execute=true routes/folders.js /api/folders* public/ index.html Browser-UI (Datei-Browser + Stepping + Senden-Modus) index.css Design-System (dark theme, CSS-Variablen) test/ units.test.js Einheiten + Cursor-Marker-Funktionen fileStore.test.js Persistenz-Tests (;!-Marker, dir-Support) activeState.test.js ActiveState-Tests (Teaching, Stepping, Cursor-Persistenz) driverClient.test.js WS-Client (Erfolg, Fehler, Timeout) doc/ fileBrowser.md Browser-UI Ist-Zustand commandsFromGCodeFile.md Senden-Modus Konzept + Implementierungsplan draft_filehandeling.md Original-Konzept (historisch) draft_filehandeling_API.md Original-API-Entwurf (historisch) GCodeFiles/ Programm-Storage zur Laufzeit ``` ## Status Produktiv einsetzbar. Offen: Upload via Browser, Inline-Editor, Live-Updates via SSE (statt 5s-Polling). Siehe [`doc/fileBrowser.md`](doc/fileBrowser.md).