diff --git a/app/lib/transport/mock/mock_flight_controller_link.dart b/app/lib/transport/mock/mock_flight_controller_link.dart index 0e85806..ae48bc4 100644 --- a/app/lib/transport/mock/mock_flight_controller_link.dart +++ b/app/lib/transport/mock/mock_flight_controller_link.dart @@ -22,8 +22,8 @@ class MockFlightControllerLink implements FlightControllerLink { _anchorLon = initialWaypoints.isEmpty ? 13.4 : initialWaypoints.first.lon, _activeWaypointIndex = initialWaypoints.isEmpty ? null : 0; - final double _anchorLat; - final double _anchorLon; + double _anchorLat; + double _anchorLon; final _random = math.Random(); bool _armed = false; @@ -111,6 +111,22 @@ class MockFlightControllerLink implements FlightControllerLink { _telemetryController = null; } + /// Setzt die simulierte Position auf den ersten Wegpunkt von [waypoints] + /// zurueck und loescht die bisherige Drift (Doku: "beim Laden einer + /// Mission Mock-Drohnenposition auf ersten Wegpunkt setzen, solange die + /// Drohne nicht verbunden ist") - der Aufrufer (CurrentMissionMetaNotifier) + /// ruft dies nur auf, wenn [FlightControllerLink] tatsaechlich dieser Mock + /// ist, also noch keine echte Verbindung (WLAN/MSP) aktiv ist. Ohne + /// Wegpunkte faellt die Position auf den alten Berlin-Fallback zurueck, + /// analog zum Konstruktor. + void resetPosition(List waypoints) { + _anchorLat = waypoints.isEmpty ? 52.5 : waypoints.first.lat; + _anchorLon = waypoints.isEmpty ? 13.4 : waypoints.first.lon; + _driftNorthM = 0; + _driftEastM = 0; + _activeWaypointIndex = waypoints.isEmpty ? null : 0; + } + @override Future uploadMission(FlatWaypointList mission) async { // iNAV/MAVLink erlauben Mission-Upload nur im unarmed-Zustand (Doku 4.5). diff --git a/app/lib/ui/providers/mission_meta_provider.dart b/app/lib/ui/providers/mission_meta_provider.dart index df0ea5c..3fb5004 100644 --- a/app/lib/ui/providers/mission_meta_provider.dart +++ b/app/lib/ui/providers/mission_meta_provider.dart @@ -3,10 +3,12 @@ import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../domain/waypoint/flat_waypoint_list.dart'; +import '../../transport/mock/mock_flight_controller_link.dart'; import 'current_mission_provider.dart'; import 'database_provider.dart'; import 'follow_drone_provider.dart'; import 'map_controller_provider.dart'; +import 'telemetry_provider.dart'; import 'terrain_provider.dart'; /// Metadaten der aktuell in Bearbeitung befindlichen Mission - ergaenzt @@ -110,9 +112,21 @@ class CurrentMissionMetaNotifier extends Notifier { /// Doku 3.11, siehe fitMapToWaypoints in map_controller_provider.dart) - /// das Laden einer anderen Mission waehrend des Verfolgens soll auf der /// neuen Mission stehen bleiben, nicht sofort wieder zur Drohne springen. + /// + /// Solange keine echte Verbindung (WLAN/MSP) aktiv ist - also + /// flightControllerLinkProvider noch der Mock ist - setzt dies zusaetzlich + /// die simulierte Drohnenposition auf den ersten Wegpunkt der neu + /// geladenen Mission zurueck (Doku: "beim Laden einer Mission Mock- + /// Drohnenposition auf ersten Wegpunkt setzen"), damit die Live-Details- + /// Ansicht sofort zur neuen Route passt statt an der vorherigen (oder dem + /// Berlin-Fallback) haengen zu bleiben. void _fitMapToWaypoints(List waypoints) { ref.read(followDroneProvider.notifier).set(false); fitMapControllerToWaypoints(ref.read(mapControllerProvider), waypoints); + final link = ref.read(flightControllerLinkProvider); + if (link is MockFlightControllerLink) { + link.resetPosition(waypoints); + } } /// Benennt eine gespeicherte Mission um (Doku 3.6: nur ueber die diff --git a/app/lib/ui/screens/fly/fly_screen.dart b/app/lib/ui/screens/fly/fly_screen.dart index 123328a..b86d680 100644 --- a/app/lib/ui/screens/fly/fly_screen.dart +++ b/app/lib/ui/screens/fly/fly_screen.dart @@ -288,6 +288,7 @@ class _FlyScreenState extends ConsumerState { totalDurationSec: missionStats.durationSec, flyMissionStatus: flyMissionStatus, warnings: warnings, + showWarningsBanner: false, onDetailsTap: _openWaypointList, onWarningsTap: () => _openWarningsPanel(warnings), onSettingsTap: () => Navigator.of(context).push(MaterialPageRoute( diff --git a/app/lib/ui/widgets/mission_footer_bar.dart b/app/lib/ui/widgets/mission_footer_bar.dart index caf9d4f..57a5a70 100644 --- a/app/lib/ui/widgets/mission_footer_bar.dart +++ b/app/lib/ui/widgets/mission_footer_bar.dart @@ -23,6 +23,7 @@ class MissionFooterBar extends ConsumerWidget { required this.totalDurationSec, this.flyMissionStatus, required this.warnings, + this.showWarningsBanner = true, required this.onDetailsTap, required this.onWarningsTap, required this.onSettingsTap, @@ -41,6 +42,13 @@ class MissionFooterBar extends ConsumerWidget { /// Distanz/Zeit dorthin. Bleibt im Plan-Modus null. final FlyMissionStatus? flyMissionStatus; final List warnings; + + /// Blendet das Text-Warnbanner ueber der Fusszeile aus (Doku: "Banner mit + /// Warnungen ueber der Fusszeile im Fly-Modus entfernen") - der runde + /// Warnungsknopf in BottomStatsBar (oeffnet weiterhin WarningsPanel) bleibt + /// davon unberuehrt, nur die staendig sichtbare Textzeile verschwindet. + /// Default true, damit der Plan-Modus unveraendert bleibt. + final bool showWarningsBanner; final VoidCallback onDetailsTap; final VoidCallback onWarningsTap; final VoidCallback onSettingsTap; @@ -52,7 +60,7 @@ class MissionFooterBar extends ConsumerWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - if (warnings.isNotEmpty) + if (showWarningsBanner && warnings.isNotEmpty) Container( width: double.infinity, color: const Color(0xFF7C2D12),