diff --git a/AGENTS.md b/AGENTS.md index bb3642b..b18d703 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,7 +16,8 @@ als Referenz oder Ziel verwendet werden. 1. `README.md` 2. `doc/INDEX.md` 3. `doc/00_architecture.md` -4. bei Protokoll-/Layoutänderungen zusätzlich: +4. `doc/07_known_limitations.md` +5. bei Protokoll-/Layoutänderungen zusätzlich: - `src/Protocol.cs` - `src/DeviceConfig.cs` - `../VersaMCU/AGENTS.md` @@ -33,6 +34,10 @@ als Referenz oder Ziel verwendet werden. bauen und getrennt committen. - Host-Command-IDs niemals ungeprüft als Shellkommando ausführen. Eine spätere Host-Aktionsfunktion benötigt ein explizites sicheres Mapping. +- Das Binärmodell akzeptiert alle Firmware-Animationen `0..6`; der aktuelle + `ActionDialog` bietet davon nur Static, Blink, Pulse und ColorCycle an. + Bestehende, im Dialog nicht angebotene Werte nicht stillschweigend + überschreiben. ## Threading und Transfers diff --git a/README.md b/README.md index 1b9a487..66d1239 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,10 @@ nicht zum aktuellen Scope. ## Voraussetzungen - Windows 10/11 -- .NET SDK +- .NET 7 SDK mit Windows-Desktop-Unterstützung - geflashte `VersaMCU`-Firmware -- Board per USB als CDC-Device verbunden +- Board per USB als Composite Device (Keyboard-HID, Consumer-HID und CDC) + verbunden ## Starten @@ -27,7 +28,9 @@ dotnet run --project src/VersaGUI.csproj - liest beim Verbinden zuerst die Config und danach die Makros - validiert Chunkzahl, eindeutige Indizes, Vollständigkeit, CRC und Feldwerte - zeigt MX-Buttons und Encoder-Aktionen an +- bearbeitet alle drei Profile über die Profilauswahl - schreibt Config und Makros getrennt, aber in einem UI-Vorgang auf das Board +- sendet einen Protokoll-Ping und zeigt die Antwort an ## Aktueller Datenstand @@ -40,6 +43,10 @@ dotnet run --project src/VersaGUI.csproj - globale Helligkeit - per-LED-Helligkeit +Globale Helligkeit und Encoder-Sensitivität werden bytegenau erhalten, besitzen +aber derzeit keine eigenen Bedienelemente. Per-LED-Helligkeit kann über JSON +gesetzt werden. + ### MacroTable - 32 Slots @@ -73,7 +80,8 @@ wait for ACK/NACK - HID-Key-Zuweisung inklusive Modifier - Consumer-Keys -- Host-Commands +- Host-Command-IDs konfigurieren und empfangen; Desktop-Aktionen werden noch + nicht ausgeführt - Makros mit bis zu 8 Steps - Profilwechsel als ActionType - LED-Farbe, Animation und Periode pro MX-Button @@ -90,6 +98,7 @@ VersaGUI/ |-- Program.cs |-- TrayApp.cs |-- SerialManager.cs + |-- ChunkTransferBuffer.cs |-- DeviceConfig.cs |-- ConfigForm.cs |-- ActionDialog.cs @@ -109,13 +118,21 @@ dotnet run --project tests/VersaGUI.ContractTests/VersaGUI.ContractTests.csproj - die App ist Windows-only - Host-Command-Events enthalten Command-ID, Key-/Encoder-ID und Richtung; ein sicheres Mapping auf konkrete Desktop-Aktionen ist noch nicht implementiert +- der ActionDialog bietet aktuell vier der sieben vom Binärformat unterstützten + LED-Animationen an +- ein vollständig ausbleibendes Dump-Ende besitzt noch keinen Empfangs-Timeout + +Die vollständige Liste steht in +[`doc/07_known_limitations.md`](doc/07_known_limitations.md). ## Einstieg für Coding-LLMs Repository-Anweisungen, gemeinsame Binärverträge und Verifikation stehen in [`AGENTS.md`](AGENTS.md). -## Weiterfuehrende Doku +## Weiterführende Doku -- [doc/02_device_config.md](doc/02_device_config.md) +- [Dokumentationsindex](doc/INDEX.md) +- [Datenlayout](doc/02_device_config.md) +- [Bekannte Einschränkungen](doc/07_known_limitations.md) - [../VersaMCU/doc/07_serial_protocol.md](../VersaMCU/doc/07_serial_protocol.md) diff --git a/doc/00_architecture.md b/doc/00_architecture.md index d02c530..5ae158e 100644 --- a/doc/00_architecture.md +++ b/doc/00_architecture.md @@ -16,7 +16,7 @@ |---|---| | `TrayApp` | `ApplicationContext`; hält Tray-Icon, öffnet ConfigForm, verarbeitet Board-Events | | `SerialManager` | Verbindungsverwaltung, WMI-Erkennung, Lese-Thread, Sende-Methoden | -| `ConfigForm` | Hauptfenster (Grid + Encoder-Panel + Footer); öffnet ActionDialog | +| `ConfigForm` | Hauptfenster (Profilauswahl, Grid, Encoder-Panel und Footer); öffnet ActionDialog | | `ActionDialog` | Modaler Dialog zum Bearbeiten einer Aktion + LED-Einstellungen | | `DeviceConfig` | C#-Spiegel von `SDeviceConfig`; Validierung und Serialisierung (740 B) | | `MacroTable` | C#-Spiegel von `SMacroTable`; Validierung und Serialisierung (512 B) | @@ -46,7 +46,7 @@ Benutzer → ConfigForm → ActionDialog UI-Thread : TrayApp, ConfigForm, ActionDialog, alle WinForms-Controls BG-Thread : SerialManager.ReadLoop() – blockiert auf ReadByte() Timer-Thread : SerialManager._reconnectTimer → TryConnect() alle 3 s -Sende-Task : ConfigForm.OnSave() → Task.Run() (blockiert ~400 ms für Transfer) +Sende-Task : ConfigForm.OnSave() → Task.Run() (blockiert bis ACK/NACK oder Timeout) ``` Alle Board-Events werden per `SynchronizationContext.Post` auf den UI-Thread gepostet. Controls dürfen nie vom BG-Thread angefasst werden. @@ -75,3 +75,8 @@ Disconnect → ReadLoop bricht ab → Disconnected-Event → 5 s Backoff → Tim gemeinsamen Transfer-Lock; einzelne Pakete unter einem Write-Lock. - **Host-Commands**: Pakete sind vollständig definiert, die Ausführung einer Command-ID als Desktop-Aktion ist bewusst noch nicht implementiert. +- **UI-Modell-Grenze**: Das Binärmodell kann alle sieben Firmware-Animationen + lesen und schreiben. Der ActionDialog bietet aktuell vier davon zur Auswahl. + +Bewusst offene Punkte stehen gesammelt in +[`07_known_limitations.md`](07_known_limitations.md). diff --git a/doc/01_serial_manager.md b/doc/01_serial_manager.md index 2d15fbf..bbbcd40 100644 --- a/doc/01_serial_manager.md +++ b/doc/01_serial_manager.md @@ -84,7 +84,10 @@ _configAckOk / _macroAckOk – volatile bool (true = ACK, false = NACK) | `SendConfig(cfg)` | bool | BEGIN(0x10) → 124×DATA(0x11) → COMMIT(0x12), 5 ms zwischen Chunks, wartet auf ACK/NACK | | `SendMacros(macros)` | bool | BEGIN(0x20) → 86×DATA(0x21) → COMMIT(0x22), 5 ms zwischen Chunks, wartet auf ACK/NACK | -`SendConfig` und `SendMacros` blockieren ~1,5 s (Chunks + NVM-Zeit) → werden in `Task.Run()` aus `ConfigForm.OnSave()` aufgerufen. +Die reine Chunk-Pausierung beträgt nominal etwa 640 ms für Config und 450 ms +für Makros, jeweils zuzüglich Port-, NVM- und ACK-Zeit. Nach jedem COMMIT wird +höchstens 3 s auf ACK/NACK gewartet. Deshalb laufen beide Methoden aus +`ConfigForm.OnSave()` in `Task.Run()` und niemals auf dem UI-Thread. Vor `BEGIN` wird das zugehörige ACK-Gate geleert. Beide Blob-Sender teilen einen Transfer-Lock, prüfen jeden `Send()`-Rückgabewert und brechen bei diff --git a/doc/02_device_config.md b/doc/02_device_config.md index 57529f0..6fe03cf 100644 --- a/doc/02_device_config.md +++ b/doc/02_device_config.md @@ -25,6 +25,7 @@ Sie muessen deshalb synchron zu `VersaMCU/src/config/nvm_config.h` und `macro_co | `ActiveProfileIndex` | aktives Profil 0..2 | | `GlobalBrightness` | globale LED-Helligkeit | | `Profiles[3]` | komplette Profil-Daten | +| `EncSensitivity[4]` | im Vertrag enthalten; Firmware nutzt den Wert derzeit nicht | Fuer die GUI gibt es zusaetzlich Komfortzugriffe auf das aktive Profil: @@ -62,6 +63,32 @@ Jedes Profil belegt 236 Byte: 20 x led_period_ms ``` +### Action-Daten + +| Typ | Datenbereich | +|---|---| +| `None` | Daten werden ignoriert | +| `HidKey` | Low-Byte Keycode `0x00..0x65`, High-Byte Modifier | +| `HidConsumer` | Usage `0x0000..0x03FF` | +| `HostCommand` | frei definierte Command-ID `0x0000..0xFFFF` | +| `Macro` | Slot `0..31` | +| `ProfileSwitch` | Profil `0..2`; `0x00FF` oder `0xFFFF` bedeuten „nächstes Profil“ | + +### LED-Animationen im Binärformat + +| Wert | Enum | +|---|---| +| `0` | `Static` | +| `1` | `Blink` | +| `2` | `Pulse` | +| `3` | `FadeIn` | +| `4` | `FadeOut` | +| `5` | `ColorCycle` | +| `6` | `ColorFade` | + +`DeviceConfig` akzeptiert alle Werte `0..6`. Der aktuelle ActionDialog bietet +nur `Static`, `Blink`, `Pulse` und `ColorCycle` an. + ## CRC CRC16-CCITT: @@ -109,10 +136,20 @@ Ein Step besteht aus: - `keycode` - `modifier` +`keycode == 0` beendet die Ausführung des Slots. Belegte Steps nach der ersten +Lücke werden daher zwar serialisiert, von der Firmware aber nicht ausgeführt. + Es gibt kein Magic und keine CRC fuer die Makrotabelle. Beim Transfer prüft die GUI trotzdem exakte Größe und alle HID-Keycodes; die Firmware prüft zusätzlich die vollständige Chunkmenge. +## Validierung + +`ToBytes()` verweigert ungültige Objektzustände. `FromBytes()` verändert das +bestehende Objekt erst, nachdem Größe, Magic, Version, CRC, aktives Profil, +Action-Werte, LED-Enums und die Mindestperiode von 2 ms für `Pulse` geprüft +wurden. Für die Makrotabelle gelten exakte 512 Byte und Keycodes bis `0x65`. + ## Wichtig fuer Aenderungen Wenn sich Firmware-Layout, Magic, Version, Profilzahl oder Makrogroesse aendern, muessen mindestens diese Stellen zusammen angepasst werden: diff --git a/doc/03_tray_app.md b/doc/03_tray_app.md index bae9fc5..055113e 100644 --- a/doc/03_tray_app.md +++ b/doc/03_tray_app.md @@ -38,8 +38,11 @@ Icon und Tooltip spiegeln den Verbindungsstatus: | `EvtMacroAck` | `serial.SignalMacroAck()` — gibt SendMacros()-Thread frei | | `EvtMacroNack` | `serial.SignalMacroNack()` — gibt SendMacros()-Thread frei (Fehler) | | `EvtPong` | MessageBox "Ping OK" | -| `EvtKeyDown/Up` | Command-ID in Byte 2/3 empfangen | -| `EvtEncCw/Ccw` | Command-ID und Encoderrichtung empfangen | +| `EvtKeyDown/Up` | Host-Command-ID in Byte 2/3 empfangen | +| `EvtEncCw/Ccw` | Host-Command-ID und Encoderrichtung empfangen | + +Diese vier Host-Events sendet die Firmware nur für Actions vom Typ +`HostCommand`. `TrayApp` dekodiert die 16-Bit-ID, führt sie aber noch nicht aus. ACK/NACK-Events zeigen keine eigene MessageBox mehr — das Ergebnis wird nach Abschluss beider Transfers gebündelt in `ConfigForm.OnSave()` angezeigt. @@ -56,8 +59,11 @@ OnDisconnected(): Icon + Text + Menü-Item aktualisieren ``` -Ein unvollständiger oder ungültiger Dump wird einmal wiederholt. Schlägt auch -der zweite Versuch fehl, zeigt das Tray-Icon eine Warnung. +Ein mit `END` abgeschlossener, aber unvollständiger oder ungültiger Dump wird +einmal wiederholt. Schlägt auch der zweite Versuch fehl, zeigt das Tray-Icon +eine Warnung. Bleibt `END` vollständig aus, gibt es derzeit keinen +Empfangs-Timeout; siehe +[`07_known_limitations.md`](07_known_limitations.md). ## Offene Punkte diff --git a/doc/04_config_form.md b/doc/04_config_form.md index 6ba5d2c..5a6372f 100644 --- a/doc/04_config_form.md +++ b/doc/04_config_form.md @@ -4,7 +4,11 @@ ## Verantwortung -Hauptkonfigurationsfenster: zeigt alle 20 MX-Buttons und 4 Encoder, öffnet `ActionDialog` bei Klick, speichert Config + Makros auf das Board. +Hauptkonfigurationsfenster: wählt eines von drei Profilen, zeigt dessen 20 +MX-Buttons und 4 Encoder, öffnet `ActionDialog` bei Klick und speichert Config +plus Makros auf das Board. + +Oberhalb des Tasten-Grids befindet sich die Profilauswahl für Profil 1 bis 3. ## Layout @@ -45,15 +49,23 @@ Entspricht `key_id - 5` in der Firmware. Im TableLayoutPanel: Spalte=col, Zeile= ```csharp Task.Run(() => { - _serial.SendConfig(_config); // ~300 ms - Thread.Sleep(50); - _serial.SendMacros(_macros); // ~250 ms + bool cfgOk = _serial.SendConfig(_config); // wartet auf ACK/NACK + bool macroOk = _serial.SendMacros(_macros); InvokeOnUi(() => { /* Button-Text + Enabled zurücksetzen */ }); }); ``` Save-Button wird während der Übertragung deaktiviert, Text wechselt zu "Wird gesendet...". Save-Button ist nur aktiviert wenn Board verbunden (`_serial.IsConnected`). +Config und Makros werden sequenziell gesendet; ein gemeinsamer Transfer-Lock +verhindert parallele Blob-Transfers. + +## Profile + +Die Profilauswahl setzt `ActiveProfileIndex` und schaltet alle Komfortzugriffe +von `DeviceConfig` auf das gewählte Profil um. `RefreshAll()` synchronisiert +die Auswahl nach Board-Reload oder JSON-Import und zeichnet anschließend die +20 MX- und 12 Encoder-Schaltflächen neu. ## Import / Export @@ -64,7 +76,9 @@ Fehler (IO, JSON-Parse, falsche Version) werden per `MessageBox` angezeigt. ## RefreshAll -Wird von `TrayApp` nach erfolgreicher Config vom Board aufgerufen (über `_configForm?.RefreshAll()`). Aktualisiert alle 20 MX-Buttons und 12 Encoder-Buttons ohne Dialog. +Wird von `TrayApp` nach erfolgreicher Config oder Makrotabelle vom Board sowie +nach JSON-Import aufgerufen. Aktualisiert Profilauswahl, alle 20 MX-Buttons und +12 Encoder-Buttons ohne Dialog. ## Extensions (in derselben Datei) diff --git a/doc/05_action_dialog.md b/doc/05_action_dialog.md index 327f2e7..5f3bc72 100644 --- a/doc/05_action_dialog.md +++ b/doc/05_action_dialog.md @@ -39,8 +39,9 @@ LED-Panels (`_colorPanel`, `_animPanel`) erscheinen zusätzlich wenn `showColor= // Index 2: "Profil 2" → Data = 1 // Index 3: "Profil 3" → Data = 2 -// Initialbelegung: -_profileCombo.SelectedIndex = action.Data == 0xFFFF ? 0 : action.Data + 1; +// Initialbelegung: beide von der Firmware akzeptierten Zykluswerte erkennen +_profileCombo.SelectedIndex = + action.Data is 0x00FF or 0xFFFF ? 0 : action.Data + 1; // In OnOk(): data = _profileCombo.SelectedIndex == 0 @@ -48,7 +49,9 @@ data = _profileCombo.SelectedIndex == 0 : (ushort)(_profileCombo.SelectedIndex - 1); ``` -Im Board wird `0xFFFF` als `(uint8_t)0xFF` gespeichert und in der Firmware als "nächstes Profil" interpretiert. +`SAction.data` ist ein 16-Bit-Feld. Die GUI schreibt für „nächstes Profil“ +kanonisch `0xFFFF`; Firmware und Deserialisierung akzeptieren zusätzlich den +älteren Wert `0x00FF`. ## Tasten-Capture (HID-Modus) @@ -62,7 +65,10 @@ WinForms behandelt Pfeil- und Enter-Tasten als "Dialog Keys" in `ProcessDialogKe ## Makro-Capture -Jeder der 8 Steps hat einen eigenen Capture-Button. `_captureStep` (0–7, -1 = inaktiv) zeigt welcher Step gerade aufnimmt. Capture-Logik identisch mit HID-Modus, schreibt in `_stepKeycodes[captureStep]`. +Jeder der 8 Steps hat einen eigenen Capture-Button. `_captureStep` (0–7, +-1 = inaktiv) zeigt, welcher Step gerade aufnimmt. Capture-Logik identisch mit +HID-Modus, schreibt in `_stepKeycodes[captureStep]`. Der erste leere Step +beendet die Makrosequenz; deshalb dürfen belegte Steps keine Lücke enthalten. ## Schlüssellookup (layout-unabhängig) @@ -107,6 +113,14 @@ Scan-Code → HID Usage via s_scanToHid[sc] Nur wenn `showColor=true` (MX-Buttons). Enthält: - **Farbpicker**: `ColorDialog` → `_colorBtn.BackColor` - **Animations-Dropdown**: Statisch / Blinken / Pulsieren / Regenbogen -- **Periode-Dropdown**: Presets von "Sehr langsam (8 s)" bis "Sehr schnell (250 ms)" +- **Periode-Dropdown**: 500 ms, 1 s, 2 s oder 4 s Bei "Regenbogen" wird der Farbpicker ausgeblendet (Farbe irrelevant). + +Das Binärmodell kennt außerdem `FadeIn`, `FadeOut` und `ColorFade`; diese drei +Werte sind im Dialog noch nicht auswählbar. Per-LED-Helligkeit wird im +Binärformat und JSON erhalten, besitzt hier aber ebenfalls kein Bedienelement. +Beim Öffnen einer nicht angebotenen Animation fällt die Auswahl derzeit auf +`Static` zurück. Unbekannte, aber formal gültige Consumer-Usages fallen auf +„Play / Pause“ zurück. Details stehen in +[`07_known_limitations.md`](07_known_limitations.md). diff --git a/doc/06_config_json.md b/doc/06_config_json.md index a3af2d6..efee2e4 100644 --- a/doc/06_config_json.md +++ b/doc/06_config_json.md @@ -14,7 +14,14 @@ Menschenlesbares JSON mit `System.Text.Json` (`WriteIndented=true`, Enums als St { "index": 0, "action": { "type": "HidKey", "data": 260 }, - "led": { "r": 80, "g": 40, "b": 0, "anim": "ColorCycle", "period_ms": 4000 } + "led": { + "r": 80, + "g": 40, + "b": 0, + "brightness": 255, + "anim": "ColorCycle", + "period_ms": 4000 + } }, ... ], @@ -32,7 +39,9 @@ Menschenlesbares JSON mit `System.Text.Json` (`WriteIndented=true`, Enums als St ## Serialisierung -`ConfigJson.Serialize(cfg)` → JSON-String. Exportiert alle 20 Buttons + 4 Encoder vollständig. +`ConfigJson.Serialize(cfg)` → JSON-String. Exportiert alle 20 Buttons und vier +Encoder des aktuell aktiven Profils vollständig, einschließlich +Per-LED-Helligkeit. ## Deserialisierung @@ -47,6 +56,9 @@ Menschenlesbares JSON mit `System.Text.Json` (`WriteIndented=true`, Enums als St ## Anmerkungen - `MacroTable` wird **nicht** exportiert (kein JSON-Format für Makros definiert) +- globale Helligkeit und Encoder-Sensitivität werden nicht exportiert +- `profile` wählt das Zielprofil aus und macht es gleichzeitig zum aktiven + Profil; die beiden anderen Profile bleiben unverändert - `data` enthält den `ushort`-Wert direkt (für HidKey z.B. `Keycode | (Modifier << 8)`) - Die Datei ist kein Binärformat und kann manuell bearbeitet werden - Ein ungültiger Wert wird vor dem Anwenden mit `InvalidDataException` diff --git a/doc/07_known_limitations.md b/doc/07_known_limitations.md new file mode 100644 index 0000000..d12c8fd --- /dev/null +++ b/doc/07_known_limitations.md @@ -0,0 +1,69 @@ +# Bekannte Einschränkungen + +Diese Seite trennt bewusst offene Punkte von bereits implementierten +Verträgen. Sie ist vor Änderungen an Verbindungslogik, ActionDialog oder +Binärformat zu lesen. + +## Host-Commands + +Firmware und GUI übertragen für `HostCommand`: + +- Key- beziehungsweise Encoder-ID +- Richtung über die Event-ID +- 16-Bit-Command-ID in Byte 2/3 + +`TrayApp` führt die Command-ID noch nicht als Desktop-Aktion aus. Eine spätere +Implementierung benötigt ein explizites Allowlist-Mapping; beliebige +Shellbefehle aus Config-Daten sind nicht zulässig. + +## LED- und globale Einstellungen + +Das Binärformat und `DeviceConfig` unterstützen sieben Animationen: +`Static`, `Blink`, `Pulse`, `FadeIn`, `FadeOut`, `ColorCycle` und `ColorFade`. +Der `ActionDialog` bietet aktuell nur die ersten drei sowie `ColorCycle` an. +Beim Öffnen einer nicht angebotenen Animation fällt die Auswahl auf `Static` +zurück und würde beim Bestätigen entsprechend gespeichert. + +Per-LED-Helligkeit wird binär und im JSON erhalten, besitzt aber kein +Bedienelement. Globale Helligkeit und Encoder-Sensitivität werden binär +erhalten, sind weder im Hauptfenster editierbar noch Teil des JSON-Exports. +Die Firmware verwendet Encoder-Sensitivität derzeit ebenfalls nicht. + +## ActionDialog + +- Die Consumer-Liste enthält zwölf vordefinierte Usages. Andere formal gültige + Usages bis `0x03FF` können aus dem Board gelesen werden, fallen beim Öffnen + des Dialogs aber auf „Play / Pause“ zurück. +- Das Periodenfeld bietet nur 500, 1000, 2000 und 4000 ms. Andere geladene + Perioden fallen im Dialog auf 4000 ms zurück. +- Ein Makro endet am ersten Step mit `keycode == 0`; Lücken vor späteren + belegten Steps werden nicht ausgeführt. + +## Transfers und Verbindung + +- Das CDC-Protokoll besitzt kein Byte-Framing, keine Paket-CRC und keine + Transfer-ID. Verliert der Stream ein Byte, kann die 8-Byte-Grenze bis zum + Reconnect verschoben bleiben. +- Chunkzahl, eindeutige Indizes, Vollständigkeit und END-Metadaten werden + geprüft. Bleibt ein Dump jedoch ohne `END`, existiert noch kein + Empfangs-Timeout oder Watchdog; die automatische Wiederholung startet nur + nach einem als ungültig erkannten `END`. +- `RequestConfig()` und `RequestMacros()` melden einen unmittelbar + fehlgeschlagenen `Send()` nicht an `TrayApp`. +- Während `SendConfig()` und `SendMacros()` ist nur die Speichern-Schaltfläche + deaktiviert. Andere Editoraktionen werden nicht global gesperrt. + +## Testabdeckung + +Die Console-Contract-Tests prüfen: + +- Configgröße, CRC und Roundtrip +- ausgewählte ungültige Configfelder +- Makrogröße und HID-Keycode-Grenze +- Chunkvollständigkeit und doppelte Chunks +- 16-Bit-Host-Command-Payload + +Nicht automatisiert geprüft werden WinForms-Interaktionen, WMI-Porterkennung, +echte SerialPort-Fehler, Reconnects, NVM-Schreibzeiten, HID-Ausgabe und +Hardwareverhalten. Dafür ist weiterhin ein Test mit angeschlossenem VersaPad +erforderlich. diff --git a/doc/INDEX.md b/doc/INDEX.md index 917158d..9656f5f 100644 --- a/doc/INDEX.md +++ b/doc/INDEX.md @@ -15,6 +15,7 @@ gegen `../../VersaMCU` geprüft werden. | [04_config_form.md](04_config_form.md) | Grid, Speichern, Import/Export | | [05_action_dialog.md](05_action_dialog.md) | Key-Capture, Action-Auswahl, LED-Einstellungen | | [06_config_json.md](06_config_json.md) | JSON-Format und Grenzen | +| [07_known_limitations.md](07_known_limitations.md) | Bewusst offene GUI-, Protokoll- und Testgrenzen | ## Schnellreferenz @@ -23,5 +24,6 @@ gegen `../../VersaMCU` geprüft werden. - DTR und Connect-Pfad -> [00_architecture.md](00_architecture.md), [01_serial_manager.md](01_serial_manager.md) - Makro-Slot-Konvention -> [02_device_config.md](02_device_config.md) - Host-Command-Status -> [03_tray_app.md](03_tray_app.md) +- bekannte Einschränkungen -> [07_known_limitations.md](07_known_limitations.md) - automatisierte Binär-/Transferverträge -> - `../tests/VersaGUI.ContractTests/` + [`../tests/VersaGUI.ContractTests/`](../tests/VersaGUI.ContractTests/) diff --git a/src/ActionDialog.cs b/src/ActionDialog.cs index 563579f..9048a11 100644 --- a/src/ActionDialog.cs +++ b/src/ActionDialog.cs @@ -5,9 +5,12 @@ // HID Tastatur → Capture-Button (Klicken + Taste drücken) + Modifier-Checkboxen // HID Consumer → Dropdown mit benannten Medien-/Lautstärke-Aktionen // Host Command → Zahlen-Eingabe (Command-ID) +// Makro → acht HID-Key-Steps +// Profilwechsel → Zielprofil oder zyklisch nächstes Profil // Keine Aktion → nichts // -// Optional (nur MX-Buttons): LED-Basisfarbe + LED-Animation + Geschwindigkeit +// Optional (nur MX-Buttons): LED-Basisfarbe + vier auswählbare Animationen + +// Geschwindigkeit. Das Binärmodell akzeptiert zusätzlich drei Animationen. namespace VersaGUI; @@ -405,7 +408,7 @@ public class ActionDialog : Form var macroHint = new Label { - Text = "Klicke einen Step, dann Taste drücken. Leere Steps werden übersprungen.", + Text = "Klicke einen Step, dann Taste drücken. Der erste leere Step beendet das Makro.", Location = new Point(0, 0), Size = new Size(396, 28), ForeColor = SystemColors.GrayText, diff --git a/src/ConfigForm.cs b/src/ConfigForm.cs index 77833c3..81fe439 100644 --- a/src/ConfigForm.cs +++ b/src/ConfigForm.cs @@ -2,6 +2,7 @@ // Hauptfenster der VersaPad-Konfiguration. // // Layout: +// Profil-Auswahl (Profil 1–3) // ┌── Tasten (4 × 5 Grid) ──────────────────────────────────────────────┐ // │ Jede Taste = Button mit Hintergrundfarbe (= LED-Base) und │ // │ Aktionsbeschriftung. Klick öffnet ActionDialog. │ @@ -9,7 +10,7 @@ // ┌── Encoder ─────────────────────────────────────────────────────────┐ // │ 4 Zeilen à [SW][CW][CCW] – ohne LED-Farbe │ // └────────────────────────────────────────────────────────────────────┘ -// [Auf Board speichern] [Schließen] +// [Auf Board speichern] [Ping] [Exportieren] [Importieren] [Schließen] // // "Auf Board speichern" sendet Config und Makros in 6-Byte-Chunks über CDC. // Die Schaltfläche ist deaktiviert, wenn das Board nicht verbunden ist. @@ -287,7 +288,7 @@ public class ConfigForm : Form _saveBtn.Enabled = false; _saveBtn.Text = "Wird gesendet..."; - // SendConfig + SendMacros blockieren ~400ms → Background-Thread + // SendConfig + SendMacros warten auf ACK/NACK → Background-Thread Task.Run(() => { bool cfgOk = _serial.SendConfig(_config); // wartet intern auf CONFIG_ACK/NACK @@ -318,6 +319,9 @@ public class ConfigForm : Form // Alle Buttons neu zeichnen (z.B. nach Config-Laden vom Board) public void RefreshAll() { + if (_profileCombo.SelectedIndex != _config.ActiveProfileIndex) + _profileCombo.SelectedIndex = _config.ActiveProfileIndex; + for (int i = 0; i < 20; i++) RefreshMxButton(i); for (int enc = 0; enc < 4; enc++) for (int act = 0; act < 3; act++) diff --git a/src/DeviceConfig.cs b/src/DeviceConfig.cs index e39cdc3..3191848 100644 --- a/src/DeviceConfig.cs +++ b/src/DeviceConfig.cs @@ -39,7 +39,7 @@ public enum ActionType : byte HidConsumer = 2, // data = HID Consumer Usage ID HostCommand = 3, // data = Command-ID für ein sicheres App-Mapping Macro = 4, // data = Makro-Slot-Index (0–31) - ProfileSwitch = 5, // data = Profil 0–2 oder 0xFFFF für nächstes Profil + ProfileSwitch = 5, // data = Profil 0–2 oder 0x00FF/0xFFFF für nächstes Profil } // Ein Step in einem Makro (keycode=0 → leerer/letzter Step) diff --git a/src/Protocol.cs b/src/Protocol.cs index 797f35d..406b81a 100644 --- a/src/Protocol.cs +++ b/src/Protocol.cs @@ -39,10 +39,10 @@ public static class Protocol // ── Events: Board → App (0x81–0xFF) ────────────────────────────────────── // Host-Events übertragen die Command-ID little-endian in Data[2..3]. - public const byte EvtKeyDown = 0x81; // key_id → Button gedrückt - public const byte EvtKeyUp = 0x82; // key_id → Button losgelassen - public const byte EvtEncCw = 0x83; // enc_id → Schritt CW - public const byte EvtEncCcw = 0x84; // enc_id → Schritt CCW + public const byte EvtKeyDown = 0x81; // Host-Action: key_id → gedrückt + public const byte EvtKeyUp = 0x82; // Host-Action: key_id → losgelassen + public const byte EvtEncCw = 0x83; // Host-Action: enc_id → Schritt CW + public const byte EvtEncCcw = 0x84; // Host-Action: enc_id → Schritt CCW public const byte EvtPong = 0x85; // Antwort auf CmdPing public const byte EvtConfigAck = 0x90; // Config erfolgreich in NVM geschrieben public const byte EvtConfigNack = 0x91; // Config CRC/Magic ungültig