import 'package:flutter/foundation.dart'; import 'state/parse.dart'; /// The three directions MSDK's FlightAssistant sets distances for. enum SensingDirection { horizontal('Horizontal', 'Horizontal'), up('Upward', 'Upward'), down('Downward', 'Downward'); const SensingDirection(this.sdk, this.label); /// `DJIFlightAssistantObstacleSensingDirection` name. final String sdk; final String label; } /// Which of the two distances. enum SensingKind { /// `setVisualObstaclesAvoidanceDistance`: where the aircraft brakes. avoidance, /// `setMaxPerceptionDistance`: how far it looks (the warning distance). perception, } /// The aircraft's obstacle braking and perception distances (plan P8, GO 4 / /// Pilot's Vision "obstacle distance" rows). A null value is a direction the /// aircraft refused to report, and its row hides. @immutable class SensingDistances { const SensingDistances({this.avoidance = const {}, this.perception = const {}}); factory SensingDistances.fromBridge(Map m) { Map read(Object? v) { final Map? g = asMap(v); return {for (final SensingDirection d in SensingDirection.values) d: finite(g?[d.sdk])}; } return SensingDistances(avoidance: read(m['avoidance']), perception: read(m['perception'])); } final Map avoidance; final Map perception; double? value(SensingKind k, SensingDirection d) => (k == SensingKind.avoidance ? avoidance : perception)[d]; bool get isEmpty => avoidance.values.every((double? v) => v == null) && perception.values.every((double? v) => v == null); SensingDistances withValue(SensingKind k, SensingDirection d, double v) => SensingDistances( avoidance: k == SensingKind.avoidance ? {...avoidance, d: v} : avoidance, perception: k == SensingKind.perception ? {...perception, d: v} : perception, ); /// Range and step a row offers. MSDK 4.18 documents no limits; these follow /// DJI Pilot's sliders and the flight controller refuses anything it does not /// accept (the row then rolls back). Unverified on hardware. static ({double min, double max, double step}) range(SensingKind k, SensingDirection d) => switch ((k, d)) { (SensingKind.avoidance, SensingDirection.down) => (min: 0.5, max: 3, step: 0.1), (SensingKind.avoidance, _) => (min: 1, max: 10, step: 0.5), (SensingKind.perception, SensingDirection.down) => (min: 1, max: 10, step: 0.5), (SensingKind.perception, _) => (min: 5, max: 40, step: 1), }; /// [v] moved [steps] steps within the range, snapped to the step. static double stepped(SensingKind k, SensingDirection d, double v, int steps) { final ({double min, double max, double step}) r = range(k, d); final double next = ((v / r.step).round() + steps) * r.step; return double.parse(next.clamp(r.min, r.max).toStringAsFixed(2)); } }