Commit Graph
15 Commits
Author SHA1 Message Date
Constantin Leue ffad444e50 Drop "Details:" label from the mission stats pill to save footer space
The icons (flag/ruler/clock) already convey what each value means, so
the label was redundant. Gave the pill a stable Key since tests relied
on the now-removed text to find and tap it.
2026-08-07 21:20:16 +02:00
Constantin LeueandClaude Sonnet 5 7ff88c5435 Footer: drone status pill always sizes to its content, mission pill absorbs the rest
The drone status pill and the mission pill previously sat in two equally
sized Expanded regions. That squeezed the drone pill's FittedBox down to
illegibility whenever its content grew (e.g. "disconnected"), while the
mission pill often had unused space to spare.

A plain flex-ratio rebalance (e.g. mission flex:1 vs drone flex:4) turned
out to be the wrong tool: Expanded/Flexible always force a fixed
fractional share regardless of actual content need, so it either starved
the mission pill even for short names, or capped the drone pill below
what it needed.

bottom_stats_bar.dart: the drone-side content (status pill or drone-name
chip + send/warning/settings buttons) is now a plain, non-flex Row child,
exactly like _detailsPill()/_AltitudeProfileToggleButton already were -
it always gets exactly the width its current content needs, never more,
never less. The mission pill remains the sole Expanded element and
absorbs whatever space is left, ellipsizing first if it's tight.

widget_test.dart: two tests exercising the footer at the default 800x600
test viewport started hitting a real RenderFlex overflow, since that
width was never realistic for this landscape-only app's footer content
in the first place (previously masked by the drone pill silently
shrinking via FittedBox). Set a realistic device-sized viewport
(2424x1080, matching the Pixel_10a emulator) for just those two tests.

Verified on the Pixel_10a emulator: "disconnected" now renders fully
legible in the drone pill, and the mission pill still reads normally
alongside it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 21:35:19 +02:00
Constantin LeueandClaude Sonnet 5 46e51b3920 Drohnen-Status-Pille zeigt bei Verbindungsverlust rot/disconnected statt eingefrorener Werte
Waehrend eines Verbindungsverlusts frieren die TelemetryFrame-Werte
bewusst ein (telemetryProvider.value bleibt auf dem letzten Stand, siehe
vorherigen Commit) - die Fusszeilen-Pille im Fly-Modus hat daraus bislang
weiterhin den zuletzt bekannten (ggf. laengst veralteten) Zustand
abgeleitet, statt den Verbindungsverlust selbst widerzuspiegeln.

- domain/telemetry/drone_status.dart: neuer DroneState.disconnected,
  computeDroneStatus() bekommt einen neuen Parameter `connected` - hat
  Vorrang vor allem anderen (auch vor Emergency) und uebersteuert
  connectionQuality auf rot. positionQuality bleibt bewusst unveraendert
  (folgt weiterhin den eingefrorenen Werten) - laut Anfrage sollen nur
  Drohnen-Icon und Verbindungsqualitaet, nicht die Positionsqualitaet, rot
  erzwungen werden.
- fly_screen.dart: `connected: !telemetryAsync.hasError` statt der
  bisherigen Ableitung aus einzelnen Telemetriewerten.
- bottom_stats_bar.dart: neues Label/Farbe ("disconnected", rot) fuer den
  neuen Zustand in der Drohnen-Status-Pille.

Auf dem Pixel_10a-Emulator verifiziert: WiFi-Verbindungsart (noch nie
verbunden) zeigt bereits korrekt rotes Papierflieger-Icon, rote
Verbindungsqualitaet, rotes Satelliten-Icon und "disconnected" in rot;
Mock-Verbindung zeigt unveraendert normal gruen "ready".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 17:38:55 +02:00
Constantin LeueandClaude Sonnet 5 b09d00ab9f Rename the Fly-mode Warnings tab to System Messages, log connection/upload events
Renames DroneEvent/DroneEventSeverity/droneEventLogProvider/
DroneStatusWarningsPanel to SystemMessage/SystemMessageSeverity/
systemMessageLogProvider/DroneStatusMessagesPanel throughout, matching
what the tab now actually shows - general system messages, not just
drone-health warnings.

Adds a new SystemMessageSeverity.info level (blue) for messages that
aren't a warning/error, and three new message sources on top of the
existing battery/failsafe detection:
- "Connected", logged the first time telemetryProvider produces a frame
  after having none - the mirror image of the existing "Connection lost"
  detection (which already fires on the first stream error after having
  had data), so no new transport-specific dependency was needed.
