Files
dmc/app/lib/domain/mission/drone_profile.dart
T
Constantin LeueandClaude Sonnet 5 e902197462 System messages fuer GPS-Fix und Drone connected/disconnected, Batterie-Schwellwerte auf Pro-Zelle umgestellt
- detectSystemMessages() erkennt jetzt auch den Uebergang zu einem GPS-Fix
  (flankengetriggert wie Battery/Failsafe), Meldung "GPS fix acquired (N
  satellites)".
- Die automatisch erkannten Verbindungsmeldungen heissen jetzt "Drone
  connected"/"Drone disconnected" statt "Connected"/"Connection lost" -
  praeziser an die tatsaechliche Bedeutung angelehnt (Eintreffen/Ausbleiben
  echter Telemetrie-Frames, nicht nur des rohen Socket-Zustands).
- DroneProfile: feste Pack-Alarmspannung (batteryVoltageGreenMinV/RedMinV)
  ersetzt durch batteryCellCount + Pro-Zelle-Schwellwerte
  (batteryVoltageGreenMinPerCellV/RedMinPerCellV, LiPo-Standardwerte 3.4/3.2
  V als Default). Die alten Feldnamen bleiben als berechnete Getter
  (Zellenzahl * Pro-Zelle-Wert) erhalten, damit
  telemetry_field_status.dart unveraendert bleibt. T1-Ranger-Standardprofil
  auf 4S gesetzt.
- DB-Schema v8 -> v9 (additiv, alte Pack-Spannungs-Spalten bleiben als tote
  Spalten stehen), Repository und Share-Codec-Im-/Export entsprechend
  angepasst; alte Exportdateien ohne die neuen Felder fallen auf die
  DroneProfile-Defaults zurueck statt eine unbekannte Zellenzahl zu raten.
- DroneProfileEditor: "Battery voltage alarm"-Gruppe um ein Zellenzahl-Feld
  erweitert und auf V/Zelle umbenannt.
- Tests ergaenzt/angepasst: GPS-Fix-Erkennung (Unit + Provider-Integration),
  neuer v8->v9-Migrationstest analog zum bestehenden v7->v8-Test,
  Connected/Disconnected-Umbenennung in Provider- und Widget-Tests,
  Cell-Count-Roundtrip in Repository- und Share-Codec-Tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 13:06:49 +02:00

