Best Practices für die Prüfung und Anzeige des Zustands externer Geräte — Design jenseits eines einzigen „Verbunden“
· Go Komura · Windows, Externe Geräte, Geräteintegration, Zustandsverwaltung, UI/UX, Überwachung
Industriekameras, Barcode-Leser, SPS, Messgeräte, Drucker, serielle Geräte, USB-Geräte. In Windows-Anwendungen, die mit externen Geräten verbunden sind, entstehen Störfälle sehr häufig nicht durch den eigentlichen Defekt selbst, sondern zuerst dadurch, dass die Zustandsanzeige auf dem Bildschirm von der Realität abweicht.
Zum Beispiel solche Zustände.
- Das OS sieht das Gerät, aber ein anderer Prozess hält es belegt, sodass es nicht genutzt werden kann
openwar erfolgreich, aber Referenzfahrt, Warm-up oder Authentifizierung sind noch nicht abgeschlossen- Das Gerät hängt noch dran, antwortet aber längst nicht mehr
- Der Erfassungs-Thread ist abgestorben, aber der letzte Wert bleibt auf dem Bildschirm stehen
- Es handelt sich um ein unerwartetes Gerät oder eine unerwartete Firmware, trotzdem steht dort einfach „Verbunden“
Was man hier eigentlich wissen möchte, ist nicht nur, ob eine Verbindung besteht. Es ist, was man gerade gefahrlos tun darf.
Zielgruppe und Voraussetzungen dieses Artikels
| Punkt | Inhalt |
|---|---|
| Zielgruppe | Personen, die Windows-Anwendungen entwerfen und implementieren, die mit externen Geräten verbunden sind. Gedacht für alle, die bei einer bestehenden App die Rückmeldungen „der Bildschirm zeigt Verbunden, aber es funktioniert nicht“ verringern möchten |
| Vorausgesetztes Wissen | Die Fähigkeit, in irgendeiner Sprache Anwendungen zu schreiben. Kenntnisse eines bestimmten SDKs oder Gerätetreibers werden nicht vorausgesetzt |
| Vorausgesetzte Umgebung | Es wird von einer Windows-Desktopanwendung ausgegangen. Die Art der Zustandstrennung und der Anzeigegedanke selbst sind jedoch nicht vom Betriebssystem abhängig |
| Nicht behandelt | Die Nutzung einzelner Hersteller-SDK-APIs, die treiberseitige Implementierung |
In diesem Artikel verwendete Begriffe
Begriffe, die im Original auf Englisch stehen, werden hier vorab zeilenweise erklärt. Sie bleiben auch im deutschen Text in ihrer englischen Form stehen.
| Begriff | Bedeutung in einer Zeile |
|---|---|
| PLC | Programmable Logic Controller. Ein Industriecontroller zur Steuerung von Produktionsanlagen |
| firmware | In das Gerät eingebettete Software. Auch bei gleicher Modellnummer kann die Version je nach Exemplar unterschiedlich sein |
| heartbeat | Eine leichte, regelmäßig ausgetauschte Abfrage oder Benachrichtigung zur Lebenszeichenprüfung |
| poll / event | poll ist das Verfahren, bei dem man selbst regelmäßig nachfragt; event ist das Verfahren, bei dem man auf eine Benachrichtigung der Gegenseite wartet |
| stale | Ein Zustand, in dem ein Wert veraltet ist. Er konnte zwar abgerufen werden, aber der aktuell angezeigte Wert ist nicht mehr unbedingt aktuell |
| freshness budget | Die vorab festgelegte Obergrenze: „Wird diese Zeit überschritten, gilt der Wert nicht mehr als aktuell“ |
| flapping | Ein Zustand, der in kurzer Zeit hin- und herspringt. Tritt bei Wackelkontakten oder kurzen Unterbrechungen auf |
| reconcile | Den internen Zustand mit der Realität abgleichen und neu in Übereinstimmung bringen |
| interlock | Ein Sicherheitsmechanismus, der den Betrieb stoppt. Solange er offen ist, bewegt sich das Gerät nicht |
| PnP | Plug and Play. Der Mechanismus, mit dem das OS das Anschließen und Trennen eines Geräts erkennt und konfiguriert |
| RTT | round-trip time. Die Zeit von der Anfrage bis zum Eintreffen der Antwort |
1. Das Wichtigste zuerst
Bei der Prüfung und Anzeige des Zustands externer Geräte wirkt am stärksten: den Zustand nicht auf ein einziges Boolean zusammenzufassen.
Mindestens diese Punkte sollten getrennt gehalten werden.
- Existenz: Ist es für das OS sichtbar?
- Sitzungsaufbau: Hat die eigene App open / login / initialize abgeschlossen?
- Antwortverhalten: Antwortet es auf Heartbeat oder Status-Abfragen?
- Betriebsbereitschaft: Kann die eigentliche Operation gerade entgegengenommen werden?
- Datenaktualität: Sind die angezeigten Werte aktuell?
- Konfigurationsübereinstimmung: Ist es das erwartete Gerät, Modell, die erwartete Firmware?
- Überwachungsintegrität: Lebt der Überwachungsprozess überhaupt noch?
Ziemlich vereinfacht gesagt:
Die Existenzprüfung liegt beim OS, die Nutzbarkeit bei der App, die Aktualitätsbeurteilung beim Bildschirm.
Allein wenn man diese drei nicht vermischt, wird die Zustandsanzeige deutlich stabiler.
flowchart TB
subgraph OS["Was das OS beantworten kann"]
E["Existenz<br/>Ist das interface sichtbar?"]
end
subgraph APP["Was nur die App beantworten kann"]
S["Sitzung<br/>open / login / initialize abgeschlossen?"]
R["Antwortverhalten<br/>Antwortet eine leichte Anfrage fristgerecht?"]
F["Betriebsbereitschaft<br/>Kann jetzt eine Operation entgegengenommen werden?"]
C["Konfigurationsübereinstimmung<br/>Erwartetes Gerät / Modell / Firmware?"]
end
subgraph UIL["Was die Anzeigeseite beantwortet"]
D["Datenaktualität<br/>Ist der angezeigte Wert aktuell?"]
W["Überwachungsintegrität<br/>Lebt der Überwachungsprozess selbst noch?"]
end
E --> S --> R --> F --> D
C -.->|"Stimmt dies nicht,<br/>ist es auch bei allem anderen unbrauchbar"| F
W -.->|"Steht dies still,<br/>veralten alle Beurteilungen"| D
Abbildung 1: Den Zustand nicht auf ein einziges Boolean zusammenfassen, sondern danach trennen, wer die Frage beantworten kann. Selbst wenn die obere Ebene erfüllt ist, heißt das nicht, dass auch die untere erfüllt ist
2. Warum „Verbunden“ gefährlich ist
Das Wort „Verbunden“ trägt in einem einzigen Begriff stillschweigend mehrere Bedeutungen.
Tatsächlich sind darin mindestens diese Fragen vermischt.
- Sieht das OS das interface des Zielgeräts?
- Konnte die eigene App das Gerät öffnen / anmelden / initialisieren?
- Antwortet es fristgerecht auf eine leichte Anfrage?
- Kann die gerade angeforderte Operation gefahrlos ausgeführt werden?
- Ist der auf dem Bildschirm angezeigte Wert aktuell?
- Ist es das erwartete Gerät, Modell, die erwartete Firmware?
Je nachdem, welche dieser sechs Bedingungen erfüllt sind, ändert sich die Bedeutung von „nutzbar“.
Zum Beispiel sind die folgenden vier Zustände alle verschieden.
- Nicht verbunden Das OS hat das Ziel-Interface von vornherein nicht gefunden
- Verbunden / wird geprüft Physisch sichtbar, aber Initialisierung oder Authentifizierung sind noch nicht abgeschlossen
- Verbunden / nicht nutzbar Es antwortet zwar, kann aber wegen warming up, busy, interlock, fehlendem Medium usw. nicht arbeiten
- Wert veraltet Früher konnte abgerufen werden, aber der angezeigte Wert überschreitet das freshness budget
Fasst man all dies zu „Verbunden“ zusammen, kann der Bediener nicht entscheiden, was zu tun ist.
3. Die zuerst zu trennenden Zustände
Empfehlenswert ist: den internen Zustand mehrachsig halten und ihn für die UI bei Bedarf zusammenfassen.
3.1 Die intern zu trennenden Zustandsachsen
| Achse | Was sie bedeutet | Typische Prüfmethode | Beispiel für die UI-Anzeige |
|---|---|---|---|
| Existenz | Ist das Ziel-Interface für das OS sichtbar? | Aufzählung beim Start, arrival-/removal-Benachrichtigung | Nicht verbunden / Verbunden |
| Sitzung | Hat die eigene App open / login / initialize abgeschlossen? | Ergebnis der Handle-/SDK-Initialisierung | Wird geprüft / Wird initialisiert |
| Antwortverhalten | Antwortet es auf Status-Abfrage oder Heartbeat? | Leichte Anfrage mit Timeout | Antwortet / Antwort verzögert / Keine Antwort |
| Betriebsbereitschaft | Ist die eigentliche Operation gerade möglich? | Gerätespezifischer Status | Nutzbar / busy / warming up |
| Datenaktualität | Ist der angezeigte Wert aktuell? | Timestamp / Sequenz | Aktuell / Wert veraltet |
| Konfigurationsübereinstimmung | Stimmt es mit dem erwarteten Gerät überein? | Modell / Seriennummer / Firmware / Profil | Zielgerät / Unerwartetes Gerät |
| Überwachungsintegrität | Lebt der Überwachungspfad der App noch? | Worker-Heartbeat / Loop-Verzögerung | Wird überwacht / Überwachung gestoppt |
Wichtig ist hier, einen schlechten Gerätezustand von einem für die App nicht beobachtbaren Zustand zu trennen.
3.2 Die UI muss nicht alles gleichrangig zeigen
Ein mehrachsiger interner Zustand klingt danach, als würde der Bildschirm unübersichtlich. Aber die UI muss nicht alles mit gleichem Gewicht anzeigen.
Empfehlenswert sind drei Ebenen.
- Oben der Zusammenfassungszustand
- Darunter der Grund
- Bei Bedarf ein Detailpanel
Zum Beispiel:
- Zusammenfassung:
Verbunden / nicht nutzbar - Grund:
Warming upNoch etwa 18 Sekunden - Detail:
ModellSeriennummerFirmwareletzter Heartbeatletzte Frame-Zeit
Trennt man so, lässt sich die Informationsmenge erheblich erhöhen, ohne die Lesbarkeit zu verschlechtern.
Das Grundgerüst des Bildschirms sieht dann etwa so aus.
+-- Kamera vorgelagerter Prozess -----------------------------+
|
| [Zusammenfassung] ! Verbunden / nicht nutzbar
| [Grund] Warming up - noch etwa 18 Sekunden
|
| [Detail] v ausklappen (standardmäßig eingeklappt)
| Modell ACME-CAM-2000
| Seriennummer A1B2C3
| Firmware 2.4.1
| letzter Heartbeat 10:23:41.512 (vor 0,5 s)
| letztes Frame 10:23:41.402 (vor 0,6 s)
|
+---------------------------------------------------------------+
Wichtig ist: je weiter unten, desto weniger Leser dürfen es sein. Die Zusammenfassung liest jeder in einer Sekunde, den Grund liest, wer wissen will, „warum es steht“, und das Detail öffnet nur, wer die Ursache eingrenzt. Mit dieser Prämisse angeordnet, wird der Bildschirm auch bei umfangreicherem Detail nicht unruhig.
Zeigt man umgekehrt Modell oder Seriennummer dauerhaft in derselben Größe wie die Zusammenfassung, geht die wichtigste Zeile darin unter.
4. Best Practices für die Zustandsprüfung
4.1 Aufzählung beim Start und Ankunfts-/Entfernungsbenachrichtigungen
Die Grundlage für den Umgang mit externen Geräten unter Windows ist, beim Start die vorhandenen Geräte aufzuzählen und danach arrival-/removal-Benachrichtigungen zu empfangen.
Besonders wichtig sind diese drei Punkte.
- Benachrichtigungen allein erfassen keine bereits vorhandenen Geräte
- Für runtime communication ist eine interface class natürlicher als eine setup class
- Die Reihenfolge von remove-Benachrichtigung und I/O-Fehler kann sich vertauschen
Als praktische Regel ist es einfach.
- Beim Start aufzählen
- Benachrichtigungen abonnieren
- Bei Erhalt einer Benachrichtigung erneut aufzählen und den internen Zustand abgleichen (reconcile)
4.2 „Existiert“, „lässt sich öffnen“, „antwortet“ und „ist nutzbar“ trennen
Störfälle bei externen Geräten häufen sich, wenn man dies zusammen behandelt.
- Existiert Das interface ist für das OS sichtbar
- Lässt sich öffnen Handle bzw. Sitzung lassen sich ohne Konflikt mit einem anderen Prozess oder Berechtigungsprobleme halten
- Antwortet Es antwortet innerhalb des Timeouts auf eine leichte Anfrage
- Ist nutzbar Die eigentliche Operation kann entgegengenommen werden
Diese vier sind nicht dasselbe.
4.3 event und poll kombinieren
Statt sich vollständig auf eine reine event- oder eine reine poll-Basis festzulegen, ist in der Praxis Erkennung per event, Integritätsprüfung per poll gut handhabbar.
- arrival / removal: event
- heartbeat / status query: poll
- Aktualitätsbeurteilung: timestamp / sequence
Mit dieser Aufteilung lassen sich Verbindungserkennung und tatsächliche Nutzbarkeit leichter trennen.
4.4 Überwachungsprozess und UI trennen
Führt man open / read / status query direkt im UI-Thread aus, vermischen sich Anzeigebelange und Überwachungsbelange leicht.
Empfehlenswert ist folgende Form:
- Ein Überwachungs-Worker aktualisiert einen state store
- Die UI abonniert den state store und rendert
- UI-Bedienungen werden als command an die Überwachungsschicht übergeben
Damit lassen sich „Überwachung gestoppt“ und „Gerät gestoppt“ leichter getrennt behandeln.
Als Grundgerüst in C# geschrieben, sieht das etwa in diesem Umfang aus (unter der Annahme von .NET 8 / C# 12). Der Trick ist eine Einbahnstraße: Nur der Überwachungs-Worker schreibt, die UI liest nur und zeichnet.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
public enum DeviceAvailability
{
Unknown, // Noch kein einziges Mal beobachtet
Absent, // Interface für das OS nicht sichtbar
Initializing, // open / login / initialize läuft
Ready, // Operation kann entgegengenommen werden
Unavailable, // Antwortet, ist aber wegen busy / warming up usw. nicht nutzbar
NotResponding, // Heartbeat antwortet nicht
Mismatched, // Unerwartetes Gerät / Firmware
}
// Unveränderlicher Snapshot für die UI. Als record, damit Unterschiede über Werte-Gleichheit erkannt werden
public sealed record DeviceSnapshot(
string DeviceKey, // Robuster Schlüssel, z. B. die Seriennummer
string DisplayName,
DeviceAvailability Availability,
string Reason, // Grund wie „Warming up“
DateTimeOffset? LastSuccessAt, // Zeitpunkt der letzten erfolgreichen Beobachtung
long Sequence, // Vom Gerät vergebene Sequenznummer
string FirmwareVersion);
public sealed class DeviceStateStore
{
private readonly ConcurrentDictionary<string, DeviceSnapshot> _snapshots = new();
public event Action<DeviceSnapshot>? Changed;
// Wird nur vom Überwachungs-Worker aufgerufen
public void Publish(DeviceSnapshot snapshot)
{
_snapshots.TryGetValue(snapshot.DeviceKey, out var previous);
_snapshots[snapshot.DeviceKey] = snapshot;
// Nur benachrichtigen, wenn sich der Wert geändert hat. Bei jedem poll zu
// benachrichtigen würde die UI unnötig neu rendern
if (previous != snapshot)
{
Changed?.Invoke(snapshot);
}
}
public IReadOnlyList<DeviceSnapshot> Current() => _snapshots.Values.ToList();
}
Die UI-Seite kapselt das Abonnieren und das Zurückwechseln auf den UI-Thread an einer einzigen Stelle.
using System;
using System.Windows.Forms;
public sealed class DeviceStatusPresenter
{
private readonly DeviceStateStore _store;
private readonly Control _uiContext; // Anker für den Wechsel zurück auf den UI-Thread
private readonly Label _summary;
private readonly Label _reason;
public DeviceStatusPresenter(DeviceStateStore store, Control uiContext, Label summary, Label reason)
{
_store = store;
_uiContext = uiContext;
_summary = summary;
_reason = reason;
// Das Abonnement an die Lebensdauer des Bildschirms binden. So verwandelt sich ein
// vergessener Dispose-Aufruf nicht in „es wird weiter an das geschlossene Formular gesendet“
_uiContext.Disposed += (_, _) => Dispose();
_store.Changed += OnChanged; // Vergisst man das Abonnieren, ändert sich der Bildschirm trotz Update nicht
}
public void Dispose() => _store.Changed -= OnChanged;
private void OnChanged(DeviceSnapshot snapshot)
{
// Der Überwachungs-Worker läuft auch weiter, während der Bildschirm geschlossen wird.
// Ein BeginInvoke nach dem Zerstören des Handles wirft eine Ausnahme, und diese
// Ausnahme läuft über Changed?.Invoke zurück zum Überwachungs-Worker.
// Das äußert sich als „beim Schließen des Bildschirms stoppte die Überwachung“
if (_uiContext.IsDisposed || _uiContext.Disposing || !_uiContext.IsHandleCreated)
{
return;
}
try
{
if (_uiContext.InvokeRequired)
{
_uiContext.BeginInvoke(() => Render(snapshot));
return;
}
Render(snapshot);
}
catch (ObjectDisposedException)
{
// Wurde nach der obigen Prüfung geschlossen. Diese Lücke lässt sich prinzipiell
// nicht schließen, daher wird die Anzeige aufgegeben. Die Überwachung wird nicht gestoppt
}
catch (InvalidOperationException)
{
// Handle nicht erstellt / bereits zerstört. Ebenso
}
}
private void Render(DeviceSnapshot snapshot)
{
_summary.Text = snapshot.Availability switch
{
DeviceAvailability.Absent => "Nicht verbunden",
DeviceAvailability.Initializing => "Verbunden / wird geprüft",
DeviceAvailability.Ready => "Nutzbar",
DeviceAvailability.Unavailable => "Verbunden / nicht nutzbar",
DeviceAvailability.NotResponding => "Keine Antwort",
DeviceAvailability.Mismatched => "Unerwartetes Gerät",
_ => "Wird geprüft",
};
_reason.Text = snapshot.Reason;
}
}
Der Moment des Schließens ist bei diesem Aufbau die anfälligste Stelle. Changed?.Invoke(snapshot) ruft den Handler synchron aus dem Thread des Überwachungs-Workers auf. Wird nach dem Schließen des Formulars und dem Zerstören des Control-Handles BeginInvoke aufgerufen, entsteht eine Ausnahme, und diese Ausnahme läuft über Publish zurück zum Überwachungs-Worker. Das Aufräumen des Bildschirms reißt damit die Überwachung selbst mit. Und das Symptom ist „stürzt beim Beenden manchmal ab“ — die Reproduktionsbedingungen sind kaum zu fassen.
Es reicht nicht, das Abonnement nur mit Dispose zu entfernen. Hat der Worker mitten im Abmelden bereits mit Invoke begonnen, lässt sich dieser Aufruf nicht mehr stoppen. Drei Punkte sind entscheidend.
- Das Abonnement an die Lebensdauer des Bildschirms binden. Über
Control.Disposedwird das Abonnement automatisch entfernt, sodass ein vergessenesDisposesich nicht in „sendet weiter an das geschlossene Formular“ verwandelt - Nicht an sich schließende oder bereits geschlossene Controls senden.
IsDisposed/Disposing/IsHandleCreatedprüfen und an dieser Stelle sofort verwerfen - Die verbleibende Lücke mit einer Ausnahme abfangen. Dass zwischen der Prüfung und
BeginInvokegeschlossen wird, lässt sich prinzipiell nicht ausschließen.ObjectDisposedExceptionundInvalidOperationExceptionwerden hier abgefangen, und nur die Anzeige wird aufgegeben. Fängt man sie nicht ab, reißt es die Überwachung mit
Der entscheidende Punkt ist die Festlegung: „Ein Anzeige-Update beim Schließen darf verworfen werden.“ Diese eine Darstellung hat keinen Wert, aber dass der Überwachungs-Worker weiterlebt, hat einen.
4.5 freshness getrennt danach beurteilen, ob Daten ankommen und ob sich der Inhalt weiterentwickelt
Die Beurteilung der Datenaktualität reicht nicht aus, wenn man nur auf den Empfangszeitpunkt schaut. Denn es gibt die Fehlerform, dass der callback vom SDK weiter eintrifft, aber der Timestamp oder die Sequenznummer des Werts stehen bleibt.
Deshalb trennt man die Aktualität des Empfangs von der Aktualität des Inhalts.
using System;
public sealed record Reading(
long Sequence, // Vom Gerät vergebene Sequenznummer
DateTimeOffset ValueTimestamp, // Vom Gerät dem Wert zugewiesener Zeitpunkt
DateTimeOffset ReceivedAt, // Zeitpunkt, zu dem die App empfangen hat. Für die Anzeige
long ReceivedTicks); // Monoton steigender Timestamp desselben Empfangs. Für die Beurteilung
public enum Freshness
{
Fresh,
Stale,
Unknown,
}
public static class FreshnessPolicy
{
// freshness budget: wird dies überschritten, nicht mehr mit dem Gesicht von live zeigen
public static readonly TimeSpan Budget = TimeSpan.FromSeconds(5);
/// <param name="lastAdvancedTicks">Monoton steigender Timestamp, zu dem die Sequenznummer zuletzt weitergerückt ist</param>
/// <param name="nowTicks">Monoton steigender Timestamp zum Zeitpunkt der Beurteilung</param>
public static Freshness Evaluate(
Reading? previous, Reading? current,
long lastAdvancedTicks, long nowTicks, TimeProvider clock)
{
if (current is null)
{
return Freshness.Unknown; // Noch nie erfolgreich abgerufen
}
if (clock.GetElapsedTime(current.ReceivedTicks, nowTicks) > Budget)
{
return Freshness.Stale; // Es kommt schon grundsätzlich nichts mehr an
}
if (previous is null)
{
return Freshness.Fresh; // Beim ersten Mal gibt es nichts zum Vergleichen, daher nur nach dem Empfangszeitpunkt beurteilen
}
if (current.Sequence < previous.Sequence)
{
// Die Sequenznummer ist zurückgesprungen. Neustart des Geräts, Austausch gegen ein
// anderes Exemplar oder SDK-Reinitialisierung vermuten
return Freshness.Unknown;
}
if (current.Sequence == previous.Sequence &&
clock.GetElapsedTime(lastAdvancedTicks, nowTicks) > Budget)
{
// Der Empfang läuft weiter, aber der Inhalt wird nicht mehr aktualisiert
return Freshness.Stale;
}
return Freshness.Fresh;
}
}
lastAdvancedTicks wird wie folgt mit der eigenen Uhr gehalten.
public sealed class FreshnessTracker(TimeProvider clock)
{
private readonly object _gate = new();
private Reading? _previous;
private long _lastAdvancedTicks;
// Wird bei jedem Empfang aufgerufen. ReceivedAt und ReceivedTicks gehören zum selben Empfang
public Reading Capture(long sequence, DateTimeOffset valueTimestamp) =>
new(sequence, valueTimestamp, clock.GetLocalNow(), clock.GetTimestamp());
public Freshness Observe(Reading current)
{
lock (_gate)
{
if (_previous is null || current.Sequence > _previous.Sequence)
{
_lastAdvancedTicks = current.ReceivedTicks;
}
var result = FreshnessPolicy.Evaluate(
_previous, current, _lastAdvancedTicks, clock.GetTimestamp(), clock);
_previous = current;
return result;
}
}
// Bricht der Empfang vollständig ab, wird Observe kein weiteres Mal aufgerufen.
// Ein Timer ruft dies regelmäßig auf und beurteilt die „Alterung“ des zuletzt empfangenen Werts neu.
// Der Zustand wird dabei nicht verändert, daher ist beliebig häufiges Aufrufen sicher
public Freshness Reevaluate()
{
lock (_gate)
{
return FreshnessPolicy.Evaluate(
_previous, _previous, _lastAdvancedTicks, clock.GetTimestamp(), clock);
}
}
}
Mit Observe allein lässt sich nicht erkennen, dass ein Gerät verstummt ist. Observe wird nur beim Empfang aufgerufen, und das dabei übergebene Reading wurde gerade eben erst erzeugt. Die Differenz zwischen ReceivedTicks und der aktuellen Zeit liegt daher fast bei null, sodass über diesen Pfad Stale nur dann entsteht, wenn „der Empfang zwar weiterläuft, die Sequenznummer aber nicht voranschreitet“. Bleibt der callback vom SDK vollständig aus — die Fehlerform, die man am dringendsten wissen möchte —, wird Observe gar nicht mehr aufgerufen, und der Bildschirm bleibt beim zuletzt berechneten Fresh stehen.
Deshalb richtet man einen von der Empfangsrate unabhängigen, periodischen Prüfpfad ein. Genau das ist das obige Reevaluate, das aus einem Timer aufgerufen wird. Der Zyklus sollte kürzer als das budget sein (bei einem budget von 5 Sekunden etwa alle 1 Sekunde). Bei gleicher Länge kann es im schlimmsten Fall fast doppelt so lange dauern, bis es bemerkt wird.
// System.Threading.Timer. Die Beurteilung läuft auch ohne Empfang weiter.
// RenderFreshness ist eine eigene Methode, die wie der Presenter aus 4.4 über denselben
// Pfad auf den Bildschirm angewendet wird.
_freshnessTimer = new Timer(
_ => RenderFreshness(_tracker.Reevaluate()),
null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
Da Observe und Reevaluate gleichzeitig von verschiedenen Threads aufgerufen werden, wird das Innere von FreshnessTracker mit lock geschützt. Lässt man das weg, vermischen sich das Ersetzen und das Auslesen von _previous, was zu dem schwer aufzuspürenden Fehler führt, dass gelegentlich eine um eine Generation veraltete Beurteilung ausgegeben wird.
Entscheidend ist hier, nicht die Differenz zu ValueTimestamp zu bilden. ValueTimestamp ist ein Wert, der von der Uhr des Geräts vergeben wurde, und es gibt keine Garantie, dass diese mit der eigenen Uhr übereinstimmt. Zieht man beide voneinander ab, wird ein gerade eben eingetroffener Wert allein deshalb stale, weil die Geräteuhr nachgeht, und umgekehrt bleibt ein stehen gebliebener Wert dauerhaft fresh, wenn die Geräteuhr vorgeht. Das budget muss stets auf die mit der eigenen Uhr gemessene verstrichene Zeit angewendet werden. ValueTimestamp sollte nur dazu dienen, auf dem Bildschirm anzuzeigen, „von wann das Gerät den Wert angibt“, oder zusammen mit der Sequenznummer als Hinweis darauf zu dienen, dass das Gerät selbst gestoppt hat.
Und auch diese „eigene Uhr“ reicht nicht als Differenz von DateTimeOffset aus. Das ist eine Wanduhr, die durch NTP-Synchronisation, manuelle Einstellung oder die Umstellung der Sommerzeit springen kann. Läuft die Zeit zurück, wird die verstrichene Zeit negativ, und obwohl das Gerät getrennt ist, bleibt es bei Fresh. Läuft sie vor, wird ein gerade eben eingetroffener Wert in diesem Moment Stale. Bildschirme, die 24 Stunden laufen, treffen das besonders oft.
Für die budget-Beurteilung wird daher ein monoton steigender Timestamp verwendet. TimeProvider.GetTimestamp() liefert einen hochauflösenden, auf Stopwatch basierenden Wert, und GetElapsedTime(Start, Ende) liefert die verstrichene Zeit zwischen zwei Zeitpunkten (siehe die Referenzlinks in Kapitel 10; .NET 8 oder neuer). Das wanduhrbasierte ReceivedAt bleibt sicherheitshalber nur dazu, auf dem Bildschirm „Empfangen um 10:15:03“ anzuzeigen, und wird bei der Beurteilung „wie viele Sekunden sind vergangen“ nicht angefasst — diese Aufteilung ist die sichere. Ein weiterer Vorteil, TimeProvider dazwischenzuschalten, ist, dass sich für Tests eine Testuhr einsetzen lässt.
Mit diesem Aufbau kann die UI-Seite, sobald sie Freshness.Stale erhält, unmittelbar die Richtlinie aus 5.3 anwenden — „neben dem Wert das age anzeigen“, „aus der Bedienbarkeitsbeurteilung ausschließen“.
Dass Unknown getrennt von Stale gehalten wird, dient dazu, noch unbekannt nicht mit veraltet zu vermischen. Ersteres kann sich durch Abwarten klären, Letzteres bessert sich durch Abwarten nicht.
4.6 Die Geräteidentifikation stabilisieren
Verfolgt man den Zustand nur anhand rein optischer Bezeichner wie eines friendly name oder COM3, kommt es leicht zu einer Verwechslung von Geräten.
Sicherer ist es, nach Möglichkeit intern einen robusten Schlüssel zu führen, etwa:
- serial number
- logical device id
- stable device path
- die geräteseitige eigene ID
5. Best Practices für die Anzeige
5.1 Entscheidungstabelle auf einen Blick
| Tatsächlicher Zustand | UI-Zusammenfassung | Ergänzende Anzeige |
|---|---|---|
| Kein interface | Nicht verbunden | Kabel, Stromversorgung, USB-Anschluss prüfen |
| interface vorhanden, wird initialisiert | Verbunden / wird geprüft | Wird initialisiert, authentifiziert, aufgewärmt |
| Antwortet, Betriebsbedingung nicht erfüllt | Verbunden / nicht nutzbar | busy, kein Medium, interlock offen |
| Antwortet, Wert veraltet | Verbunden / Wert veraltet | Letztes Update vor 12 Sekunden |
| Keine Antwort | Keine Antwort | Wird neu verbunden, Kommunikations-Timeout |
| Unerwartetes Gerät | Unerwartetes Gerät | Modell / Seriennummer / Firmware stimmen nicht überein |
| Überwachung gestoppt | Überwachungsstörung | Überwachungs-Worker gestoppt, Neustart nötig |
5.2 Formulierung als „Zustand + Grund + nächste Handlung“
Fehler oder Störung allein sind als Anzeige schwach.
Formuliert man die Meldung entlang dieser drei Elemente, ist der Bediener deutlich weniger ratlos.
- Zustand: Was passiert
- Grund: Warum diese Beurteilung erfolgt ist
- Nächste Handlung: Was zu tun ist
Zum Beispiel:
Verbunden / nicht nutzbar - Warming up - Bitte etwa 18 Sekunden wartenKeine Antwort - Heartbeat-Timeout - Kabel und Stromversorgung prüfenUnerwartetes Gerät - Firmware 2.1.0 erforderlich - Zielgerät prüfen
Vergleicht man das mit Formulierungen, die in der Praxis häufig anzutreffen sind, wird deutlich, was jeweils fehlt.
| Häufige unzureichende Formulierung | Was fehlt | Beispiel für eine bessere Formulierung |
|---|---|---|
Fehler |
Weder Zustand noch Grund noch nächste Handlung | Keine Antwort - Heartbeat-Timeout - Kabel und Stromversorgung prüfen |
Verbunden |
Zustand mehrdeutig. Es bleibt unklar, ob es gerade nutzbar ist | Verbunden / nicht nutzbar - Warming up - Bitte etwa 18 Sekunden warten |
Gerät wurde nicht gefunden |
Kein Grund, keine nächste Handlung | Nicht verbunden - Das Ziel-Interface ist nicht aufgelistet - Kabel und Stromversorgung prüfen |
Fehler 0x80070005 ist aufgetreten |
Kein für Menschen lesbarer Zustand, keine Handlung | Nicht nutzbar - Port kann nicht geöffnet werden. 0x80070005 Zugriff verweigert - Prüfen, ob eine andere App denselben Port verwendet |
Es wird erneut versucht ... |
Unklar, bis wann, wie oft, was als Nächstes passiert | Wiederverbindung Versuch 3 / max. 10 - Nächster Versuch in 8 Sekunden - Auch manuelles Wiederverbinden möglich |
Normal |
Unklar, ab welchem Zeitpunkt normal | Nutzbar - Letztes Update vor 0,5 Sekunden |
Gemeinsam ist den verbesserten Formulierungen: Kann der Bediener allein anhand dieses Bildschirms die nächste Handlung entscheiden? Bleibt einem nur „Bitte den Support kontaktieren“ übrig, dann fehlt nicht die Formulierung, sondern das Zustandsdesign selbst.
5.3 Veraltete Daten nicht verbergen
Ein last known value ist nützlich. Es ist aber sicherer, ihn nicht mit dem Gesicht eines live value zu zeigen.
Empfehlenswert ist:
- Ein Timestamp neben dem Wert
- Eine age-Anzeige für den Wert
- Farbe oder Label ändern, sobald er stale wird
- Nach Ablauf einer bestimmten Zeit aus der Bedienbarkeitsbeurteilung ausschließen
5.4 Anzeigeort je nach Wichtigkeit wechseln
Die status bar ist praktisch, wird aber leicht übersehen. Eine critical-Störung nur in der Ecke der status bar zu platzieren, sollte vermieden werden.
- Geringfügige Zustandsänderung: status bar
- Hinweis, bei dem die Arbeit fortgesetzt werden kann: inline notice
- Störung, die einen Bedienstopp erfordert: Hauptanzeigebereich, Dialog, Banner
Diese Aufteilung ist naheliegend.
5.5 Bei der Anzeige mehrerer Geräte Zusammenfassung und Detail trennen
Bei Bildschirmen, die mehrere Geräte behandeln, wird es unübersichtlich, wenn ständig alle Details aller Geräte angezeigt werden.
- Oben eine Gesamtübersicht
- Darunter eine Zeile pro Gerät
- Bei Auswahl ein Detailpanel
In dieser dreistufigen Anordnung lassen sich Gesamtüberblick und Einzelfalleingrenzung gut miteinander vereinen.
Das Grundgerüst des Bildschirms sieht dann so aus.
+-- Geräteliste ----------------------------------------------+
|
| [Gesamtübersicht] Nutzbar 6 / 8 Hinweis 1 Störung 1
|
| [Zeile pro Gerät]
| Zustand Anzeigename Grund Letztes Update
| -------- --------------- ---------------- --------
| Nutzbar Kamera vorgel. - vor 0,5 s
| Nutzbar Labeldrucker - vor 1,2 s
| > Nicht nutzbar Prüfkamera Warming up vor 0,6 s <- ausgewählt
| Keine Antwort Barcode-Leser heartbeat timeout vor 48 s
|
| [Detailpanel] Prüfkamera
| serial A1B2C3 / firmware 2.4.1 / noch etwa 18 Sekunden
| [ Wiederverbinden ] [ Log öffnen ]
|
+----------------------------------------------------------------+
Mit diesem dreistufigen Aufbau lassen sich auf demselben Bildschirm gleichzeitig bedienen: wer nur die Gesamtübersicht ansieht (kann die Linie heute laufen), wer die Zeilen ansieht (welches Gerät steht) und wer das Detailpanel ansieht (was zu tun ist, um es zu beheben).
Für die Reihenfolge der Zeilen ist es handhabbarer, Störungen nach oben zu ziehen. Springt die Reihenfolge jedoch jede Sekunde um, kommt es zu Fehlklicks. Die Neusortierung sollte daher erst nach dem in 6.2 beschriebenen Glätten von Flapping, auf Basis des endgültigen Zustands, erfolgen.
6. Best Practices für Wiederverbindung und Betrieb
6.1 Wiederverbindung mit Backoff versehen
Es ist sicherer, bei einer Wiederverbindung nach einem Antwortstopp nicht in der kürzestmöglichen Schleife immer wieder anzuklopfen.
- Es belastet Gerät / Treiber / SDK
- Die Logs laufen über
- Vorübergehende Instabilität verschlimmert sich
- Die UI flackert stark
Realistisch ist:
- Beim ersten Mal sofort retry
- Falls das nicht klappt, das Intervall stufenweise verlängern
- Eine Obergrenze setzen
- Zusätzlich einen manuellen
Wiederverbinden-Button anbieten
Stellt man Zustände und Übergangsbedingungen als Diagramm dar, wird sichtbar, wo Backoff und die Obergrenze für Wiederverbindungen wirken.
stateDiagram-v2
[*] --> Unknown
Unknown --> Absent: Auch nach Aufzählung nicht gefunden
Unknown --> Present: Aufzählung beim Start / Ankunftsbenachrichtigung
Absent --> Present: Ankunftsbenachrichtigung
Present --> Absent: Entfernungsbenachrichtigung (aus jedem Zustand)
state Present {
[*] --> Detected
Detected --> Opening: open / login / initialize
Opening --> Ready: Initialisierung und Konfigurationsprüfung erfolgreich
Opening --> Fault: Initialisierung fehlgeschlagen (Wiederholung möglich)
Opening --> Mismatch: Unerwartetes Gerät / Modell / Firmware
Ready --> Busy: warming up / läuft / interlock
Busy --> Ready: Operation kann entgegengenommen werden
Busy --> Fault: I/O-Fehler
Ready --> Fault: I/O-Fehler / Ausbleiben der Antwort bestätigt
Fault --> Reconnecting: Backoff einlegen
Reconnecting --> Opening: Wartezeit abgelaufen
Reconnecting --> RetryExhausted: Obergrenze der Wiederverbindungen erreicht
Fault --> Opening: Manuelle Wiederverbindung
RetryExhausted --> Opening: Manuelle Wiederverbindung (kehrt automatisch nicht zurück)
Mismatch --> Opening: Nach Korrektur der Konfiguration manuell wiederverbinden
}
Abbildung 2: Dieses Diagramm behandelt nur die Lebensdauer von Verbindung und Sitzung, die Datenaktualität ist nicht enthalten. Aktualität ist eine von der Betriebsbereitschaft unabhängige Achse (Abbildung 1) — sowohl in Ready als auch in Busy kann sie in gleicher Weise veralten. Vermischt man sie mit dem Zustand, entstehen Verwechslungen wie „während des Betriebs riss der Wert ab, und nach der Rückkehr war es bereits Ready“. Auf dem Bildschirm werden der Zustand aus diesem Diagramm und die Aktualitätsbeurteilung aus Abschnitt 4.5 getrennt gehalten und kombiniert. Auch bei Erreichen der Wiederholungsobergrenze wird nicht auf Absent (nicht verbunden) zurückgesetzt, sondern im nicht automatisch zurückkehrenden RetryExhausted verharrt — das Gerät kann sichtbar bleiben und dennoch defekt sein, und die Anzeige „nicht verbunden“ würde zu einer falschen Wiederherstellungsmaßnahme verleiten. Auch eine Konfigurationsabweichung lässt sich nicht durch Wiederholung beheben, weshalb sie aus dem Kreis der automatischen Wiederverbindung ausgeschlossen ist
6.2 Flapping glätten
Bei Situationen wie einem USB-Wackelkontakt oder kurzen Netzwerkunterbrechungen springt der Zustand in kurzer Zeit hin und her. Zeigt man hier die rohen Events unverändert in der UI, wird das ziemlich unübersichtlich.
Deshalb ist diese Aufteilung handhabbar:
- Das interne Log behält die rohen Events unverändert
- Die UI wartet eine kurze Bestätigungsphase ab, bevor sie die Anzeige festlegt
- Kritische Störungen werden jedoch sofort angezeigt
6.3 Die mindestens aufzubewahrenden Logs
Die Verbesserung der Zustandsanzeige geht praktisch Hand in Hand mit dem Log-Design.
| Feld | Beispiel |
|---|---|
| Timestamp | 2026-03-20T10:23:41.512+09:00 |
| Robuster Geräteschlüssel | camera:A1B2C3 |
| Anzeigename | Kamera vorgelagerter Prozess |
| Alter Zustand -> neuer Zustand | Ready -> Stale |
| Grund | heartbeat timeout firmware mismatch |
| Fehlercode | HRESULT Win32 SDK code |
| Letzter Erfolg | 2026-03-20T10:23:36.011+09:00 |
| Age / RTT | 5,5 s 320 ms |
| Anzahl Wiederholungen | 3 |
| App-/Firmware-Version | App 1.8.2 / FW 2.4.1 |
Besonders wichtig ist das Zustandsübergangslog.
6.4 Überwachungsstopp nicht mit Gerätestopp verwechseln
- Die poll loop ist an einer Ausnahme gestorben
- Der SDK-callback ist ausgeblieben
- Der acquisition worker ist in ein deadlock geraten
- Nur die Aktualisierung des state store ist stehen geblieben
In solchen Fällen kann das Gerät noch leben, aber die App kann es nicht mehr beobachten.
Zeigt man diesen Zustand nur als Nicht verbunden oder Keine Antwort an, sieht es wie ein geräteseitiges Problem aus.
Deshalb ist es besser, die Integrität des Überwachungspfads als eigene Achse zu führen.
7. Leicht übersehene Punkte nach Gerätetyp
7.1 USB-/PnP-Geräte
- Benachrichtigungen allein erfassen keine bereits vorhandenen Geräte
- Zur Laufzeit ist eine interface class natürlicher als eine setup class
- Ein composite device kann mehrere interfaces bereitstellen
- Die Reihenfolge von remove-Benachrichtigung und I/O-Fehler kann sich vertauschen
7.2 Serielle Geräte
Dass COMx sichtbar ist, ist noch kein Grund zur Beruhigung.
- Der Port selbst existiert, aber das Zielgerät hängt nicht daran
- Ein anderer Prozess hat ihn geöffnet
- Es antwortet schon lange nicht mehr
- read / write bleiben durch Timeout hängen
Bei seriellen Geräten ist es besonders sicher, Existenz, Antwort und Nutzbarkeit getrennt zu halten.
7.3 Netzwerkgeräte
Ein erfolgreicher ping sollte nicht mit der Nutzbarkeit für die App gleichgesetzt werden.
- Lässt sich der Name auflösen?
- Lässt sich eine TCP-Verbindung herstellen?
- Gelingt der Handshake auf Anwendungsschicht?
- Ist der Status ready?
- Ist der Wert fresh?
Es gibt hier mehrere Stufen.
7.4 SDK-abhängige Kameras / Messgeräte
Es ist sicherer, nicht allein deshalb, weil callbacks vom SDK eintreffen, von live auszugehen.
- Der callback thread selbst bleibt stehen
- Frames kommen an, aber der Timestamp schreitet nicht voran
- Der image stream kommt an, aber der control channel ist tot
- Die Neuanwendung der Konfiguration nach einem reconnect ist noch nicht abgeschlossen
Da so etwas vorkommt, gibt zusätzliche Sicherheit, auch eine Integritätsprüfung von außerhalb des SDKs zu führen.
8. Was man nicht tun sollte
- Den Zustand auf
Verbunden / Nicht verbunden / Fehlerzusammenfassen - Annehmen, dass Benachrichtigungen allein auch bereits vorhandene Geräte erfassen
- Einen erfolgreichen
openunmittelbar alsNutzbarwerten - Einen last known value mit dem Gesicht von fresh zeigen
- Den Timestamp nicht anzeigen
- open / read / status query im UI-Thread ausführen
- retry in kürzestmöglicher Schleife durchführen
- Eine critical-Störung nur in der status bar anzeigen
Nicht verbundenmitÜberwachung gestopptverwechseln- Ein Gerät nur über friendly name oder
COM3identifizieren
9. Zusammenfassung
Wirklich wichtig bei Anwendungen zur Anbindung externer Geräte ist es, festzulegen, was man geprüft haben muss, um wie viel sagen zu dürfen.
Besonders wirksam ist diese Trennung:
Es ist vorhanden Die eigene App kann es öffnen Es antwortet Die Operation ist jetzt möglich Der angezeigte Wert ist aktuell
Diese fünf Punkte trennen.
Darauf aufbauend lauten die praktischen Leitlinien grob:
- Beim Start aufzählen, danach Benachrichtigungen
- Nutzbarkeit anhand von Heartbeat und gerätespezifischem Status entscheiden
- Angezeigten Werten Timestamp und age mitgeben
- Kritische Störungen an einem schwer zu übersehenden Ort anzeigen
- Überwachungsstörungen nicht als Gerätestörung erscheinen lassen
Wichtiger als sagen zu können „Verbunden“ ist in der Praxis, wie selten diese Anzeige von der Realität abweicht.
10. Referenzlinks
- Microsoft Learn, TimeProvider Class (dass
GetTimestampeinen hochauflösenden, aufStopwatchbasierenden Wert liefert undGetElapsedTime(Int64, Int64)die verstrichene Zeit zwischen zwei Zeitpunkten liefert) - Microsoft Learn, CM_Register_Notification
- Microsoft Learn, Registering for Notification of Device Interface Arrival and Device Removal
- Microsoft Learn, Registering for Device Notification
- Microsoft Learn, Comparison of setup classes and interface classes
- Microsoft Learn, Device Information Sets
- Microsoft Learn, SetupDiEnumDeviceInterfaces
- Microsoft Learn, Communications functions
- Microsoft Learn, ClearCommError
- Microsoft Learn, COMMTIMEOUTS structure
- Microsoft Learn, WaitCommEvent
- Microsoft Learn, Monitoring Communications Events
- Microsoft Learn, Status Bars (Design basics)
- Microsoft Learn, UX checklist for desktop applications
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Windows-Zeitsynchronisation (w32time) und Unternehmenssysteme — „Die Protokoll-Zeitstempel stimmen nicht überein“ von den Grundlagen her lösen
Warum weichen die Zeitstempel zwischen einem Gerät und einem PC voneinander ab? Dieser Artikel erklärt es anhand der Funktionsweise des W...
Windows-App-Outsourcing und Auftragsentwicklung: Was Sie vor der Beauftragung klären sollten
Bevor Sie die Entwicklung einer Windows-App outsourcen oder in Auftrag geben, sollten Sie folgende Punkte klären: Überarbeitung bestehend...
Was bedeutet Windows' „Speicherauslastung“ eigentlich? — Working Set, Private Bytes, Commit und die Auslagerungsdatei richtig lesen
Der Speicherwert im Task-Manager, Working Set, Private Bytes und Commit sind nicht dasselbe. Dieser Artikel erklärt das Verhältnis von vi...
Praktische Multithreading-Best-Practices: C-Edition — Sicher schreiben nach Win32-API-Konvention
Bei Multithreading in C mit Win32 sind Thread-Erstellung über _beginthreadex, SRW-Locks und Bedingungsvariablen, Interlocked-Funktionen s...
Praktische Multithreading-Best-Practices: C++-Edition — Unfälle mit RAII und jthread strukturell beseitigen
In C++ ist Multithreading eine Welt, in der eine Datenrace zu undefiniertem Verhalten wird. Dieser Artikel behandelt die Falle des std::t...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
In Anwendungen zur Anbindung externer Geräte hängt die Betriebsqualität nicht nur von der Kommunikationsverarbeitung ab, sondern auch von der Konsistenz zwischen Zustandsverwaltung und UI-Anzeige. Wird dies bereits in der Entwurfsphase geordnet, sinkt die Zahl der Störfälle.
Technische Beratung und Design-Review
Ein Zustandsdesign, bei dem „Verbunden“ allein nicht ausreicht, lässt sich leichter beurteilen, wenn man es entlang der Achsen Erkennung, Antwortprüfung, Verfügbarkeit, Datenaktualität und Wiederverbindung getrennt betrachtet.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Warum reicht es nicht aus, den Gerätezustand nur als „Verbunden“ anzuzeigen?
- Weil das Wort „Verbunden“ mehrere Fragen stillschweigend zu einer zusammenfasst: Ist das Gerät für das OS sichtbar, konnte die eigene App es öffnen, antwortet es, kann jetzt tatsächlich bedient werden, sind die angezeigten Werte aktuell, und handelt es sich um das erwartete Gerät. „Nicht verbunden“, „verbunden / wird geprüft“, „verbunden / nicht nutzbar“ und „Wert veraltet“ sind alles unterschiedliche Zustände. Werden sie alle auf „Verbunden“ zusammengefasst, kann der Bediener nicht entscheiden, was zu tun ist.
- Wie sollte der Zustand eines externen Geräts intern aufgeteilt gehalten werden?
- Getrennt gehalten werden sollten Existenz (ist es für das OS sichtbar), Sitzungsaufbau (ist open/login/initialize abgeschlossen), Antwortverhalten (antwortet es auf einen Heartbeat), Betriebsbereitschaft (kann jetzt eine Operation entgegengenommen werden), Datenaktualität (sind die angezeigten Werte aktuell), Konfigurationsübereinstimmung (ist es das erwartete Gerät bzw. die erwartete Firmware) und Überwachungsintegrität (lebt der Überwachungsprozess selbst noch). Grob gesagt: Die Existenzprüfung liegt beim OS, die Nutzbarkeit bei der App und die Aktualitätsbeurteilung beim Bildschirm. Die UI wird lesbarer, wenn man sie in drei Ebenen — Zusammenfassung, Grund, Detail — gliedert.
- Sollte die Geräteerkennung und die Prüfung der Betriebsintegrität über Events oder Polling erfolgen?
- Statt sich vollständig auf eine der beiden Varianten festzulegen, ist in der Praxis die Aufteilung Erkennung per Event, Integritätsprüfung per Polling gut handhabbar. Konkret: arrival/removal werden über Event-Benachrichtigungen empfangen, Heartbeat oder Status-Abfragen erfolgen per regelmäßigem Polling, und die Aktualität wird über Timestamp oder Sequenznummer beurteilt. Da Benachrichtigungen allein bereits vorhandene Geräte nicht erfassen, gilt in der Praxis die Regel: beim Start aufzählen, danach Benachrichtigungen abonnieren, und bei Erhalt einer Benachrichtigung erneut aufzählen und den internen Zustand abgleichen (reconcile).
- Wie sollte die Wiederverbindung zu einem nicht mehr antwortenden Gerät implementiert werden?
- Nicht in der kürzestmöglichen Schleife immer wieder anklopfen, sondern mit Backoff versehen. Realistisch ist: beim ersten Mal sofort erneut versuchen, danach die Intervalle stufenweise verlängern, eine Obergrenze setzen und zusätzlich einen manuellen „Wiederverbinden“-Button anbieten. Ein Vorgehen in kürzestmöglicher Schleife belastet Gerät und SDK, lässt die Logs überlaufen und verschlimmert vorübergehende Instabilität. Bei Flapping — etwa durch einen wackligen USB-Kontakt, bei dem der Zustand in kurzer Zeit hin- und herspringt — hilft es, auf der UI-Seite eine kurze Bestätigungsphase abzuwarten, bevor die Anzeige endgültig festgelegt wird.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.