- "Connection type changed to X", from watching connectionSettingsProvider
  (skips the initial load so it doesn't fire on every app start).
- "Mission sent (N waypoints)" / "Mission upload failed: ...", logged
  from FlyScreen's send handler via a new public log() method on the
  notifier, alongside the existing snackbar.

Verified on the Pixel_10a emulator: entering Fly mode logs "Connected"
once the mock telemetry starts, and tapping the send button logs
"Mission sent (3 waypoints)" right after the snackbar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 10:47:38 +02:00
Constantin LeueandClaude Sonnet 5 13475cc4f3 Implement waypoint mission upload (MSP_SET_WP) and Fly-mode send button
Fills in MspFlightControllerLink.uploadMission(), which previously just
threw UnimplementedError, plus a new verifyMission() (both now on the
generic FlightControllerLink interface, protocol-neutral by signature -
MAVLink/ArduPilot get their own implementation later without touching
callers).

All iNAV-specific encoding lives in the new msp_waypoint_codec.dart:
- encodeMspSetWaypoint(): the 21-byte MSP_SET_WP payload. Action/P1/P2/P3
  byte layout was checked against the actual iNAV 9.1.0 source
  (navigation.c/navigation.h), not guessed - notably our generic `loiter`
  action has no configurable duration, so it maps to
  NAV_WP_ACTION_HOLD_TIME with the max representable p1 (int16 max, not
  0xFFFF - that would read as -1 and end the hold immediately instead of
  never).
- parseMspWpGetInfo(): decodes MSP_WP_GETINFO's validity/count fields,
  used by verifyMission() to confirm the FC actually accepted the full
  mission (Doku 2.2/4.5 Ready-to-Fly-Gate: "upload + verified").

uploadMission() sends one MSP_SET_WP per waypoint in order (iNAV has no
batch command - WP#1 resets the FC's mission list, every next number must
follow immediately, only the last carries NAV_WP_FLAG_LAST) and rejects
missions above NAV_MAX_WAYPOINTS upfront instead of silently truncating.
MissionSyncService now calls the real verifyMission() instead of always
confirming, throwing MissionVerificationException when the FC doesn't
confirm the mission.

FlyScreen's footer swaps the warnings button for a send button (Doku:
"ersetze den warnings button mit einem wp send button", pink horizontal
PaperPlaneIcon, matching the existing paper-plane drone iconography) -
warnings/event log stay reachable via the drone status pill's Warnings
tab. BottomStatsBar/MissionFooterBar gained onSendTap/sending in place of
the old forceShowWarningsButton.

Verified end to end on the Pixel_10a emulator: tapping send with WLAN as
the active connection type triggers the real WifiNetworkSpecifier flow
through MspFlightControllerLink (correctly reports "no devices found" -
expected, no real mLRS bridge on the emulator); the actual MSP_SET_WP/
MSP_WP_GETINFO wire behavior is covered by tests against a fake FC
responder over LoopbackTransport instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 09:10:32 +02:00
Constantin Leue 8e4df2d0d3 drone status menue with status and critical event log implemented 2026-08-04 23:15:56 +02:00
Constantin Leue 75736793f2 optimizing footer optics by unifying icon and text size 2026-08-04 16:08:45 +02:00
Constantin Leue 1a17fe5b36 drone status pill in footer for fly mode implemented. footer layout and sizes homogenized 2026-08-04 15:47:53 +02:00
Constantin Leue a66c199b37 live mission details in footer implemented 2026-08-04 09:48:01 +02:00
Constantin Leue cb91436fe4 UI collapsable height profile, english settings menue 2026-07-31 09:49:28 +02:00
Constantin Leue 2ce1444b90 UI adjustments 2026-07-30 21:34:48 +02:00
Constantin Leue 078d58dbc0 MSP protocol implementation and settings menu 2026-07-30 21:18:48 +02:00
Constantin LeueandClaude Sonnet 5 c5a8c68a8e Lock map rotation, add mission warnings list with per-issue navigation
Kartendrehung (Doku 4.6): flutter_map's Zwei-Finger-Twist-Geste bewusst
deaktiviert (InteractionOptions mit InteractiveFlag.all & ~rotate in
mission_map.dart) - die Drohne fliegt nordorientiert, eine gedrehte Karte
wuerde die Peilung von Wegpunkten/Reticle nur verwirren, zumal der HTML-
Demonstrator gar keine Drehung kennt.

Warnungsliste (Doku 3.10), analog dem #warningsBtn/#warningsPanel-Flow im
HTML-Demonstrator:

- domain/mission/mission_warnings.dart: computeMissionWarnings() sammelt
  alle aktuell zutreffenden Probleme (Route-Geometrie ausserhalb der
  Drohnenfaehigkeiten, zu starker Wind, zu geringer Bodenabstand,
  Reichweite/Ausdauer ueberschritten) zu einer Liste aus {text, action}-
  Eintraegen - reine Funktion aus bereits vorhandenen Zwischenergebnissen,
  unabhaengig von Riverpod/Widgets testbar.
