Drone Status Menue: Sensor-Status, Arming, Temperaturen und Steig-/Sinkrate hinzugefuegt
Neue Zeile ganz oben mit farblich kodierten Sensor-Badges (ACC/BARO/MAG/ GPS/RNG/OF/PITOT/TEMP, aus dem sensorStatus-Bitfeld von MSP2_INAV_STATUS, zuvor schon dokumentiert aber ungenutzt), darunter ein Zeilenpaar fuer Arming-Status und die ersten 3 Temperatursensoren (neu: MSP2_INAV_ TEMPERATURES, 0x201E). Ans Ende der Liste die Steig-/Sinkrate (vario aus MSP_ALTITUDE, bislang nur die Hoehe selbst wurde daraus gelesen). - msp_commands.dart: inavTemperatures-Konstante ergaenzt; die Sensor- Status-Bits (bisher nur als Doc-Kommentar bei MSP2_INAV_STATUS notiert) zu echten Konstanten (MspSensorStatusBits) promoviert, da jetzt tatsaechlich gebraucht. - msp_telemetry_codec.dart: parseMspAltitudeVerticalSpeedMs, parseMspInavStatusSensorStatus, parseMspInavTemperaturesC ergaenzt + Unit-Tests. - TelemetryFrame: sensorStatusBits (Bitmaske, protokollneutral durchgereicht wie navMode), temperaturesC (erste 3 Sensoren, null je nicht konfiguriertem Slot), verticalSpeedMs. - msp_telemetry_poller.dart: vario kommt aus derselben MSP_ALTITUDE- Antwort wie die Hoehe (keine zusaetzliche Anfrage), sensorStatus aus derselben MSP2_INAV_STATUS-Antwort wie ARMED; MSP2_INAV_TEMPERATURES neu im 2-Hz-Statuszyklus abgefragt. - MockFlightControllerLink: synthetische Sensor-/Temperatur-/Vario-Werte (kein Pitot/Rangefinder/Opflow am T1 Ranger vorgesehen), leicht schwankend, damit die neuen Felder auch ohne Hardware sichtbar auf Werteaenderungen reagieren. - drone_status_messages_panel.dart: eigene Sensor-Status-Zeile (lokale Bit-Konstanten statt MSP-Import, analog zum bestehenden navMode-Muster in domain/telemetry/drone_status.dart, damit die UI protokollneutral bleibt), Arming+Temperaturen-Zeilenpaar, Vertical-speed-Zeile am Ende. Auf dem Pixel_10a-Emulator verifiziert: Sensor-Badges gruen/grau je nach Bitmaske, Arming/Temperaturen-Paar, Vertical speed am Listenende. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
08791cac0c
commit
9cece7dbb6
@@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../../domain/telemetry/drone_status.dart';
|
||||
import '../../domain/telemetry/system_message.dart';
|
||||
import '../../domain/telemetry/telemetry_field_status.dart';
|
||||
import '../../transport/flight_controller_link.dart';
|
||||
import '../dmc_colors.dart';
|
||||
import '../providers/active_drone_profile_provider.dart';
|
||||
import '../providers/system_message_log_provider.dart';
|
||||
@@ -136,13 +137,30 @@ class _DroneStatusMessagesPanelState extends ConsumerState<DroneStatusMessagesPa
|
||||
);
|
||||
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 != null ? '${frame.numSat}' : '—', level: fieldStatus.numSat),
|
||||
children: [
|
||||
// Eigene, volle Zeile ganz oben statt Teil des 2-Spalten-Rasters
|
||||
// (Doku: "eine zeile ganz oben fuer sensor status") - die einzelnen
|
||||
// Sensor-Badges brauchen mehr horizontalen Platz als eine
|
||||
// Wertkachel bieten wuerde.
|
||||
_sensorStatusRow(frame),
|
||||
const SizedBox(height: 8),
|
||||
..._statusGrid([
|
||||
// Arming + erste 3 Temperaturen als eigenes Zeilenpaar direkt
|
||||
// unter der Sensor-Status-Zeile (Doku), noch vor den
|
||||
// farbcodierten Feldern.
|
||||
_statusRow(
|
||||
'Arming',
|
||||
frame == null ? '—' : (frame.armed ? 'Armed' : 'Disarmed'),
|
||||
level: frame?.armed == true ? SignalLevel.green : null,
|
||||
),
|
||||
_statusRow('Temperatures', _formatTemperatures(frame)),
|
||||
// Farbcodierte Felder (Doku: "drone status felder im menue auch
|
||||
// farblich kodieren", Schwellwerte je Drohnenprofil - siehe
|
||||
// computeTelemetryFieldStatus), informelle Felder ohne Ampel-
|
||||
// Bewertung (Koordinaten/Heading/Alt/Speed/Vertical 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 != null && frame.hasFix ? frame.hdop.toStringAsFixed(2) : '—',
|
||||
@@ -169,7 +187,12 @@ class _DroneStatusMessagesPanelState extends ConsumerState<DroneStatusMessagesPa
|
||||
_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' : '—'),
|
||||
]),
|
||||
// Ans Ende der Liste (Doku: "ans ende der liste noch die
|
||||
// steig/sinkrate hinzufuegen") - bildet mit "Speed" das letzte
|
||||
// Zeilenpaar.
|
||||
_statusRow('Vertical speed', _formatVerticalSpeed(frame)),
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -253,6 +276,89 @@ class _DroneStatusMessagesPanelState extends ConsumerState<DroneStatusMessagesPa
|
||||
);
|
||||
}
|
||||
|
||||
// --- Sensor status ---
|
||||
|
||||
/// Lokale Kopie der Sensor-Bit-Bedeutung (siehe `MspSensorStatusBits` in
|
||||
/// transport/msp/msp_commands.dart) - bewusst nicht von dort importiert,
|
||||
/// damit dieses UI-Widget kein MSP-/Protokollwissen braucht.
|
||||
/// [TelemetryFrame.sensorStatusBits] ist protokollneutral durchgereicht,
|
||||
/// genau wie `navMode` (siehe die entsprechenden lokalen Konstanten in
|
||||
/// domain/telemetry/drone_status.dart).
|
||||
static const _sensorBits = [
|
||||
(bit: 1 << 0, label: 'ACC'),
|
||||
(bit: 1 << 1, label: 'BARO'),
|
||||
(bit: 1 << 2, label: 'MAG'),
|
||||
(bit: 1 << 3, label: 'GPS'),
|
||||
(bit: 1 << 4, label: 'RNG'),
|
||||
(bit: 1 << 5, label: 'OF'),
|
||||
(bit: 1 << 6, label: 'PITOT'),
|
||||
(bit: 1 << 7, label: 'TEMP'),
|
||||
];
|
||||
static const _sensorHardwareFailureBit = 1 << 15;
|
||||
|
||||
Widget _sensorStatusRow(TelemetryFrame? frame) {
|
||||
final bits = frame?.sensorStatusBits ?? 0;
|
||||
final hardwareFailure = (bits & _sensorHardwareFailureBit) != 0;
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.04),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: hardwareFailure
|
||||
? Border.all(color: DmcColors.warnRed.withValues(alpha: 0.6))
|
||||
: null,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Sensor Status',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 12),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: [
|
||||
for (final s in _sensorBits)
|
||||
_sensorChip(s.label, frame != null && (bits & s.bit) != 0),
|
||||
if (hardwareFailure) _sensorChip('HW FAULT', true, color: DmcColors.warnRed),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _sensorChip(String label, bool active, {Color? color}) {
|
||||
final chipColor = color ?? (active ? DmcColors.flyModeColor : Colors.white38);
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: chipColor.withValues(alpha: active ? 0.18 : 0.06),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: chipColor.withValues(alpha: active ? 0.8 : 0.3)),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(color: chipColor, fontSize: 11, fontWeight: FontWeight.w600),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatTemperatures(TelemetryFrame? frame) {
|
||||
if (frame == null) return '—';
|
||||
return frame.temperaturesC.map((t) => t == null ? '—' : '${t.round()}°').join(' / ');
|
||||
}
|
||||
|
||||
String _formatVerticalSpeed(TelemetryFrame? frame) {
|
||||
if (frame == null) return '—';
|
||||
final v = frame.verticalSpeedMs;
|
||||
final sign = v >= 0 ? '+' : '-';
|
||||
return '$sign${v.abs().toStringAsFixed(1)} m/s';
|
||||
}
|
||||
|
||||
// --- System Messages ---
|
||||
|
||||
Widget _messagesTab() {
|
||||
|
||||
Reference in New Issue
Block a user