Refresh GUI documentation and profile synchronization

This commit is contained in:
2026-07-24 10:35:47 +02:00
parent 08db94bb6b
commit 7d381218f3
15 changed files with 226 additions and 35 deletions
+6 -1
View File
@@ -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
+22 -5
View File
@@ -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)
+7 -2
View File
@@ -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).
+4 -1
View File
@@ -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
+37
View File
@@ -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:
+10 -4
View File
@@ -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
+19 -5
View File
@@ -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)
+19 -5
View File
@@ -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` (07, -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` (07,
-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).
+14 -2
View File
@@ -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`
+69
View File
@@ -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.
+3 -1
View File
@@ -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/)
+5 -2
View File
@@ -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,
+6 -2
View File
@@ -2,6 +2,7 @@
// Hauptfenster der VersaPad-Konfiguration.
//
// Layout:
// Profil-Auswahl (Profil 13)
// ┌── 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++)
+1 -1
View File
@@ -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 (031)
ProfileSwitch = 5, // data = Profil 02 oder 0xFFFF für nächstes Profil
ProfileSwitch = 5, // data = Profil 02 oder 0x00FF/0xFFFF für nächstes Profil
}
// Ein Step in einem Makro (keycode=0 → leerer/letzter Step)
+4 -4
View File
@@ -39,10 +39,10 @@ public static class Protocol
// ── Events: Board → App (0x810xFF) ──────────────────────────────────────
// 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