drone status menue: fixed empty display bug without active telemetry stream

This commit is contained in:
Constantin Leue
2026-08-05 21:23:43 +02:00
parent c7e4e5bf63
commit 5fa55ac67d
2 changed files with 78 additions and 57 deletions
@@ -43,6 +43,15 @@ SignalLevel _levelLowerIsBetter(double value, double greenMax, double redMax) {
/// Reine Ableitung aus normalisierten Telemetriewerten + den
/// Ampel-Schwellwerten des aktiven Drohnenprofils - kein MSP-/
/// Protokollwissen, siehe computeDroneStatus-Doku.
///
/// [hasData] ist false, solange noch gar kein Telemetrie-Frame vorliegt
/// (Doku: "Felder auch anzeigen koennen, bevor Daten empfangen wurden") -
/// erzwingt dann fuer ALLE Felder rot, unabhaengig von den uebergebenen
/// Platzhalterwerten. Ohne das wuerde z.B. "Current draw" (ein Feld, bei dem
/// ein NIEDRIGER Wert gut ist) mit dem neutralen Platzhalter 0 faelschlich
/// gruen erscheinen ("sehr niedriger Verbrauch" statt "keine Daten") - anders
/// als bei den "hoeher ist besser"-Feldern, wo ein 0/negativer Platzhalter
/// zufaellig schon unter der Rot-Schwelle liegt.
TelemetryFieldStatus computeTelemetryFieldStatus({
required int numSat,
required bool hasFix,
@@ -52,7 +61,18 @@ TelemetryFieldStatus computeTelemetryFieldStatus({
required int linkQuality,
required int snrDb,
required DroneProfile profile,
bool hasData = true,
}) {
if (!hasData) {
return const TelemetryFieldStatus(
numSat: SignalLevel.red,
hdop: SignalLevel.red,
currentA: SignalLevel.red,
batteryVoltage: SignalLevel.red,
rxQuality: SignalLevel.red,
rxSnr: SignalLevel.red,
);
}
return TelemetryFieldStatus(
numSat: _levelHigherIsBetter(
numSat.toDouble(),
@@ -110,66 +110,67 @@ class _DroneStatusWarningsPanelState extends ConsumerState<DroneStatusWarningsPa
// --- Status ---
// Zeigt die Statuszeilen unabhaengig davon, ob bereits ein Telemetrie-Frame
// vorliegt (Doku: "endloser Lade-Screen ohne die Datenfelder anzuzeigen" -
// z.B. WLAN gewaehlt, aber noch nicht verbunden) - `telemetry` bleibt dann
// dauerhaft AsyncLoading, .value ist einfach null. Analog zu FlyScreen.
// droneStatus (siehe dortige Doku) werden Platzhalterwerte verwendet, damit
// computeTelemetryFieldStatus trotzdem eine (dann durchgehend rote) Ampel
// liefert, statt hier zusaetzlich zwischen "noch nie verbunden" und
// "Verbindung verloren" zu unterscheiden. Einzelne Werte zeigen dennoch
// '—' statt eines erfundenen Messwerts (0 A/0 V saehe sonst wie ein echter,
// kritischer Messwert statt wie "keine Daten" aus).
Widget _statusTab() {
final telemetry = ref.watch(telemetryProvider);
final frame = ref.watch(telemetryProvider).value;
final droneProfile = ref.watch(activeDroneProfileProvider);
return telemetry.when(
data: (frame) {
final fieldStatus = computeTelemetryFieldStatus(
numSat: frame.numSat,
hasFix: frame.hasFix,
hdop: frame.hdop,
currentA: frame.currentA,
batteryVoltage: frame.batteryVoltage,
linkQuality: frame.linkQuality,
snrDb: frame.snrDb,
numSat: frame?.numSat ?? 0,
hasFix: frame?.hasFix ?? false,
hdop: frame?.hdop ?? 99.0,
currentA: frame?.currentA ?? 0,
batteryVoltage: frame?.batteryVoltage ?? 0,
linkQuality: frame?.linkQuality ?? 0,
snrDb: frame?.snrDb ?? -99,
profile: droneProfile,
hasData: frame != null,
);
return ListView(
padding: const EdgeInsets.all(16),
children: _statusGrid([
// Farbcodierte Felder zuerst (Doku: "drone status felder im
// menue auch farblich kodieren", Schwellwerte je Drohnenprofil -
// siehe computeTelemetryFieldStatus), informelle Felder ohne
// Ampel-Bewertung (Koordinaten/Heading/Alt/Speed) danach. Je 2
// Felder pro Zeile (Doku: "platz effizienter nutzen").
_statusRow('GPS satellites', '${frame.numSat}', level: fieldStatus.numSat),
// Farbcodierte Felder zuerst (Doku: "drone status felder im menue
// auch farblich kodieren", Schwellwerte je Drohnenprofil - siehe
// computeTelemetryFieldStatus), informelle Felder ohne Ampel-
// Bewertung (Koordinaten/Heading/Alt/Speed) danach. Je 2 Felder pro
// Zeile (Doku: "platz effizienter nutzen").
_statusRow('GPS satellites', frame != null ? '${frame.numSat}' : '—', level: fieldStatus.numSat),
_statusRow(
'GPS precision (HDOP)',
frame.hasFix ? frame.hdop.toStringAsFixed(2) : '—',
frame != null && frame.hasFix ? frame.hdop.toStringAsFixed(2) : '—',
level: fieldStatus.hdop,
),
_statusRow(
'Current draw',
'${frame.currentA.toStringAsFixed(1)} A',
frame != null ? '${frame.currentA.toStringAsFixed(1)} A' : '—',
level: fieldStatus.currentA,
),
_statusRow(
'Battery voltage',
'${frame.batteryVoltage.toStringAsFixed(2)} V',
frame != null ? '${frame.batteryVoltage.toStringAsFixed(2)} V' : '—',
level: fieldStatus.batteryVoltage,
),
_statusRow('RX quality', '${frame.linkQuality}%', level: fieldStatus.rxQuality),
_statusRow('RX SNR', '${frame.snrDb} dB', level: fieldStatus.rxSnr),
_statusRow('RX quality', frame != null ? '${frame.linkQuality}%' : '—', level: fieldStatus.rxQuality),
_statusRow('RX SNR', frame != null ? '${frame.snrDb} dB' : '—', level: fieldStatus.rxSnr),
_statusRow(
'GPS coordinates',
frame.hasFix
frame != null && frame.hasFix
? '${frame.lat.toStringAsFixed(6)}, ${frame.lon.toStringAsFixed(6)}'
: '—',
),
_statusRow('Heading', '${frame.headingDeg.round()}°'),
_statusRow('Altitude', '${frame.altitudeM.toStringAsFixed(0)} m'),
_statusRow('Speed', '${frame.speedMs.toStringAsFixed(1)} m/s'),
_statusRow('Heading', frame != null ? '${frame.headingDeg.round()}°' : '—'),
_statusRow('Altitude', frame != null ? '${frame.altitudeM.toStringAsFixed(0)} m' : '—'),
_statusRow('Speed', frame != null ? '${frame.speedMs.toStringAsFixed(1)} m/s' : '—'),
]),
);
},
loading: () => const Center(
child: CircularProgressIndicator(color: Color(0xFF4D9DFF)),
),
error: (err, st) => const Center(
child: Text('No telemetry', style: TextStyle(color: Colors.white60, fontSize: 15)),
),
);
}
/// Ordnet die Statuszeilen zu je 2 pro Zeile an (Doku: "2 felder pro zeile