fix(tactical): implement dynamic MGRS grid conversion, triangle of error resection accuracy, clean surveyed IDW elevation, and robust offline sync validation
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../config/app_config.dart';
|
||||
import 'landmark_database.dart';
|
||||
import 'offline_routing_engine.dart';
|
||||
import 'offline_routing_package_service.dart';
|
||||
|
||||
enum PackageSyncStatus {
|
||||
notInstalled,
|
||||
@@ -24,6 +25,9 @@ class OfflinePackageInfo {
|
||||
final bool canSyncNow;
|
||||
final int daysUntilNextSync;
|
||||
|
||||
/// هل غراف الطرق الحقيقي (Valhalla) مثبت على الجهاز؟
|
||||
final bool realRoadRoutingReady;
|
||||
|
||||
const OfflinePackageInfo({
|
||||
required this.packageId,
|
||||
required this.name,
|
||||
@@ -35,12 +39,13 @@ class OfflinePackageInfo {
|
||||
this.status = PackageSyncStatus.notInstalled,
|
||||
this.canSyncNow = true,
|
||||
this.daysUntilNextSync = 0,
|
||||
this.realRoadRoutingReady = false,
|
||||
});
|
||||
}
|
||||
|
||||
class OfflinePackageManager {
|
||||
static const String _pkgKey = 'offline_jordan_pkg_v2';
|
||||
static const int syncCooldownDays = 14; // 14-day server protection rule
|
||||
static const int syncCooldownDays = 14; // Cooldown enabled: updates allowed every 14 days
|
||||
static String get defaultServerUrl => AppConfig.serverUrl;
|
||||
static String get defaultApiKey => AppConfig.apiKey;
|
||||
|
||||
@@ -48,6 +53,13 @@ class OfflinePackageManager {
|
||||
static ValueNotifier<String> syncStatusMessage = ValueNotifier('جاهز للمزامنة');
|
||||
static ValueNotifier<PackageSyncStatus> packageStatus = ValueNotifier(PackageSyncStatus.installed);
|
||||
|
||||
/// Reset sync lock and preferences / تصفير قفل المزامنة بالكامل
|
||||
static Future<void> resetSyncLock() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove('${_pkgKey}_date');
|
||||
await prefs.remove('${_pkgKey}_synced');
|
||||
}
|
||||
|
||||
static Future<OfflinePackageInfo> getPackageInfo() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final syncDateStr = prefs.getString('${_pkgKey}_date');
|
||||
@@ -58,24 +70,31 @@ class OfflinePackageManager {
|
||||
int remainingDays = 0;
|
||||
|
||||
if (lastDate != null) {
|
||||
final daysSinceLastSync = DateTime.now().difference(lastDate).inDays;
|
||||
if (daysSinceLastSync < syncCooldownDays) {
|
||||
final daysSinceSync = DateTime.now().difference(lastDate).inDays;
|
||||
if (daysSinceSync < syncCooldownDays) {
|
||||
canSync = false;
|
||||
remainingDays = syncCooldownDays - daysSinceLastSync;
|
||||
remainingDays = syncCooldownDays - daysSinceSync;
|
||||
}
|
||||
}
|
||||
|
||||
// حالة حزمة التوجيه الحقيقية (شبكة الطرق الكاملة)
|
||||
final routingInstalled = await OfflineRoutingPackageService.isInstalled();
|
||||
final routingVersion = await OfflineRoutingPackageService.installedVersion();
|
||||
|
||||
return OfflinePackageInfo(
|
||||
packageId: 'jordan-tactical-offline-v2',
|
||||
name: 'حزمة الأردن التكتيكية الميدانية الكاملة (Jordan Tactical Pack)',
|
||||
version: '2.4.0',
|
||||
version: routingInstalled ? (routingVersion ?? '2.4.0') : '2.4.0',
|
||||
totalLandmarks: LandmarkDatabase.allLandmarks.length,
|
||||
totalRoadNodes: 36,
|
||||
sizeFormatted: '3.5 MB (شبكة الطرق) + 25 MB (المعالم)',
|
||||
totalRoadNodes: routingInstalled ? 450000 : 36,
|
||||
sizeFormatted: routingInstalled
|
||||
? 'غراف طرق الأردن الحقيقي + SRTM مثبت محلياً'
|
||||
: '3.5 MB (شبكة الطرق) + 25 MB (المعالم)',
|
||||
lastSyncTime: lastDate,
|
||||
status: isSynced ? PackageSyncStatus.installed : PackageSyncStatus.notInstalled,
|
||||
canSyncNow: canSync,
|
||||
daysUntilNextSync: remainingDays,
|
||||
realRoadRoutingReady: routingInstalled,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,18 +104,17 @@ class OfflinePackageManager {
|
||||
String? apiKey,
|
||||
bool force = false,
|
||||
}) async {
|
||||
final activeUrl = serverUrl ?? defaultServerUrl;
|
||||
final activeKey = apiKey ?? defaultApiKey;
|
||||
|
||||
// Check 14-day cooldown unless forced
|
||||
if (!force) {
|
||||
final info = await getPackageInfo();
|
||||
if (!info.canSyncNow && info.lastSyncTime != null) {
|
||||
syncStatusMessage.value = 'الحزمة محدثة بالفعل • المزامنة القادمة بعد ${info.daysUntilNextSync} يوم لحماية السيرفر.';
|
||||
return false;
|
||||
if (!info.canSyncNow && info.status == PackageSyncStatus.installed) {
|
||||
debugPrint('Package sync skipped (14-day cooldown active. ${info.daysUntilNextSync} days left).');
|
||||
return true; // Already synced and in cooldown
|
||||
}
|
||||
}
|
||||
|
||||
final activeUrl = serverUrl ?? defaultServerUrl;
|
||||
final activeKey = apiKey ?? defaultApiKey;
|
||||
|
||||
try {
|
||||
packageStatus.value = PackageSyncStatus.downloading;
|
||||
syncProgress.value = 0.1;
|
||||
@@ -122,9 +140,30 @@ class OfflinePackageManager {
|
||||
debugPrint('Synced $count landmarks to local storage.');
|
||||
|
||||
syncProgress.value = 0.75;
|
||||
syncStatusMessage.value = 'جاري بناء وتحديث شبكة التوجيه الطوبولوجية...';
|
||||
syncStatusMessage.value = 'جاري تنزيل شبكة الطرق الحقيقية للأردن (غراف التوجيه + الارتفاعات)...';
|
||||
|
||||
// 3. Initialize & warm up on-device routing engine
|
||||
// 3. Download & install the real Jordan road graph routing package
|
||||
// (Valhalla tiles built from the same OSM data as server GraphHopper)
|
||||
final routingOk = await OfflineRoutingPackageService.downloadAndInstall(
|
||||
serverUrl: activeUrl,
|
||||
apiKey: activeKey,
|
||||
);
|
||||
if (!routingOk) {
|
||||
final hasLocal = await OfflineRoutingPackageService.isInstalled();
|
||||
if (!hasLocal) {
|
||||
debugPrint('Routing package failed to download and no local offline cache exists.');
|
||||
syncStatusMessage.value = 'خطأ: تعذر تنزيل شبكة الطرق الأوفلاين. يرجى التحقق من الاتصال بالخادم.';
|
||||
packageStatus.value = PackageSyncStatus.error;
|
||||
return false;
|
||||
} else {
|
||||
debugPrint('Using previously installed local routing package.');
|
||||
}
|
||||
}
|
||||
|
||||
syncProgress.value = 0.90;
|
||||
syncStatusMessage.value = 'جاري بناء وتحديث محرك التوجيه المحلي...';
|
||||
|
||||
// 4. Initialize & warm up on-device routing engine
|
||||
OfflineRoutingEngine.initialize();
|
||||
|
||||
syncProgress.value = 0.95;
|
||||
|
||||
Reference in New Issue
Block a user