Drone/Netzwerk-Connect-Disconnect wirklich repariert (Riverpods Auto-Retry verschluckte den Fehlerzustand)
Der vorherige Fix (msp_telemetry_poller.dart, addError nach 3 fehl- geschlagenen Zyklen) hat die Erkennung allein nicht repariert - aus zwei Gruenden, beide jetzt behoben: 1. telemetryProvider brach bei WLAN-Verbindungsverlust (wifiLinkStateProvider != connected) den Strom bisher mit einem stillen `return;` ab, statt ueberhaupt subscribeTelemetry() zu abonnieren. Damit blieb der Provider nach einem zuvor erfolgreichen Verbindungsaufbau unbegrenzt auf dem letzten AsyncData(...) haengen, sobald das WLAN-Netz verloren ging - komplett unabhaengig vom MSP-Poller-Fix, der in diesem Fall nie erreicht wird. Ersetzt durch eine neue WifiLinkNotConnectedException. 2. Der eigentliche Grund, warum ich das beim ersten Fix nicht bemerkt habe: Riverpod 3.x wiederholt einen fehlgeschlagenen Provider standardmaessig automatisch (ProviderContainer.defaultRetry) und haelt ihn dabei in AsyncLoading(error: ..., retrying: true) statt sofort auf AsyncError zu wechseln - AsyncValue.when()s error:-Zweig (systemMessageAutoLogProvider) feuert dafuer nicht, nur der loading:-Zweig (No-Op). Das betraf sowohl die neue WifiLinkNotConnectedException als auch das per yield* durchgereichte addError aus dem MSP-Poller - beide blieben dadurch unbegrenzt "am Wiederholen haengen", nie als AsyncError sichtbar. Mit retry: (retryCount, error) => null gezielt fuer telemetryProvider deaktiviert - die eigentliche Wiederherstellung passiert ohnehin reaktiv (ref.watch(wifiLinkStateProvider) bzw. der Poller-Takt selbst), nicht ueber Riverpods Backoff. Neuer Test in telemetry_provider_test.dart deckt jetzt die komplette Kette end-to-end ab (echter MspFlightControllerLink + LoopbackTransport + ueberschriebener wifiLinkStateProvider, keine der bisherigen Tests in system_message_log_provider_test.dart haette diesen Fehler auffangen koennen, da sie telemetryProvider selbst immer ueberschreiben statt seine eigene Generatorfunktion zu durchlaufen). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
1b8c67a02b
commit
e5c87fba5f
@@ -10,6 +10,25 @@ import 'connection_settings_provider.dart';
|
||||
import 'current_mission_provider.dart';
|
||||
import 'wifi_connection_provider.dart';
|
||||
|
||||
/// Wird von [telemetryProvider] geworfen, wenn `wifiLinkStateProvider`
|
||||
/// (weder) verbunden ist - siehe dortige Doku. Frueher liess die
|
||||
/// Stream-Generatorfunktion den Strom in diesem Fall einfach per `return;`
|
||||
/// enden, ohne je [FlightControllerLink.subscribeTelemetry] zu abonnieren -
|
||||
/// dabei blieb `telemetryProvider` fuer jeden Beobachter (u.a.
|
||||
/// systemMessageAutoLogProvider) unbegrenzt in AsyncLoading haengen, auch
|
||||
/// nachdem zuvor bereits Frames flossen (WLAN-Netz verloren/mLRS-Bruecke
|
||||
/// aus): AsyncValue.when()s loading-Zweig feuert dort nicht, "Drone
|
||||
/// disconnected" wurde also nie geloggt. Ein echter Fehler statt eines
|
||||
/// stillen Stream-Endes sorgt dafuer, dass genau dieser Uebergang als
|
||||
/// AsyncError sichtbar wird.
|
||||
class WifiLinkNotConnectedException implements Exception {
|
||||
const WifiLinkNotConnectedException(this.linkState);
|
||||
final LinkState linkState;
|
||||
|
||||
@override
|
||||
String toString() => 'WifiLinkNotConnectedException($linkState)';
|
||||
}
|
||||
|
||||
/// FlightControllerLink fuer den Fly-Modus (Doku 3.1): bei WLAN als
|
||||
/// gewaehlter Verbindungsart ein echter MSP-Link ueber den UDP-Transport,
|
||||
/// sonst (ConnectionType.mock, oder solange die Einstellungen noch nicht
|
||||
@@ -74,12 +93,35 @@ final flightControllerLinkProvider = Provider<FlightControllerLink>((ref) {
|
||||
/// FlyScreen zeigt den Fallback-Marker auf dem ersten Wegpunkt). Fuer
|
||||
/// Mock/Bluetooth/5G/USB gilt diese Einschraenkung nicht, dort ist
|
||||
/// "verbinden" ohnehin folgenlos bzw. noch nicht umgesetzt.
|
||||
final telemetryProvider = StreamProvider.autoDispose<TelemetryFrame>((ref) async* {
|
||||
final link = ref.watch(flightControllerLinkProvider);
|
||||
if (link is MspFlightControllerLink &&
|
||||
ref.watch(wifiLinkStateProvider).value != LinkState.connected) {
|
||||
return;
|
||||
}
|
||||
await link.connect();
|
||||
yield* link.subscribeTelemetry();
|
||||
});
|
||||
final telemetryProvider = StreamProvider.autoDispose<TelemetryFrame>(
|
||||
(ref) async* {
|
||||
final link = ref.watch(flightControllerLinkProvider);
|
||||
if (link is MspFlightControllerLink) {
|
||||
final wifiState = ref.watch(wifiLinkStateProvider).value;
|
||||
if (wifiState != LinkState.connected) {
|
||||
// Bewusst ein Fehler statt eines stillen `return;` (siehe
|
||||
// [WifiLinkNotConnectedException]-Doku) - fuer den allerersten
|
||||
// Aufbau (noch nie verbunden) harmlos, da systemMessageAutoLogProvider
|
||||
// den Fehler ignoriert, solange zuvor noch keine Frames flossen.
|
||||
throw WifiLinkNotConnectedException(wifiState ?? LinkState.disconnected);
|
||||
}
|
||||
}
|
||||
await link.connect();
|
||||
yield* link.subscribeTelemetry();
|
||||
},
|
||||
// Riverpods eingebautes automatisches Wiederholen (Standard seit Riverpod
|
||||
// 3.x) haelt einen fehlgeschlagenen Provider absichtlich in
|
||||
// AsyncLoading(..., retrying: true) statt sofort in AsyncError zu
|
||||
// wechseln - AsyncValue.when()s error:-Zweig (u.a. in
|
||||
// systemMessageAutoLogProvider, siehe dortige Doku zu "Drone
|
||||
// disconnected") feuert dafuer aber gar nicht, nur der loading:-Zweig
|
||||
// (No-Op). Betrifft nicht nur [WifiLinkNotConnectedException] oben,
|
||||
// sondern genauso ein error-Event aus dem per yield* durchgereichten
|
||||
// [MspTelemetryPoller.frames]-Strom (siehe dortige _disconnectAfterFailures-
|
||||
// Doku) - beide Faelle blieben dadurch unbegrenzt "am Wiederholen", nie
|
||||
// als AsyncError sichtbar. retry: null deaktiviert das gezielt fuer
|
||||
// diesen Provider: die eigentliche Wiederherstellung passiert ohnehin
|
||||
// reaktiv ueber ref.watch(wifiLinkStateProvider)/den Poller-Takt selbst,
|
||||
// nicht ueber Riverpods Backoff.
|
||||
retry: (retryCount, error) => null,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user