From 28a1469f61f3cac399ae5ebaa46c0b00a7ad7b97 Mon Sep 17 00:00:00 2001 From: Dominic Gundel Date: Thu, 15 Oct 2026 08:58:45 +0200 Subject: [PATCH] Doku Anycubic-Stack: MQTT, ACE und Restart aus den Quellen. --- CHANGELOG.md | 1 + docs/Anycubic-Stack.md | 126 +++++++++++++++++++++++++++++++++++++++++ docs/Senden-Queue.md | 2 +- 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 docs/Anycubic-Stack.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 43202e2..19552a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Alle Änderungen an Grotti. Format nach [Keep a Changelog](https://keepachangelo ### Neu - Cursor-Regel `.cursor/rules/grotti.mdc` (Changelog, Doku, Version, Sicherheit). +- Doku [Anycubic-Stack](docs/Anycubic-Stack.md): Schichten gklib/gkapi/Rinkhals, MQTT Start/Stop/10101, ACE/`.acm`, FIRMWARE_RESTART — aus Klipper-go und Rinkhals-Quellcode. ## [2.0.0] – 2026-10-15 diff --git a/docs/Anycubic-Stack.md b/docs/Anycubic-Stack.md new file mode 100644 index 0000000..6d1bfca --- /dev/null +++ b/docs/Anycubic-Stack.md @@ -0,0 +1,126 @@ +# Anycubic-Stack: wie Druck, Abbruch und ACE wirklich laufen + +Stand **15.10.2026**. Quellen: Anycubic [Klipper-go](https://github.com/ANYCUBIC-3D/Klipper-go), [K3-klipper-mcu](https://github.com/ANYCUBIC-3D/K3-klipper-mcu); Rinkhals `kobra.py`, `mmu_ace.py`, `mmu_ace_metadata.py`, Docs `firmware/mqtt.md` und `firmware/goklipper.md` (lokal unter `D:\Projekte\_ref\`). Nicht geraten — am Quellcode und an der Rinkhals-Doku gelesen. + +## Schichten + +```mermaid +flowchart TB + subgraph host [PC] + Grotti[Grotti] + end + subgraph printer [Kobra 3 / S1] + Moonraker[Moonraker + kobra.py] + MmuAce[mmu_ace + mmu_ace_metadata] + Gkapi[gkapi MQTT Broker] + Gklib[gklib GoKlipper] + Ui[K3SysUi Touch] + Mcu[MCU Firmware] + AceHw[ACE Hardware] + end + Grotti -->|"HTTP :80 upload / print/start / cancel"| Moonraker + Grotti -->|"MQTTS :9883 print start/stop"| Gkapi + Grotti -->|"HTTP :18910 info/ctrl Credentials"| Gkapi + Moonraker -->|"bei print/start oft MQTT lokal :2883"| Gkapi + Moonraker --> Gklib + MmuAce --> Moonraker + Gkapi --> Gklib + Ui --> Gkapi + Gklib --> Mcu + Gklib --> AceHw +``` + +| Komponente | Offen? | Rolle | +| --- | --- | --- | +| **gklib** (GoKlipper) | teilweise ([Klipper-go](https://github.com/ANYCUBIC-3D/Klipper-go)) | Klipper-Host: Motion, Heater, `virtual_sdcard`, ACE-G-codes | +| **MCU** | teilweise ([K3-klipper-mcu](https://github.com/ANYCUBIC-3D/K3-klipper-mcu)) | Stepper/Sensoren | +| **gkapi** | nein (proprietär) | MQTT-Broker, Druck-Tasks, Cloud/LAN, Credentials `:18910` | +| **K3SysUi** | nein | Touch-UI | +| **Rinkhals Moonraker** | ja | Vanilla Moonraker + `kobra.py` + `mmu_ace*` | + +Anycubic schreibt in den READMEs: proprietäre Features sind gekapselt. MQTT-Taskverwaltung und Cloud stecken nicht in Klipper-go. + +## Zwei Welten beim Druckstart + +1. **Klipper-Pfad** (Moonraker `POST /printer/print/start` → gklib `virtual_sdcard`): setzt `print_stats`, lädt G-code in gklib. +2. **Anycubic-Task** (MQTT `type: print`, `action: start` an Topic `…/slicer/printer/{modelId}/{deviceId}/print`): legt den Job an, den Display und Cloud kennen; steuert LeviQ/`auto_leveling`, ACE-Map in der Payload. + +Rinkhals `kobra.py` `patch_mqtt_print` hängt den Moonraker-`print/start` oft auf MQTT um (`mqtt_print_file`), lokal `127.0.0.1:2883`. Grotti kann denselben Start von außen über **MQTTS `:9883`** mit Credentials aus `:18910` machen — dann mit eigener `task_settings` und `ams_settings` (siehe [Senden-Queue](Senden-Queue)). + +### MQTT Start (belegt) + +Aus Rinkhals-Doku `firmware/mqtt.md` und `kobra.py`: + +- Publish: `anycubic/anycubicCloud/v1/slicer/printer/{modelId}/{deviceId}/print` +- Report: `…/printer/public/{modelId}/{deviceId}/print/report` +- Pflichtfelder Start: `filename`, `filetype` (1), `taskid` +- Optional: `ams_settings.ams_box_mapping`, `task_settings.auto_leveling` (0/1 Integer) +- Rinkhals und Grotti setzen `taskid` auf eine Zufallszahl; die Doc-Minimalbeispiel nutzt `"-1"` — beides kommt vor + +### MQTT Stop (belegt) — warum 10101 + +```json +{ "type": "print", "action": "stop", "data": { "taskid": "-1" } } +``` + +Moonraker `/printer/print/cancel` trifft nur gklib (`print_stats` → `cancelled`). Der Anycubic-Task in gkapi bleibt → nächster MQTT-Start mit **code 10101** („Print task already exists“ / chinesische Meldung). Rinkhals `mqtt_print_file` kennt 10101 und **retryt einmal**; Grotti sendet bei Abbruch Moonraker-cancel **und** MQTT-stop. + +### Report-Timeout + +`kobra.py` wartet max. 30 s auf `print/report`. Nach Bettnetz/`auto_leveling` kann der Report später kommen, der Druck trotzdem laufen (`info/query` → `project.task_id`). Grotti prüft bei Timeout deshalb `print_info` (MQTT `info/query`). + +## FIRMWARE_RESTART + +In **Klipper-go** ist der Befehl implementiert wie bei Klipper: + +- `Cmd_FIRMWARE_RESTART` → `Request_restart("firmware_restart")` (`gcode.go`) +- Nach dem Reactor-Ende: Event `project:firmware_restart` → MCU `_firmware_restart` (`k3c.go`, `Mcu.go`) + +Auf dem Gerät **deadlockt gklib** trotzdem (beobachtet, Rinkhals #32): Log zeigt Annahme des Befehls, danach keine weitere Verarbeitung, Moonraker bleibt in `startup`. Rinkhals `patch_klipper_restart` **lehnt** `RESTART` / `FIRMWARE_RESTART` unter GoKlipper ab. Grotti ebenso. Config: Display-Reboot oder Strom. Hängenden Fehler: **Fehler löschen** (`SDCARD_RESET_FILE`), nicht Firmware-Restart. + +Der Open-Source-Code erklärt den Intent des Befehls; die Deadlock-Ursache steckt vermutlich im ausgelieferten Binary / der Anbindung an gkapi — nicht im veröffentlichten Happy Path. + +## ACE + +### In gklib (Klipper-go `extras_ace.go`) + +G-codes: `ACE_CHANGE_TOOL`, `ACE_FEED`, `ACE_RETRACT`, `ACE_SET_SLOT`, Trocknen, Endless Spool, … Kommunikation über Seriell (`extras_ace_commun.go`). `Get_status` liefert ACE-`info` + Endless-Spool-Flags — **nicht** das Moonraker-Objekt `mmu` mit `gate_color`. + +### In Rinkhals Moonraker (`mmu_ace.py`) + +- Emuliert Happy-Hare-ähnliches **`mmu`**-Objekt (`gate_color`, `gate_material`, …) für Clients +- Beim MQTT-Start: `register_print_data_patcher` → wenn keine `ams_settings` in der Payload, baut Identity-/TTG-Map aus den Gates und hängt `ams_settings` an +- Liest/schreibt Sidecar **`basename.acm`** neben dem G-code; ohne ACE kann sie nach `.acm.disabled` umbenannt werden +- Auto-Feed-Logik vor Start, wenn nichts im Toolhead ist (Issue #464) + +### Upload-Metadaten (`mmu_ace_metadata.py`) + +Beim Moonraker-Upload: ganze Datei lesen, ggf. umschreiben. Fingerprint `; processed by MmuAcePatcher`. Trailer `; referenced_tools = 0,2,…`. Ohne Trailer: gklib „skipping auto-feed (no trailer found)“. Grotti setzt Fingerprint + Trailer vor dem Upload, damit der teure Durchlauf entfällt ([Senden-Queue](Senden-Queue)). + +### `.acm` vs MQTT-`ams_settings` + +Zwei Kanäle für dieselbe Zuordnung: + +| Kanal | Wann | +| --- | --- | +| Datei `name.gcode.acm` / `name.acm` auf dem Drucker | Stock/gklib liest Sidecar beim Start | +| MQTT-Feld `data.ams_settings` | gkapi / Rinkhals-Patcher legt Map in die Payload | + +Ohne Map: Rinkhals füllt oft Identity T0→Platz0 … — deshalb Grotti bei eigener ACE-Zuordnung per MQTT mit expliziter Map startet und beide `.acm`-Varianten hochlädt ([ACE-Live](ACE-Live)). + +## Was Grotti bewusst nutzt + +| Aktion | Weg | Beleg | +| --- | --- | --- | +| Einfacher Start | Upload + Moonraker `print/start` → Rinkhals MQTT | `kobra.py` `patch_mqtt_print` | +| Start mit Bettnetz oder ACE-Map | Upload (+ `.acm`) + Grotti-MQTT `:9883` | `anycubic_lan.start_print`, mqtt.md | +| Abbruch | Moonraker cancel + MQTT stop `taskid:-1` | `mqtt_stop_print`, mqtt.md | +| Credentials | `:18910/info` + `/ctrl` AES | `anycubic_lan.credentials` | +| Slots anzeigen | Moonraker `mmu` (Rinkhals) | `mmu_ace.py` | +| Kein Firmware-Restart | abgelehnt | Rinkhals #32, `patch_klipper_restart` | + +## Offene Grenzen + +- Quellcode von **gkapi** (Task-State-Machine, exakte 10101-Bedingungen) ist nicht öffentlich. +- Deadlock in **FIRMWARE_RESTART** ist beobachtet und in Rinkhals abgefangen, Root Cause im Binary nicht freigelegt. +- Kobra X / K4-Stack ist ein anderes System (C++-Klipper), nicht GoKlipper — für unsere Drucker irrelevant. diff --git a/docs/Senden-Queue.md b/docs/Senden-Queue.md index be4af0a..bf02f8a 100644 --- a/docs/Senden-Queue.md +++ b/docs/Senden-Queue.md @@ -1,6 +1,6 @@ # Grotti: Senden und Start dauert -Stand **14.10.2026**. Spulen kommen vom Ziel-Drucker zur Startzeit (`ace_map` + eingelegte Rolle), nicht aus einem freien Dropdown. `sends` speichert `ace_map` und Bettnetz für „Nochmal“. +Stand **14.10.2026** (Stack-Quellen 15.10.2026 in [Anycubic-Stack](Anycubic-Stack)). Spulen kommen vom Ziel-Drucker zur Startzeit (`ace_map` + eingelegte Rolle), nicht aus einem freien Dropdown. `sends` speichert `ace_map` und Bettnetz für „Nochmal“. ## Warum Sekunden / Minuten