Line Sender
This commit is contained in:
@@ -1,153 +1,127 @@
|
||||
# ROADMAP — appRobotFileservice Web-UI (Datei-Browser)
|
||||
# appRobotFileservice — Web-UI (Datei-Browser)
|
||||
|
||||
## Ziel
|
||||
|
||||
Einfache Web-Oberfläche direkt auf Port 2100, die GCode-Programme verwaltet.
|
||||
Kein Framework, kein Build-Schritt — reines HTML/CSS/JS, serviert als static files.
|
||||
> Stand: 2026-06-15
|
||||
> Beschreibt den implementierten Ist-Zustand der Browser-Oberfläche auf Port 2100.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Datei-Browser (aktuell implementiert)
|
||||
## Implementierter Funktionsumfang
|
||||
|
||||
**Dateien:** `public/index.html`, `public/index.css`
|
||||
|
||||
### Funktionen
|
||||
### Programm-Liste (linke Spalte)
|
||||
|
||||
| Feature | Status | Endpunkt |
|
||||
|---|---|---|
|
||||
| Programme auflisten | ✅ | `GET /api/programs?dir=` |
|
||||
| Unterordner auflisten | ✅ | `GET /api/folders?dir=` |
|
||||
| Breadcrumb-Navigation | ✅ | — |
|
||||
| In Unterordner navigieren (Klick) | ✅ | — |
|
||||
| Programm laden (Einzelklick) | ✅ | `PUT /api/active` |
|
||||
| Neue Datei anlegen (📄+) | ✅ | `POST /api/programs` |
|
||||
| Neuen Ordner anlegen (📁+) | ✅ | `POST /api/folders` |
|
||||
| Auswahl löschen (🗑) — Datei oder Ordner | ✅ | `DELETE /api/programs/:id` / `DELETE /api/folders/:name` |
|
||||
| Ordner-Löschen rekursiv | ✅ | `DELETE /api/folders/:name?dir=` |
|
||||
| Auto-Refresh (5 s Polling) | ✅ | — |
|
||||
|
||||
### Aktives Programm (rechte Spalte)
|
||||
|
||||
| Feature | Status | Endpunkt |
|
||||
|---|---|---|
|
||||
| Programm-Liste laden | ✅ | `GET /api/programs` |
|
||||
| Aktives Programm anzeigen (Zeilenliste in Grad) | ✅ | `GET /api/active` |
|
||||
| Programm als aktiv setzen (FLoad) | ✅ | `PUT /api/active` |
|
||||
| Programm löschen | ✅ | `DELETE /api/programs/:id` |
|
||||
| Cursor in Tabelle hervorheben + auto-scrollen | ✅ | — |
|
||||
| Stepping: First / Prev / Next / Last | ✅ | `POST /api/active/first|prev|next|last` |
|
||||
| Programm leeren (FClear) | ✅ | `POST /api/active/clear` |
|
||||
| Cursor in Tabelle hervorheben + auto-scrollen | ✅ | — |
|
||||
| Auto-Refresh (5 s Polling) | ✅ | — |
|
||||
| Collapse-Karten (wie appRobotHoming) | ✅ | — |
|
||||
| Download `.gcode` | ✅ | `GET /api/programs/:id/download` |
|
||||
| Zeilen löschen (🗑 pro Zeile, pending-State) | ✅ | `DELETE /api/active/lines/:index` |
|
||||
| Abbrechen / Speichern Edit-Bar | ✅ | — |
|
||||
|
||||
### Was Phase 1 noch fehlt
|
||||
### Navigieren / Senden Toggle (rechts in Controls-Zeile)
|
||||
|
||||
- **Download `.gcode`** — Endpunkt `GET /api/programs/:id/download` muss im Server
|
||||
hinzugefügt werden (liefert die rohe Datei mit `Content-Disposition: attachment`).
|
||||
- **Upload `.gcode`** — Datei vom Desktop in den Service laden (Phase 2).
|
||||
- **Umbenennen** — `PUT /api/programs/:id` mit neuem `name`.
|
||||
| Feature | Status | Details |
|
||||
|---|---|---|
|
||||
| Modus-Schalter `↕ Navigieren` / `▶ Senden` | ✅ | Drei-Gruppen-Layout |
|
||||
| Navigieren-Modus: nur Cursor bewegen | ✅ | `POST /api/active/next` |
|
||||
| Senden-Modus: G-Code-Zeile an Driver senden | ✅ | `POST /api/active/next?execute=true` |
|
||||
| Pfeil-Buttons während Fahrt gesperrt | ✅ | Bis HTTP-Antwort kommt |
|
||||
| Driver-Fehlermeldung im UI anzeigen | ✅ | HTTP 502 → Statuszeile |
|
||||
| Senden-Button deaktiviert wenn kein Driver | ✅ | `GET /api/config` beim Start |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Datei-Verwaltung
|
||||
|
||||
### Download-Endpunkt (kleines Backend-Feature für Phase 1.5)
|
||||
## Architektur
|
||||
|
||||
```
|
||||
GET /api/programs/:id/download
|
||||
→ Content-Type: text/plain
|
||||
→ Content-Disposition: attachment; filename="<id>.gcode"
|
||||
→ Body: rohe .gcode-Datei
|
||||
Browser (index.html)
|
||||
│
|
||||
├──[Navigieren]─► POST /api/active/next (Fileservice)
|
||||
│ Cursor +1, ;! in .gcode gespeichert
|
||||
│
|
||||
└──[Senden]──────► POST /api/active/next?execute=true (Fileservice)
|
||||
Fileservice: Cursor +1, sendet G-Code-Zeile
|
||||
│ server-seitig (lokales Netz)
|
||||
└──► appRobotDriver WS
|
||||
Inverse Kinematik → GRBL → Roboter
|
||||
M114-Broadcast / Fehler zurück an Fileservice
|
||||
Fileservice → HTTP 200 (OK) oder 502 (Driver-Fehler)
|
||||
```
|
||||
|
||||
Implementierung: 1 Route in `src/routes/programs.js` + `store.gcodePath(id)`.
|
||||
|
||||
### Upload
|
||||
|
||||
```
|
||||
POST /api/programs/upload
|
||||
Body: multipart/form-data { file: <gcode-Datei> }
|
||||
→ speichert unter slugify(filename) in GCodeFiles/
|
||||
```
|
||||
|
||||
Im Frontend: `<input type="file" accept=".gcode,.ngc">` + `FormData` fetch.
|
||||
|
||||
### Umbenennen / Metadaten bearbeiten
|
||||
|
||||
`PUT /api/programs/:id` ist bereits vorhanden — nur das Frontend-Formular fehlt.
|
||||
Inline-Eingabefeld in der Programm-Zeile, Enter → PUT.
|
||||
Der Browser verbindet sich **niemals direkt** mit dem Driver — der Fileservice
|
||||
ist die einzige Aussenschnittstelle.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Verzeichnis-Navigation
|
||||
## Cursor-Persistenz
|
||||
|
||||
Aktuell: alles flach im `GCodeFiles/`-Ordner.
|
||||
Später: Unterordner für Projekte (z. B. `GCodeFiles/greifen/`, `GCodeFiles/ablegen/`).
|
||||
|
||||
### Backend
|
||||
|
||||
Neue Konfiguration: `storageDir` bleibt Wurzel; zusätzlicher optionaler `subDir`-Parameter.
|
||||
Der Cursor wird als `;!`-Marker direkt **in der `.gcode`-Datei** gespeichert
|
||||
(primäre Quelle, sichtbar in jedem Texteditor). Das `.json`-Sidecar enthält `cursor`
|
||||
nur noch als Fallback für Altdateien ohne Marker.
|
||||
|
||||
```
|
||||
GET /api/programs?dir=greifen → listet GCodeFiles/greifen/
|
||||
PUT /api/active { id, dir } → lädt GCodeFiles/<dir>/<id>.gcode
|
||||
POST /api/dirs { name } → legt Unterordner an
|
||||
DELETE /api/dirs/:name → löscht (leer) Unterordner
|
||||
G90 G1 x0 y300 z0 a90.00 b-90.00 c0.00 e0.00 f1000 ;1
|
||||
G90 G1 x10 y300 z0 a0.00 b-90.00 c0.00 e0.00 f1000 ;2! ← Cursor-Zeile
|
||||
```
|
||||
|
||||
Wichtig: Pfad-Traversal verhindern — `assertValidId` auf jede Pfad-Komponente anwenden.
|
||||
|
||||
### Frontend
|
||||
|
||||
Breadcrumb-Leiste oberhalb der Programmliste:
|
||||
```
|
||||
GCodeFiles/ > greifen/ > [zurück]
|
||||
```
|
||||
|
||||
Doppelklick auf Ordner-Zeile → navigiert hinein.
|
||||
Klick auf Breadcrumb-Segment → navigiert zurück.
|
||||
`store.read()` liefert immer saubere Zeilen (kein `!`) und `cursor` als Index.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Live-Updates (WebSocket oder SSE)
|
||||
## Verzeichnis-Navigation
|
||||
|
||||
Aktuell: Polling alle 5 s.
|
||||
Besser: Server-Sent Events (SSE) oder WebSocket, damit die UI sofort
|
||||
reagiert wenn der Driver per FPoint einen neuen Punkt schreibt.
|
||||
|
||||
### Option A — Server-Sent Events (einfach, read-only)
|
||||
|
||||
```
|
||||
GET /api/events
|
||||
→ text/event-stream
|
||||
→ event: active-changed\ndata: <ActiveState-JSON>\n\n
|
||||
```
|
||||
|
||||
Der Fileservice emittiert nach jedem `_persist()` ein SSE-Event.
|
||||
Frontend: `new EventSource('/api/events')` statt `setInterval`.
|
||||
|
||||
### Option B — WebSocket (bidirektional, für spätere Steuerung)
|
||||
|
||||
Erlaubt auch WS-basierte FCode-Befehle direkt von der Web-UI.
|
||||
Aufwändiger, aber konsistent mit dem Driver-Pattern.
|
||||
|
||||
**Empfehlung für Phase 4:** SSE — eine Zeile Mehraufwand gegenüber Polling,
|
||||
kein zusätzliches Protokoll.
|
||||
- Echte Unterverzeichnisse in `GCodeFiles/` (max. 5 Ebenen tief)
|
||||
- Segmente validiert mit `/^[a-z0-9_-]+$/`
|
||||
- `dir`-Parameter in allen relevanten Endpoints (`?dir=training/run1`)
|
||||
- Breadcrumb: klickbare Segmente, Root = `GCodeFiles`
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — Inline-Editor
|
||||
## Noch nicht implementiert / offene Punkte
|
||||
|
||||
Einzelne Zeilen direkt im Browser bearbeiten.
|
||||
|
||||
```
|
||||
PUT /api/active/lines/:index { line: "G90 G1 x0 y300 z0 a90.00 …" }
|
||||
DELETE /api/active/lines/:index
|
||||
POST /api/active/lines { line, atIndex }
|
||||
```
|
||||
|
||||
Alle drei Endpunkte sind bereits im Backend implementiert.
|
||||
Frontend: Klick auf Tabellenzeile → `<input>` erscheint inline; Escape abbricht, Enter speichert.
|
||||
| Feature | Priorität | Hinweis |
|
||||
|---|---|---|
|
||||
| Upload `.gcode` vom Desktop | P2 | `POST /api/programs/upload` (multipart) |
|
||||
| Programm umbenennen | P2 | `PUT /api/programs/:id` (Backend vorhanden, UI fehlt) |
|
||||
| Zeile inline bearbeiten | P3 | `PUT /api/active/lines/:index` (Backend vorhanden) |
|
||||
| Zeile einfügen | P3 | `POST /api/active/lines { atIndex }` (Backend vorhanden) |
|
||||
| Live-Updates via SSE | P4 | statt 5s-Polling; `GET /api/events` |
|
||||
| `FPlay` / `FStop` im Browser | P4 | Playback-Modus |
|
||||
|
||||
---
|
||||
|
||||
## Datei-Übersicht (nach Phase 1)
|
||||
## Datei-Übersicht
|
||||
|
||||
```
|
||||
appRobotFileservice/
|
||||
public/
|
||||
index.html HTML-Struktur + JavaScript (inline)
|
||||
index.css Design-System (identisch mit appRobotHoming/styles.css)
|
||||
doc/
|
||||
fileBrowser_ROADMAP.md diese Datei
|
||||
src/
|
||||
server.js +express.static('public/')
|
||||
routes/
|
||||
programs.js TODO Phase 2: /download-Endpunkt
|
||||
active.js komplett
|
||||
public/
|
||||
index.html HTML + JavaScript (inline)
|
||||
index.css Design-System (dark, CSS-Variablen)
|
||||
src/
|
||||
server.js GET /api/config → driverConfigured
|
||||
driverClient.js WS-Client zum Driver (für Senden-Modus)
|
||||
routes/
|
||||
programs.js /api/programs* inkl. /download
|
||||
active.js /api/active* mit ?execute=true für Step-Routes
|
||||
folders.js /api/folders*
|
||||
doc/
|
||||
fileBrowser.md diese Datei (Ist-Zustand)
|
||||
commandsFromGCodeFile.md Senden-Modus Konzept + Implementierungsplan
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user