Refresh GUI documentation and profile synchronization
This commit is contained in:
@@ -16,7 +16,8 @@ als Referenz oder Ziel verwendet werden.
|
|||||||
1. `README.md`
|
1. `README.md`
|
||||||
2. `doc/INDEX.md`
|
2. `doc/INDEX.md`
|
||||||
3. `doc/00_architecture.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/Protocol.cs`
|
||||||
- `src/DeviceConfig.cs`
|
- `src/DeviceConfig.cs`
|
||||||
- `../VersaMCU/AGENTS.md`
|
- `../VersaMCU/AGENTS.md`
|
||||||
@@ -33,6 +34,10 @@ als Referenz oder Ziel verwendet werden.
|
|||||||
bauen und getrennt committen.
|
bauen und getrennt committen.
|
||||||
- Host-Command-IDs niemals ungeprüft als Shellkommando ausführen. Eine spätere
|
- Host-Command-IDs niemals ungeprüft als Shellkommando ausführen. Eine spätere
|
||||||
Host-Aktionsfunktion benötigt ein explizites sicheres Mapping.
|
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
|
## Threading und Transfers
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ nicht zum aktuellen Scope.
|
|||||||
## Voraussetzungen
|
## Voraussetzungen
|
||||||
|
|
||||||
- Windows 10/11
|
- Windows 10/11
|
||||||
- .NET SDK
|
- .NET 7 SDK mit Windows-Desktop-Unterstützung
|
||||||
- geflashte `VersaMCU`-Firmware
|
- geflashte `VersaMCU`-Firmware
|
||||||
- Board per USB als CDC-Device verbunden
|
- Board per USB als Composite Device (Keyboard-HID, Consumer-HID und CDC)
|
||||||
|
verbunden
|
||||||
|
|
||||||
## Starten
|
## Starten
|
||||||
|
|
||||||
@@ -27,7 +28,9 @@ dotnet run --project src/VersaGUI.csproj
|
|||||||
- liest beim Verbinden zuerst die Config und danach die Makros
|
- liest beim Verbinden zuerst die Config und danach die Makros
|
||||||
- validiert Chunkzahl, eindeutige Indizes, Vollständigkeit, CRC und Feldwerte
|
- validiert Chunkzahl, eindeutige Indizes, Vollständigkeit, CRC und Feldwerte
|
||||||
- zeigt MX-Buttons und Encoder-Aktionen an
|
- 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
|
- schreibt Config und Makros getrennt, aber in einem UI-Vorgang auf das Board
|
||||||
|
- sendet einen Protokoll-Ping und zeigt die Antwort an
|
||||||
|
|
||||||
## Aktueller Datenstand
|
## Aktueller Datenstand
|
||||||
|
|
||||||
@@ -40,6 +43,10 @@ dotnet run --project src/VersaGUI.csproj
|
|||||||
- globale Helligkeit
|
- globale Helligkeit
|
||||||
- per-LED-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
|
### MacroTable
|
||||||
|
|
||||||
- 32 Slots
|
- 32 Slots
|
||||||
@@ -73,7 +80,8 @@ wait for ACK/NACK
|
|||||||
|
|
||||||
- HID-Key-Zuweisung inklusive Modifier
|
- HID-Key-Zuweisung inklusive Modifier
|
||||||
- Consumer-Keys
|
- Consumer-Keys
|
||||||
- Host-Commands
|
- Host-Command-IDs konfigurieren und empfangen; Desktop-Aktionen werden noch
|
||||||
|
nicht ausgeführt
|
||||||
- Makros mit bis zu 8 Steps
|
- Makros mit bis zu 8 Steps
|
||||||
- Profilwechsel als ActionType
|
- Profilwechsel als ActionType
|
||||||
- LED-Farbe, Animation und Periode pro MX-Button
|
- LED-Farbe, Animation und Periode pro MX-Button
|
||||||
@@ -90,6 +98,7 @@ VersaGUI/
|
|||||||
|-- Program.cs
|
|-- Program.cs
|
||||||
|-- TrayApp.cs
|
|-- TrayApp.cs
|
||||||
|-- SerialManager.cs
|
|-- SerialManager.cs
|
||||||
|
|-- ChunkTransferBuffer.cs
|
||||||
|-- DeviceConfig.cs
|
|-- DeviceConfig.cs
|
||||||
|-- ConfigForm.cs
|
|-- ConfigForm.cs
|
||||||
|-- ActionDialog.cs
|
|-- ActionDialog.cs
|
||||||
@@ -109,13 +118,21 @@ dotnet run --project tests/VersaGUI.ContractTests/VersaGUI.ContractTests.csproj
|
|||||||
- die App ist Windows-only
|
- die App ist Windows-only
|
||||||
- Host-Command-Events enthalten Command-ID, Key-/Encoder-ID und Richtung; ein
|
- Host-Command-Events enthalten Command-ID, Key-/Encoder-ID und Richtung; ein
|
||||||
sicheres Mapping auf konkrete Desktop-Aktionen ist noch nicht implementiert
|
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
|
## Einstieg für Coding-LLMs
|
||||||
|
|
||||||
Repository-Anweisungen, gemeinsame Binärverträge und Verifikation stehen in
|
Repository-Anweisungen, gemeinsame Binärverträge und Verifikation stehen in
|
||||||
[`AGENTS.md`](AGENTS.md).
|
[`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)
|
- [../VersaMCU/doc/07_serial_protocol.md](../VersaMCU/doc/07_serial_protocol.md)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `TrayApp` | `ApplicationContext`; hält Tray-Icon, öffnet ConfigForm, verarbeitet Board-Events |
|
| `TrayApp` | `ApplicationContext`; hält Tray-Icon, öffnet ConfigForm, verarbeitet Board-Events |
|
||||||
| `SerialManager` | Verbindungsverwaltung, WMI-Erkennung, Lese-Thread, Sende-Methoden |
|
| `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 |
|
| `ActionDialog` | Modaler Dialog zum Bearbeiten einer Aktion + LED-Einstellungen |
|
||||||
| `DeviceConfig` | C#-Spiegel von `SDeviceConfig`; Validierung und Serialisierung (740 B) |
|
| `DeviceConfig` | C#-Spiegel von `SDeviceConfig`; Validierung und Serialisierung (740 B) |
|
||||||
| `MacroTable` | C#-Spiegel von `SMacroTable`; Validierung und Serialisierung (512 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
|
UI-Thread : TrayApp, ConfigForm, ActionDialog, alle WinForms-Controls
|
||||||
BG-Thread : SerialManager.ReadLoop() – blockiert auf ReadByte()
|
BG-Thread : SerialManager.ReadLoop() – blockiert auf ReadByte()
|
||||||
Timer-Thread : SerialManager._reconnectTimer → TryConnect() alle 3 s
|
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.
|
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.
|
gemeinsamen Transfer-Lock; einzelne Pakete unter einem Write-Lock.
|
||||||
- **Host-Commands**: Pakete sind vollständig definiert, die Ausführung einer
|
- **Host-Commands**: Pakete sind vollständig definiert, die Ausführung einer
|
||||||
Command-ID als Desktop-Aktion ist bewusst noch nicht implementiert.
|
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).
|
||||||
|
|||||||
@@ -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 |
|
| `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 |
|
| `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
|
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
|
einen Transfer-Lock, prüfen jeden `Send()`-Rückgabewert und brechen bei
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Sie muessen deshalb synchron zu `VersaMCU/src/config/nvm_config.h` und `macro_co
|
|||||||
| `ActiveProfileIndex` | aktives Profil 0..2 |
|
| `ActiveProfileIndex` | aktives Profil 0..2 |
|
||||||
| `GlobalBrightness` | globale LED-Helligkeit |
|
| `GlobalBrightness` | globale LED-Helligkeit |
|
||||||
| `Profiles[3]` | komplette Profil-Daten |
|
| `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:
|
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
|
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
|
## CRC
|
||||||
|
|
||||||
CRC16-CCITT:
|
CRC16-CCITT:
|
||||||
@@ -109,10 +136,20 @@ Ein Step besteht aus:
|
|||||||
- `keycode`
|
- `keycode`
|
||||||
- `modifier`
|
- `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.
|
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
|
Beim Transfer prüft die GUI trotzdem exakte Größe und alle HID-Keycodes; die
|
||||||
Firmware prüft zusätzlich die vollständige Chunkmenge.
|
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
|
## Wichtig fuer Aenderungen
|
||||||
|
|
||||||
Wenn sich Firmware-Layout, Magic, Version, Profilzahl oder Makrogroesse aendern, muessen mindestens diese Stellen zusammen angepasst werden:
|
Wenn sich Firmware-Layout, Magic, Version, Profilzahl oder Makrogroesse aendern, muessen mindestens diese Stellen zusammen angepasst werden:
|
||||||
|
|||||||
+10
-4
@@ -38,8 +38,11 @@ Icon und Tooltip spiegeln den Verbindungsstatus:
|
|||||||
| `EvtMacroAck` | `serial.SignalMacroAck()` — gibt SendMacros()-Thread frei |
|
| `EvtMacroAck` | `serial.SignalMacroAck()` — gibt SendMacros()-Thread frei |
|
||||||
| `EvtMacroNack` | `serial.SignalMacroNack()` — gibt SendMacros()-Thread frei (Fehler) |
|
| `EvtMacroNack` | `serial.SignalMacroNack()` — gibt SendMacros()-Thread frei (Fehler) |
|
||||||
| `EvtPong` | MessageBox "Ping OK" |
|
| `EvtPong` | MessageBox "Ping OK" |
|
||||||
| `EvtKeyDown/Up` | Command-ID in Byte 2/3 empfangen |
|
| `EvtKeyDown/Up` | Host-Command-ID in Byte 2/3 empfangen |
|
||||||
| `EvtEncCw/Ccw` | Command-ID und Encoderrichtung 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.
|
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
|
Icon + Text + Menü-Item aktualisieren
|
||||||
```
|
```
|
||||||
|
|
||||||
Ein unvollständiger oder ungültiger Dump wird einmal wiederholt. Schlägt auch
|
Ein mit `END` abgeschlossener, aber unvollständiger oder ungültiger Dump wird
|
||||||
der zweite Versuch fehl, zeigt das Tray-Icon eine Warnung.
|
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
|
## Offene Punkte
|
||||||
|
|
||||||
|
|||||||
+19
-5
@@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
## Verantwortung
|
## 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
|
## Layout
|
||||||
|
|
||||||
@@ -45,15 +49,23 @@ Entspricht `key_id - 5` in der Firmware. Im TableLayoutPanel: Spalte=col, Zeile=
|
|||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
Task.Run(() => {
|
Task.Run(() => {
|
||||||
_serial.SendConfig(_config); // ~300 ms
|
bool cfgOk = _serial.SendConfig(_config); // wartet auf ACK/NACK
|
||||||
Thread.Sleep(50);
|
bool macroOk = _serial.SendMacros(_macros);
|
||||||
_serial.SendMacros(_macros); // ~250 ms
|
|
||||||
InvokeOnUi(() => { /* Button-Text + Enabled zurücksetzen */ });
|
InvokeOnUi(() => { /* Button-Text + Enabled zurücksetzen */ });
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Save-Button wird während der Übertragung deaktiviert, Text wechselt zu "Wird gesendet...".
|
Save-Button wird während der Übertragung deaktiviert, Text wechselt zu "Wird gesendet...".
|
||||||
Save-Button ist nur aktiviert wenn Board verbunden (`_serial.IsConnected`).
|
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
|
## Import / Export
|
||||||
|
|
||||||
@@ -64,7 +76,9 @@ Fehler (IO, JSON-Parse, falsche Version) werden per `MessageBox` angezeigt.
|
|||||||
|
|
||||||
## RefreshAll
|
## 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)
|
## Extensions (in derselben Datei)
|
||||||
|
|
||||||
|
|||||||
+19
-5
@@ -39,8 +39,9 @@ LED-Panels (`_colorPanel`, `_animPanel`) erscheinen zusätzlich wenn `showColor=
|
|||||||
// Index 2: "Profil 2" → Data = 1
|
// Index 2: "Profil 2" → Data = 1
|
||||||
// Index 3: "Profil 3" → Data = 2
|
// Index 3: "Profil 3" → Data = 2
|
||||||
|
|
||||||
// Initialbelegung:
|
// Initialbelegung: beide von der Firmware akzeptierten Zykluswerte erkennen
|
||||||
_profileCombo.SelectedIndex = action.Data == 0xFFFF ? 0 : action.Data + 1;
|
_profileCombo.SelectedIndex =
|
||||||
|
action.Data is 0x00FF or 0xFFFF ? 0 : action.Data + 1;
|
||||||
|
|
||||||
// In OnOk():
|
// In OnOk():
|
||||||
data = _profileCombo.SelectedIndex == 0
|
data = _profileCombo.SelectedIndex == 0
|
||||||
@@ -48,7 +49,9 @@ data = _profileCombo.SelectedIndex == 0
|
|||||||
: (ushort)(_profileCombo.SelectedIndex - 1);
|
: (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)
|
## Tasten-Capture (HID-Modus)
|
||||||
|
|
||||||
@@ -62,7 +65,10 @@ WinForms behandelt Pfeil- und Enter-Tasten als "Dialog Keys" in `ProcessDialogKe
|
|||||||
|
|
||||||
## Makro-Capture
|
## 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)
|
## 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:
|
Nur wenn `showColor=true` (MX-Buttons). Enthält:
|
||||||
- **Farbpicker**: `ColorDialog` → `_colorBtn.BackColor`
|
- **Farbpicker**: `ColorDialog` → `_colorBtn.BackColor`
|
||||||
- **Animations-Dropdown**: Statisch / Blinken / Pulsieren / Regenbogen
|
- **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).
|
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
@@ -14,7 +14,14 @@ Menschenlesbares JSON mit `System.Text.Json` (`WriteIndented=true`, Enums als St
|
|||||||
{
|
{
|
||||||
"index": 0,
|
"index": 0,
|
||||||
"action": { "type": "HidKey", "data": 260 },
|
"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
|
## 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
|
## Deserialisierung
|
||||||
|
|
||||||
@@ -47,6 +56,9 @@ Menschenlesbares JSON mit `System.Text.Json` (`WriteIndented=true`, Enums als St
|
|||||||
## Anmerkungen
|
## Anmerkungen
|
||||||
|
|
||||||
- `MacroTable` wird **nicht** exportiert (kein JSON-Format für Makros definiert)
|
- `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)`)
|
- `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
|
- Die Datei ist kein Binärformat und kann manuell bearbeitet werden
|
||||||
- Ein ungültiger Wert wird vor dem Anwenden mit `InvalidDataException`
|
- Ein ungültiger Wert wird vor dem Anwenden mit `InvalidDataException`
|
||||||
|
|||||||
@@ -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
@@ -15,6 +15,7 @@ gegen `../../VersaMCU` geprüft werden.
|
|||||||
| [04_config_form.md](04_config_form.md) | Grid, Speichern, Import/Export |
|
| [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 |
|
| [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 |
|
| [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
|
## 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)
|
- 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)
|
- Makro-Slot-Konvention -> [02_device_config.md](02_device_config.md)
|
||||||
- Host-Command-Status -> [03_tray_app.md](03_tray_app.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 ->
|
- automatisierte Binär-/Transferverträge ->
|
||||||
`../tests/VersaGUI.ContractTests/`
|
[`../tests/VersaGUI.ContractTests/`](../tests/VersaGUI.ContractTests/)
|
||||||
|
|||||||
+5
-2
@@ -5,9 +5,12 @@
|
|||||||
// HID Tastatur → Capture-Button (Klicken + Taste drücken) + Modifier-Checkboxen
|
// HID Tastatur → Capture-Button (Klicken + Taste drücken) + Modifier-Checkboxen
|
||||||
// HID Consumer → Dropdown mit benannten Medien-/Lautstärke-Aktionen
|
// HID Consumer → Dropdown mit benannten Medien-/Lautstärke-Aktionen
|
||||||
// Host Command → Zahlen-Eingabe (Command-ID)
|
// Host Command → Zahlen-Eingabe (Command-ID)
|
||||||
|
// Makro → acht HID-Key-Steps
|
||||||
|
// Profilwechsel → Zielprofil oder zyklisch nächstes Profil
|
||||||
// Keine Aktion → nichts
|
// 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;
|
namespace VersaGUI;
|
||||||
|
|
||||||
@@ -405,7 +408,7 @@ public class ActionDialog : Form
|
|||||||
|
|
||||||
var macroHint = new Label
|
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),
|
Location = new Point(0, 0),
|
||||||
Size = new Size(396, 28),
|
Size = new Size(396, 28),
|
||||||
ForeColor = SystemColors.GrayText,
|
ForeColor = SystemColors.GrayText,
|
||||||
|
|||||||
+6
-2
@@ -2,6 +2,7 @@
|
|||||||
// Hauptfenster der VersaPad-Konfiguration.
|
// Hauptfenster der VersaPad-Konfiguration.
|
||||||
//
|
//
|
||||||
// Layout:
|
// Layout:
|
||||||
|
// Profil-Auswahl (Profil 1–3)
|
||||||
// ┌── Tasten (4 × 5 Grid) ──────────────────────────────────────────────┐
|
// ┌── Tasten (4 × 5 Grid) ──────────────────────────────────────────────┐
|
||||||
// │ Jede Taste = Button mit Hintergrundfarbe (= LED-Base) und │
|
// │ Jede Taste = Button mit Hintergrundfarbe (= LED-Base) und │
|
||||||
// │ Aktionsbeschriftung. Klick öffnet ActionDialog. │
|
// │ Aktionsbeschriftung. Klick öffnet ActionDialog. │
|
||||||
@@ -9,7 +10,7 @@
|
|||||||
// ┌── Encoder ─────────────────────────────────────────────────────────┐
|
// ┌── Encoder ─────────────────────────────────────────────────────────┐
|
||||||
// │ 4 Zeilen à [SW][CW][CCW] – ohne LED-Farbe │
|
// │ 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.
|
// "Auf Board speichern" sendet Config und Makros in 6-Byte-Chunks über CDC.
|
||||||
// Die Schaltfläche ist deaktiviert, wenn das Board nicht verbunden ist.
|
// Die Schaltfläche ist deaktiviert, wenn das Board nicht verbunden ist.
|
||||||
@@ -287,7 +288,7 @@ public class ConfigForm : Form
|
|||||||
_saveBtn.Enabled = false;
|
_saveBtn.Enabled = false;
|
||||||
_saveBtn.Text = "Wird gesendet...";
|
_saveBtn.Text = "Wird gesendet...";
|
||||||
|
|
||||||
// SendConfig + SendMacros blockieren ~400ms → Background-Thread
|
// SendConfig + SendMacros warten auf ACK/NACK → Background-Thread
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
bool cfgOk = _serial.SendConfig(_config); // wartet intern auf CONFIG_ACK/NACK
|
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)
|
// Alle Buttons neu zeichnen (z.B. nach Config-Laden vom Board)
|
||||||
public void RefreshAll()
|
public void RefreshAll()
|
||||||
{
|
{
|
||||||
|
if (_profileCombo.SelectedIndex != _config.ActiveProfileIndex)
|
||||||
|
_profileCombo.SelectedIndex = _config.ActiveProfileIndex;
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++) RefreshMxButton(i);
|
for (int i = 0; i < 20; i++) RefreshMxButton(i);
|
||||||
for (int enc = 0; enc < 4; enc++)
|
for (int enc = 0; enc < 4; enc++)
|
||||||
for (int act = 0; act < 3; act++)
|
for (int act = 0; act < 3; act++)
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ public enum ActionType : byte
|
|||||||
HidConsumer = 2, // data = HID Consumer Usage ID
|
HidConsumer = 2, // data = HID Consumer Usage ID
|
||||||
HostCommand = 3, // data = Command-ID für ein sicheres App-Mapping
|
HostCommand = 3, // data = Command-ID für ein sicheres App-Mapping
|
||||||
Macro = 4, // data = Makro-Slot-Index (0–31)
|
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)
|
// Ein Step in einem Makro (keycode=0 → leerer/letzter Step)
|
||||||
|
|||||||
+4
-4
@@ -39,10 +39,10 @@ public static class Protocol
|
|||||||
|
|
||||||
// ── Events: Board → App (0x81–0xFF) ──────────────────────────────────────
|
// ── Events: Board → App (0x81–0xFF) ──────────────────────────────────────
|
||||||
// Host-Events übertragen die Command-ID little-endian in Data[2..3].
|
// 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 EvtKeyDown = 0x81; // Host-Action: key_id → gedrückt
|
||||||
public const byte EvtKeyUp = 0x82; // key_id → Button losgelassen
|
public const byte EvtKeyUp = 0x82; // Host-Action: key_id → losgelassen
|
||||||
public const byte EvtEncCw = 0x83; // enc_id → Schritt CW
|
public const byte EvtEncCw = 0x83; // Host-Action: enc_id → Schritt CW
|
||||||
public const byte EvtEncCcw = 0x84; // enc_id → Schritt CCW
|
public const byte EvtEncCcw = 0x84; // Host-Action: enc_id → Schritt CCW
|
||||||
public const byte EvtPong = 0x85; // Antwort auf CmdPing
|
public const byte EvtPong = 0x85; // Antwort auf CmdPing
|
||||||
public const byte EvtConfigAck = 0x90; // Config erfolgreich in NVM geschrieben
|
public const byte EvtConfigAck = 0x90; // Config erfolgreich in NVM geschrieben
|
||||||
public const byte EvtConfigNack = 0x91; // Config CRC/Magic ungültig
|
public const byte EvtConfigNack = 0x91; // Config CRC/Magic ungültig
|
||||||
|
|||||||
Reference in New Issue
Block a user