Fly mode previously showed just a black placeholder. It now renders the same MissionMap (route + waypoint markers) and footer (warnings banner, altitude profile, BottomStatsBar) as the Plan screen, minus the reticle/wheels since flying observes the route rather than replanning it. Waypoints can still be edited via the details list for spontaneous in-flight changes. Live drone position comes from a new telemetryProvider/ flightControllerLinkProvider pair (autoDispose), the first place FlightControllerLink/TelemetryFrame get wired into the UI - currently backed by MockFlightControllerLink until a real MSP transport exists (Doku 4.19). A drone marker renders on the map once a telemetry frame arrives. The Fly-mode header keeps the fit-to-area button but replaces "zoom to home" with "zoom to drone" (FlyMapControls) - the first waypoint isn't a meaningful reference point anymore once airborne. Extracted computeMissionStats and MissionFooterBar out of PlanScreen so Plan/Fly share the exact same stats/footer logic instead of duplicating it, and extracted NavIconButton out of MapSearchControls so both Plan's and Fly's map controls use the same button widget. Widget tests that now mount FlyScreen switch from the ProviderScope-based _wrap() helper to a manual ProviderContainer with an explicit FlightControllerLink.disconnect() call before test end - the mock's Timer.periodic doesn't get cancelled by container disposal alone, and flutter_test's pending-timer check runs before addTearDown callbacks. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
38 lines
1.5 KiB
Dart
38 lines
1.5 KiB
Dart
import 'package:dmc_app/domain/mission/mission_stats.dart';
|
|
import 'package:dmc_app/domain/waypoint/flat_waypoint_list.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
Waypoint _wp(double lat, double lon, {double speed = 15}) =>
|
|
Waypoint(lat: lat, lon: lon, altitudeM: 60, speedMs: speed, catchRadiusM: 60);
|
|
|
|
void main() {
|
|
group('computeMissionStats', () {
|
|
test('leere/einzelne Wegpunktliste ergibt Distanz/Dauer 0', () {
|
|
expect(computeMissionStats(const []).distanceM, 0);
|
|
expect(computeMissionStats([_wp(0, 0)]).distanceM, 0);
|
|
expect(computeMissionStats([_wp(0, 0)]).durationSec, 0);
|
|
});
|
|
|
|
test('zwei Wegpunkte: Distanz aus Grosskreis, Dauer aus Zielgeschwindigkeit', () {
|
|
final stats = computeMissionStats([_wp(0, 0), _wp(0, 0.01, speed: 20)]);
|
|
|
|
// 0.01 Grad Laengendifferenz am Aequator entspricht rund 1112 m.
|
|
expect(stats.distanceM, closeTo(1112.6, 1));
|
|
expect(stats.durationSec, closeTo(stats.distanceM / 20, 0.01));
|
|
});
|
|
|
|
test('mehrere Beine summieren sich, Dauer je Bein mit dessen Zielgeschwindigkeit', () {
|
|
final stats = computeMissionStats(
|
|
[_wp(0, 0, speed: 10), _wp(0, 0.01, speed: 20), _wp(0, 0.02, speed: 10)],
|
|
);
|
|
|
|
final leg1 = 1112.6;
|
|
final leg2 = 1112.6;
|
|
expect(stats.distanceM, closeTo(leg1 + leg2, 2));
|
|
// Erstes Bein zaehlt mit der Geschwindigkeit des Zielwegpunkts (20),
|
|
// zweites Bein mit dessen Zielwegpunkt (10).
|
|
expect(stats.durationSec, closeTo(leg1 / 20 + leg2 / 10, 0.1));
|
|
});
|
|
});
|
|
}
|