Szinkronizáció javítása,

This commit is contained in:
2026-07-12 02:14:49 +02:00
parent c73adaf9a1
commit 4bf73c6cc7
3 changed files with 86 additions and 20 deletions
+33 -4
View File
@@ -13,6 +13,10 @@ class PhoneGpsConnection implements GnssConnection {
final _positionController = StreamController<Position>.broadcast(); final _positionController = StreamController<Position>.broadcast();
StreamSubscription<Position>? _positionSub; StreamSubscription<Position>? _positionSub;
int _retryCount = 0;
static const _maxRetries = 5;
Timer? _retryTimer;
@override @override
Stream<String> get nmeaLines => const Stream.empty(); // Nincs NMEA Stream<String> get nmeaLines => const Stream.empty(); // Nincs NMEA
@@ -42,20 +46,44 @@ class PhoneGpsConnection implements GnssConnection {
} }
_stateController.add(GnssConnectionState.connected); _stateController.add(GnssConnectionState.connected);
_retryCount = 0;
await _startPositionStream();
}
// Belső GPS folyamatos olvasása Future<void> _startPositionStream() async {
await _positionSub?.cancel();
_positionSub = Geolocator.getPositionStream( _positionSub = Geolocator.getPositionStream(
locationSettings: const LocationSettings( locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high, accuracy: LocationAccuracy.high,
distanceFilter: 0, // Folyamatos frissítés distanceFilter: 0, // Folyamatos frissítés
), ),
).listen((Position pos) { ).listen(
_positionController.add(pos); (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 @override
Future<void> disconnect() async { Future<void> disconnect() async {
_retryTimer?.cancel();
await _positionSub?.cancel(); await _positionSub?.cancel();
_stateController.add(GnssConnectionState.disconnected); _stateController.add(GnssConnectionState.disconnected);
} }
@@ -67,6 +95,7 @@ class PhoneGpsConnection implements GnssConnection {
@override @override
void dispose() { void dispose() {
_retryTimer?.cancel();
_positionSub?.cancel(); _positionSub?.cancel();
_positionController.close(); _positionController.close();
_stateController.close(); _stateController.close();
+18 -2
View File
@@ -2,6 +2,7 @@ import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:terepi_seged/services/app_database.dart'; import 'package:terepi_seged/services/app_database.dart';
import 'package:terepi_seged/services/app_logger.dart';
import 'stakeout_service.dart'; import 'stakeout_service.dart';
@@ -52,8 +53,23 @@ class StakeoutSyncService extends GetxService {
} }
Future<bool> _pushThenPull() async { Future<bool> _pushThenPull() async {
await _push(); try {
return _pull(); 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 ───────────────────────────────────────────────────────── // ── PUSH ─────────────────────────────────────────────────────────
+35 -14
View File
@@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:supabase_flutter/supabase_flutter.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/contact_service.dart';
import 'package:terepi_seged/services/device_identity_service.dart'; import 'package:terepi_seged/services/device_identity_service.dart';
import 'package:terepi_seged/services/stakeout_sync_service.dart'; import 'package:terepi_seged/services/stakeout_sync_service.dart';
@@ -81,31 +82,35 @@ class TsSyncService extends GetxService {
lastError.value = ''; lastError.value = '';
try { try {
lastError.value = '';
// Eszköz-regiszter frissítése (last_seen_at). // Eszköz-regiszter frissítése (last_seen_at).
if (Get.isRegistered<DeviceIdentityService>()) { if (Get.isRegistered<DeviceIdentityService>()) {
await DeviceIdentityService.to.registerDevice(); await _isolate(
'eszköz regisztációja', DeviceIdentityService.to.registerDevice);
} }
await _discoverMemberProjects(); await _isolate('tagság felderítése', _discoverMemberProjects);
await _push(); await _isolate('feltöltés', _push);
await _pull(); await _isolate('letöltés', _pull);
// Megosztott rétegek (5. lépés) — ha a service be van kötve. // Megosztott rétegek (5. lépés) — ha a service be van kötve.
if (Get.isRegistered<LayerSyncService>()) { if (Get.isRegistered<LayerSyncService>()) {
await LayerSyncService.to.pullAll(); await _isolate('rétegek', LayerSyncService.to.pullAll);
} }
if (Get.isRegistered<StakeoutSyncService>()) { if (Get.isRegistered<StakeoutSyncService>()) {
await StakeoutSyncService.to.sync(); await _isolate('kitűzés', StakeoutSyncService.to.sync);
} }
if (Get.isRegistered<ContactService>()) { if (Get.isRegistered<ContactService>()) {
await ContactService.to.flush(); await _isolate('kapcsolatok', () => ContactService.to.flush());
} }
lastSyncedAt.value = DateTime.now(); lastSyncedAt.value = DateTime.now();
} catch (e) { } catch (e, s) {
lastError.value = e.toString(); lastError.value = e.toString();
AppLogger.e('TsSyncService - SyncNow', lastError.value,
error: e, stack: s);
} finally { } finally {
await refreshPendingCount(); await refreshPendingCount();
isSyncing.value = false; isSyncing.value = false;
@@ -139,11 +144,11 @@ class TsSyncService extends GetxService {
// ═════════════════════════════════════════════════════════════════ // ═════════════════════════════════════════════════════════════════
Future<void> _push() async { Future<void> _push() async {
await _pushProjects(); await _isolate('projektek push', _pushProjects);
await _pushMeasuredPoints(); await _isolate('mérési pontok', _pushMeasuredPoints);
await pushTracks(); await _isolate('track-ek push', pushTracks);
await pushTrackPoints(); await _isolate('track-pontok push', pushTrackPoints);
await _pushNoteItems(); await _isolate('jegyzetek push', _pushNoteItems);
} }
Future<void> _pushProjects() async { Future<void> _pushProjects() async {
@@ -287,7 +292,7 @@ class TsSyncService extends GetxService {
// Minden szinkronizált (nem lokális) projekt. // Minden szinkronizált (nem lokális) projekt.
final projects = await AppDatabase.instance.listProjects(); final projects = await AppDatabase.instance.listProjects();
for (final p in projects.where((p) => !p.isLocalOnly)) { 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; if (localIso == null || localIso.isEmpty) return null;
return DateTime.parse(localIso).toUtc().toIso8601String(); 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<void> _isolate(String label, Future<void> 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';
}
}
} }