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();
StreamSubscription<Position>? _positionSub;
int _retryCount = 0;
static const _maxRetries = 5;
Timer? _retryTimer;
@override
Stream<String> 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<void> _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<void> 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();
+18 -2
View File
@@ -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<bool> _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 ─────────────────────────────────────────────────────────
+35 -14
View File
@@ -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<DeviceIdentityService>()) {
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<LayerSyncService>()) {
await LayerSyncService.to.pullAll();
await _isolate('rétegek', LayerSyncService.to.pullAll);
}
if (Get.isRegistered<StakeoutSyncService>()) {
await StakeoutSyncService.to.sync();
await _isolate('kitűzés', StakeoutSyncService.to.sync);
}
if (Get.isRegistered<ContactService>()) {
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<void> _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<void> _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<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';
}
}
}