# VersaGUI – Architektur-Übersicht ## Technologie-Stack | Merkmal | Wert | |---|---| | Sprache | C# / .NET 7 | | UI-Framework | WinForms (`[STAThread]`) | | Einstiegspunkt | `Program.cs` → `Application.Run(new TrayApp())` | | Laufzeitmodell | `ApplicationContext` (kein `Form` als Hauptfenster) | | Threading | UI-Thread + 1 Hintergrund-Lese-Thread + Timer-Thread | ## Komponentenübersicht | Datei | Verantwortung | |---|---| | `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 | | `ActionDialog` | Modaler Dialog zum Bearbeiten einer Aktion + LED-Einstellungen | | `DeviceConfig` | C#-Spiegel von `SDeviceConfig`; Serialisierung/Deserialisierung (223 B) | | `MacroTable` | C#-Spiegel von `SMacroTable`; Serialisierung/Deserialisierung (256 B) | | `ConfigJson` | JSON-Import/Export für `DeviceConfig` | | `Protocol` | Konstanten für alle Command/Event-IDs (spiegelt `usb_serial.h`) | ## Datenfluss ``` Board → SerialManager (ReadLoop, BG-Thread) → SynchronizationContext.Post (→ UI-Thread) → TrayApp.OnPacket() ├── Config-Dump: _rxConfigBuf aufbauen → DeviceConfig.FromBytes() ├── Makro-Dump: _rxMacroBuf aufbauen → MacroTable.FromBytes() └── HOST_COMMAND-Events: (TODO: Aktion ausführen) Benutzer → ConfigForm → ActionDialog → DeviceConfig / MacroTable (in-memory ändern) → SerialManager.SendConfig() + SendMacros() (BG-Task) → Board (chunked, 6 B/Paket) ``` ## Threading-Modell ``` 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) ``` Alle Board-Events werden per `SynchronizationContext.Post` auf den UI-Thread gepostet. Controls dürfen nie vom BG-Thread angefasst werden. ## Verbindungslebenszyklus ``` Start → Timer feuert → TryConnect() → WMI-Suche (VID 0x239A / PID 0x0042) → SerialPort öffnen (DtrEnable=true VOR Open()!) → 200 ms warten → ReadLoop starten → Connected-Event → RequestConfig() + RequestMacros() Disconnect → ReadLoop bricht ab → Disconnected-Event → 5 s Backoff → Timer läuft weiter ``` ## Invarianten / Constraints - **DtrEnable=true muss VOR `Open()` gesetzt werden**: SAMD21 prüft DTR für `usb_serial_send()`. Der Default-Wert false würde alle Board→PC-Antworten still verwerfen. - **IOException ≠ Disconnect**: .NET 7 wirft `IOException` statt `TimeoutException` bei `ReadByte()`-Timeout. Nur als echten Fehler behandeln wenn `_port.IsOpen == false`. - **Packed-kompatible Serialisierung**: `DeviceConfig.ToBytes()` muss exakt 223 B in derselben Reihenfolge wie `SDeviceConfig` (packed C++) erzeugen. Jede Änderung am Firmware-Layout muss hier gespiegelt werden. - **Config-Version**: `DeviceConfig.Version == 2`. `FromBytes()` prüft Magic + Version + CRC; schlägt einer fehl → Methode gibt `false` zurück, Config bleibt unverändert. - **Debug-Log**: `versapad_rx.txt` in `%TEMP%` – vor Release-Nutzung entfernen (TODO).