Report a Tripod or Cinematic switch the aircraft did not take
On the Mavic Pro the TRIPOD_MODE_ENABLED write succeeds but the flight controller keeps Tripod off, and the panel showed "Tripod on" for a moment before silently flipping back. The gate now reads the key back after the write and refuses with "The aircraft kept Tripod off." when it did not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
789818679a
commit
a7984d7f28
2 files changed
+31
-4
No files matched your search
@@ -942,10 +942,21 @@ class SafetyGate {
|
||||
: 'Speed drops to about 1 m/s and the sticks become very gentle.'),
|
||||
];
|
||||
|
||||
Future<void> setSlowMode({required bool cinematic, required bool on}) async {
|
||||
/// The write can succeed while the flight controller keeps the old value (a
|
||||
/// Mavic Pro kept Tripod off on the ground and in the simulator), so it is
|
||||
/// read back after [settle] and a refusal is reported instead of the switch
|
||||
/// silently flipping back.
|
||||
Future<void> setSlowMode({required bool cinematic, required bool on, Duration settle = const Duration(milliseconds: 800)}) async {
|
||||
final List<GateCheck> checks = checkSlowMode(cinematic: cinematic, on: on);
|
||||
if (checks.any((GateCheck c) => c.blocks)) throw MissionRefused(checks);
|
||||
await dji.setKey(DjiKey.flightController(cinematic ? 'CINEMATIC_MODE_ENABLED' : 'TRIPOD_MODE_ENABLED'), on);
|
||||
final DjiKey key = DjiKey.flightController(cinematic ? 'CINEMATIC_MODE_ENABLED' : 'TRIPOD_MODE_ENABLED');
|
||||
await dji.setKey(key, on);
|
||||
await Future<void>.delayed(settle);
|
||||
final Object? now = await dji.getKey(key);
|
||||
if (now is bool && now != on) {
|
||||
final String name = cinematic ? 'Cinematic' : 'Tripod';
|
||||
throw MissionRefused(<GateCheck>[GateCheck.block('The aircraft kept $name ${now ? 'on' : 'off'}.')]);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Hyperlapse (plan P7) ────────────────────────────────────────────────────
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:latlong2/latlong.dart' show LatLng;
|
||||
import 'package:pilotvault_fly/dji_service.dart';
|
||||
import 'package:pilotvault_fly/flight_model.dart';
|
||||
import 'package:pilotvault_fly/fly/action_rail_state.dart';
|
||||
import 'package:pilotvault_fly/fly/fly_prefs.dart';
|
||||
@@ -146,12 +147,21 @@ void main() {
|
||||
test('Tripod and Cinematic are key writes behind the gate', () async {
|
||||
final FakeDjiService dji = FakeDjiService();
|
||||
final SafetyGate g = SafetyGate(model: _model(telemetryTick()), dji: dji);
|
||||
await g.setSlowMode(cinematic: false, on: true);
|
||||
await g.setSlowMode(cinematic: false, on: true, settle: Duration.zero);
|
||||
expect(dji.calls.last, 'setKey(TRIPOD_MODE_ENABLED, true)');
|
||||
await g.setSlowMode(cinematic: true, on: false);
|
||||
await g.setSlowMode(cinematic: true, on: false, settle: Duration.zero);
|
||||
expect(dji.calls.last, 'setKey(CINEMATIC_MODE_ENABLED, false)');
|
||||
});
|
||||
|
||||
test('a Tripod write the aircraft ignores is reported, not silent', () async {
|
||||
final _KeepsTripodOff dji = _KeepsTripodOff();
|
||||
final SafetyGate g = SafetyGate(model: _model(telemetryTick()), dji: dji);
|
||||
await expectLater(
|
||||
g.setSlowMode(cinematic: false, on: true, settle: Duration.zero),
|
||||
throwsA(isA<MissionRefused>().having((MissionRefused e) => e.toString(), 'message', contains('kept Tripod off'))),
|
||||
);
|
||||
});
|
||||
|
||||
test('Free hyperlapse: frames from the video length, then the camera\'s interval mode', () async {
|
||||
const HyperlapseSpec h = HyperlapseSpec(intervalSeconds: 3, videoSeconds: 10);
|
||||
expect(h.frames, 250);
|
||||
@@ -339,3 +349,9 @@ class _FlatGround implements ElevationSource {
|
||||
@override
|
||||
Future<List<double>> elevations(List<LatLng> points) async => List<double>.filled(points.length, 12);
|
||||
}
|
||||
|
||||
/// A flight controller that accepts the Tripod write and keeps it off (Mavic Pro, 2026-10-22).
|
||||
class _KeepsTripodOff extends FakeDjiService {
|
||||
@override
|
||||
Future<Object?> getKey(DjiKey key) async => key.key == 'TRIPOD_MODE_ENABLED' ? false : super.getKey(key);
|
||||
}
|
||||
Reference in new issue
Block a user