P7b of the GO 4-parity plan. Both modes open from the MODES picker on the live video, where they take over the touches the camera and gimbal used. ActiveTrack: drag a box round the subject (or tap it), then GO. The operator's own box follows on the video, coloured by confidence, with confirm / reject when it asks and the reason when it cannot follow. Trace, Profile and Spotlight switch live from the mode panel. TapFly: tap where to fly, then GO; the aircraft's heading point and any obstacle bypass show while it flies, and the speed changes live. Forward only on aircraft without rear sensors; Backward and Free otherwise. Both go through new SafetyGate checks (flying, P mode, above 2 m, nothing else in control, battery), start on GO when the check is clean and ask for the slide when it warns. Stop on the rail and Exit stop them. MissionBridge now listens to the ActiveTrack and TapFly operators. Not yet run on a simulator or aircraft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
160 lines
4.9 KiB
Dart
160 lines
4.9 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.',
|
|
built: true,
|
|
terms: <String>[
|
|
..._common,
|
|
'ActiveTrack follows what the camera sees. It can lose the subject behind trees or in crowds, and it does not see obstacles to the sides or behind on every aircraft.',
|
|
'Do not use it to follow cars or boats at speed, or over water and roads you cannot see the end of.',
|
|
],
|
|
),
|
|
ModeInfo(
|
|
mode: IntelligentMode.tapFly,
|
|
label: 'TapFly',
|
|
icon: 'chevronsRight',
|
|
group: 'Vision',
|
|
blurb: 'Tap the video to fly that way.',
|
|
built: true,
|
|
terms: <String>[
|
|
..._common,
|
|
'TapFly keeps flying towards the point until you stop it. Obstacle avoidance only works in the direction the sensors face, and not at all in Free.',
|
|
],
|
|
),
|
|
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];
|
|
}
|