From 4bf73c6cc73d4b137f2086fa66841339b37e1acd Mon Sep 17 00:00:00 2001 From: "torok.istvan" Date: Sun, 12 Jul 2026 02:14:49 +0200 Subject: [PATCH] =?UTF-8?q?Szinkroniz=C3=A1ci=C3=B3=20jav=C3=ADt=C3=A1sa,?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/services/gnss/phone_gps_connection.dart | 37 ++++++++++++++-- lib/services/stakeout_sync_service.dart | 20 ++++++++- lib/services/ts_sync_service.dart | 49 +++++++++++++++------ 3 files changed, 86 insertions(+), 20 deletions(-) diff --git a/lib/services/gnss/phone_gps_connection.dart b/lib/services/gnss/phone_gps_connection.dart index db95ac1..16d08d8 100644 --- a/lib/services/gnss/phone_gps_connection.dart +++ b/lib/services/gnss/phone_gps_connection.dart @@ -13,6 +13,10 @@ class PhoneGpsConnection implements GnssConnection { final _positionController = StreamController.broadcast(); StreamSubscription? _positionSub; + int _retryCount = 0; + static const _maxRetries = 5; + Timer? _retryTimer; + @override Stream get nmeaLines => const Stream.empty(); // Nincs NMEA @@ -42,20 +46,44 @@ class PhoneGpsConnection implements GnssConnection { } _stateController.add(GnssConnectionState.connected); + _retryCount = 0; + await _startPositionStream(); + } - // Belső GPS folyamatos olvasása + Future _startPositionStream() async { + await _positionSub?.cancel(); _positionSub = Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 0, // Folyamatos frissítés ), - ).listen((Position pos) { - _positionController.add(pos); - }); + ).listen( + (Position pos) { + _retryCount = 0; + _positionController.add(pos); + }, + onError: _handleStreamError, + onDone: () => + _handleStreamError(Exception('A GPS-stream váratlanul lezárult.')), + ); + } + + /// A Geolocator stream NEM öngyógyuló — lásd a PhoneGpsSource-nál már + /// javított, ugyanilyen hibát. Ez a kapcsolat idáig SEMMILYEN hiba + /// esetén nem próbálkozott újra, csendben, véglegesen elhallgatott. + void _handleStreamError(Object e) { + _retryCount++; + if (_retryCount > _maxRetries) { + _stateController.add(GnssConnectionState.error); + return; + } + _retryTimer?.cancel(); + _retryTimer = Timer(const Duration(seconds: 1), _startPositionStream); } @override Future disconnect() async { + _retryTimer?.cancel(); await _positionSub?.cancel(); _stateController.add(GnssConnectionState.disconnected); } @@ -67,6 +95,7 @@ class PhoneGpsConnection implements GnssConnection { @override void dispose() { + _retryTimer?.cancel(); _positionSub?.cancel(); _positionController.close(); _stateController.close(); diff --git a/lib/services/stakeout_sync_service.dart b/lib/services/stakeout_sync_service.dart index 63b30eb..21cd39d 100644 --- a/lib/services/stakeout_sync_service.dart +++ b/lib/services/stakeout_sync_service.dart @@ -2,6 +2,7 @@ import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:terepi_seged/services/app_database.dart'; +import 'package:terepi_seged/services/app_logger.dart'; import 'stakeout_service.dart'; @@ -52,8 +53,23 @@ class StakeoutSyncService extends GetxService { } Future _pushThenPull() async { - await _push(); - return _pull(); + try { + await _push().timeout(const Duration(seconds: 25)); + } catch (e, s) { + lastError.value = 'kitűzés push: $e'; + AppLogger.e('StackeoutSyncService - _pushThenPull', lastError.value, + error: e, stack: s); + } + try { + return _pull().timeout(const Duration(seconds: 25)); + } catch (e, s) { + lastError.value = lastError.value.isEmpty + ? 'kitűzés pull: $e' + : '${lastError.value} · kitűzés pull: $e'; + AppLogger.e('StackeoutSyncService - _pushThenPull', lastError.value, + error: e, stack: s); + return false; + } } // ── PUSH ───────────────────────────────────────────────────────── diff --git a/lib/services/ts_sync_service.dart b/lib/services/ts_sync_service.dart index b643842..ca499e1 100644 --- a/lib/services/ts_sync_service.dart +++ b/lib/services/ts_sync_service.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:get/get.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; +import 'package:terepi_seged/services/app_logger.dart'; import 'package:terepi_seged/services/contact_service.dart'; import 'package:terepi_seged/services/device_identity_service.dart'; import 'package:terepi_seged/services/stakeout_sync_service.dart'; @@ -81,31 +82,35 @@ class TsSyncService extends GetxService { lastError.value = ''; try { + lastError.value = ''; // Eszköz-regiszter frissítése (last_seen_at). if (Get.isRegistered()) { - await DeviceIdentityService.to.registerDevice(); + await _isolate( + 'eszköz regisztációja', DeviceIdentityService.to.registerDevice); } - await _discoverMemberProjects(); - await _push(); - await _pull(); + await _isolate('tagság felderítése', _discoverMemberProjects); + await _isolate('feltöltés', _push); + await _isolate('letöltés', _pull); // Megosztott rétegek (5. lépés) — ha a service be van kötve. if (Get.isRegistered()) { - await LayerSyncService.to.pullAll(); + await _isolate('rétegek', LayerSyncService.to.pullAll); } if (Get.isRegistered()) { - await StakeoutSyncService.to.sync(); + await _isolate('kitűzés', StakeoutSyncService.to.sync); } if (Get.isRegistered()) { - await ContactService.to.flush(); + await _isolate('kapcsolatok', () => ContactService.to.flush()); } lastSyncedAt.value = DateTime.now(); - } catch (e) { + } catch (e, s) { lastError.value = e.toString(); + AppLogger.e('TsSyncService - SyncNow', lastError.value, + error: e, stack: s); } finally { await refreshPendingCount(); isSyncing.value = false; @@ -139,11 +144,11 @@ class TsSyncService extends GetxService { // ═════════════════════════════════════════════════════════════════ Future _push() async { - await _pushProjects(); - await _pushMeasuredPoints(); - await pushTracks(); - await pushTrackPoints(); - await _pushNoteItems(); + await _isolate('projektek push', _pushProjects); + await _isolate('mérési pontok', _pushMeasuredPoints); + await _isolate('track-ek push', pushTracks); + await _isolate('track-pontok push', pushTrackPoints); + await _isolate('jegyzetek push', _pushNoteItems); } Future _pushProjects() async { @@ -287,7 +292,7 @@ class TsSyncService extends GetxService { // Minden szinkronizált (nem lokális) projekt. final projects = await AppDatabase.instance.listProjects(); for (final p in projects.where((p) => !p.isLocalOnly)) { - await _pullProject(p.id!, p.uuid); + await _isolate('letöltés (${p.name})', () => _pullProject(p.id!, p.uuid)); } } @@ -446,4 +451,20 @@ class TsSyncService extends GetxService { if (localIso == null || localIso.isEmpty) return null; return DateTime.parse(localIso).toUtc().toIso8601String(); } + + /// Egy lépés elszigetelt futtatása: hiba vagy időtúllépés esetén NEM + /// dobja tovább — csak feljegyzi és a szinkron a KÖVETKEZŐ lépéssel + /// folytatódik. Enélkül egyetlen hibás sor (típushiba, FK-ütközés stb.) + /// vagy egy beragadt hálózati hívás CSENDBEN leállítaná az összes + /// további lépést, minden ciklusban, örökre. + Future _isolate(String label, Future Function() fn, + {Duration timeout = const Duration(seconds: 25)}) async { + try { + await fn().timeout(timeout); + } catch (e) { + lastError.value = lastError.value.isEmpty + ? '$label: $e' + : '${lastError.value} · $label: $e'; + } + } }