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>
151 lines
4.4 KiB
Dart
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];
|
|
}
|