import 'dart:async'; import '../../domain/waypoint/flat_waypoint_list.dart'; import '../flight_controller_link.dart'; /// Synthetische Telemetrie fuer UI-Entwicklung/Tests ohne Hardware oder SITL /// (Architektur-Doku 3.1). class MockFlightControllerLink implements FlightControllerLink { bool _armed = false; int? _activeWaypointIndex; StreamController? _telemetryController; Timer? _ticker; double _heading = 0; double _batteryPercent = 100; @override FcCapabilities get capabilities => const FcCapabilities( supportsMultiMission: true, supportsInFlightUpload: false, maxWaypoints: 60, ); @override Future connect() async { _telemetryController = StreamController.broadcast(); _ticker = Timer.periodic(const Duration(seconds: 1), (_) { // Kurs dreht sich langsam weiter statt fest zu stehen, damit die // Rotation des Drohnen-Markers (DroneMarkerIcon) auch ohne Hardware // sichtbar ueberprueft werden kann (Doku 9: UI-Entwicklung ohne // Hardware). _heading = (_heading + 5) % 360; // Langsam sinkender Akkustand, damit die Anzeige auch ohne Hardware // sichtbar auf Werteaenderungen reagiert (Doku 9). _batteryPercent = (_batteryPercent - 0.1).clamp(0, 100); _telemetryController?.add(TelemetryFrame( lat: 52.5, lon: 13.4, hasFix: true, numSat: 12, altitudeM: 120, speedMs: 18, headingDeg: _heading, armed: _armed, batteryPercent: _batteryPercent.round(), linkQuality: 95, flightMode: _activeWaypointIndex != null ? 'Waypoint mission (en route)' : 'Idle', activeWaypointIndex: _activeWaypointIndex, )); }); } @override Future disconnect() async { _ticker?.cancel(); await _telemetryController?.close(); _telemetryController = null; } @override Future uploadMission(FlatWaypointList mission) async { // iNAV/MAVLink erlauben Mission-Upload nur im unarmed-Zustand (Doku 4.5). if (_armed) { throw StateError('Mission-Upload verweigert: Drohne ist armed.'); } _activeWaypointIndex = mission.waypoints.isEmpty ? null : 0; } @override Future setFlightMode(FlightMode mode) async {} @override Future arm() async { _armed = true; } @override Stream subscribeTelemetry() { final controller = _telemetryController; if (controller == null) { throw StateError('subscribeTelemetry() vor connect() aufgerufen.'); } return controller.stream; } @override Future readActiveWaypointIndex() async => _activeWaypointIndex; }