Add Flutter project skeleton (Android-only MVP)

Five-layer architecture per DMC_Architektur_und_Design.md: FlightControllerLink
abstraction (Mock + MSP stub), protocol-neutral mission domain layer, App-Mode
state machine with Ready-to-Fly gate, Drift persistence schema, and placeholder
UI screens wired via go_router. App icon generated from design/Y2_pink_dark.svg.
Verified with flutter analyze and flutter test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Constantin Leue
2026-07-26 21:28:25 +02:00
co-authored by Claude Sonnet 5
parent 62d655fe8e
commit 346fcc2183
45 changed files with 2884 additions and 0 deletions
@@ -0,0 +1,71 @@
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<TelemetryFrame>? _telemetryController;
Timer? _ticker;
@override
FcCapabilities get capabilities => const FcCapabilities(
supportsMultiMission: true,
supportsInFlightUpload: false,
maxWaypoints: 60,
);
@override
Future<void> connect() async {
_telemetryController = StreamController<TelemetryFrame>.broadcast();
_ticker = Timer.periodic(const Duration(seconds: 1), (_) {
_telemetryController?.add(TelemetryFrame(
lat: 52.5,
lon: 13.4,
altitudeM: 120,
speedMs: 18,
armed: _armed,
activeWaypointIndex: _activeWaypointIndex,
));
});
}
@override
Future<void> disconnect() async {
_ticker?.cancel();
await _telemetryController?.close();
_telemetryController = null;
}
@override
Future<void> 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<void> setFlightMode(FlightMode mode) async {}
@override
Future<void> arm() async {
_armed = true;
}
@override
Stream<TelemetryFrame> subscribeTelemetry() {
final controller = _telemetryController;
if (controller == null) {
throw StateError('subscribeTelemetry() vor connect() aufgerufen.');
}
return controller.stream;
}
@override
Future<int?> readActiveWaypointIndex() async => _activeWaypointIndex;
}