100 lines
3.8 KiB
Dart
100 lines
3.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:intaleq_maps/intaleq_maps.dart';
|
|
import '../services/offline_routing_engine.dart';
|
|
import '../services/tactical_api_service.dart';
|
|
import '../services/turn_by_turn_navigation_engine.dart';
|
|
|
|
/// ============================================================================
|
|
/// [NavigationController] - وحدة التحكم بالملاحة وتوجيه القوافل التكتيكية
|
|
/// ============================================================================
|
|
/// English:
|
|
/// GetX Controller managing hybrid tactical convoy routing (Online API + Sovereign
|
|
/// Offline Engine fallback), active turn-by-turn guidance, and navigation metrics.
|
|
///
|
|
/// العربية:
|
|
/// متحكم GetX لإدارة توجيه القوافل والآليات العسكرية (هجين: سيرفر أونلاين + محرك أوفلاين محلي)،
|
|
/// وتتبع مسار الملاحة خطوة بخطوة وحساب السرعة والوقت المتبقي للهدف.
|
|
/// ============================================================================
|
|
class NavigationController extends GetxController {
|
|
// ── Observables / الحالات التفاعلية ──────────────────────────────────────
|
|
final Rx<OfflineRoutePlan?> activeRoute = Rx<OfflineRoutePlan?>(null);
|
|
final Rx<TacticalVehicleProfile> vehicleProfile =
|
|
Rx<TacticalVehicleProfile>(TacticalVehicleProfile.convoy);
|
|
final RxBool isCalculating = false.obs;
|
|
|
|
/// Calculate Hybrid Tactical Route / احتساب مسار تكتيكي هجين (سيرفر + محلي)
|
|
Future<OfflineRoutePlan?> calculateRoute({
|
|
required LatLng origin,
|
|
required LatLng destination,
|
|
TacticalVehicleProfile? profile,
|
|
bool autoStart = false,
|
|
IntaleqMapController? mapController,
|
|
}) async {
|
|
final activeProfile = profile ?? vehicleProfile.value;
|
|
vehicleProfile.value = activeProfile;
|
|
isCalculating.value = true;
|
|
|
|
OfflineRoutePlan? plan;
|
|
|
|
// 1. Try Online NestJS Server Routing API
|
|
try {
|
|
final serverRoute = await TacticalApiService.calculateTacticalRoute(
|
|
origin: origin,
|
|
destination: destination,
|
|
profile: activeProfile.name,
|
|
);
|
|
|
|
if (serverRoute != null && serverRoute.points.isNotEmpty) {
|
|
plan = OfflineRoutePlan(
|
|
polylinePoints: serverRoute.points,
|
|
totalDistanceKm: serverRoute.distanceKm,
|
|
estimatedDurationMinutes: serverRoute.durationMinutes,
|
|
profile: activeProfile,
|
|
tacticalWaypoints: const ['موقع الانطلاق', 'الهدف التكتيكي المحدد'],
|
|
isOffline: false,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Online route fallback: $e');
|
|
}
|
|
|
|
// 2. Fallback to 100% Sovereign On-Device Offline Routing Engine
|
|
plan ??= OfflineRoutingEngine.calculateOnDeviceRoute(
|
|
start: origin,
|
|
destination: destination,
|
|
profile: activeProfile,
|
|
);
|
|
|
|
activeRoute.value = plan;
|
|
isCalculating.value = false;
|
|
|
|
if (autoStart) {
|
|
startNavigation(mapController: mapController);
|
|
}
|
|
|
|
return plan;
|
|
}
|
|
|
|
/// Start Active Turn-by-Turn Guidance / بدء الإرشاد الملاحي الحي
|
|
void startNavigation({IntaleqMapController? mapController}) {
|
|
if (activeRoute.value == null) return;
|
|
TurnByTurnNavigationEngine.startNavigation(
|
|
plan: activeRoute.value!,
|
|
simulate: false,
|
|
controller: mapController,
|
|
);
|
|
}
|
|
|
|
/// Stop Navigation / إنهاء الملاحة
|
|
void stopNavigation() {
|
|
TurnByTurnNavigationEngine.stopNavigation();
|
|
}
|
|
|
|
/// Reset Route State / إعادة ضبط المسار
|
|
void reset() {
|
|
stopNavigation();
|
|
activeRoute.value = null;
|
|
}
|
|
}
|