Files
maps-saas/packages/tactical_app/lib/controllers/navigation_controller.dart
T

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;
}
}