import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:dmc_app/domain/waypoint/flat_waypoint_list.dart'; import 'package:dmc_app/transport/msp/msp_waypoint_codec.dart'; Waypoint _wp({ double lat = 52.5, double lon = 13.4, double alt = 120, double speed = 15, WaypointAction action = WaypointAction.none, int? jumpTargetIndex, int? jumpRepeatCount, }) => Waypoint( lat: lat, lon: lon, altitudeM: alt, speedMs: speed, catchRadiusM: 60, action: action, jumpTargetIndex: jumpTargetIndex, jumpRepeatCount: jumpRepeatCount, ); /// Liest die 21-Byte MSP_SET_WP-Payload wie der echte FC (`fc_msp.c`, /// `case MSP_SET_WP`) - dient nur der Testverifikation, dupliziert bewusst /// nicht encodeMspSetWaypoint(). ({ int wpNo, int action, int lat, int lon, int alt, int p1, int p2, int p3, int flag, }) _decode(Uint8List payload) { final d = ByteData.sublistView(payload); return ( wpNo: d.getUint8(0), action: d.getUint8(1), lat: d.getInt32(2, Endian.little), lon: d.getInt32(6, Endian.little), alt: d.getInt32(10, Endian.little), p1: d.getInt16(14, Endian.little), p2: d.getInt16(16, Endian.little), p3: d.getInt16(18, Endian.little), flag: d.getUint8(20), ); } void main() { group('encodeMspSetWaypoint', () { test('Payload ist immer genau 21 Byte lang (dataSize-Check des FC)', () { final payload = encodeMspSetWaypoint(_wp(), wireIndex: 1, isLast: true); expect(payload.length, 21); }); test('wp_no, lat/lon (1e7), Hoehe (cm) und flag werden korrekt kodiert', () { final payload = encodeMspSetWaypoint( _wp(lat: 52.5, lon: 13.4, alt: 120), wireIndex: 3, isLast: false, ); final d = _decode(payload); expect(d.wpNo, 3); expect(d.lat, 525000000); expect(d.lon, 134000000); expect(d.alt, 12000); expect(d.flag, 0); }); test('isLast setzt NAV_WP_FLAG_LAST (0xA5)', () { final payload = encodeMspSetWaypoint(_wp(), wireIndex: 1, isLast: true); expect(_decode(payload).flag, 0xA5); }); test('WaypointAction.none -> NAV_WP_ACTION_WAYPOINT, p1 = Speed in cm/s', () { final d = _decode(encodeMspSetWaypoint(_wp(speed: 15), wireIndex: 1, isLast: false)); expect(d.action, 0x01); expect(d.p1, 1500); expect(d.p2, 0); expect(d.p3, 0); }); test('WaypointAction.landing -> NAV_WP_ACTION_LAND, p1 = Speed in cm/s', () { final d = _decode(encodeMspSetWaypoint( _wp(speed: 12, action: WaypointAction.landing), wireIndex: 1, isLast: false, )); expect(d.action, 0x08); expect(d.p1, 1200); expect(d.p2, 0); }); test( 'WaypointAction.loiter -> NAV_WP_ACTION_HOLD_TIME mit maximalem p1 ' '(int16-Max statt 0xFFFF, sonst wuerde der FC das Halten sofort statt ' 'dauerhaft beenden), p2 = Speed in cm/s', () { final d = _decode(encodeMspSetWaypoint( _wp(speed: 10, action: WaypointAction.loiter), wireIndex: 1, isLast: false, )); expect(d.action, 0x03); expect(d.p1, 0x7FFF); expect(d.p2, 1000); }); test( 'WaypointAction.jump -> NAV_WP_ACTION_JUMP, p1 = 1-basierte Ziel-WP#, ' 'p2/p3 = Wiederholzahl', () { final d = _decode(encodeMspSetWaypoint( _wp(action: WaypointAction.jump, jumpTargetIndex: 2, jumpRepeatCount: 5), wireIndex: 4, isLast: false, )); expect(d.action, 0x06); // 0-basierter App-Index 2 -> 1-basierte FC-WP# 3. expect(d.p1, 3); expect(d.p2, 5); expect(d.p3, 5); }); test('WaypointAction.jump ohne jumpRepeatCount kodiert -1 (endlos)', () { final d = _decode(encodeMspSetWaypoint( _wp(action: WaypointAction.jump, jumpTargetIndex: 0), wireIndex: 2, isLast: false, )); expect(d.p2, -1); expect(d.p3, -1); }); }); group('parseMspWpGetInfo', () { test('dekodiert maxWaypoints/isValid/waypointCount', () { final info = parseMspWpGetInfo(Uint8List.fromList([0, 15, 1, 5])); expect(info.maxWaypoints, 15); expect(info.isValid, isTrue); expect(info.waypointCount, 5); }); test('isValid == false bei 0', () { final info = parseMspWpGetInfo(Uint8List.fromList([0, 15, 0, 0])); expect(info.isValid, isFalse); }); }); }