- domain/wind/wind_math.dart: computeLegWindBad() ergaenzt (Bodenge-
  schwindigkeit <= 1 m/s oder Windgeschwindigkeit > Drohnen-Maximum) -
  bislang gab es nur die Windkomponente/Bodengeschwindigkeit selbst, aber
  keine "zu stark"-Markierung.
- ui/widgets/warnings_panel.dart: Vollbild-Liste, jede Zeile verweist per
  Aktionslabel auf den Loesungs-Screen ("Open altitude view"/"Open speed
  view"/"Show on map").
- ui/widgets/bottom_stats_bar.dart: roter Warnungs-Button neben den
  Mission-/Drohnen-Chips, nur sichtbar solange Warnungen aktiv sind.
- ui/widgets/waypoint_list_panel.dart: `_PanelTab` zu `PanelTab` public
  gemacht plus neuer `initialTab`-Parameter, damit das Panel direkt auf
  dem Altitude- oder Speed-Tab geoeffnet werden kann statt immer auf der
  Liste zu starten.
- ui/providers/map_controller_provider.dart: fitMapToWaypoints()-Hilfs-
  funktion extrahiert und in MapSearchControls' Fit-Button (vorher eigene
  Kopie der Logik) sowie fuer die "Show on map"-Warnungsaktion
  wiederverwendet.
- ui/screens/plan/plan_screen.dart: berechnet die Warnungsliste, ersetzt
  den bisherigen Einzel-Banner (nur "Route exceeds...") durch alle
  aktuellen Warnungstexte (durch " · " getrennt, wie im HTML-Demonstrator)
  und verdrahtet Warnungs-Button/-Liste mit der passenden Navigation.

