SharedMapWidget és térkép nézet refraktorálása
This commit is contained in:
@@ -92,4 +92,8 @@ class BtSerialGnssConnection implements GnssConnection {
|
||||
_nmeaController.close();
|
||||
_stateController.close();
|
||||
}
|
||||
|
||||
void sendData(Uint8List data) {
|
||||
_connection?.output.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,68 @@
|
||||
// lib/services/gnss/gnss_service.dart
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:nmea/nmea.dart';
|
||||
import 'package:terepi_seged/services/gnss/gnss_device_service.dart';
|
||||
|
||||
import '../../gnss_sentences/gngga.dart';
|
||||
import 'gnss_connection.dart';
|
||||
import '../../gnss_sentences/gngst.dart';
|
||||
import '../../gnss_sentences/gnrmc.dart';
|
||||
import 'bt_serial_gnss_connection.dart';
|
||||
import 'ble_gnss_connection.dart';
|
||||
import 'gnss_device_service.dart';
|
||||
import 'gnss_connection.dart';
|
||||
|
||||
class GnssService extends GetxService {
|
||||
static GnssService get to => Get.find();
|
||||
|
||||
GnssConnection? _connection;
|
||||
|
||||
// Reaktív állapot — a controllerek Obx-szel figyelhetik
|
||||
// ── Kapcsolat állapot ─────────────────────────────────────────────
|
||||
final connectionState = GnssConnectionState.disconnected.obs;
|
||||
final activeConnectionType = Rxn<GnssConnectionType>();
|
||||
|
||||
// Parsed NMEA adatok
|
||||
// ── GGA adatok ────────────────────────────────────────────────────
|
||||
final latitude = 0.0.obs;
|
||||
final longitude = 0.0.obs;
|
||||
final altitude = 0.0.obs;
|
||||
final geoidSeparation = 0.0.obs;
|
||||
final altitude = 0.0.obs; // MSL (ortometrikus)
|
||||
final geoidSeparation = 0.0.obs; // N — geoid undulációja
|
||||
final gpsQuality = 0.obs;
|
||||
final utcFix = ''.obs;
|
||||
final satelliteCount = 0.obs;
|
||||
final hdop = 0.0.obs;
|
||||
|
||||
// Utolsó nyers GGA sor — NtripService küldi vissza a casternek
|
||||
final lastGgaLine = ''.obs;
|
||||
|
||||
// ── GST adatok (pontossági hibák) ─────────────────────────────────
|
||||
final latitudeError = 0.0.obs;
|
||||
final longitudeError = 0.0.obs;
|
||||
final altitudeError = 0.0.obs;
|
||||
|
||||
// ── RMC adatok (dátum/idő) ────────────────────────────────────────
|
||||
final gpsDateTime = DateTime(2000).obs;
|
||||
|
||||
// Segédmező: van-e érvényes adat
|
||||
bool get hasValidData => gpsQuality.value > 0;
|
||||
|
||||
// ── Belső ─────────────────────────────────────────────────────────
|
||||
final NmeaDecoder _decoder = NmeaDecoder();
|
||||
StreamSubscription? _nmeaSub;
|
||||
StreamSubscription? _stateSub;
|
||||
String _utcTime = '';
|
||||
String _utcDate = '';
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_decoder.registerTalkerSentence('GGA', (l) => Gngga(raw: l));
|
||||
_decoder
|
||||
..registerTalkerSentence('GGA', (l) => Gngga(raw: l))
|
||||
..registerTalkerSentence('GST', (l) => Gngst(raw: l))
|
||||
..registerTalkerSentence('RMC', (l) => Gnrmc(raw: l));
|
||||
}
|
||||
|
||||
// ── Kapcsolódás ──────────────────────────────────────────────────
|
||||
// ── Kapcsolódás ───────────────────────────────────────────────────
|
||||
|
||||
Future<void> connectBtSerial(String macAddress) async {
|
||||
await _disconnect();
|
||||
@@ -60,6 +85,27 @@ class GnssService extends GetxService {
|
||||
await _doConnect(deviceId);
|
||||
}
|
||||
|
||||
/// Eszközváltás — GnssDevicePicker hívja.
|
||||
Future<void> onDeviceChanged(GnssDevice device) async {
|
||||
switch (device.type) {
|
||||
case GnssConnectionType.btSerial:
|
||||
await connectBtSerial(device.address);
|
||||
case GnssConnectionType.ble:
|
||||
await connectBle(device.address);
|
||||
case GnssConnectionType.phoneGps:
|
||||
await _disconnect();
|
||||
connectionState.value = GnssConnectionState.disconnected;
|
||||
activeConnectionType.value = GnssConnectionType.phoneGps;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reconnect() async {
|
||||
final device = GnssDeviceService.to.selectedDevice.value;
|
||||
if (device == null) return;
|
||||
await _disconnect();
|
||||
await onDeviceChanged(device);
|
||||
}
|
||||
|
||||
Future<void> _doConnect(String address) async {
|
||||
_stateSub = _connection!.connectionState.listen((s) {
|
||||
connectionState.value = s;
|
||||
@@ -74,26 +120,74 @@ class GnssService extends GetxService {
|
||||
await _connection?.disconnect();
|
||||
_connection?.dispose();
|
||||
_connection = null;
|
||||
connectionState.value = GnssConnectionState.disconnected;
|
||||
}
|
||||
|
||||
// ── NMEA parsing — egy helyen, nem háromban ──────────────────────
|
||||
Future<void> disconnect() => _disconnect();
|
||||
|
||||
/// RTCM adat továbbítása a GNSS vevőnek (NtripService hívja).
|
||||
void sendToReceiver(Uint8List data) {
|
||||
if (_connection == null) return;
|
||||
if (connectionState.value != GnssConnectionState.connected) return;
|
||||
(_connection as BtSerialGnssConnection?)?.sendData(data);
|
||||
}
|
||||
|
||||
// ── NMEA parsing ──────────────────────────────────────────────────
|
||||
|
||||
void _parseNmea(String line) {
|
||||
if (!line.startsWith('\$GNGGA') && !line.startsWith('\$GPGGA')) return;
|
||||
if (line.startsWith('\$GNGGA') || line.startsWith('\$GPGGA')) {
|
||||
_parseGga(line);
|
||||
} else if (line.startsWith('\$GNGST') && hasValidData) {
|
||||
_parseGst(line);
|
||||
} else if (line.startsWith('\$GNRMC') && hasValidData) {
|
||||
_parseRmc(line);
|
||||
}
|
||||
}
|
||||
|
||||
void _parseGga(String line) {
|
||||
try {
|
||||
final sentence = _decoder.decode(line);
|
||||
if (sentence == null || !sentence.valid || sentence is! Gngga) return;
|
||||
if (sentence.gpsQualityIndicator == 0) return;
|
||||
final s = _decoder.decode(line);
|
||||
if (s == null || !s.valid || s is! Gngga) return;
|
||||
if (s.gpsQualityIndicator == 0) return;
|
||||
|
||||
latitude.value = sentence.latitude;
|
||||
longitude.value = sentence.longitude;
|
||||
altitude.value = sentence.altitudeAboveMeanSeaLevel;
|
||||
geoidSeparation.value = sentence.geoidSeparation;
|
||||
gpsQuality.value = sentence.gpsQualityIndicator;
|
||||
utcFix.value = sentence.utcOfPositionFix;
|
||||
satelliteCount.value = sentence.numberOfSvsInUse;
|
||||
hdop.value = sentence.hdop;
|
||||
latitude.value = s.latitude;
|
||||
longitude.value = s.longitude;
|
||||
altitude.value = s.altitudeAboveMeanSeaLevel;
|
||||
geoidSeparation.value = s.geoidSeparation;
|
||||
gpsQuality.value = s.gpsQualityIndicator;
|
||||
utcFix.value = s.utcOfPositionFix;
|
||||
satelliteCount.value = s.numberOfSvsInUse;
|
||||
hdop.value = s.hdop;
|
||||
lastGgaLine.value = line;
|
||||
_utcTime = s.utcOfPositionFix;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
void _parseGst(String line) {
|
||||
try {
|
||||
final s = _decoder.decode(line);
|
||||
if (s == null || !s.valid || s is! Gngst) return;
|
||||
latitudeError.value = s.latitudeError;
|
||||
longitudeError.value = s.longitudeError;
|
||||
altitudeError.value = s.heightError;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
void _parseRmc(String line) {
|
||||
try {
|
||||
final s = _decoder.decode(line);
|
||||
if (s == null || !s.valid || s is! Gnrmc) return;
|
||||
_utcDate = s.date;
|
||||
if (_utcDate.length >= 6 && _utcTime.length >= 6) {
|
||||
gpsDateTime.value = DateTime(
|
||||
2000 + int.parse('${_utcDate[4]}${_utcDate[5]}'),
|
||||
int.parse('${_utcDate[2]}${_utcDate[3]}'),
|
||||
int.parse('${_utcDate[0]}${_utcDate[1]}'),
|
||||
int.parse('${_utcTime[0]}${_utcTime[1]}'),
|
||||
int.parse('${_utcTime[2]}${_utcTime[3]}'),
|
||||
int.parse('${_utcTime[4]}${_utcTime[5]}'),
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -102,24 +196,4 @@ class GnssService extends GetxService {
|
||||
_disconnect();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
// ── Eszközváltás — a GnssDevicePickerDialog hívja ───────────────
|
||||
|
||||
Future<void> onDeviceChanged(GnssDevice device) async {
|
||||
switch (device.type) {
|
||||
case GnssConnectionType.btSerial:
|
||||
await connectBtSerial(device.address);
|
||||
|
||||
case GnssConnectionType.ble:
|
||||
await connectBle(device.address);
|
||||
|
||||
case GnssConnectionType.phoneGps:
|
||||
// Külső GPS kapcsolat lezárása ha volt
|
||||
await _disconnect();
|
||||
connectionState.value = GnssConnectionState.disconnected;
|
||||
activeConnectionType.value = GnssConnectionType.phoneGps;
|
||||
// A telefon GPS kezelése a map_controller _startPhoneGps()-ben van
|
||||
// itt csak jelezzük hogy phone módra váltottunk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
// lib/services/ntrip_service.dart
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// NTRIP kapcsolatot kezelő singleton service.
|
||||
///
|
||||
/// Felelőssége:
|
||||
/// - Socket kapcsolat az NTRIP casterhez
|
||||
/// - RTCM adatok fogadása és továbbítása a GNSS vevőnek
|
||||
/// - GGA mondatok küldése a casternek (5 másodpercenként)
|
||||
/// - Beállítások tárolása SharedPreferences-ben
|
||||
///
|
||||
/// Használat:
|
||||
/// ```dart
|
||||
/// // Csatlakozás előtt add meg a callback-et:
|
||||
/// NtripService.to.onRtcmData = (data) => connection.output.add(data);
|
||||
/// await NtripService.to.connect();
|
||||
/// ```
|
||||
class NtripService extends GetxService {
|
||||
static NtripService get to => Get.find();
|
||||
|
||||
// ── Reaktív állapot ───────────────────────────────────────────────
|
||||
final isConnected = false.obs;
|
||||
final receivedBytes = 0.obs;
|
||||
final packetCount = 0.obs;
|
||||
final ggaSentCount = 0.obs;
|
||||
final ggaLastSentTime = ''.obs;
|
||||
|
||||
// ── Beállítások ───────────────────────────────────────────────────
|
||||
final host = '84.206.45.44'.obs; // gnssnet.hu IP
|
||||
final port = 2101.obs;
|
||||
final mountpoint = 'SGO_RTK3.2'.obs;
|
||||
final username = ''.obs;
|
||||
final password = ''.obs;
|
||||
|
||||
// ── UI controllerek (beállítás dialóghoz) ────────────────────────
|
||||
final hostController = TextEditingController();
|
||||
final portController = TextEditingController();
|
||||
final mountpointController = TextEditingController();
|
||||
final usernameController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
|
||||
// ── Belső állapot ────────────────────────────────────────────────
|
||||
Socket? _socket;
|
||||
StreamSubscription? _socketSub;
|
||||
String _lastGgaMessage = '';
|
||||
DateTime _lastGgaSentTime =
|
||||
DateTime.now().subtract(const Duration(seconds: 30));
|
||||
|
||||
/// Callback: RTCM adat érkezett → a controller továbbítja a GNSS vevőnek.
|
||||
/// Beállítás: `NtripService.to.onRtcmData = (data) => connection.output.add(data);`
|
||||
Function(Uint8List)? onRtcmData;
|
||||
|
||||
// ── Inicializálás ────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<void> onInit() async {
|
||||
super.onInit();
|
||||
await _loadSettings();
|
||||
_syncControllersFromValues();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
disconnect();
|
||||
hostController.dispose();
|
||||
portController.dispose();
|
||||
mountpointController.dispose();
|
||||
usernameController.dispose();
|
||||
passwordController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
// ── Kapcsolat ────────────────────────────────────────────────────
|
||||
|
||||
Future<void> connect() async {
|
||||
if (isConnected.value) return;
|
||||
|
||||
try {
|
||||
_socket = await Socket.connect(
|
||||
InternetAddress(host.value),
|
||||
port.value,
|
||||
timeout: const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
_socket!.encoding = ascii;
|
||||
isConnected.value = true;
|
||||
receivedBytes.value = 0;
|
||||
packetCount.value = 0;
|
||||
|
||||
// HTTP fejléc összeállítása
|
||||
final header = _buildNtripHeader();
|
||||
_socket!.add(_toUint8List(header));
|
||||
|
||||
// Adatfogadás
|
||||
_socketSub = _socket!.listen(
|
||||
_onData,
|
||||
onError: _onError,
|
||||
onDone: _onDone,
|
||||
);
|
||||
} catch (e) {
|
||||
isConnected.value = false;
|
||||
Get.snackbar(
|
||||
'NTRIP hiba',
|
||||
'Nem sikerült csatlakozni: $e',
|
||||
backgroundColor: const Color(0xFFB71C1C),
|
||||
colorText: const Color(0xFFFFFFFF),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
if (!isConnected.value && _socket == null) return;
|
||||
await _socketSub?.cancel();
|
||||
await _socket?.flush();
|
||||
_socket?.close();
|
||||
_socket?.destroy();
|
||||
_socket = null;
|
||||
isConnected.value = false;
|
||||
receivedBytes.value = 0;
|
||||
}
|
||||
|
||||
void reconnect() async {
|
||||
await disconnect();
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
await connect();
|
||||
}
|
||||
|
||||
// ── GGA küldés ───────────────────────────────────────────────────
|
||||
|
||||
/// Az NMEA feldolgozó hívja minden GGA mondatnál.
|
||||
/// 5 másodpercenként küld egyet az NTRIP casternek.
|
||||
void onGgaReceived(String ggaLine, String utcTime) {
|
||||
_lastGgaMessage = ggaLine;
|
||||
|
||||
if (!isConnected.value) return;
|
||||
if (ggaLine.isEmpty) return;
|
||||
|
||||
final elapsed = DateTime.now().difference(_lastGgaSentTime).inSeconds;
|
||||
if (elapsed < 5) return;
|
||||
|
||||
_sendGga(ggaLine);
|
||||
ggaSentCount.value++;
|
||||
ggaLastSentTime.value = utcTime;
|
||||
_lastGgaSentTime = DateTime.now();
|
||||
}
|
||||
|
||||
void _sendGga(String ggaMessage) {
|
||||
if (_socket == null || !isConnected.value) return;
|
||||
_socket!.add(_toUint8List('$ggaMessage\r\n'));
|
||||
}
|
||||
|
||||
// ── Belső adatfogadás ────────────────────────────────────────────
|
||||
|
||||
void _onData(Uint8List data) {
|
||||
receivedBytes.value = data.length;
|
||||
packetCount.value++;
|
||||
|
||||
// Csak RTCM adat (>14 byte) kerül a GNSS vevőhöz
|
||||
if (data.length > 14) {
|
||||
onRtcmData?.call(data);
|
||||
}
|
||||
}
|
||||
|
||||
void _onError(dynamic error) {
|
||||
_socket?.destroy();
|
||||
isConnected.value = false;
|
||||
Get.snackbar(
|
||||
'NTRIP kapcsolat hiba',
|
||||
error.toString(),
|
||||
backgroundColor: const Color(0xFFB71C1C),
|
||||
colorText: const Color(0xFFFFFFFF),
|
||||
);
|
||||
}
|
||||
|
||||
void _onDone() async {
|
||||
await _socketSub?.cancel();
|
||||
await _socket?.flush();
|
||||
_socket?.destroy();
|
||||
_socket = null;
|
||||
isConnected.value = false;
|
||||
receivedBytes.value = 0;
|
||||
}
|
||||
|
||||
// ── HTTP fejléc összeállítás ─────────────────────────────────────
|
||||
|
||||
String _buildNtripHeader() {
|
||||
final auth = _toBase64('${username.value}:${password.value}');
|
||||
final host = '${this.host.value}:${port.value}';
|
||||
|
||||
return 'GET /${mountpoint.value} HTTP/1.1\r\n'
|
||||
'User-Agent: SharpGps iter.dk\r\n'
|
||||
'Accept: */*\r\n'
|
||||
'Connection: close\r\n'
|
||||
'Authorization: Basic $auth\r\n'
|
||||
'Host:$host\r\n'
|
||||
'Ntrip-Version:Ntrip/2.0\r\n'
|
||||
'\r\n';
|
||||
}
|
||||
|
||||
// ── Beállítások mentése / betöltése ──────────────────────────────
|
||||
|
||||
Future<void> saveSettings() async {
|
||||
// Szinkronizálás a controllerektől az Rx értékekbe
|
||||
host.value = hostController.text.trim();
|
||||
port.value = int.tryParse(portController.text) ?? 2101;
|
||||
mountpoint.value = mountpointController.text.trim();
|
||||
username.value = usernameController.text.trim();
|
||||
password.value = passwordController.text;
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('ntrip_host', host.value);
|
||||
await prefs.setInt('ntrip_port', port.value);
|
||||
await prefs.setString('ntrip_mountpoint', mountpoint.value);
|
||||
await prefs.setString('ntrip_username', username.value);
|
||||
await prefs.setString('ntrip_password', password.value);
|
||||
}
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
host.value = prefs.getString('ntrip_host') ?? '84.206.45.44';
|
||||
port.value = prefs.getInt('ntrip_port') ?? 2101;
|
||||
mountpoint.value = prefs.getString('ntrip_mountpoint') ?? 'SGO_RTK3.2';
|
||||
username.value = prefs.getString('ntrip_username') ?? '';
|
||||
password.value = prefs.getString('ntrip_password') ?? '';
|
||||
}
|
||||
|
||||
void _syncControllersFromValues() {
|
||||
hostController.text = host.value;
|
||||
portController.text = port.value.toString();
|
||||
mountpointController.text = mountpoint.value;
|
||||
usernameController.text = username.value;
|
||||
// Jelszót nem pre-töltjük biztonsági okokból
|
||||
}
|
||||
|
||||
// ── Segédfüggvények ──────────────────────────────────────────────
|
||||
|
||||
String _toBase64(String str) => base64.encode(ascii.encode(str));
|
||||
|
||||
Uint8List _toUint8List(String str) => Uint8List.fromList(str.codeUnits);
|
||||
}
|
||||
Reference in New Issue
Block a user