refactor(tactical-app): modernize state management with GetX controllers, bindings, and clean MVP architecture
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user