Dokumentation
This commit is contained in:
22
README.md
22
README.md
@@ -103,6 +103,12 @@ Die Eingaben kommen per WebSocket an den HTTPS-Server und werden in `server/Inpu
|
|||||||
- Statischer Bearer-Token für `PUT /api/robot`. Fehlt die Variable, generiert
|
- Statischer Bearer-Token für `PUT /api/robot`. Fehlt die Variable, generiert
|
||||||
`RobotConfigService` beim ersten Start einen zufälligen Key und speichert ihn in
|
`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.
|
`data/robot/.apikey` (nicht im Repo). Der Key wird beim Start einmalig geloggt.
|
||||||
|
- `SHELLY_URL`
|
||||||
|
- URL für den Shelly Smart Plug Emergency-Stop: `http://<IP>/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-Konfiguration
|
||||||
|
|
||||||
@@ -125,7 +131,13 @@ Relevante Abschnitte für den Driver:
|
|||||||
"controllers": {
|
"controllers": {
|
||||||
"base": { "ip": "fluidNcBase.local", "port": 2300, "protocol": "telnet", "axes": ["x","y","z"], "heartbeatInterval": 10000 },
|
"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 },
|
"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 }
|
"hand": { "ip": "fluidNcHand.local", "port": 5000, "protocol": "telnet", "axes": ["c","e","b"], "heartbeatInterval": 10000 },
|
||||||
|
"emergencyStop": {
|
||||||
|
"protocol": "shelly",
|
||||||
|
"url": "http://<SHELLY-IP>/rpc/Switch.Set?id=0&on=false",
|
||||||
|
"urlOn": "http://<SHELLY-IP>/rpc/Switch.Set?id=0&on=true",
|
||||||
|
"urlStatus": "http://<SHELLY-IP>/rpc/Switch.GetStatus?id=0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -172,11 +184,15 @@ Socket geschlossen und der bestehende Reconnect-Mechanismus startet automatisch.
|
|||||||
- `/style.css`
|
- `/style.css`
|
||||||
- `/allApps.css`
|
- `/allApps.css`
|
||||||
- API-Endpunkte:
|
- API-Endpunkte:
|
||||||
- `/api/status`
|
- `/api/status` — Sender-Status inkl. `isGCodeReceiver`-Flag
|
||||||
- `/api/position`
|
- `/api/position`
|
||||||
- `/api/robot` — `GET`: aktuelle `robot.json`; `PUT`: überschreibt sie (Auth erforderlich)
|
- `/api/robot` — `GET`: aktuelle `robot.json`; `PUT`: überschreibt sie (Auth erforderlich)
|
||||||
- `/api/robot/history` — Liste aller Snapshots
|
- `/api/robot/history` — Liste aller Snapshots
|
||||||
- `/api/robot/history/:ts` — einen bestimmten Snapshot abrufen
|
- `/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
|
## Wichtige Dateien
|
||||||
|
|
||||||
@@ -194,6 +210,7 @@ Socket geschlossen und der bestehende Reconnect-Mechanismus startet automatisch.
|
|||||||
- `robot/RobotController.js` — wendet geparste Befehle auf das Modell an (Steuerlogik)
|
- `robot/RobotController.js` — wendet geparste Befehle auf das Modell an (Steuerlogik)
|
||||||
- `robot/GCode.js` — Fassade + Datei-Befehle
|
- `robot/GCode.js` — Fassade + Datei-Befehle
|
||||||
- `robot/TelnetSenderGRBL.js`
|
- `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)
|
- `robot/fluidnc/FluidNCClient.js` — alternative WebSocket-basierte FluidNC-Anbindung mit Reconnect-Logik (noch nicht integriert)
|
||||||
- `GCodeFiles/` — enthalten Beispiel- und Log-G-Code-Dateien
|
- `GCodeFiles/` — enthalten Beispiel- und Log-G-Code-Dateien
|
||||||
|
|
||||||
@@ -224,6 +241,7 @@ Architektur- und Refactoring-Aufgaben sind in `doc/ToDo_*.md` dokumentiert:
|
|||||||
| `doc/ToDo_10_VerbindungsVerlust.md` | Verbindungsverlust erkennen, Watchdog, UI-Statusanzeige | 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_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/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 |
|
| `doc/ToDo_49_Cleanup.md` | Pre-Release-Cleanup: tote Code, Zertifikate, ToDos, README | offen |
|
||||||
|
|
||||||
### Empfohlene Bearbeitungsreihenfolge
|
### Empfohlene Bearbeitungsreihenfolge
|
||||||
|
|||||||
315
doc/15_EmergencyStop_done.md
Normal file
315
doc/15_EmergencyStop_done.md
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
# 15 — Emergency Stop (NotAus) — Implementierung
|
||||||
|
|
||||||
|
Datum: 2026-06-12
|
||||||
|
Status: ✅ implementiert und getestet
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Überblick
|
||||||
|
|
||||||
|
Der Emergency Stop schaltet den Roboter auf zwei Wegen sofort ab:
|
||||||
|
|
||||||
|
1. **Feed Hold** — alle FluidNC-Controller erhalten das Realtime-Byte `!` (sofortiger Bewegungs-Stopp, keine Verzögerung)
|
||||||
|
2. **Stromabschaltung** — ein Shelly Smart Plug schneidet die Versorgungsspannung über einen HTTP-GET-Aufruf ab
|
||||||
|
|
||||||
|
Beide Aktionen laufen **parallel** (Promise.allSettled). Fällt der Shelly-Aufruf aus, stoppt der Feed-Hold trotzdem die Bewegung — und umgekehrt.
|
||||||
|
|
||||||
|
Nach dem Neustart der Anlage wird mit **Alarm-Unlock** (`$X`) jeder FluidNC-Controller aus dem ALARM-Zustand befreit, bevor neue Bewegungsbefehle akzeptiert werden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architektur
|
||||||
|
|
||||||
|
```
|
||||||
|
robot.json (controllers.emergencyStop)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
RobotConfig.load() ← liest protocol, url, urlOn, urlStatus
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
startRobot.js ← verzweigt auf ShellyEmergencyStop (isGCodeReceiver: false)
|
||||||
|
│ oder TelnetSenderGRBL (isGCodeReceiver: true)
|
||||||
|
▼
|
||||||
|
InfoServer (senders[]) ← hält alle Sender-Instanzen
|
||||||
|
│
|
||||||
|
├── POST /api/emergency-stop → instance.emergencyStop() auf ALLEN Sendern
|
||||||
|
├── POST /api/power-on → instance.powerOn() nur auf Shelly-Sendern
|
||||||
|
├── POST /api/alarm-unlock → instance.alarmUnlock() auf ALLEN Sendern
|
||||||
|
└── GET /api/power-status → instance.getArmed() des ersten Shelly-Senders
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Beteiligte Dateien
|
||||||
|
|
||||||
|
| Datei | Rolle |
|
||||||
|
|---|---|
|
||||||
|
| `robot/SenderInterface.js` | Abstrakte Basisklasse; Default-no-ops für `emergencyStop()` und `alarmUnlock()` |
|
||||||
|
| `robot/TelnetSenderGRBL.js` | Überschreibt `emergencyStop()` → sendet `!`; `alarmUnlock()` → sendet `$X\r\n` |
|
||||||
|
| `robot/ShellyEmergencyStop.js` | Neue Klasse: steuert Shelly Smart Plug per HTTP GET |
|
||||||
|
| `robot/RobotConfig.js` | Liest `emergencyStop`-Eintrag aus `robot.json` (protocol, url, urlOn, urlStatus) |
|
||||||
|
| `startRobot.js` | Erzeugt `ShellyEmergencyStop`-Instanz, setzt `isGCodeReceiver: false` |
|
||||||
|
| `server/InfoServer.js` | Stellt die vier API-Endpunkte bereit, gibt `isGCodeReceiver` im Status aus |
|
||||||
|
| `public/app.js` | SVG-Button, Polling `/api/power-status`, Click-Handler mit console.warn |
|
||||||
|
| `public/index.html` | Emergency-Stop-Panel (SVG-Button, Alarm-Unlock-Button, Status-Divs) |
|
||||||
|
| `public/style.css` | Stile für Panel, SVG-Button, Alarm-Unlock-Button, Status-Texte |
|
||||||
|
| `data/robot/robot.json` | Konfiguration: `controllers.emergencyStop` mit allen drei URLs |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## robot.json — Konfiguration
|
||||||
|
|
||||||
|
```json
|
||||||
|
"controllers": {
|
||||||
|
"base": { "ip": "...", "port": 2300, "protocol": "telnet", ... },
|
||||||
|
"elbow": { ... },
|
||||||
|
"hand": { ... },
|
||||||
|
"emergencyStop": {
|
||||||
|
"protocol": "shelly",
|
||||||
|
"url": "http://<SHELLY-IP>/rpc/Switch.Set?id=0&on=false",
|
||||||
|
"urlOn": "http://<SHELLY-IP>/rpc/Switch.Set?id=0&on=true",
|
||||||
|
"urlStatus": "http://<SHELLY-IP>/rpc/Switch.GetStatus?id=0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig — Docker/mDNS:** `.local`-Hostnamen (mDNS) werden innerhalb von Docker-Containern **nicht** aufgelöst. Die FluidNC-Controller werden über `GRBL_BASE_IP` etc. als echte IPs konfiguriert; für den Shelly muss entweder die IP direkt in `robot.json` eingetragen werden, oder die Env-Variable `SHELLY_URL` wird gesetzt.
|
||||||
|
|
||||||
|
### Env-Variable `SHELLY_URL`
|
||||||
|
|
||||||
|
```
|
||||||
|
SHELLY_URL=http://192.168.0.99/rpc/Switch.Set?id=0&on=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Überschreibt `url` aus `robot.json` (analog zu `GRBL_BASE_IP`). `urlOn` und `urlStatus` werden dann automatisch aus `SHELLY_URL` abgeleitet. Werden `urlOn`/`urlStatus` explizit in `robot.json` gesetzt, haben diese Vorrang.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ShellyEmergencyStop.js
|
||||||
|
|
||||||
|
### Konstruktor
|
||||||
|
|
||||||
|
```js
|
||||||
|
new ShellyEmergencyStop(url, options = {})
|
||||||
|
// url = Switch.Set?id=0&on=false (Off-URL)
|
||||||
|
// options.urlOn = Switch.Set?id=0&on=true (optional, sonst aus url abgeleitet)
|
||||||
|
// options.urlStatus = Switch.GetStatus?id=0 (optional, sonst aus url abgeleitet)
|
||||||
|
```
|
||||||
|
|
||||||
|
URL-Ableitung (Fallback, wenn nicht explizit konfiguriert):
|
||||||
|
- `_onUrl = url.replace('on=false', 'on=true')`
|
||||||
|
- `_statusUrl = url.replace(/\/rpc\/.*$/, '/rpc/Switch.GetStatus?id=0')`
|
||||||
|
|
||||||
|
### Methoden
|
||||||
|
|
||||||
|
| Methode | Aktion | Shelly-RPC |
|
||||||
|
|---|---|---|
|
||||||
|
| `emergencyStop()` | Strom abschalten | `Switch.Set?id=0&on=false` |
|
||||||
|
| `powerOn()` | Strom einschalten | `Switch.Set?id=0&on=true` |
|
||||||
|
| `alarmUnlock()` | no-op (kein FluidNC-Alarm) | — |
|
||||||
|
| `getArmed()` | Schaltzustand abfragen | `Switch.GetStatus?id=0` |
|
||||||
|
| `getStatus()` | Sender-Status für InfoServer | — |
|
||||||
|
|
||||||
|
### `getArmed()` — Rückgabe
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "ok": true, "armed": true, "voltage": 234.9, "power": 15.5 }
|
||||||
|
```
|
||||||
|
|
||||||
|
`armed: true` = `output: true` im Shelly-Response = Strom eingeschaltet = Roboter bestromt.
|
||||||
|
|
||||||
|
### State-Machine
|
||||||
|
|
||||||
|
| State | Bedeutung |
|
||||||
|
|---|---|
|
||||||
|
| `ready` | Shelly bereit, noch kein Aufruf |
|
||||||
|
| `stopped` | `emergencyStop()` erfolgreich ausgeführt |
|
||||||
|
| `error` | HTTP-Fehler oder Netzwerkfehler |
|
||||||
|
| `disconnected` | `disconnect()` aufgerufen |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TelnetSenderGRBL — Emergency Stop
|
||||||
|
|
||||||
|
### `emergencyStop()`
|
||||||
|
|
||||||
|
Sendet das **Realtime-Byte `!`** (FluidNC Feed Hold):
|
||||||
|
- Kein Zeilenende (`\r\n`) — sofortige Wirkung, unabhängig vom aktuellen Befehlspuffer
|
||||||
|
- Loggt: `⚠️ [EmergencyStop] Feed Hold '!' → <hostname>`
|
||||||
|
- Gibt `{ ok: false, error: 'not connected' }` wenn kein Socket
|
||||||
|
|
||||||
|
### `alarmUnlock()`
|
||||||
|
|
||||||
|
Sendet `$X\r\n` (FluidNC Alarm Unlock):
|
||||||
|
- Entsperrt den ALARM-Zustand nach Power-Loss (z.B. `ALARM:1`)
|
||||||
|
- Nur aufrufen nachdem Roboterstellung manuell geprüft wurde
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SenderInterface — Default-no-ops
|
||||||
|
|
||||||
|
`SenderInterface.js` definiert Basisimplementierungen, damit alle Sender das Interface erfüllen ohne jede Methode selbst zu implementieren:
|
||||||
|
|
||||||
|
```js
|
||||||
|
async emergencyStop() { return { ok: true, skipped: true }; }
|
||||||
|
async alarmUnlock() { return { ok: true, skipped: true }; }
|
||||||
|
```
|
||||||
|
|
||||||
|
`skipped: true` wird von `InfoServer` als Erfolg gewertet (zählt nicht als Fehler).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## InfoServer — API-Endpunkte
|
||||||
|
|
||||||
|
### `GET /api/power-status`
|
||||||
|
|
||||||
|
Fragt den Shelly (ersten Sender mit `getArmed()`-Methode) nach dem Schaltzustand:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "ok": true, "armed": true, "voltage": 234.9, "power": 15.5 }
|
||||||
|
```
|
||||||
|
|
||||||
|
Kein Shelly konfiguriert:
|
||||||
|
```json
|
||||||
|
{ "ok": false, "armed": false, "error": "no shelly configured" }
|
||||||
|
```
|
||||||
|
|
||||||
|
### `POST /api/emergency-stop`
|
||||||
|
|
||||||
|
Ruft `emergencyStop()` auf **allen** Sendern parallel auf:
|
||||||
|
- FluidNC: sendet `!`
|
||||||
|
- Shelly: schneidet Strom ab
|
||||||
|
- Loggt: `⚠️ [EmergencyStop] 2026-06-12T... — [Base:ok, Elbow:ok, Hand:ok, EmergencyStop:ok]`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "ok": true, "at": "2026-06-12T16:34:08.863Z", "results": [...] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### `POST /api/power-on`
|
||||||
|
|
||||||
|
Ruft `powerOn()` nur auf Sendern auf, die diese Methode haben (Shelly):
|
||||||
|
- Loggt: `⚠️ [PowerOn] 2026-06-12T...`
|
||||||
|
|
||||||
|
### `POST /api/alarm-unlock`
|
||||||
|
|
||||||
|
Ruft `alarmUnlock()` auf allen Sendern auf:
|
||||||
|
- FluidNC: sendet `$X\r\n`
|
||||||
|
- Shelly: skipped
|
||||||
|
- Loggt: `⚠️ [AlarmUnlock] 2026-06-12T...`
|
||||||
|
|
||||||
|
### `GET /api/status` — `isGCodeReceiver`
|
||||||
|
|
||||||
|
Das Status-Objekt jedes Senders enthält neu das Feld `isGCodeReceiver`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "EmergencyStop",
|
||||||
|
"isGCodeReceiver": false,
|
||||||
|
"state": "stopped",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`false` = Shelly (kein G-Code-Empfänger, keine URL in der UI anzeigen).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## startRobot.js — Sender-Erzeugung
|
||||||
|
|
||||||
|
```js
|
||||||
|
for (const [key, ctrl] of Object.entries(cfg.controllers)) {
|
||||||
|
if (ctrl.protocol === 'shelly') {
|
||||||
|
const instance = new ShellyClass(ctrl.url, {
|
||||||
|
urlOn: ctrl.urlOn,
|
||||||
|
urlStatus: ctrl.urlStatus,
|
||||||
|
});
|
||||||
|
senders.push({ name, instance, isGCodeReceiver: false });
|
||||||
|
} else {
|
||||||
|
// Telnet (FluidNC)
|
||||||
|
const instance = new TelnetSenderClass(ctrl.ip, ctrl.port, ...axes7, { heartbeatInterval });
|
||||||
|
senders.push({ name, instance, isGCodeReceiver: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nur Telnet-Sender empfangen G-Code
|
||||||
|
senders.filter(s => s.isGCodeReceiver).forEach(s => robot.cmdReceivers.push(s.instance));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frontend — app.js
|
||||||
|
|
||||||
|
### SVG-Button (Zwei-Zustands-Design)
|
||||||
|
|
||||||
|
Der SVG-Button wechselt komplett das Erscheinungsbild je nach `armed`-Zustand:
|
||||||
|
|
||||||
|
| Zustand | Outer Ring | Inner Button | Gradient | Text |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `armed: true` | Gold `#FFD700` | Rot, r=21 | `#ff5555 → #cc0000 → #880000` | EMERGENCY STOP |
|
||||||
|
| `armed: false` | Navy `#1a3050` | Blau, r=19 | `#5c9fcf → #255f96 → #0c3660` | START ROBOT |
|
||||||
|
|
||||||
|
### Polling
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Alle 2 Sekunden + sofort beim Start
|
||||||
|
pollPowerStatus();
|
||||||
|
setInterval(pollPowerStatus, 2000);
|
||||||
|
```
|
||||||
|
|
||||||
|
`GET /api/power-status` → `updateEmergencyStopButton(data.armed)`.
|
||||||
|
Startzustand: `updateEmergencyStopButton(false)` — damit der onclick sofort aktiv ist, bevor der erste Poll antwortet.
|
||||||
|
|
||||||
|
### Click-Handler (`handleEstopClick`)
|
||||||
|
|
||||||
|
- Liest `_lastArmed` dynamisch zum Klick-Zeitpunkt
|
||||||
|
- Zeigt Ladezustand (`⏳ …`), Erfolg (`✅ EmergencyStop OK`) oder Fehler (`⚠️ Teilfehler: …`) im `#estop-action-status`-Div
|
||||||
|
- Schreibt `console.warn('⚠️ [EmergencyStop] …')` für Browser-DevTools-Log
|
||||||
|
|
||||||
|
### FluidNC-Controller-Panel
|
||||||
|
|
||||||
|
Shelly-Sender werden ohne URL angezeigt (`isGCodeReceiver === false`), mit CSS-Kreisen statt Emoji:
|
||||||
|
|
||||||
|
| State | Farbe |
|
||||||
|
|---|---|
|
||||||
|
| `ready` | Hellblau `#89bcde` |
|
||||||
|
| `stopped` | Fast-Weiß `#dce9f5` |
|
||||||
|
| `error` | Gedämpft-Rot `#9a3a3a` |
|
||||||
|
| `disconnected` | Grau `#4a5a6a` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Restart-Ablauf nach NotAus
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Roboter mechanisch prüfen — steht er frei?
|
||||||
|
2. [START ROBOT] klicken → POST /api/power-on
|
||||||
|
└── Shelly schaltet Strom ein
|
||||||
|
3. FluidNC bootet (ca. 5–10 s)
|
||||||
|
4. [Restart — Alarm-Unlock] klicken → POST /api/alarm-unlock
|
||||||
|
└── alle FluidNC-Controller erhalten $X\r\n
|
||||||
|
5. Roboter ist wieder betriebsbereit
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
| Ort | Level | Beispiel |
|
||||||
|
|---|---|---|
|
||||||
|
| Node / Container | `console.warn` | `⚠️ [EmergencyStop] 2026-06-12T16:34:08Z — [Base:ok, Elbow:ok, Hand:ok, EmergencyStop:ok]` |
|
||||||
|
| Node / Container | `console.warn` | `⚠️ [EmergencyStop] Feed Hold '!' → fluidNcBase.local` |
|
||||||
|
| Node / Container | `console.warn` | `⚠️ [Shelly] power OFF → OK` |
|
||||||
|
| Browser DevTools | `console.warn` | `⚠️ [EmergencyStop] wird ausgeführt …` |
|
||||||
|
| Browser DevTools | `console.warn` | `⚠️ [EmergencyStop] OK` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
| Datei | Abgedeckte Szenarien |
|
||||||
|
|---|---|
|
||||||
|
| `test/ShellyEmergencyStop.test.js` | Konstruktor, URL-Ableitung, explizite urlOn/urlStatus, emergencyStop/powerOn/getArmed (ok, HTTP-Fehler, Netzwerkfehler, null-URL) |
|
||||||
|
| `test/Sender.Telnet.emergencyStop.test.js` | TelnetSenderGRBL.emergencyStop/alarmUnlock (verbunden, getrennt, write-Fehler); SenderInterface-Defaults |
|
||||||
|
| `test/RobotConfig.test.js` | DEFAULTS.emergencyStop, url/urlOn/urlStatus aus robot.json, SHELLY_URL-Env-Override, keine ip/port/axes |
|
||||||
|
| `test/StartRobot.test.js` | 4 Sender inkl. Shelly; isGCodeReceiver:false; Shelly nicht in cmdReceivers |
|
||||||
|
| `test/InfoServer.test.js` | POST /api/emergency-stop, /api/alarm-unlock, /api/power-on; GET /api/power-status (mit/ohne Shelly); isGCodeReceiver im Status |
|
||||||
@@ -10429,3 +10429,8 @@
|
|||||||
2026-06-12T21:15:19.320Z ::ffff:127.0.0.1: M114
|
2026-06-12T21:15:19.320Z ::ffff:127.0.0.1: M114
|
||||||
2026-06-12T21:15:19.533Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
|
2026-06-12T21:15:19.533Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
|
||||||
2026-06-12T21:15:19.762Z ::ffff:127.0.0.1: G1 X1
|
2026-06-12T21:15:19.762Z ::ffff:127.0.0.1: G1 X1
|
||||||
|
2026-06-12T21:21:46.340Z ::ffff:127.0.0.1: M114
|
||||||
|
2026-06-12T21:21:46.356Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
|
||||||
|
2026-06-12T21:21:46.525Z ::ffff:127.0.0.1: M114
|
||||||
|
2026-06-12T21:21:46.740Z ::ffff:127.0.0.1: G1 X1 Y2 Z3
|
||||||
|
2026-06-12T21:21:46.964Z ::ffff:127.0.0.1: G1 X1
|
||||||
|
|||||||
@@ -14646,3 +14646,5 @@
|
|||||||
2026-06-12T21:13:04.257Z ::ffff:127.0.0.1 : Ping
|
2026-06-12T21:13:04.257Z ::ffff:127.0.0.1 : Ping
|
||||||
2026-06-12T21:15:19.049Z ::ffff:127.0.0.1 : Ping
|
2026-06-12T21:15:19.049Z ::ffff:127.0.0.1 : Ping
|
||||||
2026-06-12T21:15:19.098Z ::ffff:127.0.0.1 : Ping
|
2026-06-12T21:15:19.098Z ::ffff:127.0.0.1 : Ping
|
||||||
|
2026-06-12T21:21:46.291Z ::ffff:127.0.0.1 : Ping
|
||||||
|
2026-06-12T21:21:46.316Z ::ffff:127.0.0.1 : Ping
|
||||||
|
|||||||
Reference in New Issue
Block a user