From 20a8078f3cd1e87d0589803c73200b04b134f31a Mon Sep 17 00:00:00 2001 From: "torok.istvan" Date: Tue, 19 May 2026 00:10:41 +0200 Subject: [PATCH] =?UTF-8?q?GnssService=20jav=C3=ADt=C3=A1s,=20refrakt?= =?UTF-8?q?=C3=A1l=C3=A1s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/map_survey_controller.dart | 6 +- lib/services/gnss/gnss_service.dart | 101 ++++++++++++++---- lib/widgets/coordinate_panel.dart | 5 +- 3 files changed, 89 insertions(+), 23 deletions(-) diff --git a/lib/pages/map_survey/presentations/controllers/map_survey_controller.dart b/lib/pages/map_survey/presentations/controllers/map_survey_controller.dart index b8d47d3..00fe3df 100644 --- a/lib/pages/map_survey/presentations/controllers/map_survey_controller.dart +++ b/lib/pages/map_survey/presentations/controllers/map_survey_controller.dart @@ -129,6 +129,7 @@ class MapSurveyController extends GetxController { // ── Telefon GPS fallback ────────────────────────────────────────── StreamSubscription? _phoneLocationSub; + StreamSubscription? _gnssUpdateSub; // ── UI controllerek ─────────────────────────────────────────────── final pointIdController = TextEditingController(); @@ -161,7 +162,9 @@ class MapSurveyController extends GetxController { }; // ── GnssService pozíció változás → EOV, marker, NTRIP GGA ──── - ever(_gnss.gpsQuality, (_) => _onGnssUpdate()); + _gnssUpdateSub = _gnss.onDataUpdated.listen((_) { + _onGnssUpdate(); + }); // ── Supabase realtime ───────────────────────────────────────── _supaChannel = Supabase.instance.client @@ -194,6 +197,7 @@ class MapSurveyController extends GetxController { super.onClose(); _phoneLocationSub?.cancel(); + _gnssUpdateSub?.cancel(); await _supaChannel?.unsubscribe(); pointIdController.dispose(); diff --git a/lib/services/gnss/gnss_service.dart b/lib/services/gnss/gnss_service.dart index c97c67f..d1a7fe3 100644 --- a/lib/services/gnss/gnss_service.dart +++ b/lib/services/gnss/gnss_service.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; +import 'package:googleapis/privateca/v1.dart'; import 'package:nmea/nmea.dart'; import 'package:terepi_seged/services/gnss/gnss_device_service.dart'; import 'package:terepi_seged/services/gnss/phone_gps_connection.dart'; @@ -45,6 +46,9 @@ class GnssService extends GetxService { // ── RMC adatok (dátum/idő) ──────────────────────────────────────── final gpsDateTime = DateTime(2000).obs; + Timer? _reconnectTimer; + bool _isClosing = false; + // Segédmező: van-e érvényes adat bool get hasValidData => gpsQuality.value > 0; @@ -54,11 +58,16 @@ class GnssService extends GetxService { StreamSubscription? _stateSub; StreamSubscription? _positionSub; + final _updateController = StreamController.broadcast(); + Stream get onDataUpdated => _updateController.stream; + String _utcTime = ''; String _utcDate = ''; bool _intentionalDisconnection = false; + String _digitsOnly(String value) => value.replaceAll(RegExp(r'[^0-9]'), ''); + @override void onInit() { super.onInit(); @@ -106,14 +115,17 @@ class GnssService extends GetxService { break; case GnssConnectionType.btSerial: await connectBtSerial(device.address); + break; case GnssConnectionType.ble: await connectBle(device.address); + break; case GnssConnectionType.phoneGps: await _disconnect(); _connection = PhoneGpsConnection(); //connectionState.value = GnssConnectionState.disconnected; activeConnectionType.value = GnssConnectionType.phoneGps; await _doConnect('iternal'); + break; } } @@ -127,9 +139,13 @@ class GnssService extends GetxService { Future _doConnect(String address) async { _stateSub = _connection!.connectionState.listen((s) { connectionState.value = s; - if (s == GnssConnectionState.disconnected && - _intentionalDisconnection == false) { - Future.delayed(const Duration(seconds: 3), () => reconnect()); + if (s == GnssConnectionState.connected) { + _reconnectTimer?.cancel(); + return; + } + + if (s == GnssConnectionState.disconnected) { + _scheduleReconnect(); } }); @@ -141,6 +157,8 @@ class GnssService extends GetxService { } void _parseDirectPosition(Position pos) { + if (_isClosing) return; + latitude.value = pos.latitude; longitude.value = pos.longitude; altitude.value = pos.altitude; @@ -151,17 +169,22 @@ class GnssService extends GetxService { // A Geolocator a pontosságot (accuracy) méterben adja vissza latitudeError.value = pos.accuracy; longitudeError.value = pos.accuracy; - altitudeError.value = pos.altitudeAccuracy ?? 0.0; + altitudeError.value = pos.altitudeAccuracy; // Az RMC (idő) adatait is beállítjuk a telefon idejéből gpsDateTime.value = pos.timestamp; + + _emitUpdate(); } Future _disconnect() async { _intentionalDisconnection = true; + _reconnectTimer?.cancel(); + await _nmeaSub?.cancel(); await _positionSub?.cancel(); await _stateSub?.cancel(); + await _connection?.disconnect(); _connection?.dispose(); _connection = null; @@ -180,11 +203,15 @@ class GnssService extends GetxService { // ── NMEA parsing ────────────────────────────────────────────────── void _parseNmea(String line) { - if (line.startsWith('\$GNGGA') || line.startsWith('\$GPGGA')) { + if (_isClosing || line.length < 6 || !line.startsWith(r'$')) return; + + final sentenceType = line.substring(3, 6); + + if (sentenceType == 'GGA') { _parseGga(line); - } else if (line.startsWith('\$GNGST') && hasValidData) { + } else if (sentenceType == 'GST' && hasValidData) { _parseGst(line); - } else if (line.startsWith('\$GNRMC') && hasValidData) { + } else if (sentenceType == 'RMC' && hasValidData) { _parseRmc(line); } } @@ -205,7 +232,11 @@ class GnssService extends GetxService { hdop.value = s.hdop; lastGgaLine.value = line; _utcTime = s.utcOfPositionFix; - } catch (_) {} + + _emitUpdate(); + } catch (e) { + print('GGA parsing error: $e'); + } } void _parseGst(String line) { @@ -215,30 +246,45 @@ class GnssService extends GetxService { latitudeError.value = s.latitudeError; longitudeError.value = s.longitudeError; altitudeError.value = s.heightError; - } catch (_) {} + } catch (e) { + print('GST parse error: $e'); + } } 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]}'), + final date = _digitsOnly(_utcDate); + final time = _digitsOnly(s.utcOfPositionFix); + + if (date.length >= 6 && time.length >= 6) { + gpsDateTime.value = DateTime.utc( + 2000 + int.parse(date.substring(4, 6)), + int.parse(date.substring(2, 4)), + int.parse(date.substring(0, 2)), + int.parse(time.substring(0, 2)), + int.parse(time.substring(2, 4)), + int.parse(time.substring(4, 6)), ); } - } catch (_) {} + } catch (e) { + print('RMC parse error: $e'); + } } @override - void onClose() { - _disconnect(); + void onClose() async { + _isClosing = true; + + await _disconnect(); + + if (!_updateController.isClosed) { + _updateController.close(); + } + super.onClose(); } @@ -273,4 +319,19 @@ class GnssService extends GetxService { print('Nem sikerült lekérni a kezdőpozíciót: $e'); } } + + void _emitUpdate() { + if (_isClosing || _updateController.isClosed) return; + _updateController.add(null); + } + + void _scheduleReconnect() { + if (_intentionalDisconnection || _isClosing) return; + if (_reconnectTimer?.isActive ?? false) return; + + _reconnectTimer = Timer(const Duration(seconds: 3), () { + if (_intentionalDisconnection || _isClosing) return; + unawaited(reconnect()); + }); + } } diff --git a/lib/widgets/coordinate_panel.dart b/lib/widgets/coordinate_panel.dart index 3484b89..490e9e1 100644 --- a/lib/widgets/coordinate_panel.dart +++ b/lib/widgets/coordinate_panel.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; +import 'package:terepi_seged/pages/map_survey/presentations/controllers/map_survey_controller.dart'; import 'package:terepi_seged/services/ntrip_service.dart'; import '../services/gnss/gnss_service.dart'; @@ -35,7 +36,7 @@ class CoordinatePanel extends StatelessWidget { }); /// Gyors factory — MapSurveyController mezőiből. - factory CoordinatePanel.fromController(dynamic ctrl) { + factory CoordinatePanel.fromController(MapSurveyController ctrl) { final y = RxDouble(0.0); final x = RxDouble(0.0); // Figyeli az eov változást és frissíti Y/X-et @@ -45,7 +46,7 @@ class CoordinatePanel extends StatelessWidget { }); return CoordinatePanel( eovY: y, - eovX: x, + eovX: ctrl.eov.value.X.obs, horError: ctrl.gpsLatitudeError as RxDouble, vertError: ctrl.gpsAltitudeError as RxDouble, altitudeMsl: ctrl.gpsAltitude as RxDouble,