VersaGUI/doc/01_serial_manager.md

96 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SerialManager
**Datei:** `SerialManager.cs`
## Verantwortung
- COM-Port-Erkennung per WMI (VID/PID)
- Verbindungsaufbau und automatischer Reconnect
- Hintergrund-Lese-Thread mit Paket-Assembler
- Sende-Methoden für alle Protokoll-Operationen
- ACK/NACK-Synchronisation zwischen ReadLoop-Thread und `SendConfig`/`SendMacros`-Task
## COM-Port-Erkennung
```csharp
VidPid = "VID_239A&PID_0042" // muss mit platformio.ini übereinstimmen
SELECT Name, DeviceID FROM Win32_PnPEntity WHERE DeviceID LIKE '%VID_239A&PID_0042%'
// Name enthält z.B. "USB Serial Device (COM3)" → extrahiert "COM3"
```
Erkennt das Board auch bei wechselnder COM-Nummer. Funktioniert auch nach USB-Reinitialisierung (Kabel abziehen/stecken nach SWD-Flash).
## Verbindungsaufbau (TryConnect)
```
Monitor.TryEnter(_connectLock) nur ein gleichzeitiger Versuch
FindPort() per WMI
SerialPort erstellen:
ReadTimeout = 500 ms
WriteTimeout = 500 ms
DtrEnable = true ← VOR Open() setzen! (SAMD21 CDC-Constraint)
port.Open()
Thread.Sleep(200) CDC stabilisieren
ReadLoop-Thread starten
Connected-Event → UI-Thread
```
**Reconnect-Timer**: alle 3 s, Start nach 500 ms. 5 s Backoff (`_waitingAfterDisconnect`) nach Verbindungsverlust damit Board vollständig re-enumerieren kann.
## ReadLoop (Hintergrund-Thread)
```
while (port.IsOpen):
b = port.ReadByte() blockiert 500 ms (ReadTimeout)
buf[bufFill++] = b
if bufFill < 8: continue
→ SerialPacket fertig → SynchronizationContext.Post → PacketReceived auf UI-Thread
bufFill = 0
Bei IOException:
if port.IsOpen: ignorieren ← .NET 7: IOException = normaler Timeout (kein Fehler!)
else: break → Disconnect
```
## ACK/NACK-Synchronisation
`SendConfig` und `SendMacros` blockieren nach COMMIT auf ein `SemaphoreSlim`-Gate bis das Board antwortet:
```
_configAckGate / _macroAckGate SemaphoreSlim(0, 1)
_configAckOk / _macroAckOk volatile bool (true = ACK, false = NACK)
```
| Methode | Aufruf durch | Wirkung |
|---|---|---|
| `SignalConfigAck()` | TrayApp bei EvtConfigAck | `_configAckOk = true`, Gate freigeben |
| `SignalConfigNack()` | TrayApp bei EvtConfigNack | `_configAckOk = false`, Gate freigeben |
| `SignalMacroAck()` | TrayApp bei EvtMacroAck | `_macroAckOk = true`, Gate freigeben |
| `SignalMacroNack()` | TrayApp bei EvtMacroNack | `_macroAckOk = false`, Gate freigeben |
`SendConfig()` und `SendMacros()` geben `bool` zurück (`true` = ACK erhalten, `false` = NACK oder Timeout nach 3 s).
## Sende-Methoden
| Methode | Rückgabe | Funktion |
|---|---|---|
| `Send(pkt)` | void | Rohe 8-Byte-Übertragung (fire-and-forget) |
| `SetLedOverride(keyId, r, g, b)` | void | CMD 0x01 |
| `ClearLedOverride(keyId)` | void | CMD 0x02 |
| `SetLedBase(keyId, r, g, b)` | void | CMD 0x03 |
| `RequestConfig()` | void | CMD 0x13 Board antwortet mit Config-Dump |
| `RequestMacros()` | void | CMD 0x23 Board antwortet mit Makro-Dump |
| `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.
## Events
| Event | Gefeuert wenn |
|---|---|
| `Connected` | Verbindung erfolgreich hergestellt (auf UI-Thread) |
| `Disconnected` | Verbindung verloren (auf UI-Thread) |
| `PacketReceived` | Vollständiges 8-Byte-Paket empfangen (auf UI-Thread) |