import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../domain/telemetry/system_message.dart'; import '../../transport/connection_type.dart'; import '../../transport/flight_controller_link.dart'; import 'connection_settings_provider.dart'; import 'telemetry_provider.dart'; /// Haelt die Liste der System Messages plus das manuelle [log()] (Doku: /// "aendere das warnings MenĂ¼ in System Messages"). Bewusst schlank, ohne /// jede Telemetrie-/Verbindungsbeobachtung: mission_meta_provider.dart und /// active_drone_profile_provider.dart rufen log() auch aus dem Plan-Modus /// heraus auf (Missions-/Drohnenprofilwechsel) - wuerde diese Klasse selbst /// telemetryProvider beobachten, risse schon ein simpler Missionswechsel im /// Plan-Modus ungewollt den kompletten Telemetrie-/Transport-Stack (inkl. /// echtem MSP/UDP-Transport) mit hoch. Die automatische Erkennung /// (Drone connected/disconnected/GPS-Fix/Batterie/Failsafe/Verbindungstyp- /// Wechsel) sitzt deshalb separat in [systemMessageAutoLogProvider], die /// ausschliesslich der Fly-Modus beobachtet. class SystemMessageLogNotifier extends Notifier> { @override List build() => []; /// Fuegt einen Eintrag manuell hinzu (Doku: "Wegpunkte uebertragen", /// "Wechsel der Mission oder des Drohnenprofils") - fuer Ereignisse, die /// vom Aufloeser einer konkreten Aktion gemeldet werden, statt aus /// Telemetrie/Einstellungen automatisch erkannt zu werden. void log(String message, {SystemMessageSeverity severity = SystemMessageSeverity.info}) { state = [ SystemMessage(timestamp: DateTime.now(), message: message, severity: severity), ...state, ]; } void clear() => state = []; } final systemMessageLogProvider = NotifierProvider>( SystemMessageLogNotifier.new); /// Automatische Erkennung von Drone-connected/-disconnected/GPS-Fix/ /// Batterie-/Failsafe-/Verbindungstyp-Aenderungen (Doku: "wenn die /// Verbindung hergestellt wird/verloren geht, der verbindungstyp geaendert /// wird, gps fix aquired") - getrennt von /// [systemMessageLogProvider] selbst (siehe dessen Doku), damit Plan-Modus- /// Code, der nur log() fuer eine manuelle Meldung braucht, nicht versehentlich /// telemetryProvider mit hochzieht. autoDispose: nur beobachtet, waehrend /// irgendwer (FlyScreen) diesen Provider liest - verlaesst der Nutzer den /// Fly-Modus, wird die Beobachtung wieder abgebaut wie bei telemetryProvider /// selbst. final systemMessageAutoLogProvider = Provider.autoDispose((ref) { TelemetryFrame? previousFrame; var hadData = false; ref.listen>(telemetryProvider, (previous, next) { next.when( data: (frame) { final log = ref.read(systemMessageLogProvider.notifier); // Flankengetriggert wie "Drone disconnected" unten, nur umgekehrt - // anders als dort ist hier kein Sonderfall fuer die allererste // Verbindung noetig: der Uebergang "noch keine Daten" -> "erste // Daten" ist immer ein echtes "Drone connected", auch beim // allerersten Mal. Bewusst an das Eintreffen eines echten // Telemetrie-Frames gebunden statt an den rohen Socket-/Link- // Zustand (Doku: "drone connected/disconnected (receiving valid // telemetry)") - ein offener Socket ohne auswertbare MSP-Antworten // zaehlt also nicht als "connected". if (!hadData) { log.log('Drone connected'); } final events = detectSystemMessages( previous: previousFrame, current: frame, now: DateTime.now(), ); previousFrame = frame; hadData = true; for (final event in events) { log.log(event.message, severity: event.severity); } }, error: (error, stackTrace) { // Nur melden, wenn zuvor bereits Frames flossen - sonst wuerde // schon das (normale) Ausbleiben der allerersten Verbindung als // "Drone disconnected" auftauchen. if (hadData) { ref.read(systemMessageLogProvider.notifier) .log('Drone disconnected', severity: SystemMessageSeverity.critical); } previousFrame = null; hadData = false; }, loading: () {}, ); }); // Verbindungsart-Wechsel (Doku: "Verbindungstyp geaendert") - nur ab dem // zweiten geladenen Wert protokollieren, sonst wuerde bereits das // erstmalige Laden der Einstellungen aus der DB als "Wechsel" erscheinen. ConnectionType? previousType; var sawFirstLoad = false; ref.listen(connectionSettingsProvider, (previous, next) { final type = next.value?.connectionType; if (type == null) return; if (!sawFirstLoad) { sawFirstLoad = true; previousType = type; return; } if (type != previousType) { ref.read(systemMessageLogProvider.notifier) .log('Connection type changed to ${_connectionTypeLabel(type)}'); previousType = type; } }); }); String _connectionTypeLabel(ConnectionType type) => switch (type) { ConnectionType.wifi => 'WiFi mLRS UDP', ConnectionType.mock => 'Mock (Test)', };