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:
co-authored by
Claude Sonnet 5
parent
62d655fe8e
commit
346fcc2183
@@ -0,0 +1,57 @@
|
||||
import '../domain/waypoint/flat_waypoint_list.dart';
|
||||
|
||||
/// Abstrakte Flugmodi, protokollunabhaengig (Architektur-Doku 3.1).
|
||||
enum FlightMode { missionRun, guidedPoint, returnHome, hold }
|
||||
|
||||
/// Feature-Flags des angebundenen Flightcontrollers (Doku 3.1).
|
||||
class FcCapabilities {
|
||||
const FcCapabilities({
|
||||
required this.supportsMultiMission,
|
||||
required this.supportsInFlightUpload,
|
||||
required this.maxWaypoints,
|
||||
});
|
||||
|
||||
final bool supportsMultiMission;
|
||||
final bool supportsInFlightUpload;
|
||||
final int maxWaypoints;
|
||||
}
|
||||
|
||||
/// Normalisiertes Telemetrie-Frame, das an die UI weitergereicht wird (Doku 3.4).
|
||||
class TelemetryFrame {
|
||||
const TelemetryFrame({
|
||||
required this.lat,
|
||||
required this.lon,
|
||||
required this.altitudeM,
|
||||
required this.speedMs,
|
||||
required this.armed,
|
||||
this.activeWaypointIndex,
|
||||
});
|
||||
|
||||
final double lat;
|
||||
final double lon;
|
||||
final double altitudeM;
|
||||
final double speedMs;
|
||||
final bool armed;
|
||||
final int? activeWaypointIndex;
|
||||
}
|
||||
|
||||
/// Zentrale Abstraktion fuer alle Flightcontroller-Interaktionen (Doku 3.1).
|
||||
/// Implementierungen: MspFlightControllerLink (iNAV), MavlinkFlightControllerLink
|
||||
/// (ArduPilot, Post-MVP), MockFlightControllerLink (Tests ohne Hardware).
|
||||
abstract class FlightControllerLink {
|
||||
FcCapabilities get capabilities;
|
||||
|
||||
Future<void> connect();
|
||||
|
||||
Future<void> disconnect();
|
||||
|
||||
Future<void> uploadMission(FlatWaypointList mission);
|
||||
|
||||
Future<void> setFlightMode(FlightMode mode);
|
||||
|
||||
Future<void> arm();
|
||||
|
||||
Stream<TelemetryFrame> subscribeTelemetry();
|
||||
|
||||
Future<int?> readActiveWaypointIndex();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import '../../domain/waypoint/flat_waypoint_list.dart';
|
||||
import '../flight_controller_link.dart';
|
||||
|
||||
/// iNAV-Anbindung via MSP (Architektur-Doku 3.1). Eigenstaendiger Adapter,
|
||||
/// da kein fertiges Dart-Paket fuer MSP verfuegbar ist (Doku 4.19).
|
||||
///
|
||||
/// Protokoll-Implementierung (Framing, MSP_SET_WP, MSP_NAV_STATUS, ...) ist
|
||||
/// noch offen - dies ist bewusst nur das Geruest fuer die Abstraktion.
|
||||
class MspFlightControllerLink implements FlightControllerLink {
|
||||
MspFlightControllerLink({required this.host, required this.port});
|
||||
|
||||
final String host;
|
||||
final int port;
|
||||
|
||||
@override
|
||||
FcCapabilities get capabilities => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> connect() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> disconnect() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> uploadMission(FlatWaypointList mission) =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> setFlightMode(FlightMode mode) => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> arm() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Stream<TelemetryFrame> subscribeTelemetry() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<int?> readActiveWaypointIndex() => throw UnimplementedError();
|
||||
}
|
||||
Reference in New Issue
Block a user