3.5 KiB
3.5 KiB
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
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) |