92 lines
3.7 KiB
Dart
92 lines
3.7 KiB
Dart
import 'dart:typed_data';
|
|
|
|
/// Transportzustand, protokollneutral (Doku Kommunikationsschicht Abschnitt 2).
|
|
enum LinkState { disconnected, connecting, connected, error }
|
|
|
|
/// Grund eines [LinkState.error] (Doku Abschnitt 3, Robustheit/Abnahme):
|
|
/// Bluetooth aus und Berechtigung abgelehnt brauchen jeweils einen eigenen,
|
|
/// erklaerenden Hinweis in der Oberflaeche statt eines generischen
|
|
/// "Verbindung fehlgeschlagen".
|
|
enum LinkErrorReason {
|
|
/// Bluetooth-Adapter ist aus. Kann ab Android 12 nicht mehr programmatisch
|
|
/// eingeschaltet werden - die Oberflaeche muss zu den Systemeinstellungen
|
|
/// verlinken.
|
|
bluetoothOff,
|
|
|
|
/// Berechtigung wurde abgelehnt, darf aber erneut angefragt werden.
|
|
permissionDenied,
|
|
|
|
/// Berechtigung wurde dauerhaft abgelehnt ("nicht mehr fragen"). Android
|
|
/// zeigt den Dialog kein zweites Mal - die Oberflaeche muss zu den
|
|
/// App-Einstellungen verlinken.
|
|
permissionDeniedPermanently,
|
|
|
|
/// Kein gekoppeltes Zielgeraet konnte automatisch bestimmt werden (weder
|
|
/// gemerkte MAC-Adresse noch ein Geraet mit "mLRS"-Praefix). Die
|
|
/// Oberflaeche muss eine Auswahlliste der gekoppelten Geraete anbieten.
|
|
deviceSelectionRequired,
|
|
|
|
/// Verbindungsaufbau zum bestimmten Geraet ist fehlgeschlagen oder wurde
|
|
/// getrennt.
|
|
connectionFailed,
|
|
|
|
/// Die per WifiNetworkSpecifier angeforderte mLRS-Netz konnte nicht
|
|
/// zustande kommen (Doku Kommunikationsschicht v2, Abschnitt 4) - z.B.
|
|
/// Nutzer hat den Systemdialog abgelehnt/keine passende SSID in Reichweite.
|
|
/// Android meldet das ueber `onUnavailable()` statt einer Exception.
|
|
networkUnavailable,
|
|
|
|
/// Das zuvor verfuegbare mLRS-Netz wurde waehrend einer bestehenden
|
|
/// Verbindung getrennt (`onLost()`) - z.B. ausser Reichweite oder Bridge
|
|
/// ausgeschaltet. Der Socket ist damit ungueltig und muss samt
|
|
/// Bindungsfenster neu erzeugt werden (Doku Abschnitt 8).
|
|
networkLost,
|
|
|
|
/// Sonstiger, nicht naeher klassifizierter Fehler.
|
|
unknown,
|
|
}
|
|
|
|
/// Protokollneutrale Transportschnittstelle (Doku Kommunikationsschicht
|
|
/// Abschnitt 2). Alles oberhalb (MSP-Encoder/-Decoder) programmiert
|
|
/// ausschliesslich gegen diese Schnittstelle - kein Import von Bluetooth-
|
|
/// oder Netzwerkpaketen ausserhalb von transport/.
|
|
///
|
|
/// Implementierungen: BluetoothClassicTransport (MVP), UdpTransport
|
|
/// (spaeter, Doku Abschnitt 6), LoopbackTransport (Tests/Entwicklung ohne
|
|
/// Hardware).
|
|
abstract class LinkTransport {
|
|
Stream<Uint8List> get incoming;
|
|
Stream<LinkState> get state;
|
|
|
|
/// Grund des zuletzt gemeldeten [LinkState.error], oder null solange kein
|
|
/// Fehler aufgetreten ist bzw. nach erfolgreicher Verbindung.
|
|
LinkErrorReason? get lastErrorReason;
|
|
|
|
/// Freitext zum zuletzt gemeldeten Fehler, fuer Diagnose/Verbindungsprotokoll
|
|
/// (Doku Abschnitt 7). Nicht fuer die direkte Nutzeranzeige gedacht - dafuer
|
|
/// dient [lastErrorReason].
|
|
String? get lastErrorMessage;
|
|
|
|
Future<void> send(Uint8List data);
|
|
Future<void> connect();
|
|
Future<void> disconnect();
|
|
void dispose();
|
|
}
|
|
|
|
/// Wird von [LinkTransport.connect] geworfen, wenn der Verbindungsaufbau
|
|
/// vorab (also bevor ueberhaupt eine Socket-Verbindung versucht wird)
|
|
/// erkennbar scheitert - z.B. Bluetooth aus, Berechtigung fehlt, oder kein
|
|
/// Geraet automatisch bestimmbar. Der gleiche [reason] wird zeitgleich ueber
|
|
/// [LinkTransport.state]/[LinkTransport.lastErrorReason] gemeldet, damit
|
|
/// sowohl der aufrufende Code (durch Warten auf [LinkTransport.connect])
|
|
/// als auch reine Beobachter der Zustands-Streams reagieren koennen.
|
|
class LinkConnectException implements Exception {
|
|
const LinkConnectException(this.reason, this.message);
|
|
|
|
final LinkErrorReason reason;
|
|
final String message;
|
|
|
|
@override
|
|
String toString() => 'LinkConnectException($reason): $message';
|
|
}
|