Getestet: 4 neue Unit-Tests fuer computeLegWindBad, 7 fuer
computeMissionWarnings (inkl. Grenzfaelle: veraltetes Gelaendeprofil,
kein Limit bei maxRangeM/maxEnduranceMin <= 0), 2 neue Widget-Tests
(Warnungs-Button erscheint bei Reichweiten-Ueberschreitung und oeffnet
die Liste; Antippen einer Wind-Warnung oeffnet den Speed-Tab direkt).
Alle 110 Tests sowie flutter analyze bestehen. Manuell auf dem
Pixel_10a-Emulator verifiziert: Steigraten-Ueberschreitung (Altitude auf
220m bei kurzer Distanz) macht Route/Banner/Warnungs-Button rot,
Warnungsliste zeigt den Eintrag mit "Show on map", Antippen schliesst
die Liste und zoomt die Karte korrekt auf die Bounding-Box beider
Wegpunkte.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 19:51:42 +02:00
Constantin LeueandClaude Sonnet 5 aaec7f0f72 Add mission/drone profile management with autosave and persistence
Vervollstaendigt Doku 3.5/3.6/7.1/7.3/7.5: ein Vollbild-Verwaltungsmenue
fuer Missionen und Drohnenprofile (per Tabs umschaltbar, wie im HTML-
Demonstrator #missionsPanel gemeinsam mit der Profilliste), Autosave der
aktuell bearbeiteten Mission, Drift-Persistenz statt der bisherigen
In-Memory-only currentMissionProvider, sowie Anzeige/Zugang ueber neue
Chips in der Fusszeile (HTML-Demonstrator: #missionNameBtn/#droneNameBtn).

Datenbank (services/database/): Missions-Tabelle um updatedAt ergaenzt,
neue DroneProfiles- und AppSettingsTable-Tabellen (schemaVersion 2 mit
onUpgrade-Migration). Drei Repositories kapseln Drift-Zugriff +
JSON-(De-)Serialisierung der Wegpunktliste: MissionRepository,
DroneProfileRepository, AppSettingsRepository - je mit Unit-Tests gegen
eine In-Memory-Datenbank (NativeDatabase.memory()).

Provider: CurrentMissionMetaNotifier haelt Name/id der aktuellen Mission
und autosaved sie 800ms-debounced (identisch zum HTML-Demonstrator:
scheduleAutosave()/flushPendingAutosave()) - inklusive Wiederherstellung
des zuletzt bearbeiteten Autosave-Drafts beim App-Start (Doku 7.5).
ActiveDroneProfileNotifier haelt das aktive Profil, seedet beim ersten
Start automatisch T1 Ranger und merkt sich die Auswahl ueber
AppSettingsTable neustart-fest.

UI: MissionsDronesPanel (Missions-Tab: Liste mit Umbenennen/Loeschen/
Laden; Drones-Tab: Liste mit Bearbeiten/Loeschen/Auswaehlen, faellt beim
Loeschen des aktiven Profils auf das naechste zurueck). DroneProfileEditor
als Formular (bewusste Vereinfachung gegenueber den Swipe-Gesten-Chips
des HTML-Demonstrators - bei elf Feldern ist ein normales Formular auf
einem Mobilgeraet zugaenglicher). Rename per AlertDialog+TextField statt
Browser-prompt().

Aktives Drohnenprofil ist jetzt tatsaechlich wirksam statt nur eine feste
Anzeige: Speed-/Alt-Drehrad-Grenzen, Fangradius neuer Wegpunkte und die
Flugpfad-Machbarkeitspruefung (Kurvenradius, Steig-/Sinkrate) in
PlanScreen und WaypointListPanel lesen jetzt activeDroneProfileProvider
statt der bisherigen statischen DefaultDroneProfile-Konstanten (die als
T1-Ranger-Seed-Werte weiterleben).

Beim Verifizieren zwei echte Bugs in BottomStatsBar gefunden und
gefixt: ein Stack+Align-ohne-Factor blaehte sich auf unbegrenzte Groesse
auf und verschob die Details-Pille aus ihrer sichtbaren Position (durch
zwei gleich grosse Expanded-Bereiche ersetzt); zwei ConstrainedBox-Chips
nebeneinander verursachten auf schmaleren Breiten einen RenderFlex-
Overflow (durch Flexible ersetzt).

Widget-Tests: AppShell/PlanScreen initialisieren beim Start jetzt die
echte Datenbank - alle Tests, die AppShell pumpen, ueberschreiben
appDatabaseProvider testweise mit einer In-Memory-Instanz. Ausserdem
mussten mehrere Tests den neuen 800ms-Autosave-Timer abwarten (wie
zuvor schon beim WaypointChip-Flash-Timer etabliert), damit nach
Testende kein Timer mehr aussteht.

Verifiziert: flutter analyze (0 issues), flutter test (59/59, davon 14
neue Repository-Tests), manuell auf Pixel_10a-Emulator - Wegpunkte
werden automatisch als "New mission" gespeichert und erscheinen in der
Liste, Umbenennen/Laden/Loeschen funktionieren, neues Drohnenprofil mit
abweichender Max-Speed wird angelegt+aktiviert+in der Fusszeile
angezeigt, Loeschen des aktiven Profils faellt auf das verbleibende
zurueck.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 14:59:09 +02:00
Constantin LeueandClaude Sonnet 5 d3c5a68636 Add waypoint list panel and bottom stats bar
- BottomStatsBar: persistent "Details: N WP · dist m · duration" pill
  (HTML prototype's #bottomBar/#statsRow), tap opens the full waypoint
  list. Mission/drone name and the Send button are intentionally not part
  of this - mission library and upload aren't wired up yet.
- A warn banner ("Route exceeds drone capabilities at highlighted point")
  appears above it whenever buildRouteGeometry finds a bad vertex or leg -
  a direct, low-effort payoff from the fillet geometry work.
- WaypointListPanel: full-screen overlay listing every waypoint
  (altitude/speed/execute/catch radius), matching the prototype's List
  tab. Only one row editable at a time; editing swaps plain text for
  draggable WaypointChip values and an execute dropdown. Row tap (outside
  edit mode) centers the map on that waypoint and closes the panel; delete
  removes it and adjusts the reticle's target index the same way the halo
  menu's remove already does.
- WaypointChip: vertical-drag value chip (HTML prototype's .value-chip),
  fixed step of 10 per 14px matching chipFieldConfig - simpler than the
  original's dual vertical/horizontal axis-lock gesture, which felt like
  more fidelity than the interaction warrants here.
- Provider gained setCatchRadius and setAction (direct assignment, as
  opposed to toggleAction's toggle-to-none semantics used by the halo
  menu) to support the list's editable fields.
- Altitude/Speed full-chart tabs from the prototype (drag-to-adjust
  altitude/speed profile charts) are a separate, larger feature and not
  part of this step.

Centering on a waypoint from the list calls MapController.move(), which -
like the reticle's auto-snap - doesn't emit MoveEnd, so snap detection is
invoked manually afterward to match the prototype's resulting behavior.

Added 4 widget tests (open empty list, row-tap centers and closes, delete
removes and updates the empty state, chip drag updates altitude) - one
required a pump(duration) after the drag to flush the chip's 120ms flash
Timer, which was otherwise still pending at test teardown. All 15 tests
and flutter analyze pass. Manually verified on the Pixel_10a emulator:
opened the list with two waypoints, edited a row's altitude via chip drag
(confirmed value change, accounting for device-pixel-ratio scaling vs the
widget-test logical-pixel case), and confirmed row-tap re-centers/closes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 07:25:32 +02:00