From 9a0ecfefabe4f0c4403351c9285cf1ec053f4117 Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Wed, 10 Jun 2026 23:18:34 +0200 Subject: [PATCH] Umbau 12: Robot-Kinematics als extends RobotBase --- doc/ToDo_9_HardwareFeedback.md | 201 +++++++++++++++----- logs/gcode_commands.log | 5 + logs/pings.log | 2 + robot/Robot.js | 9 - test/GCode.receiveGCode.G92.test.js | 2 +- test/GCode.receiveList.test.js | 2 +- test/GCode.speed.test.js | 4 +- test/GCode.test.js | 2 +- test/Robot.01_WristPoint.test.js | 2 +- test/Robot.02_UpperArm.test.js | 2 +- test/Robot.Kinematics.RoundTrip.test.js | 2 +- test/Robot.Kinematics.RoundTripList.test.js | 2 +- test/Robot.calculateSpeeds.test.js | 2 +- test/Robot.changedFlags.test.js | 2 +- test/Robot.sendCommand.test.js | 2 +- test/Speed.coordination.test.js | 2 +- 16 files changed, 169 insertions(+), 74 deletions(-) delete mode 100755 robot/Robot.js diff --git a/doc/ToDo_9_HardwareFeedback.md b/doc/ToDo_9_HardwareFeedback.md index a6f142c..c5388a4 100644 --- a/doc/ToDo_9_HardwareFeedback.md +++ b/doc/ToDo_9_HardwareFeedback.md @@ -13,6 +13,77 @@ WebSocket → GCode → calculateAngles3D → sendCommand → tSocket.write() → wird nie gelesen (data => {}) ``` +> Der blinde Kanal ist genau eine Stelle: `socket.on('data', () => {})` in +> `robot/TelnetSenderGRBL.js` (Z. ~123). Alles in diesem ToDo hängt daran. + +--- + +## FluidNC-Protokoll: gesicherte Fakten (recherchiert) + +Quellen: [Serial Protocol](http://wiki.fluidnc.com/en/support/serial_protocol), +[Automatic Reporting](http://wiki.fluidnc.com/en/support/interface/automatic_reporting), +[Cross-Channel #750](https://github.com/bdring/FluidNC/issues/750). + +1. **`?` funktioniert über Telnet.** FluidNC verarbeitet Realtime-Kommandos (`?`, + Feed-Hold, Cycle-Start, Reset) über eine **Channel-Abstraktion** auf *allen* Kanälen + (USB, WiFi, Telnet, WebUI). Der bestehende `telnet-stream`-Socket kann `?` senden und + den `<…>`-Report zurücklesen. + +2. **Statuswahl über `$10` (Bitmaske).** Bit0 = `MPos` statt `WPos`, Bit1 = `Bf` (Puffer). + → **`$10=3` liefert `MPos` + `Bf`** (auf der eingesetzten FluidNC-Version verifizieren). + - **`MPos` ist die richtige Quelle** für die Rückrechnung: der Treiber sendet die + `portValue()`-Werte als *absolute* G-Code-Koordinaten, `MPos` ist offset-fest. + - `WCO` (Work Coordinate Offset) erscheint periodisch; `WPos = MPos − WCO`. Für uns + irrelevant, solange wir `MPos` lesen. + - Beispiel: `` + +3. **`Bf:` = das kanonische Flow-Control-Signal.** Erste Zahl = freie Planner-Blöcke, + zweite = freie RX-Bytes. Damit liest man die **echte** Pufferfüllung, statt sie über + `moveTime` zu *schätzen* (das ignoriert Beschleunigung). + +4. **Auto-Reporting statt Polling.** `$Report/Interval=N` (ms) lässt FluidNC den Status + während der Bewegung **selbst** pushen — **pro Kanal** einstellbar, im Stillstand nur + bei Änderung. Ersetzt das `?`-Polling und hält die Netzlast vorhersehbar gedeckelt. + +5. **Cross-Channel-Bleed-Through (Caveat).** `ok`/Reports können auf *anderen* Kanälen + auftauchen als dem auslösenden (Issue #750). Da pro FluidNC mehrere Kanäle aktiv sind + (Treiber-Telnet **und** die `appRobot_Access*`-WebUI-Container), muss der Parser nach + **Nachrichtentyp** demultiplexen (`ok` / `error:` / `<…>`) und fremde Zeilen tolerieren — + **kein** striktes 1:1 Request→Response annehmen. + +--- + +## Designentscheidungen (festgeschrieben) + +**B3 — Umkehr-Kinematik ist disambiguierbar.** Global nicht eindeutig, aber im Arbeitsraum +dieses Roboters per **dokumentierter physikalischer Zusatzbedingung** auflösbar +(z. B. „Ellbogen höher als Hand" bzw. „Ellbogen hinter der x-Achse"). `motorStateFromPorts()` +bekommt eine feste Zweig-Wahl-Regel; die exakte Vorzeichen-Konvention wird beim Herleiten +der Umkehrung gepinnt. + +**B5 — Lockstep als abschaltbare Absicherung.** Durch die koordinierte Feedrate (ToDo_6a +`correct`) treffen ohnehin alle Achsen *gleichzeitig* am nächsten Ziel ein — Lockstep ist +die Absicherung, nicht der Hauptmechanismus. Umsetzung als Env-Schalter; **Freerun zuerst** +(reines Zeit-/`Bf`-Pacing), echtes Lockstep erst nach Paket 3/5 (braucht den Feedback-Kanal). + +**B6 — Sync ist ein G-Code-Befehl.** Läuft durch `GCodeParser` + `RobotController` +(nicht als Sonderfall in `InputWS` wie heute `M114`). **Folge:** Sync ist der erste +*asynchrone* Befehl — er muss auf die `?`-Antworten aller drei Controller *warten*, bevor +er den Roboterzustand setzt. Die Dispatch-Kette (`RobotController.applyCommand`) muss dafür +erstmals einen Promise zurückgeben/awaiten können. + +### Schalter-Übersicht (Namen sind Vorschläge) + +| Env | Werte | Default | Wirkung | +|---|---|---|---| +| `ROBOT_SPEED_MODE` | `legacy` / `correct` | `legacy` | koordinierte Feedrate (ToDo_6a) | +| `ROBOT_USE_QUEUE` | `false` / `true` | `false` | zeitgesteuerte Sende-Queue (Paket 6) | +| `ROBOT_MOTION_SYNC` | `freerun` / `lockstep` | `freerun` | Schritt-für-Schritt-Synchronisation der 3 Controller | + +> **Konsistenz-Regel:** `ROBOT_MOTION_SYNC=lockstep` ergibt nur mit `ROBOT_SPEED_MODE=correct` +> Sinn (sonst kommen die Controller zu unterschiedlichen Zeiten an und Lockstep müsste hart +> warten). Beim Start einmal prüfen und ggf. warnen. + --- ## Paket 1: GRBL-Antworten lesen @@ -26,21 +97,30 @@ WebSocket → GCode → calculateAngles3D → sendCommand → tSocket.write() ## Paket 2: Command-Queue mit ok-Handshake +> **Verhältnis zu Paket 6:** Dies ist die *einfache, synchrone* Variante (ein Befehl pro `ok`). +> Die ausgebaute, `Bf`-basierte und abschaltbare Queue steht in **Paket 6** und löst dasselbe +> Problem flüssiger. Paket 2 kann als Zwischenschritt dienen oder direkt in Paket 6 aufgehen. + - [ ] Sendepuffer einführen: Befehle erst abschicken, wenn das vorherige `ok` eingegangen ist - GRBL hat intern ~128 Byte Puffer — bei schnellen Befehlsfolgen (Datei abspielen) droht sonst Puffer-Überlauf und stille Befehlsverwerfung - Alternative: GRBL Line-Counting-Protokoll (sendet mehrere Befehle, zählt Zeichen im Puffer) + → in Paket 6 über die zweite `Bf`-Zahl (freie RX-Bytes) abgedeckt - [ ] Timeout für ausbleibende `ok`-Antworten definieren - nach X ms ohne Antwort: Fehler loggen, ggf. Verbindung zurücksetzen -## Paket 3: Hardwareposition abfragen (`?`-Status) +## Paket 3: Hardwareposition lesen (Auto-Report statt Polling) -- [ ] Periodisch GRBL-Statusabfrage senden: `?` - - GRBL antwortet mit `` - - Alternative: nach jedem abgeschlossenen Move abfragen -- [ ] Gemeldete Hardware-Position mit Softwareposition (`robot.x/y/z`) vergleichen - - bei Abweichung: warnen oder synchronisieren +- [ ] Beim Verbindungsaufbau je Controller konfigurieren: + - `$10=3` setzen → Report enthält `MPos` **und** `Bf` (Protokoll-Fakt 2) + - `$Report/Interval=N` setzen (z. B. `N=100…200`) → FluidNC **pusht** den Status während + der Bewegung selbst (Protokoll-Fakt 4). Kein `?`-Polling-Loop nötig; `?` bleibt nur als + Einzelabfrage on demand (z. B. für Sync, Paket 4). +- [ ] `data`-Handler (Paket 1) parst die gepushten `<…>`-Reports: `state`, `MPos`, `Bf` + - robust gegen Cross-Channel-Fremdzeilen (Protokoll-Fakt 5) — nach Typ demultiplexen +- [ ] Gemeldete Hardware-Position (`MPos`) mit Softwareposition vergleichen + - bei Abweichung: warnen oder synchronisieren (→ Paket 4) - schützt gegen Drift durch Endschalter-Auslösung, Motor-Stall, Verbindungsunterbrechung -- [ ] Status (`Idle`, `Run`, `Alarm`, `Hold`) für den `InfoServer` bereitstellen +- [ ] Status (`Idle`, `Run`, `Alarm`, `Hold`) + `Bf` für den `InfoServer` bereitstellen - `/api/status` um GRBL-Zustand erweitern --- @@ -60,6 +140,8 @@ Kopplung **explizit auflösen** — sie ergibt sich nicht automatisch. - Eingang: pro Sender die gelesenen Port-Werte (`{x, y, z}` Base, `{a}` Elbow, `{c, e, b}` Hand) - Ausgang: `{xMotor, alpha, beta, a, b, c, eMotor}` - Grad→Rad zurückrechnen, `factorTurnLift`/`handOpenInMM` herausrechnen, gekoppelte Ports auflösen + - **Zweig-Wahl (B3):** wo die Lösung mehrdeutig ist, die dokumentierte physikalische + Zusatzbedingung anwenden (z. B. „Ellbogen höher als Hand"). Konvention im Code festhalten. - [ ] **Round-Trip-Invariante** als Test: `portValue(motorStateFromPorts(p)) ≈ p` - dasselbe Muster wie `test/Robot.Kinematics.RoundTrip.test.js` - schützt die Umkehrfunktion gegen Drift gegenüber `portValue()` @@ -76,12 +158,16 @@ Kopplung **explizit auflösen** — sie ergibt sich nicht automatisch. Nötig nach Homing, manuellem Jog, Endschalter-Auslösung oder Reconnect — die Software weiß sonst nicht, wo der Roboter physisch wirklich steht. -- [ ] Neuer Eingabe-Befehl, z. B. `M114 R` (Read-Hardware) oder WS-Message `syncFromHardware` +- [ ] **G-Code-Befehl** (B6), z. B. `M114 R` (Read-Hardware) — durch `GCodeParser` + + `RobotController` geroutet, **nicht** als Sonderfall in `InputWS` wie heute `M114` - klar abgegrenzt vom bestehenden `M114`, das nur die **Software**-Position zurückgibt (`GCode.getM114(robot)` in `server/InputWS.js`) +- [ ] **Async-Dispatch (B6-Folge):** `RobotController.applyCommand` muss für diesen Befehl + einen Promise zurückgeben und auf die `?`-Antworten warten — der erste asynchrone Befehl + im bisher synchronen Dispatch-Pfad. - [ ] Ablauf des Sync: - 1. an alle drei Sender `?` senden, je `MPos` aus der Antwort parsen (Paket 3) - 2. `motorStateFromPorts(...)` → sieben Motorwerte rekonstruieren (Baustein oben) + 1. an alle drei Sender einmalig `?` senden, je `MPos` aus der Antwort parsen (Paket 3) + 2. `motorStateFromPorts(...)` → sieben Motorwerte rekonstruieren (Baustein oben, inkl. B3-Zweigwahl) 3. diese auf den Roboter schreiben: `robot.xMotor/alpha/beta/a/b/c/eMotor = …` 4. **Vorwärtskinematik** anstoßen: `robot.calculatePositionFromMotorAngles()` → füllt `robot.x/y/z` und `phi/theta/psi` aus den Hardwarewerten @@ -104,7 +190,8 @@ also wie weit sich jeder Controller schon zur Ziel-Position bewegt hat (0…100 - `mStart` = Port-Werte vor dem Move, `mTarget` = gesendete Port-Werte - liegt bereits vor: `robot.motorPositionOld` (Start) und `robot.motorPosition` (Ziel), über `portValue()` in Port-Werte umgerechnet -- [ ] Während der Bewegung periodisch `?` pollen (Paket 3) und je Controller berechnen: +- [ ] Aus den **auto-gepushten** Reports (Paket 3, `$Report/Interval`) je Controller berechnen + — **kein** eigenes Polling: ``` fortschritt_i = |MPos_jetzt − Start_i| / |Ziel_i − Start_i| (auf 0…1 geklemmt) ``` @@ -114,11 +201,12 @@ also wie weit sich jeder Controller schon zur Ziel-Position bewegt hat (0…100 (der langsamste bestimmt, wann der Schritt fertig ist) - im **Korrekt-Modus** (ToDo_6a) sollten alle Controller etwa gleich schnell fertig sein — eine große Spreizung der Einzel-Fortschritte ist dort ein Warnsignal (Feedrate-Fehler) +- [ ] **Lockstep-Gate (B5, nur bei `ROBOT_MOTION_SYNC=lockstep`):** den nächsten Schritt erst + freigeben, wenn der langsamste Controller `Idle`/Ziel erreicht hat. In `freerun` entfällt + das Gate — die Koordination kommt allein aus der Feedrate (ToDo_6a `correct`). - [ ] Fortschritt + Status nach außen geben - über `InfoServer` (`/api/status`) und/oder als WS-Push an die Clients - ermöglicht eine Fortschrittsanzeige beim Abspielen von Dateien (ToDo_6b) -- [ ] Zusammenspiel mit der Command-Queue (Paket 2): erst den nächsten Move senden, wenn - der vorige `Idle` erreicht hat → verhindert, dass Fortschritt mehrerer Moves verschwimmt ### Offener Kernpunkt: Was, wenn währenddessen der nächste Befehl kommt? @@ -146,12 +234,11 @@ ist. So bleibt die Bewegung flüssig (GRBL-Planner läuft nie leer), nichts geht (kein Puffer-Überlauf), und das Netz wird nicht mit Dauer-Polling oder Befehls-Salven belastet. -### Schalter (Pflicht — Absicherung wie bei ToDo_6a) +### Schalter -| Env | Default | Wirkung | -|---|---|---| -| `ROBOT_USE_QUEUE` | `false` | **Aus = exakt heutiges Fire-and-forget.** Jeder Befehl geht sofort an alle Sender, kein Pacing, keine Queue. Byte-identisch zu vorher. | -| `ROBOT_USE_QUEUE` | `true` | Neue zeitgesteuerte Queue-Logik aktiv. | +`ROBOT_USE_QUEUE` (siehe zentrale Schalter-Übersicht oben). `false` = **exakt heutiges +Fire-and-forget**, byte-identisch zu vorher; `true` = Queue-Logik aktiv. Wie `ROBOT_SPEED_MODE` +greift der Umbau **nur** bei `true` — das bestehende Sender-Test-Sicherheitsnetz bleibt gültig. ```yaml # docker-compose.yaml → appRobotDriver @@ -159,45 +246,37 @@ environment: - ROBOT_USE_QUEUE=false # oder: true ``` -> Wie `ROBOT_SPEED_MODE` greift der Umbau **nur** bei `true`. Solange `false`, ist der -> Sende-Pfad unverändert — das bestehende Sicherheitsnetz (Sender-Tests) bleibt gültig. +### Takt: `Bf` ist die Wahrheit, `moveTime` nur der Vorhersage-Hint -### Idee: zwei Uhren — eine geschätzte (gratis), eine gemessene (kostet Netz) +Die Recherche ändert den ursprünglichen „reine Zeitschätzung"-Entwurf: FluidNC liefert die +**echte** Pufferfüllung (`Bf`) ohnehin frei Haus über Auto-Reporting (Protokoll-Fakt 3+4). +Damit muss nichts mehr aufwändig geschätzt werden. -Der Trick, das Netz **nicht** zu überlasten: primär nach einer **geschätzten** Uhr takten, -die `?`-Messung nur sparsam zur Korrektur einsetzen. - -- **Geschätzte Uhr (Haupttakt, kein Netzverkehr):** Jeder Queue-Eintrag trägt seine - **voraussichtliche Ausführzeit** — die liegt mit `moveTime` aus ToDo_6a bereits vor. - Der Treiber führt je Controller einen lokalen Zeitstempel `controllerFreiAb`: +- **`Bf`-basierter Haupttakt (echte Wahrheit, kein Extra-Traffic):** Aus den auto-gepushten + Reports (Paket 3) kennt der Treiber je Controller die freien Planner-Blöcke. Regel: ``` - beim Senden: controllerFreiAb += moveTime_dieses_Befehls - nächster Send, wenn: jetzt >= controllerFreiAb − vorlauf + solange (freie Planner-Blöcke > schwelle) → nächsten Queue-Eintrag senden ``` - `vorlauf` = kleine Sicherheitsmarge, damit immer ~1 Move im GRBL-Planner wartet und die - Bewegung nicht stockt. Das ist ein reiner Timer → **null Zusatz-Traffic**. - -- **Gemessene Uhr (Korrektur, sparsam):** Die Schätzung driftet (Beschleunigung/Abbremsen - stecken nicht in `dist/feedrate`, kurze Moves dauern real länger). Deshalb ab und zu — - **nicht** bei jedem Move — per `?` (Paket 3) den echten Stand holen und `controllerFreiAb` - nachjustieren. Auslöser: Queue läuft fast leer, ein langer Move (einmal mittendrin prüfen), - oder ein fester Maximaltakt. **`?` strikt raten-begrenzen** (z. B. ≤ 5 Hz, GRBL-üblich) → - Netzlast bleibt gedeckelt. + Hält den Planner gefüllt (flüssige Bewegung) und kann **nie** überlaufen — das ist das + Standard-GRBL-Streaming, nur mit gepushtem statt gepolltem Status. +- **`moveTime` als prädiktiver Zusatz (aus ToDo_6a):** zwischen zwei Reports überbrückt die + geschätzte Ausführzeit die Lücke (z. B. um zu entscheiden, ob *jetzt schon* der nächste + Eintrag sinnvoll ist). Korrigiert wird die Schätzung laufend durch den nächsten `Bf`-Push; + sie ist nie die alleinige Quelle. ### Eintrag in der Queue -- [ ] Queue-Eintrag hält: geparster Befehl / Motorziel, `moveTime` (geschätzt), die je +- [ ] Queue-Eintrag hält: geparster Befehl / Motorziel, `moveTime` (Hint), die je Sender resultierenden G-Code-Strings, Status (`pending → sent → done`) -- [ ] `done` wird gesetzt durch geschätzte Uhr **oder** (falls gepollt) durch `?`=`Idle` am Ziel +- [ ] `done` wird aus dem `Bf`/`state`-Report abgeleitet (`Idle` am Ziel), nicht geraten ### Pacing-Schleife -- [ ] je Controller `controllerFreiAb` führen, Sendezeitpunkt aus `moveTime − vorlauf` ableiten -- [ ] Tiefe im GRBL-Planner begrenzen (z. B. ≤ 1–2 vorausgesendete Moves) — flüssig, aber - noch steuerbar -- [ ] Drift-Korrektur per ratenbegrenztem `?`; bei großer Abweichung Schätzung neu setzen -- [ ] **Optionaler Sicherheitsboden:** zusätzlich Character-Counting (Summe ungequittierter - Zeilenlängen < 128 B) als Netz gegen Puffer-Überlauf, falls die Schätzung mal stark danebenliegt +- [ ] je Controller die freien Planner-Blöcke aus dem letzten Report führen; senden, solange + über Schwelle — In-Flight-Tiefe damit implizit begrenzt (flüssig, aber steuerbar) +- [ ] `moveTime` nur zur Überbrückung zwischen Reports nutzen +- [ ] **Sicherheitsboden:** zusätzlich freie RX-Bytes aus `Bf` beachten (zweite Zahl) — nie + mehr senden, als in den RX-Puffer passt (Character-Counting fällt damit faktisch ab) ### Verhalten bei „neuer Befehl kommt mitten in der Bewegung" @@ -224,14 +303,32 @@ Zwei Betriebsarten — je nach Quelle der Befehle: - [ ] **Alarm/Error** mitten in der Queue (Paket 1) → Queue leeren, Senden stoppen, Fehler melden - [ ] **Sync-Command (Paket 4)** bei nicht-leerer Queue → erst Queue leeren; die gepufferten Ziele sind nach einem Re-Homing veraltet -- [ ] **Drei Controller driften auseinander:** Im Korrekt-Modus (ToDo_6a) laufen sie auf - dieselbe `moveTime` → ihre `controllerFreiAb` sollten zusammenbleiben; große Spreizung ist - ein Warnsignal (Feedrate-/Schätzfehler) -- [ ] **Schätzgüte:** `moveTime = dist/feedrate` ignoriert Beschleunigung; kurze Moves dauern - real länger. Vorlauf-Marge muss das abfangen; später ggf. Trapez-Profil-Schätzung verfeinern +- [ ] **Lockstep vs. Freerun (B5):** In `freerun` taktet jeder Controller eigenständig nach + seinem `Bf`. In `lockstep` kommt zusätzlich das Gate aus Paket 5 dazu — der nächste Schritt + wird erst gesendet, wenn der *langsamste* Controller `Idle`/Ziel meldet. +- [ ] **Drei Controller driften auseinander:** Im Korrekt-Modus (ToDo_6a) sollten die `Bf`-Stände + zusammenbleiben; große Spreizung ist ein Warnsignal (Feedrate-Fehler) — in `lockstep` zudem + ein Auslöser, härter zu warten. +- [ ] **`moveTime` nur Hint:** Da `Bf` die echte Wahrheit liefert, ist die Schätzungenauigkeit + (`dist/feedrate` ignoriert Beschleunigung) unkritisch — sie überbrückt nur die Lücke zwischen + zwei Reports. Keine Trapez-Profil-Verfeinerung nötig. --- ## Hinweis zur Implementierung -`robot/fluidnc/FluidNCClient.js` ist eine bidirektionale WebSocket-Anbindung an FluidNC (Port 81) mit Reconnect-Logik und `EventEmitter`-Interface — diese Klasse ist eine gute Grundlage für alle drei Pakete und sollte bei der Umsetzung von `ToDo_2` (Sender-Interface) mit evaluiert werden. +- **Aktiver Pfad:** Gelesen und konfiguriert (`$10`, `$Report/Interval`) wird auf dem + produktiv genutzten `robot/TelnetSenderGRBL.js` (Telnet-Kanal) — der `data`-Handler dort + ist die zentrale Stelle (Paket 1). +- `robot/fluidnc/FluidNCClient.js` (bidirektionale WebSocket-Anbindung, Port 81, Reconnect + + `EventEmitter`) ist eine *alternative* Anbindung. Nicht der aktive Pfad, kann aber bei + `ToDo_2` (Sender-Interface) als Referenz für das Event-Modell mit evaluiert werden. + +## Reihenfolge / Abhängigkeiten + +1. **Paket 1** (lesen) ist die Basis für alles Weitere. +2. **Paket 3** (Auto-Report `$10=3` + `$Report/Interval`, `MPos`/`Bf` parsen) liefert die Daten + für Paket 4, 5, 6. +3. **Baustein** (Umkehr-Kinematik inkl. B3-Zweigwahl) → dann **Paket 4** (Sync, async). +4. **Paket 5** (Fortschritt) und **Paket 6** (Queue) bauen auf Paket 3 auf; + **Freerun zuerst**, **Lockstep** (B5) erst danach. diff --git a/logs/gcode_commands.log b/logs/gcode_commands.log index e785a32..5d21b48 100644 --- a/logs/gcode_commands.log +++ b/logs/gcode_commands.log @@ -10349,3 +10349,8 @@ 2026-06-10T21:03:42.477Z ::ffff:127.0.0.1: M114 2026-06-10T21:03:42.692Z ::ffff:127.0.0.1: G1 X1 Y2 Z3 2026-06-10T21:03:42.921Z ::ffff:127.0.0.1: G1 X1 +2026-06-10T21:18:23.894Z ::ffff:127.0.0.1: M114 +2026-06-10T21:18:23.919Z ::ffff:127.0.0.1: M114 +2026-06-10T21:18:23.947Z ::ffff:127.0.0.1: G1 X1 Y2 Z3 +2026-06-10T21:18:24.131Z ::ffff:127.0.0.1: G1 X1 Y2 Z3 +2026-06-10T21:18:24.377Z ::ffff:127.0.0.1: G1 X1 diff --git a/logs/pings.log b/logs/pings.log index 4f6957d..809a735 100644 --- a/logs/pings.log +++ b/logs/pings.log @@ -14614,3 +14614,5 @@ 2026-06-10T21:02:32.642Z ::ffff:127.0.0.1 : Ping 2026-06-10T21:03:41.789Z ::ffff:127.0.0.1 : Ping 2026-06-10T21:03:42.257Z ::ffff:127.0.0.1 : Ping +2026-06-10T21:18:23.643Z ::ffff:127.0.0.1 : Ping +2026-06-10T21:18:23.899Z ::ffff:127.0.0.1 : Ping diff --git a/robot/Robot.js b/robot/Robot.js deleted file mode 100755 index a2e7ddf..0000000 --- a/robot/Robot.js +++ /dev/null @@ -1,9 +0,0 @@ -// robot/Robot.js — Kompatibilitäts-Alias (siehe doc/ToDo_12_InverseKinematikConfig_ROADMAP.md). -// -// Externer Code (und Tests) kann weiterhin `require('./robot/Robot')` schreiben. -// Während der Übergangsperiode zeigt der Alias auf die konkrete Default-Kinematik -// `Arm3SegmentLinearX`, damit `new Robot(l1, l2, l3)` unverändert funktioniert. -// -// Die generische Basisklasse / der Interface-Vertrag liegt in `RobotBase.js`; -// die produktive Auswahl der Kinematik übernimmt `KinematicsFactory.js`. -module.exports = require('./kinematics/Arm3SegmentLinearX'); diff --git a/test/GCode.receiveGCode.G92.test.js b/test/GCode.receiveGCode.G92.test.js index 4f725ef..1592655 100755 --- a/test/GCode.receiveGCode.G92.test.js +++ b/test/GCode.receiveGCode.G92.test.js @@ -1,5 +1,5 @@ // __tests__/Robot.inverseKinematics.test.js -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); const GCode = require('../robot/GCode.js'); var TenetSender = require('../robot/TelnetSenderGRBL.js') diff --git a/test/GCode.receiveList.test.js b/test/GCode.receiveList.test.js index f301a62..36af229 100644 --- a/test/GCode.receiveList.test.js +++ b/test/GCode.receiveList.test.js @@ -1,4 +1,4 @@ -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); const GCode = require('../robot/GCode.js'); describe("Robot Gcode receive", () => { diff --git a/test/GCode.speed.test.js b/test/GCode.speed.test.js index eae8116..ea1e4a9 100644 --- a/test/GCode.speed.test.js +++ b/test/GCode.speed.test.js @@ -1,5 +1,5 @@ const GCode = require('../robot/GCode.js'); -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); const TelnetSender = require('../robot/TelnetSenderGRBL.js'); describe('GCode Speed Tests', () => { @@ -50,7 +50,7 @@ describe('GCode Speed Tests', () => { const L2 = 300; const L3 = 20; - const Robot2 = require('../robot/Robot'); // neu geladen + const Robot2 = require('../robot/kinematics/Arm3SegmentLinearX'); // neu geladen const robot = new Robot2(L1, L2, L3); const telnetSender1 = new TelnetSender("test.test", 5000, "x", "y", "z"); diff --git a/test/GCode.test.js b/test/GCode.test.js index 7f4a746..b790c02 100755 --- a/test/GCode.test.js +++ b/test/GCode.test.js @@ -1,5 +1,5 @@ const GCode = require('../robot/GCode.js'); -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); var TenetSender = require('../robot/TelnetSenderGRBL.js') test('G91 ist ein GCode Command', () => { diff --git a/test/Robot.01_WristPoint.test.js b/test/Robot.01_WristPoint.test.js index 9c40d1d..cac861a 100755 --- a/test/Robot.01_WristPoint.test.js +++ b/test/Robot.01_WristPoint.test.js @@ -1,5 +1,5 @@ -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); diff --git a/test/Robot.02_UpperArm.test.js b/test/Robot.02_UpperArm.test.js index ae280b4..a2d7f04 100755 --- a/test/Robot.02_UpperArm.test.js +++ b/test/Robot.02_UpperArm.test.js @@ -1,4 +1,4 @@ -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); test('Grade ausgestreckt', () => { robot = new Robot(300,290,10) diff --git a/test/Robot.Kinematics.RoundTrip.test.js b/test/Robot.Kinematics.RoundTrip.test.js index e3c2715..1675ebb 100755 --- a/test/Robot.Kinematics.RoundTrip.test.js +++ b/test/Robot.Kinematics.RoundTrip.test.js @@ -1,5 +1,5 @@ // __tests__/Robot.inverseKinematics.test.js -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); describe("Robot Kinematics Roundtrip", () => { diff --git a/test/Robot.Kinematics.RoundTripList.test.js b/test/Robot.Kinematics.RoundTripList.test.js index 8dddb46..df8c352 100755 --- a/test/Robot.Kinematics.RoundTripList.test.js +++ b/test/Robot.Kinematics.RoundTripList.test.js @@ -1,4 +1,4 @@ -const Robot = require('../robot/Robot.js'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); describe("Robot Kinematics Roundtrip (parametrisiert)", () => { diff --git a/test/Robot.calculateSpeeds.test.js b/test/Robot.calculateSpeeds.test.js index 0fc19c0..590bb60 100644 --- a/test/Robot.calculateSpeeds.test.js +++ b/test/Robot.calculateSpeeds.test.js @@ -1,4 +1,4 @@ -const Robot = require('../robot/Robot'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); const MotorPosition = require('../robot/RobotMotorPosition'); describe('Robot.calculateSpeeds (ToDo_6a)', () => { diff --git a/test/Robot.changedFlags.test.js b/test/Robot.changedFlags.test.js index 007b18e..60e0c47 100644 --- a/test/Robot.changedFlags.test.js +++ b/test/Robot.changedFlags.test.js @@ -1,4 +1,4 @@ -const Robot = require('../robot/Robot') +const Robot = require('../robot/kinematics/Arm3SegmentLinearX') const MockCmdReceiver = require('./helpers/mockCmdReceiver') describe('Robot.createMotorPosition - Changed Flags', () => { diff --git a/test/Robot.sendCommand.test.js b/test/Robot.sendCommand.test.js index 569de3e..053ed90 100644 --- a/test/Robot.sendCommand.test.js +++ b/test/Robot.sendCommand.test.js @@ -1,4 +1,4 @@ -const Robot = require('../robot/Robot') +const Robot = require('../robot/kinematics/Arm3SegmentLinearX') const GCode = require('../robot/GCode.js'); const MockCmdReceiver = require('./helpers/mockCmdReceiver') diff --git a/test/Speed.coordination.test.js b/test/Speed.coordination.test.js index 89beff2..fd55f78 100644 --- a/test/Speed.coordination.test.js +++ b/test/Speed.coordination.test.js @@ -10,7 +10,7 @@ * Damit werden gleichzeitig abgesichert: moveTime-Propagation Robot→Sender, portValue * gegen den echten Sende-Pfad, und die Feedrate-Auswahl im Korrekt-Modus. */ -const Robot = require('../robot/Robot'); +const Robot = require('../robot/kinematics/Arm3SegmentLinearX'); const GCode = require('../robot/GCode'); const TelnetSender = require('../robot/TelnetSenderGRBL');