Kitűzés: kényelmi funkciók, pontok listája, export

This commit is contained in:
2026-07-06 14:26:16 +02:00
parent 6706b2b1ba
commit 2a2ce4bb92
9 changed files with 1238 additions and 33 deletions
+129 -16
View File
@@ -3,10 +3,13 @@ import 'dart:math' as math;
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:terepi_seged/controls/geoid_grid.dart';
import 'package:terepi_seged/services/app_database.dart';
import 'package:terepi_seged/services/coord_converter_service.dart';
import 'package:terepi_seged/services/gnss/gnss_service.dart';
import 'package:terepi_seged/services/project_service.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../../models/stakeout_point.dart';
@@ -49,11 +52,13 @@ class StakeoutService extends GetxService {
if (active.value == value) return;
active.value = value;
if (value) {
WakelockPlus.enable();
await load();
_applyPhase();
_scheduleHaptic();
} else {
_hapticTimer?.cancel();
WakelockPlus.disable();
}
}
@@ -74,6 +79,18 @@ class StakeoutService extends GetxService {
final hapticsEnabled = true.obs;
final soundEnabled = true.obs;
/// Átlagolás tároláskor
final averagingSeconds = 0.obs;
final isAveraging = false.obs;
final averagingElapsed = 0.obs;
/// Max. megengedett crossline eltolás (m; 0=nincs korlát)
final maxOffset = 0.0.obs;
late GeoidGrid geoidGrid;
// ── Aktuális pozíció (EOV) és navigációs értékek ────────────────
final hasPosition = false.obs;
final curEovY = 0.0.obs;
@@ -96,8 +113,11 @@ class StakeoutService extends GetxService {
bool _toleranceAnnounced = false;
@override
void onInit() {
void onInit() async {
super.onInit();
await _loadSettings();
geoidGrid = await GeoidGrid.load('assets/Grids/geoid_eht2014.gtx');
if (Get.isRegistered<GnssService>()) {
ever(GnssService.to.lastGgaLine, (_) => _onPosition());
}
@@ -109,6 +129,24 @@ class StakeoutService extends GetxService {
super.onClose();
}
Future<void> _loadSettings() async {
final prefs = await SharedPreferences.getInstance();
toleranceXY.value = prefs.getDouble('stakeout_tolerance') ?? 0.03;
averagingSeconds.value = prefs.getInt('stakeout_avg_seconds') ?? 0;
hapticsEnabled.value = prefs.getBool('stakeout_haptics') ?? true;
soundEnabled.value = prefs.getBool('stakeout_sound') ?? true;
maxOffset.value = prefs.getDouble('stakeout_max_offset') ?? 0;
}
Future<void> saveSettings() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble('stakeout_tolerance', toleranceXY.value);
await prefs.setInt('stakeout_avg_seconds', averagingSeconds.value);
await prefs.setBool('stakeout_haptics', hapticsEnabled.value);
await prefs.setBool('stakeout_sound', soundEnabled.value);
await prefs.setDouble('stakeout_max_offset', maxOffset.value);
}
// ═════════════════════════════════════════════════════════════════
// Betöltés / cél-kezelés
// ═════════════════════════════════════════════════════════════════
@@ -227,12 +265,18 @@ class StakeoutService extends GetxService {
!Get.isRegistered<CoordConverterService>()) {
return;
}
final lat = gnss.latitude.value;
final lon = gnss.longitude.value;
final alt = gnss.altitude.value;
final sep = gnss.geoidSeparation.value;
final p = CoordConverterService.to
.wgsToEovPoint(gnss.longitude.value, gnss.latitude.value);
final p = CoordConverterService.to.wgsToEovPoint(lon, lat);
curEovY.value = p.x;
curEovX.value = p.y;
curAlt.value = gnss.altitude.value;
curAlt.value = geoidGrid.toEovHeight(lat, lon, alt, sep)!;
//curAlt.value = gnss.altitude.value;
hasPosition.value = true;
// Haladási irány: legalább 0,5 m elmozdulásból (állva zajos lenne).
@@ -287,7 +331,7 @@ class StakeoutService extends GetxService {
if (withinTolerance.value && !wasWithin && !_toleranceAnnounced) {
_toleranceAnnounced = true;
if (hapticsEnabled.value) HapticFeedback.heavyImpact();
SystemSound.play(SystemSoundType.alert);
if (soundEnabled.value) SystemSound.play(SystemSoundType.alert);
} else if (!withinTolerance.value) {
_toleranceAnnounced = false;
}
@@ -330,6 +374,7 @@ class StakeoutService extends GetxService {
} else {
HapticFeedback.lightImpact();
}
if (soundEnabled.value) SystemSound.play(SystemSoundType.click);
_scheduleHaptic();
});
}
@@ -380,15 +425,29 @@ class StakeoutService extends GetxService {
// Műveletek (adatbázis: AppDatabase)
// ═════════════════════════════════════════════════════════════════
/// Tárolás a jelenlegi mért pozícióval. A rekordba az eltérés mindig
/// vonal-relatívan (inline/crossline) kerül; vonal híján É/K bontásban.
/// Tárolás a jelenlegi (vagy átlagolt) mért pozícióval. Ha az
/// [averagingSeconds] > 0, a megadott ideig gyűjti és átlagolja a
/// pozíciókat — csökkenti a pillanatnyi zaj hatását. A rekordba az
/// eltérés mindig vonal-relatívan (inline/crossline) kerül; vonal
/// híján É/K bontásban.
Future<StakeoutPoint?> storeCurrent() async {
final t = target.value;
if (t == null || !hasPosition.value) return null;
if (t == null || !hasPosition.value || isAveraging.value) return null;
final gnss = GnssService.to;
final dy = t.planEovY - curEovY.value;
final dx = t.planEovX - curEovX.value;
double useY = curEovY.value;
double useX = curEovX.value;
double useAlt = curAlt.value;
if (averagingSeconds.value > 0) {
final avg = await _collectAverage(averagingSeconds.value);
useY = avg.y;
useX = avg.x;
useAlt = avg.alt;
}
final dy = t.planEovY - useY;
final dx = t.planEovX - useX;
final lineBearing = lineBearingAt(t);
final rad = (lineBearing ?? 0) * math.pi / 180;
final inline =
@@ -396,16 +455,18 @@ class StakeoutService extends GetxService {
final crossline =
lineBearing != null ? dy * math.cos(rad) - dx * math.sin(rad) : dy;
final w = CoordConverterService.to.eovToWgsPoint(useY, useX);
final updated = t.copyWith(
status: StakeoutStatus.staked,
measuredEovY: curEovY.value,
measuredEovX: curEovX.value,
measuredEovZ: curAlt.value,
measuredLat: gnss.latitude.value,
measuredLon: gnss.longitude.value,
measuredEovY: useY,
measuredEovX: useX,
measuredEovZ: useAlt,
measuredLat: w.y,
measuredLon: w.x,
devInline: inline,
devCrossline: crossline,
devDz: t.planEovZ != null ? t.planEovZ! - curAlt.value : null,
devDz: t.planEovZ != null ? t.planEovZ! - useAlt : null,
fixQuality: gnss.gpsQuality.value,
accuracy: gnss.horizontalAccuracy,
stakedAt: DateTime.now(),
@@ -417,6 +478,36 @@ class StakeoutService extends GetxService {
return updated;
}
/// Pozíció-átlagolás: [seconds] másodpercig minden GGA-pozíciót
/// gyűjt, majd átlagol. Ha nem jött minta, a pillanatnyi értéket adja.
Future<({double y, double x, double alt})> _collectAverage(
int seconds) async {
isAveraging.value = true;
averagingElapsed.value = 0;
final ys = <double>[], xs = <double>[], alts = <double>[];
final worker = ever(curEovY, (_) {
ys.add(curEovY.value);
xs.add(curEovX.value);
alts.add(curAlt.value);
});
final ticker = Timer.periodic(
const Duration(seconds: 1), (_) => averagingElapsed.value++);
try {
await Future.delayed(Duration(seconds: seconds));
} finally {
worker.dispose();
ticker.cancel();
isAveraging.value = false;
}
if (ys.isEmpty) {
return (y: curEovY.value, x: curEovX.value, alt: curAlt.value);
}
double mean(List<double> l) => l.reduce((a, b) => a + b) / l.length;
return (y: mean(ys), x: mean(xs), alt: mean(alts));
}
Future<void> skipCurrent() async {
final t = target.value;
if (t == null) return;
@@ -428,6 +519,28 @@ class StakeoutService extends GetxService {
advance();
}
/// Státusz-állítás a listából (kihagyás / visszaállítás).
Future<void> setPointStatus(StakeoutPoint p, StakeoutStatus status) async {
final updated = p.copyWith(status: status);
await _db.updateStakeoutPoint(updated);
final i = points.indexWhere((e) => e.uuid == p.uuid);
if (i >= 0) points[i] = updated;
points.refresh();
if (target.value?.uuid == p.uuid && status != StakeoutStatus.pending) {
advance();
}
}
/// Pont törlése (soft delete — a szinkron viszi a többi eszközre is).
Future<void> deletePoint(StakeoutPoint p) async {
await _db.softDeleteStakeoutPoint(p.id!);
points.removeWhere((e) => e.uuid == p.uuid);
if (target.value?.uuid == p.uuid) {
target.value = _firstPending();
_recompute();
}
}
/// Transzverzális eltolt pont: a vonalra merőlegesen [dist] méterre
/// ([toRight] = jobbra a station-növekedés irányából nézve). Az új
/// pont lesz a cél; az eltolás-vektor a rekordba kerül.