199 lines
7.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// Ein Drohnen-Profil mit Flugleistungsdaten (Architektur-Doku 3.5/7.3):
/// FC-Typ ist bewusst NICHT Teil dieses MVP-Profils (nur iNAV/MSP im
/// Scope, siehe CLAUDE.md) - anders als im HTML-Demonstrator, der bereits
/// auf MAVLink vorbereitet.
class DroneProfile {
const DroneProfile({
this.id,
required this.name,
required this.minSpeed,
required this.cruiseSpeed,
required this.maxSpeed,
required this.minTurnRadius,
required this.loiterRadius,
required this.catchRadius,
required this.maxClimbRate,
required this.maxDescentRate,
required this.maxRangeM,
required this.maxEnduranceMin,
required this.maxAltitudeM,
this.gpsSatGreenMin = defaultGpsSatGreenMin,
this.gpsSatRedMin = defaultGpsSatRedMin,
this.hdopGreenMax = defaultHdopGreenMax,
this.hdopRedMax = defaultHdopRedMax,
this.currentGreenMaxA = defaultCurrentGreenMaxA,
this.currentRedMaxA = defaultCurrentRedMaxA,
this.batteryCellCount = defaultBatteryCellCount,
this.batteryVoltageGreenMinPerCellV = defaultBatteryVoltageGreenMinPerCellV,
this.batteryVoltageRedMinPerCellV = defaultBatteryVoltageRedMinPerCellV,
this.rxQualityGreenMin = defaultRxQualityGreenMin,
this.rxQualityRedMin = defaultRxQualityRedMin,
this.rxSnrGreenMinDb = defaultRxSnrGreenMinDb,
this.rxSnrRedMinDb = defaultRxSnrRedMinDb,
});
/// Null fuer ein noch nicht gespeichertes (neu angelegtes) Profil.
final int? id;
final String name;
final double minSpeed;
final double cruiseSpeed;
final double maxSpeed;
final double minTurnRadius;
final double loiterRadius;
final double catchRadius;
final double maxClimbRate;
final double maxDescentRate;
final double maxRangeM;
final double maxEnduranceMin;
final double maxAltitudeM;
/// Ampel-Schwellwerte fuer die farbcodierten Telemetriefelder im
/// Drohnen-Status-Menue (Doku: "Schwellwerte fuer die Ampel-Kodierung im
/// Drohnenprofil anlegen", siehe domain/telemetry/telemetry_field_status.dart)
/// - je Feld eine Gruen- und eine Rot-Schwelle, dazwischen gilt gelb.
/// *Max/*GreenMax bei Feldern, bei denen ein NIEDRIGER Wert gut ist (HDOP),
/// *Min/*GreenMin bei Feldern, bei denen ein HOHER Wert gut ist (alle
/// anderen). Default-Werte als statische Konstanten (statt nur inline im
/// Konstruktor), damit sie auch als Fallback beim Import aelterer, noch
/// ohne diese Felder exportierter Profile dienen koennen (siehe
/// drone_share_codec.dart).
final double gpsSatGreenMin;
final double gpsSatRedMin;
final double hdopGreenMax;
final double hdopRedMax;
final double currentGreenMaxA;
final double currentRedMaxA;
/// Zellenzahl des LiPo-Akkus (z.B. 4 fuer "4S") - bestimmt zusammen mit
/// den beiden *PerCellV-Schwellwerten die tatsaechliche Alarmspannung des
/// Packs (siehe [batteryVoltageGreenMinV]/[batteryVoltageRedMinV]). Vorher
/// war die Alarmspannung als feste Pack-Gesamtspannung hinterlegt - bei
/// einem Wechsel der Zellenzahl (z.B. 4S statt 6S) waeren die alten
/// Schwellwerte dann fuer den neuen Pack sinnlos gewesen, ohne dass beim
/// Bearbeiten des Profils ein Hinweis darauf bestanden haette.
final int batteryCellCount;
/// Alarm-Schwellwerte PRO ZELLE (LiPo-Standardwerte als Default: 3.4V
/// gruen/"noch sicher", 3.2V rot/"jetzt landen") - siehe
/// [batteryVoltageGreenMinV]/[batteryVoltageRedMinV] fuer die daraus
/// abgeleitete tatsaechliche Pack-Alarmspannung.
final double batteryVoltageGreenMinPerCellV;
final double batteryVoltageRedMinPerCellV;
final double rxQualityGreenMin;
final double rxQualityRedMin;
final double rxSnrGreenMinDb;
final double rxSnrRedMinDb;
static const defaultGpsSatGreenMin = 8.0;
static const defaultGpsSatRedMin = 6.0;
static const defaultHdopGreenMax = 2.0;
static const defaultHdopRedMax = 5.0;
static const defaultCurrentGreenMaxA = 20.0;
static const defaultCurrentRedMaxA = 30.0;
static const defaultBatteryCellCount = 4;
static const defaultBatteryVoltageGreenMinPerCellV = 3.4;
static const defaultBatteryVoltageRedMinPerCellV = 3.2;
static const defaultRxQualityGreenMin = 70.0;
static const defaultRxQualityRedMin = 50.0;
static const defaultRxSnrGreenMinDb = 0.0;
static const defaultRxSnrRedMinDb = -10.0;
/// Tatsaechliche Pack-Alarmspannung, aus Zellenzahl x Pro-Zelle-Schwelle
/// berechnet - [telemetry_field_status.dart] vergleicht damit die vom FC
/// gemeldete Gesamtspannung, kennt selbst aber keine Zellenzahl.
double get batteryVoltageGreenMinV =>
batteryCellCount * batteryVoltageGreenMinPerCellV;
double get batteryVoltageRedMinV =>
batteryCellCount * batteryVoltageRedMinPerCellV;
/// T1 Ranger (Heewing) - Standardprofil (Doku 3.5), analog DEFAULT_DRONE
/// im HTML-Demonstrator.
static const _t1RangerName = 'T1 Ranger';
factory DroneProfile.t1Ranger() => const DroneProfile(
name: _t1RangerName,
minSpeed: 13,
cruiseSpeed: 15,
maxSpeed: 25,
minTurnRadius: 35,
loiterRadius: 100,
catchRadius: 60,
maxClimbRate: 5,
maxDescentRate: 6,
maxRangeM: 15000,
maxEnduranceMin: 45,
maxAltitudeM: 2000,
batteryCellCount: 4,
);
/// Einzeilige Zusammenfassung fuer die Profilliste (Doku 3.5), analog
/// droneSummary() im HTML-Demonstrator.
String get summary {
final rangeKm = (maxRangeM / 100).round() / 10;
return '$minSpeed–$cruiseSpeed–$maxSpeed m/s · R${minTurnRadius.round()}m · '
'↑${maxClimbRate.round()}/↓${maxDescentRate.round()} m/s · ${rangeKm}km · '
'${maxEnduranceMin.round()}min · ceil ${maxAltitudeM.round()}m';
}
DroneProfile copyWith({
int? id,
String? name,
double? minSpeed,
double? cruiseSpeed,
double? maxSpeed,
double? minTurnRadius,
double? loiterRadius,
double? catchRadius,
double? maxClimbRate,
double? maxDescentRate,
double? maxRangeM,
double? maxEnduranceMin,
double? maxAltitudeM,
double? gpsSatGreenMin,
double? gpsSatRedMin,
double? hdopGreenMax,
double? hdopRedMax,
double? currentGreenMaxA,
double? currentRedMaxA,
int? batteryCellCount,
double? batteryVoltageGreenMinPerCellV,
double? batteryVoltageRedMinPerCellV,
double? rxQualityGreenMin,
double? rxQualityRedMin,
double? rxSnrGreenMinDb,
double? rxSnrRedMinDb,
}) {
return DroneProfile(
id: id ?? this.id,
name: name ?? this.name,
minSpeed: minSpeed ?? this.minSpeed,
cruiseSpeed: cruiseSpeed ?? this.cruiseSpeed,
maxSpeed: maxSpeed ?? this.maxSpeed,
minTurnRadius: minTurnRadius ?? this.minTurnRadius,
loiterRadius: loiterRadius ?? this.loiterRadius,
catchRadius: catchRadius ?? this.catchRadius,
maxClimbRate: maxClimbRate ?? this.maxClimbRate,
maxDescentRate: maxDescentRate ?? this.maxDescentRate,
maxRangeM: maxRangeM ?? this.maxRangeM,
maxEnduranceMin: maxEnduranceMin ?? this.maxEnduranceMin,
maxAltitudeM: maxAltitudeM ?? this.maxAltitudeM,
gpsSatGreenMin: gpsSatGreenMin ?? this.gpsSatGreenMin,
gpsSatRedMin: gpsSatRedMin ?? this.gpsSatRedMin,
hdopGreenMax: hdopGreenMax ?? this.hdopGreenMax,
hdopRedMax: hdopRedMax ?? this.hdopRedMax,
currentGreenMaxA: currentGreenMaxA ?? this.currentGreenMaxA,
currentRedMaxA: currentRedMaxA ?? this.currentRedMaxA,
batteryCellCount: batteryCellCount ?? this.batteryCellCount,
batteryVoltageGreenMinPerCellV:
batteryVoltageGreenMinPerCellV ?? this.batteryVoltageGreenMinPerCellV,
batteryVoltageRedMinPerCellV:
batteryVoltageRedMinPerCellV ?? this.batteryVoltageRedMinPerCellV,
rxQualityGreenMin: rxQualityGreenMin ?? this.rxQualityGreenMin,
rxQualityRedMin: rxQualityRedMin ?? this.rxQualityRedMin,
rxSnrGreenMinDb: rxSnrGreenMinDb ?? this.rxSnrGreenMinDb,
rxSnrRedMinDb: rxSnrRedMinDb ?? this.rxSnrRedMinDb,
);
}
}