# AppRobotDriver
Dieses Projekt empfängt G-Code und Robotersteuerbefehle, berechnet Inverse Kinematik für einen mehrgliedrigen Roboterarm und leitet die resultierenden Achsenbefehle an mehrere GRBL/FluidNC-Telnet-Sender weiter.
## Einbindung ins Projekt
Der Driver steht zwischen Eingabe-Programmen (appInput oder appAutomasiation oder jede beliebige andere Eingabeform)
welche die Eingabe steuert. Die Eingabe wird eben an den Driver weitergegeben. Hier im Driver werden die Welt-Koordinaten
in Motor-Koordinaten umgerechnet, es werden die Motoren angesteuert.
Die Motor--Steuerung erfolgt (momentan) per GCode an FluidNC Driver--Boards. Diese arbeiten jeweils mit
Motor-Koordinaten in den X, Y und Z-Achsen. Es werden mehrere FluidNC Boards unterstützt.
## Architektur
- `startRobot.js` startet zwei HTTPS-Server:
- Eingabe-Server + WebSocket für G-Code und Steuerbefehle
- Info-Server für Status, Position und einfache Weboberfläche
- `server/InputWS.js` empfängt Nachrichten von WebSocket-Clients, routet G-Code-Befehle lokal und leitet FCodes via `robot/FCodeClient.js` an `appRobotFileservice` weiter.
- `robot/GCode.js` verarbeitet G-Code, übersetzt ihn in Roboter-Koordinaten und triggert `robot.sendCommand()` (kein Datei-Handling mehr).
- `robot/FCodeClient.js` übersetzt FCodes (`FPoint`, `FPlus`, …) in REST-Aufrufe an `appRobotFileservice` (Gateway-Funktion des Drivers).
- `robot/RobotBase.js` ist die abstrakte Basisklasse / der Interface-Vertrag: generische Infrastruktur (Zustand, `sendCommand`, Motor-Geschwindigkeiten) plus die zwei abstrakten Kinematik-Methoden.
- `robot/kinematics/Arm3SegmentLinearX.js` ist die konkrete Kinematik (Inverse + Vorwärts) für den aktuellen Arm. Die Auswahl der Kinematik erfolgt über `robot/KinematicsFactory.js` (Umgebungsvariablen `ROBOT_KINEMATICS` / `ROBOT_KINEMATICS_PARAMS`). Siehe `doc/ToDo_12_InverseKinematikConfig_ROADMAP.md`.
- `robot/RobotConfig.js` liest `data/robot/robot.json` beim Start synchron und gibt einen typisierten Konfigurations-Record zurück (Kinematik-Parameter, Bewegungs-Defaults, Controller-Endpunkte). Env-Variablen überschreiben die JSON-Werte.
- `server/RobotConfigService.js` stellt `GET/PUT /api/robot` und `GET /api/robot/history` über den InfoServer bereit (Single Source of Truth für alle Apps). Schreibzugriffe erfordern `Authorization: Bearer `.
- `robot/TelnetSenderGRBL.js` formatiert die Motor-Positionen in GRBL-kompatible Befehle und sendet sie per Telnet an einen Zielcontroller.
## Eingaben
Die Eingaben kommen per WebSocket an den HTTPS-Server und werden in `server/InputWS.js` verarbeitet.
### Unterstützte Nachrichten
- `Ping`
- Antwort: Wird zurück an alle verbundenen Clients gesendet.
- `M114`
- Antwort: Positionsdaten des Roboters im JSON-Format.
- G-Code-Befehle:
- `G90`, `G91`, `G1`, `G28`
- `G92` setzt die Motorposition ohne Bewegung. Winkel (`Y`,`Z`,`A`,`B`,`C`) in **Grad** (G-Code-Konvention, wie FluidNC); `X` in mm; `E` = Greifer-Öffnung in mm (ab Null-Position eines Fingers), wird intern über die Greifer-Kopplung in `eMotor` umgerechnet. (`M92` macht dasselbe, erwartet die Winkel aber roh in **Radiant**.)
- Messungen in `X`, `Y`, `Z`, `A`, `B`, `C`, `E`, `F`
- `M1` für direkte Motor-Koordinaten
- FCodes (Datei-/Programm-Befehle) — werden durch den Driver an `appRobotFileservice` weitergeleitet:
- `FPoint`, `FPlus`, `FMinus`, `FFirst`, `FLast`, `FGoto `
- `FShow [id]`, `FList`, `FLoad `, `FSave `, `FClear`
- `FPlay`, `FStop`
- Vollständige API: `doc/fileserviceAPI.md`
### G-Code-Verarbeitung
- `GCode.receiveGCode(robot, message)`
- Gruppiert Zeilen, unterstützt Inline-Jogging (`$J=`)
- Schaltet zwischen absoluter und relativer Koordinatenverarbeitung
- Aktualisiert Position und Winkel im `robot`-Objekt
- Führt inverse Kinematik aus mit `robot.calculateAngles3D()`
- Sendet das Ergebnis an `robot.sendCommand()`
## Ausgaben
- WebSocket-Broadcasts an alle verbundenen Clients
- Nachdem ein G-Code-Befehl verarbeitet wurde, sendet das System `GCode.getM114(robot)` zurück.
- Für FCodes leitet der Driver das Ergebnis von `appRobotFileservice` weiter (Stepping-Befehle zusätzlich als Pose-Broadcast nach lokaler Ausführung).
- Telnet-Ausgabe an GRBL/FluidNC-Geräte
- `TelnetSenderGRBL.execCommand()` erzeugt `G1`/`G90`-Befehle mit Achsenzuordnung und Feedrate.
- Info-Server API
- `/api/status` — zeigt Client-Status, Sender-Status und letzte Befehle/Pings an
- `/api/position` — gibt die aktuelle Roboterposition und Motorwinkel als JSON zurück
## Konfiguration
### Starten
- `npm start`
- Alternativ: `node startRobot.js`
### Umgebungsvariablen
- `PORT`
- Standard: `2095`
- Port für den WebSocket/HTTPS-Eingabeserver
- `GRBL_BASE_IP`
- Standard: `fluidNcBase.local`
- Zielhost für den ersten Telnet-Sender
- `GRBL_ELLBOW_IP`
- Standard: `fluidNcEllbow.local`
- Zielhost für den Ellbogen-Sender
- `GRBL_HAND_IP`
- Standard: `fluidNcHand.local`
- Zielhost für den Hand-Sender
- `ROBOT_DEFAULT_FEEDRATE`
- Standard: `1000` (mm/min)
- Default-Feedrate für `G1`-Befehle, wenn keine `F`-Angabe vorhanden ist
- `ROBOT_SPEED_MODE`
- Werte: `legacy` (Standard) oder `correct`
- `legacy`: alte Speed-Regelung — jeder Sender erhält die kartesische Feedrate `F`
unverändert (Verhalten exakt wie bisher).
- `correct`: koordinierte Feedrate pro Sender, sodass alle Controller den Bewegungsschritt
gleichzeitig beenden. Aktiviert automatisch `calculateSpeeds()`.
- Details: `doc/ToDo_6a_Speed.md`
- `ROBOT_USE_SPEED_CALC`
- Werte: `true`, `1` oder sonst leer
- Interner Schalter, ob `robot.calculateSpeeds()` rechnet. Ändert allein **nicht** die
Sender-Ausgabe — dafür ist `ROBOT_SPEED_MODE=correct` nötig. Vom Korrekt-Modus
automatisch aktiviert.
- `ROBOT_KINEMATICS`
- Standard: `arm3segmentlinearx`
- Bezeichner der Kinematik-Klasse (case-insensitive). Bekannte Werte: `arm3segmentlinearx`,
`grabit` / `robot02` (Joy-IT Grab-It). Unbekannter Bezeichner → Fehler beim Start.
- `ROBOT_KINEMATICS_PARAMS`
- JSON-Objekt mit Konstruktor-Parametern, z. B. `{"l1":250,"l2":264,"l3":100}`.
Überschreibt die Werte aus `robot.json`. Wird von der Factory als viertes Argument
an den Konstruktor weitergereicht (erlaubt kinematik-spezifische Parameter wie `baseHeight`).
- `ROBOT_API_KEY`
- Statischer Bearer-Token für `PUT /api/robot`. Fehlt die Variable, generiert
`RobotConfigService` beim ersten Start einen zufälligen Key und speichert ihn in
`data/robot/.apikey` (nicht im Repo). Der Key wird beim Start einmalig geloggt.
- `FILESERVICE_URL`
- Standard: `http://appRobot_Fileservice:2100`
- URL der `appRobotFileservice` — wird von `robot/FCodeClient.js` verwendet.
Im Container-Netz entspricht das dem Docker-Dienstnamen aus dem Portainer-Stack.
- `SHELLY_URL`
- URL für den Shelly Smart Plug Emergency-Stop: `http:///rpc/Switch.Set?id=0&on=false`
- Überschreibt `controllers.emergencyStop.url` aus `robot.json` (analog zu `GRBL_BASE_IP`).
- **Wichtig in Docker:** `.local`-mDNS-Hostnamen werden im Container nicht aufgelöst —
stattdessen die echte IP verwenden (z.B. `http://192.168.0.99/rpc/Switch.Set?id=0&on=false`).
- Details: `doc/15_EmergencyStop_done.md`
### HTTPS-Konfiguration
- `https/localhost.key`
- `https/localhost.pem`
- Passphrase: Env-Variable `HTTPS_PASSPHRASE`, Default `abcd`
### robot.json
`data/robot/robot.json` ist die zentrale Konfigurationsdatei für einen konkreten Roboter.
Sie wird von `robot/RobotConfig.js` beim Start synchron gelesen. Fehlt die Datei, startet
der Driver mit Fallback-Defaults und loggt eine Warnung.
Relevante Abschnitte für den Driver:
```json
{
"kinematics": { "type": "arm3segmentlinearx" },
"motion": { "defaultFeedrate": 1000, "speedMode": "legacy" },
"controllers": {
"base": { "ip": "fluidNcBase.local", "port": 2300, "protocol": "telnet", "axes": ["x","y","z"], "heartbeatInterval": 10000 },
"elbow": { "ip": "fluidNcEllbow.local", "port": 5000, "protocol": "telnet", "axes": ["a",null,null], "heartbeatInterval": 10000 },
"hand": { "ip": "fluidNcHand.local", "port": 5000, "protocol": "telnet", "axes": ["c","e","b"], "heartbeatInterval": 10000 },
"emergencyStop": {
"protocol": "shelly",
"url": "http:///rpc/Switch.Set?id=0&on=false",
"urlOn": "http:///rpc/Switch.Set?id=0&on=true",
"urlStatus": "http:///rpc/Switch.GetStatus?id=0"
}
}
}
```
Armlängen werden aus dem `links`-Abschnitt abgeleitet (`Arm1.skeleton.to[1]` → l1 usw.).
Env-Variablen haben Vorrang vor robot.json (nützlich für Tests und schnelle Korrekturen).
Snapshots werden automatisch vor jedem PUT angelegt (`data/robot/robot_YYYYMMDD_HHmmss.json`).
Snapshots sind nicht im Repo (`.gitignore`), `robot.json` selbst schon.
### Telnet-Sender-Konfiguration
`startRobot.js` erzeugt die `TelnetSenderGRBL`-Instanzen dynamisch aus `cfg.controllers`
(geladen von `robot/RobotConfig.js`). Die Defaults entsprechen drei Controllern:
| Key | Default-IP | Port | Achsen | `heartbeatInterval` |
|-----|-----------|------|--------|---------------------|
| `base` | `fluidNcBase.local` | 2300 | `x, y, z` | 10 000 ms |
| `elbow` | `fluidNcEllbow.local` | 5000 | `a` | 10 000 ms |
| `hand` | `fluidNcHand.local` | 5000 | `c, e, b` | 10 000 ms |
IPs können per Env-Variable überschrieben werden (`GRBL_BASE_IP`, `GRBL_ELLBOW_IP`,
`GRBL_HAND_IP`). Alles andere (Port, Achsen, Controller-Anzahl, Heartbeat) wird in
`robot.json` konfiguriert.
**`heartbeatInterval`** (ms) steuert, wie oft `?` an den FluidNC-Controller gesendet wird.
Der Sender erkennt eine tote Verbindung (z.B. nach NotAus), wenn zwei aufeinanderfolgende
Heartbeats ohne Antwort bleiben (`deadTimeout = 2 × heartbeatInterval`). Danach wird der
Socket geschlossen und der bestehende Reconnect-Mechanismus startet automatisch.
## Serverschnittstellen
### WebSocket Input Server
- Läuft auf `https://localhost:`
- Erwartet WebSocket-Verbindungen und verarbeitet Nachrichten als G-Code oder Steuerbefehle
### Info Server
- Läuft auf `https://localhost:2098`
- Statische Dateien:
- `/`
- `/app.js`
- `/style.css`
- `/allApps.css`
- API-Endpunkte:
- `/api/status` — Sender-Status inkl. `isGCodeReceiver`-Flag
- `/api/position`
- `/api/robot` — `GET`: aktuelle `robot.json`; `PUT`: überschreibt sie (Auth erforderlich)
- `/api/robot/history` — Liste aller Snapshots
- `/api/robot/history/:ts` — einen bestimmten Snapshot abrufen
- `/api/power-status` — Shelly-Schaltzustand (`armed: true/false`, Spannung, Leistung)
- `/api/emergency-stop` — `POST`: Feed Hold `!` an alle FluidNC + Shelly Strom AUS
- `/api/power-on` — `POST`: Shelly Strom EIN
- `/api/alarm-unlock` — `POST`: `$X` an alle FluidNC (nach Strom-Neustart)
## Wichtige Dateien
- `startRobot.js`
- `server/InputWS.js`
- `server/InfoServer.js`
- `robot/RobotBase.js` — abstrakte Basisklasse / Interface-Vertrag (generische Infrastruktur)
- `robot/kinematics/Arm3SegmentLinearX.js` — konkrete Kinematik (Modell + Inverse/Vorwärts), Default-Arm
- `robot/kinematics/Arm3SegmentRotaryBase.js` — Kinematik für den Joy-IT „Grab-It" (Robot02), 5 Achsen + Greifer mit Drehbasis (`ROBOT_KINEMATICS=grabit`)
- `robot/KinematicsFactory.js` — wählt die Kinematik per Umgebungsvariable
- `robot/RobotConfig.js` — liest `data/robot/robot.json`, gibt typisierten Konfigurations-Record zurück
- `server/RobotConfigService.js` — REST-Endpunkte `/api/robot*` (lesen/schreiben, Snapshots, Auth)
- `data/robot/robot.json` — zentrale Roboter-Konfiguration (Single Source of Truth)
- `robot/GCodeParser.js` — wandelt rohe Nachrichten in strukturierte Befehlsobjekte
- `robot/RobotController.js` — wendet geparste Befehle auf das Modell an (Steuerlogik)
- `robot/GCode.js` — Fassade für G-Code-Verarbeitung (Bewegung, Pose, Logging)
- `robot/FCodeClient.js` — Gateway: übersetzt FCodes in REST-Aufrufe an `appRobotFileservice`
- `robot/TelnetSenderGRBL.js`
- `robot/ShellyEmergencyStop.js` — steuert Shelly Smart Plug als Emergency-Stop-Aktor (HTTP GET, kein GCode)
- `robot/fluidnc/FluidNCClient.js` — alternative WebSocket-basierte FluidNC-Anbindung mit Reconnect-Logik (noch nicht integriert)
- `GCodeFiles/` — G-Code-Programme werden jetzt in `appRobotFileservice` verwaltet
## Laufzeitvoraussetzungen
- HTTPS-Zertifikate: `https/localhost.key` und `https/localhost.pem` (Passphrase via `HTTPS_PASSPHRASE`, Default `abcd`).
- `data/robot/robot.json` — wird beim Start eingelesen; fehlt die Datei, startet der Driver mit Defaults + Warnung.
- `logs/` wird beim Start automatisch angelegt (`fs.mkdirSync('logs', { recursive: true })` in `startRobot.js`).
- Telnet-Sender werden sofort beim Start als `cmdReceivers` registriert; interne Reconnect-Logik überbrückt verzögerte Controller-Verbindungen automatisch.
## ToDo / Open Tasks
Architektur- und Refactoring-Aufgaben sind in `doc/ToDo_*.md` dokumentiert:
| Datei | Thema | Status |
|---|---|---|
| `doc/ToDo_1_Parsing.md` | G-Code-Parser-Schicht einführen | ✅ erledigt |
| `doc/ToDo_2_Anbindung.md` | Sender-Interface und Orchestrierung | ✅ erledigt |
| `doc/ToDo_3_Config.md` | Zentralisierte Konfiguration | offen |
| `doc/ToDo_4_GCode.md` | G-Code- und Datei-Handling trennen | ✅ ausgelagert → `appRobotFileservice` |
| `doc/ToDo_5_API.md` | WebSocket-Antwortlogik strukturieren | ✅ erledigt |
| `doc/ToDo_6_RobotController.md` | RobotController-Klasse einführen | ✅ erledigt |
| `doc/ToDo_6a_Speed.md` | Speed-Steuerung: Schalter, `calculateSpeeds()`-Fix, koordinierte Feedrate | ✅ erledigt (WS-Sender offen) |
| `doc/ToDo_6b_FileHandling.md` | File-Handling: fehlende Befehle, Cursor im Speicher, Fehler-Feedback | ✅ ausgelagert → `appRobotFileservice` |
| `doc/fileserviceAPI.md` | REST-API der `appRobotFileservice` (Programme, aktiver Cursor, Teaching/Playback) | ✅ implementiert |
| `doc/ToDo_7_Tests.md` | Testabdeckung und Stabilität | teilweise |
| `doc/ToDo_8_Bugs.md` | Bekannte konkrete Bugs | teilweise |
| `doc/ToDo_9_HardwareFeedback.md` | Hardware-Feedback-Loop (GRBL-Antworten, Command-Queue, Positionsabgleich) | teilweise (Baustein Port→Motor ✅, Pakete 1–6 offen) |
| `doc/ToDo_10_VerbindungsVerlust.md` | Verbindungsverlust erkennen, Watchdog, UI-Statusanzeige | offen |
| `doc/ToDo_12_InverseKinematikConfig_ROADMAP.md` | Austauschbare Kinematik: RobotBase, KinematicsFactory, Grab-It | ✅ erledigt |
| `doc/ToDo_14_robot_json_service.md` | robot.json als REST-Service, RobotConfigService, RobotConfig | teilweise (Schritte 1–4 in appRobotDriver ✅, Schritte 5–7 offen) |
| `doc/15_EmergencyStop_done.md` | Emergency Stop: Shelly + FluidNC Feed Hold, API, UI, Restart-Ablauf | ✅ erledigt |
| `doc/ToDo_49_Cleanup.md` | Pre-Release-Cleanup: tote Code, Zertifikate, ToDos, README | offen |
### Empfohlene Bearbeitungsreihenfolge
```
ToDo_8 Bugs beheben — kurz, blockiert nichts anderes
ToDo_3 Config — Fundament für alles Weitere
ToDo_1 Parser ┐
ToDo_6 RobotController ┘ zusammen, da eng verzahnt
ToDo_4 Datei-Handling — ausgelagert → appRobotFileservice (siehe drafts)
ToDo_6a Speed-Steuerung — calculateSpeeds bugfix, dann Sender-Integration
ToDo_6b File-Handling Detail — ausgelagert → appRobotFileservice (siehe drafts)
ToDo_2 Sender-Interface — mit Entscheidung: Telnet vs. FluidNC-WebSocket
ToDo_9 Hardware-Feedback — baut auf ToDo_2 auf
ToDo_10 Verbindungsverlust — baut auf ToDo_2 auf, parallel zu ToDo_9 möglich
ToDo_5 API — parallel zu ToDo_2/4 möglich
ToDo_7 Tests — begleitend zu allen obigen
```
Kurzübersicht weiterer offener Punkte:
- [ ] Dokumentation der vollständigen G-Code-Syntax erweitern
- [x] `FFirst`/`FLast` und gesamtes File-Handling → ausgelagert in `appRobotFileservice` (siehe `doc/fileserviceAPI.md`)
- [ ] `ROBOT_USE_SPEED_CALC` und `motorSpeeds` im echten Betrieb prüfen
- [ ] `FluidNCClient.js` evaluieren: als Ersatz oder Ergänzung zu `TelnetSenderGRBL`?
- [x] HTTPS-Passphrase aus Env-Variable (`HTTPS_PASSPHRASE`) — erledigt
- [x] `logs/`-Verzeichnis beim Start automatisch anlegen — erledigt (`startRobot.js`)