MSP protocol implementation and settings menu
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:dmc_app/transport/msp/msp_frame.dart';
|
||||
import 'package:dmc_app/transport/msp/msp_frame_decoder.dart';
|
||||
|
||||
/// Fuettert [bytes] in Stuecken der Groesse [chunkSize] und sammelt alle
|
||||
/// dekodierten Rahmen ueber den gesamten Strom.
|
||||
List<MspFrame> _decodeInChunks(Uint8List bytes, int chunkSize) {
|
||||
final decoder = MspFrameDecoder();
|
||||
final frames = <MspFrame>[];
|
||||
for (var offset = 0; offset < bytes.length; offset += chunkSize) {
|
||||
final end = (offset + chunkSize).clamp(0, bytes.length);
|
||||
frames.addAll(decoder.addBytes(bytes.sublist(offset, end)));
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
void main() {
|
||||
final altitudePayload = Uint8List.fromList(
|
||||
[0xD2, 0x04, 0x00, 0x00, 0xCE, 0xFF, 0xB0, 0x04, 0x00, 0x00],
|
||||
);
|
||||
final altitudeFrameBytes =
|
||||
encodeMspV2Frame(MspDirection.response, 109, altitudePayload);
|
||||
|
||||
// Abnahmekriterium (Doku Kommunikationsschicht 8): dieselbe Bytefolge in
|
||||
// Stuecken von 1, 7 und 512 Byte muss dasselbe Ergebnis liefern.
|
||||
for (final chunkSize in [1, 7, 512]) {
|
||||
test('dekodiert einen MSP_ALTITUDE-Rahmen bei Chunk-Groesse $chunkSize', () {
|
||||
final frames = _decodeInChunks(altitudeFrameBytes, chunkSize);
|
||||
|
||||
expect(frames, hasLength(1));
|
||||
expect(frames.single.direction, MspDirection.response);
|
||||
expect(frames.single.function, 109);
|
||||
expect(frames.single.payload, altitudePayload);
|
||||
});
|
||||
}
|
||||
|
||||
test('mehrere Rahmen hintereinander in einem Chunk werden alle erkannt', () {
|
||||
final gpsFrameBytes = encodeMspV2Frame(MspDirection.response, 106,
|
||||
Uint8List.fromList([1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
|
||||
|
||||
final combined = Uint8List.fromList([
|
||||
...altitudeFrameBytes,
|
||||
...gpsFrameBytes,
|
||||
]);
|
||||
|
||||
final frames = _decodeInChunks(combined, 512);
|
||||
|
||||
expect(frames, hasLength(2));
|
||||
expect(frames[0].function, 109);
|
||||
expect(frames[1].function, 106);
|
||||
});
|
||||
|
||||
test('fehlerhafte CRC wird verworfen, ohne den Strom zu verlieren', () {
|
||||
final corrupted = Uint8List.fromList(altitudeFrameBytes);
|
||||
corrupted[corrupted.length - 1] ^= 0xFF; // letztes Byte (CRC) kippen
|
||||
|
||||
final decoder = MspFrameDecoder();
|
||||
final framesFromCorrupted = decoder.addBytes(corrupted);
|
||||
expect(framesFromCorrupted, isEmpty);
|
||||
|
||||
// Direkt danach ein gueltiger Rahmen auf demselben Decoder - der
|
||||
// Automat muss wieder im Idle-Zustand sein und normal weiterlesen.
|
||||
final framesFromValid = decoder.addBytes(altitudeFrameBytes);
|
||||
expect(framesFromValid, hasLength(1));
|
||||
expect(framesFromValid.single.function, 109);
|
||||
});
|
||||
|
||||
test('Fehlerantwort (dir = \'!\') wird als solche erkannt', () {
|
||||
final errorFrameBytes =
|
||||
encodeMspV2Frame(MspDirection.error, 109, Uint8List(0));
|
||||
|
||||
final frames = MspFrameDecoder().addBytes(errorFrameBytes);
|
||||
|
||||
expect(frames, hasLength(1));
|
||||
expect(frames.single.direction, MspDirection.error);
|
||||
expect(frames.single.function, 109);
|
||||
});
|
||||
|
||||
test('Muell vor dem eigentlichen Rahmen wird ignoriert', () {
|
||||
final garbage = Uint8List.fromList([0x00, 0x24, 0x11, 0x58, 0xFF]);
|
||||
final combined = Uint8List.fromList([...garbage, ...altitudeFrameBytes]);
|
||||
|
||||
final frames = _decodeInChunks(combined, 7);
|
||||
|
||||
expect(frames, hasLength(1));
|
||||
expect(frames.single.function, 109);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user