Files
PilotVault/Fly App/lib/fly/modes/intelligent_modes.dart
T
tajniak81andClaude Opus 5.5 43a66df8df Give the Fly App GO 4-style intelligent modes and a Waypoints editor
P7a of the GO 4-parity plan. The rail's GRID button becomes MODES, which
opens a picker of the product's intelligent flight modes with first-use
terms; Waypoints is the first one built.

Waypoints opens on the big map: tap to add waypoints or POIs, long-press
to drag, "+" to insert on a leg, and a point editor for altitude, speed,
heading (route / custom / POI), gimbal (set or aim at POI), camera action
and hover, and turn radius, with a swipe-to-adjust readout. Route settings,
a local mission library and undo sit in a tool column. Start runs the
SafetyGate's pre-start check in the slide-to-confirm dialog, then uploads
and starts; a run panel shows upload and flight progress with Pause,
Resume and Stop.

MissionBridge now listens to the waypoint operator and builds the full V1
mission (per-waypoint headings, turn direction, gimbal pitch, actions).
Not yet run on a simulator or aircraft.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:22:53 +02:00

151 lines
4.4 KiB
Dart

import '../product_profile.dart';
/// The Intelligent Flight Modes picker's catalogue (plan P7, GO 4's
/// `DJILeftMenuNavigationView` + `gs_main_view`).
///
/// Which modes a product has comes from [ProductProfile.modes] (the product
/// matrix until runtime capture confirms it); which of those this app can fly
/// yet is [ModeInfo.built]. A mode that is supported but not built is listed
/// greyed out rather than hidden, so the pilot sees what is coming.
class ModeInfo {
const ModeInfo({
required this.mode,
required this.label,
required this.icon,
required this.group,
required this.blurb,
required this.terms,
this.built = false,
});
final IntelligentMode mode;
final String label;
/// A `PVIcon` name.
final String icon;
/// `Map`, `Vision`, `Shots` or `Orientation`, as the picker groups them.
final String group;
/// One line under the tile.
final String blurb;
/// The first-use disclaimer: what the pilot stays responsible for.
final List<String> terms;
/// This app can fly the mode.
final bool built;
}
abstract final class ModeCatalog {
static const List<String> _common = <String>[
'You remain responsible for the flight. Keep the aircraft in sight and be ready to take over with the remote.',
'The aircraft flies itself in this mode. Push the remote\'s pause button, or tap Stop, to make it hover.',
];
static const List<ModeInfo> all = <ModeInfo>[
ModeInfo(
mode: IntelligentMode.waypoint,
label: 'Waypoints',
icon: 'route',
group: 'Map',
blurb: 'Plan a route on the map and fly it.',
built: true,
terms: <String>[
..._common,
'The route flies straight between waypoints at the altitudes you set. It does not avoid terrain, trees or buildings: check the route and its altitudes before you start.',
'If the remote loses signal the aircraft follows the route\'s signal-lost setting.',
],
),
ModeInfo(
mode: IntelligentMode.waypointV2,
label: 'Waypoints 2.0',
icon: 'route',
group: 'Map',
blurb: 'Enterprise waypoint missions.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.pointOfInterest,
label: 'Point of Interest',
icon: 'circle',
group: 'Map',
blurb: 'Circle a subject at a set radius.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.followMe,
label: 'Follow Me',
icon: 'user',
group: 'Map',
blurb: 'The aircraft follows this phone.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.activeTrack,
label: 'ActiveTrack',
icon: 'crosshair',
group: 'Vision',
blurb: 'Draw a box around a subject to follow it.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.tapFly,
label: 'TapFly',
icon: 'chevronsRight',
group: 'Vision',
blurb: 'Tap the video to fly that way.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.quickShots,
label: 'QuickShots',
icon: 'dronie',
group: 'Shots',
blurb: 'Dronie, Rocket, Circle, Helix…',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.hyperlapse,
label: 'Hyperlapse',
icon: 'hyperlapse',
group: 'Shots',
blurb: 'A moving timelapse.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.courseLock,
label: 'Course Lock',
icon: 'compass',
group: 'Orientation',
blurb: 'Forward stays the current heading.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.homeLock,
label: 'Home Lock',
icon: 'home',
group: 'Orientation',
blurb: 'Pulling back always flies home.',
terms: _common,
),
ModeInfo(
mode: IntelligentMode.tripod,
label: 'Tripod',
icon: 'gauge',
group: 'Orientation',
blurb: 'Slow, precise control for framing.',
terms: _common,
),
];
static const List<String> groups = <String>['Map', 'Vision', 'Shots', 'Orientation'];
static ModeInfo of(IntelligentMode m) => all.firstWhere((ModeInfo i) => i.mode == m);
/// The modes to list for [p]: every mode when no product is connected (a
/// preview, like the rest of the HUD), else the product's own.
static List<ModeInfo> forProfile(ProductProfile p) =>
<ModeInfo>[for (final ModeInfo i in all) if (!p.connected || p.supportsMode(i.mode)) i];
}