Update: 2026-06-19 01:47:48
This commit is contained in:
@@ -6,6 +6,7 @@ import 'location_search_controller.dart';
|
||||
import 'nearby_drivers_controller.dart';
|
||||
import 'ride_lifecycle_controller.dart';
|
||||
import 'ui_interactions_controller.dart';
|
||||
import 'ride_simulation.dart';
|
||||
|
||||
class MapScreenBinding extends Bindings {
|
||||
@override
|
||||
@@ -21,5 +22,8 @@ class MapScreenBinding extends Bindings {
|
||||
// 3. Lifecycle and UI Interaction Controllers
|
||||
Get.lazyPut(() => RideLifecycleController());
|
||||
Get.lazyPut(() => UiInteractionsController(), fenix: true);
|
||||
|
||||
// 4. Simulation Controller (for development/testing)
|
||||
Get.lazyPut(() => RideSimulationController(), fenix: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2137,17 +2137,21 @@ class RideLifecycleController extends GetxController {
|
||||
polyLines = polyLines
|
||||
.where((p) =>
|
||||
!p.polylineId.value.startsWith('driver_route') &&
|
||||
p.polylineId.value != 'main_route' &&
|
||||
p.polylineId.value != 'route_primary' &&
|
||||
p.polylineId.value != 'route_direct')
|
||||
.toSet();
|
||||
|
||||
if (statusRide == 'Begin' ||
|
||||
currentRideState.value == RideState.inProgress) {
|
||||
// لا نرسم أي شيء في حالة البدء لأنه وصل
|
||||
polyLines = polyLines
|
||||
.where((p) => !p.polylineId.value.startsWith('driver_route'))
|
||||
.toSet();
|
||||
polyLines = {
|
||||
...polyLines.where((p) => !p.polylineId.value.startsWith('driver_route')),
|
||||
Polyline(
|
||||
polylineId: const PolylineId('main_route'),
|
||||
points: remainingPoints,
|
||||
color: const Color(0xFF2196F3),
|
||||
width: 6,
|
||||
),
|
||||
};
|
||||
} else {
|
||||
polyLines = {
|
||||
...polyLines,
|
||||
@@ -4263,6 +4267,11 @@ class RideLifecycleController extends GetxController {
|
||||
return segments;
|
||||
}
|
||||
|
||||
// Call this externally when passengerLocation changes to refresh walk line
|
||||
void updatePassengerWalkLine() {
|
||||
_updatePassengerWalkLine();
|
||||
}
|
||||
|
||||
// تحديث الخط المنقط ومكان أيقونة المشي للراكب
|
||||
void _updatePassengerWalkLine() {
|
||||
polyLines.removeWhere(
|
||||
|
||||
438
siro_rider/lib/controller/home/map/ride_simulation.dart
Normal file
438
siro_rider/lib/controller/home/map/ride_simulation.dart
Normal file
@@ -0,0 +1,438 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' show cos, pi, sin;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
|
||||
import '../../../print.dart';
|
||||
import 'map_engine_controller.dart';
|
||||
import 'map_socket_controller.dart';
|
||||
import 'nearby_drivers_controller.dart';
|
||||
import 'ride_lifecycle_controller.dart';
|
||||
import 'ride_state.dart';
|
||||
import 'location_search_controller.dart';
|
||||
|
||||
class RideSimulationController extends GetxController {
|
||||
Timer? _simulationTimer;
|
||||
int _step = 0;
|
||||
bool _isSimulating = false;
|
||||
|
||||
List<LatLng> _simulatedRoute = [];
|
||||
int _driverRouteIndex = 0;
|
||||
|
||||
String get simulationStatus => _isSimulating
|
||||
? 'Simulating... Step $_step'
|
||||
: 'Idle';
|
||||
|
||||
// آمود نقاط المسار (Jordan area: Amman center ~ 31.95, 35.91)
|
||||
static const double _centerLat = 31.95;
|
||||
static const double _centerLng = 35.91;
|
||||
static const double _routeDelta = 0.03;
|
||||
static const int _routePointCount = 100;
|
||||
|
||||
void _generateRoutePoints() {
|
||||
_simulatedRoute = [];
|
||||
for (int i = 0; i < _routePointCount; i++) {
|
||||
double fraction = i / (_routePointCount - 1);
|
||||
double lat = _centerLat + fraction * _routeDelta;
|
||||
double lng = _centerLng + fraction * _routeDelta +
|
||||
0.005 * sin(fraction * pi * 2);
|
||||
_simulatedRoute.add(LatLng(lat, lng));
|
||||
}
|
||||
}
|
||||
|
||||
void startFullSimulation() {
|
||||
if (_isSimulating) return;
|
||||
_isSimulating = true;
|
||||
_step = 0;
|
||||
_driverRouteIndex = 0;
|
||||
_generateRoutePoints();
|
||||
|
||||
Log.print('=' * 70);
|
||||
Log.print('🚀 RIDE SIMULATION STARTED — Full lifecycle test');
|
||||
Log.print('=' * 70);
|
||||
|
||||
_simulationTimer = Timer.periodic(const Duration(seconds: 2), (timer) {
|
||||
switch (_step) {
|
||||
case 0:
|
||||
_simulateSearchingState();
|
||||
break;
|
||||
case 1:
|
||||
_simulateDriverAppliedState();
|
||||
break;
|
||||
case 2:
|
||||
_simulateDriverEnRoute(isFirst: true);
|
||||
break;
|
||||
case 3:
|
||||
_simulateDriverEnRoute(isFirst: false);
|
||||
break;
|
||||
case 4:
|
||||
_simulateDriverArrivedState();
|
||||
break;
|
||||
case 5:
|
||||
_simulateRideBeginState();
|
||||
break;
|
||||
case 6:
|
||||
_simulateRideInProgress();
|
||||
break;
|
||||
case 7:
|
||||
_simulateRideFinishedState();
|
||||
timer.cancel();
|
||||
_isSimulating = false;
|
||||
Log.print('=' * 70);
|
||||
Log.print('✅ RIDE SIMULATION COMPLETED');
|
||||
Log.print('=' * 70);
|
||||
break;
|
||||
}
|
||||
_step++;
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
void stopSimulation() {
|
||||
_simulationTimer?.cancel();
|
||||
_simulationTimer = null;
|
||||
_isSimulating = false;
|
||||
_step = 0;
|
||||
_driverRouteIndex = 0;
|
||||
Log.print('🛑 Simulation stopped');
|
||||
update();
|
||||
}
|
||||
|
||||
void _logPolylineState(String stage) {
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
Log.print('');
|
||||
Log.print('╔══ Polyline State [$stage] ══╗');
|
||||
if (mapEngine.polyLines.isEmpty) {
|
||||
Log.print(' ║ (no polylines)');
|
||||
}
|
||||
for (final p in mapEngine.polyLines) {
|
||||
String colorStr;
|
||||
if (p.color == Colors.amber) {
|
||||
colorStr = 'AMBER';
|
||||
} else if (p.color.value == 0xFF2196F3) {
|
||||
colorStr = 'BLUE';
|
||||
} else if (p.color == Colors.blueGrey) {
|
||||
colorStr = 'BLUE_GREY';
|
||||
} else {
|
||||
colorStr = '#${p.color.value.toRadixString(16).padLeft(8, '0')}';
|
||||
}
|
||||
Log.print(' ║ • ${p.polylineId.value}: ${p.points.length} pts, '
|
||||
'color: $colorStr, w: ${p.width}');
|
||||
}
|
||||
Log.print('╠══ Markers [$stage] ══╣');
|
||||
if (mapEngine.markers.isEmpty) {
|
||||
Log.print(' ║ (no markers)');
|
||||
}
|
||||
for (final m in mapEngine.markers) {
|
||||
Log.print(' ║ • ${m.markerId.value}: '
|
||||
'(${m.position.latitude.toStringAsFixed(5)}, '
|
||||
'${m.position.longitude.toStringAsFixed(5)}), '
|
||||
'heading: ${m.rotation.toStringAsFixed(1)}');
|
||||
}
|
||||
Log.print('╚══════════════════════╝');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
void _verifyPolylineCondition(bool condition, String description) {
|
||||
if (!condition) {
|
||||
Log.print(' ❌ VERIFY FAILED: $description');
|
||||
} else {
|
||||
Log.print(' ✅ VERIFY PASSED: $description');
|
||||
}
|
||||
}
|
||||
|
||||
LatLng _interpolateRoutePoint(double fraction) {
|
||||
if (_simulatedRoute.isEmpty) return LatLng(_centerLat, _centerLng);
|
||||
int total = _simulatedRoute.length;
|
||||
double exactIdx = fraction * (total - 1);
|
||||
int idx = exactIdx.floor();
|
||||
if (idx >= total - 1) return _simulatedRoute.last;
|
||||
double t = exactIdx - idx;
|
||||
return LatLng(
|
||||
_simulatedRoute[idx].latitude +
|
||||
t * (_simulatedRoute[idx + 1].latitude - _simulatedRoute[idx].latitude),
|
||||
_simulatedRoute[idx].longitude +
|
||||
t * (_simulatedRoute[idx + 1].longitude - _simulatedRoute[idx].longitude),
|
||||
);
|
||||
}
|
||||
|
||||
void _simulateSearchingState() {
|
||||
Log.print('🟡 [Step $_step] STATE: SEARCHING');
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
final locSearch = Get.find<LocationSearchController>();
|
||||
|
||||
ride.currentRideState.value = RideState.searching;
|
||||
ride.rideId = 'sim_ride_001';
|
||||
ride.isSearchingWindow = true;
|
||||
|
||||
// آمود موقع الراكب
|
||||
LatLng passengerLoc = _simulatedRoute.isNotEmpty
|
||||
? _simulatedRoute.first
|
||||
: LatLng(_centerLat, _centerLng);
|
||||
locSearch.passengerLocation = passengerLoc;
|
||||
ride.passengerLocation = passengerLoc;
|
||||
ride.update();
|
||||
|
||||
_logPolylineState('searching');
|
||||
_verifyPolylineCondition(
|
||||
Get.find<MapEngineController>().polyLines.isEmpty,
|
||||
'No route polylines during searching');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
void _simulateDriverAppliedState() {
|
||||
Log.print('🟢 [Step $_step] STATE: DRIVER APPLIED');
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
final nearbyDrivers = Get.find<NearbyDriversController>();
|
||||
final locSearch = Get.find<LocationSearchController>();
|
||||
|
||||
ride.currentRideState.value = RideState.driverApplied;
|
||||
ride.statusRide = 'Apply';
|
||||
ride.isSearchingWindow = false;
|
||||
|
||||
// آمود معلومات السائق
|
||||
ride.driverId = 'sim_driver_001';
|
||||
ride.driverName = 'Simulated Captain';
|
||||
ride.make = 'Toyota';
|
||||
ride.model = 'Corolla';
|
||||
ride.carColor = 'White';
|
||||
ride.licensePlate = 'SIM-123';
|
||||
ride.driverRate = '4.8';
|
||||
ride.driverPhone = '+9627XXXXXXXX';
|
||||
ride.driverToken = 'sim_token_001';
|
||||
|
||||
// آمود مواقع البداية والنهاية
|
||||
ride.passengerLocation = _simulatedRoute.first;
|
||||
ride.myDestination = _simulatedRoute.last;
|
||||
locSearch.passengerLocation = _simulatedRoute.first;
|
||||
locSearch.myDestination = _simulatedRoute.last;
|
||||
|
||||
// محاكاة موقع السائق (45% على طول المسار)
|
||||
LatLng driverPos = _interpolateRoutePoint(0.45);
|
||||
nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [driverPos];
|
||||
|
||||
// رسم مسار السائق إلى الراكب
|
||||
ride.calculateDriverToPassengerRoute(driverPos, ride.passengerLocation);
|
||||
|
||||
// وضع ماركر السائق على الخريطة
|
||||
ride.updateDriverMarker(driverPos, 45.0);
|
||||
ride.update();
|
||||
|
||||
_logPolylineState('driver_applied');
|
||||
|
||||
// التحقق من وجود الخطوط المطلوبة
|
||||
final polylines = Get.find<MapEngineController>().polyLines;
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.polylineId.value == 'driver_route_solid'),
|
||||
'driver_route_solid (amber solid line) exists');
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.polylineId.value.startsWith('passenger_walk_line')),
|
||||
'passenger_walk_line (dashed walk line) exists');
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.color == Colors.amber),
|
||||
'Amber-colored polyline exists (driver route)');
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.color == Colors.blueGrey),
|
||||
'BlueGrey-colored polyline exists (walk dashes)');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
void _simulateDriverEnRoute({required bool isFirst}) {
|
||||
String label = isFirst ? '1' : '2';
|
||||
Log.print('🔵 [Step $_step] STATE: DRIVER APPROACHING (update #$label)');
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
final nearbyDrivers = Get.find<NearbyDriversController>();
|
||||
|
||||
_driverRouteIndex += 18;
|
||||
if (_driverRouteIndex > _routePointCount - 1) {
|
||||
_driverRouteIndex = _routePointCount - 1;
|
||||
}
|
||||
|
||||
LatLng newPos = _simulatedRoute[_driverRouteIndex];
|
||||
|
||||
// تحديث موقع السائق
|
||||
nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [newPos];
|
||||
|
||||
ride.updateDriverMarker(newPos, 50.0);
|
||||
ride.updateRemainingRoute(newPos);
|
||||
ride.update();
|
||||
|
||||
_logPolylineState('driver_en_route_$label');
|
||||
|
||||
// التحقق: driver_route_solid يبقى موجود ويتقلص
|
||||
final polylines = Get.find<MapEngineController>().polyLines;
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.polylineId.value == 'driver_route_solid'),
|
||||
'driver_route_solid still exists (shrinking)');
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.polylineId.value.startsWith('passenger_walk_line')),
|
||||
'passenger_walk_line still exists');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
void _simulateDriverArrivedState() {
|
||||
Log.print('🟣 [Step $_step] STATE: DRIVER ARRIVED');
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
|
||||
ride.currentRideState.value = RideState.driverArrived;
|
||||
ride.statusRide = 'Arrived';
|
||||
|
||||
// محاكاة وصول السائق: تحديد الموقع كآخر نقطة طريق
|
||||
LatLng driverPos = _simulatedRoute.first;
|
||||
|
||||
// تطبيق arrived logic
|
||||
ride.polyLines = ride.polyLines
|
||||
.where((p) =>
|
||||
p.polylineId.value != 'main_route' &&
|
||||
p.polylineId.value != 'route_direct' &&
|
||||
!p.polylineId.value.startsWith('driver_route'))
|
||||
.toSet();
|
||||
|
||||
// رسم المسار الجديد (من السائق إلى الوجهة النهائية)
|
||||
ride.calculateDriverToPassengerRoute(driverPos, ride.myDestination,
|
||||
isBeginPhase: true);
|
||||
|
||||
ride.update();
|
||||
|
||||
_logPolylineState('driver_arrived');
|
||||
|
||||
final polylines = Get.find<MapEngineController>().polyLines;
|
||||
// في حالة الوصول: يجب أن يظهر main_route (أزرق) بدلاً من driver_route_solid
|
||||
_verifyPolylineCondition(
|
||||
!polylines.any((p) => p.polylineId.value == 'driver_route_solid'),
|
||||
'driver_route_solid (amber) has been cleared');
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.polylineId.value == 'main_route'),
|
||||
'main_route (blue) has been drawn to destination');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
void _simulateRideBeginState() {
|
||||
Log.print('🔴 [Step $_step] STATE: RIDE IN PROGRESS (Begin)');
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
|
||||
ride.currentRideState.value = RideState.inProgress;
|
||||
ride.statusRide = 'Begin';
|
||||
|
||||
LatLng driverPos = _simulatedRoute.first;
|
||||
|
||||
// محاكاة processRideBegin: مسح الخطوط القديمة ورسم الخط الجديد
|
||||
ride.polyLines = ride.polyLines
|
||||
.where((p) =>
|
||||
p.polylineId.value != 'main_route' &&
|
||||
p.polylineId.value != 'route_direct' &&
|
||||
!p.polylineId.value.startsWith('driver_route'))
|
||||
.toSet();
|
||||
|
||||
ride.calculateDriverToPassengerRoute(driverPos, ride.myDestination,
|
||||
isBeginPhase: true);
|
||||
|
||||
ride.rideIsBeginPassengerTimer();
|
||||
ride.update();
|
||||
|
||||
_logPolylineState('ride_begin');
|
||||
|
||||
final polylines = Get.find<MapEngineController>().polyLines;
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.polylineId.value == 'main_route'),
|
||||
'main_route (blue) exists for trip');
|
||||
_verifyPolylineCondition(
|
||||
!polylines.any((p) => p.polylineId.value.startsWith('driver_route')),
|
||||
'No driver_route* polylines remain');
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.color.value == 0xFF2196F3),
|
||||
'Blue polyline (main route) is present');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
void _simulateRideInProgress() {
|
||||
Log.print('📱 [Step $_step] STATE: CAR MOVING (remaining route update)');
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
final nearbyDrivers = Get.find<NearbyDriversController>();
|
||||
|
||||
_driverRouteIndex += 30;
|
||||
if (_driverRouteIndex > _routePointCount - 1) {
|
||||
_driverRouteIndex = _routePointCount - 1;
|
||||
}
|
||||
|
||||
LatLng newPos = _simulatedRoute[_driverRouteIndex];
|
||||
|
||||
nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [newPos];
|
||||
|
||||
ride.updateDriverMarker(newPos, 70.0);
|
||||
ride.updateRemainingRoute(newPos, updateEta: true);
|
||||
ride.update();
|
||||
|
||||
_logPolylineState('ride_in_progress');
|
||||
|
||||
final polylines = Get.find<MapEngineController>().polyLines;
|
||||
_verifyPolylineCondition(
|
||||
polylines.any((p) => p.polylineId.value == 'main_route'),
|
||||
'main_route (blue) exists with remaining points');
|
||||
_verifyPolylineCondition(
|
||||
!polylines.any((p) => p.polylineId.value.startsWith('driver_route')),
|
||||
'No driver_route* polylines remain');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
void _simulateRideFinishedState() {
|
||||
Log.print('🏁 [Step $_step] STATE: RIDE FINISHED');
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
|
||||
ride.currentRideState.value = RideState.finished;
|
||||
ride.statusRide = 'Finished';
|
||||
ride.isSearchingWindow = false;
|
||||
|
||||
ride.stopAllTimers();
|
||||
mapEngine.clearPolyline();
|
||||
mapEngine.markers = {};
|
||||
ride.update();
|
||||
|
||||
_logPolylineState('ride_finished');
|
||||
|
||||
_verifyPolylineCondition(
|
||||
Get.find<MapEngineController>().polyLines.isEmpty,
|
||||
'All polylines cleared');
|
||||
_verifyPolylineCondition(
|
||||
Get.find<MapEngineController>().markers.isEmpty,
|
||||
'All markers removed');
|
||||
Log.print('');
|
||||
}
|
||||
|
||||
// Simulate a single WebSocket location update event
|
||||
void simulateSocketLocationUpdate(LatLng position, double heading) {
|
||||
final ride = Get.find<RideLifecycleController>();
|
||||
final nearbyDrivers = Get.find<NearbyDriversController>();
|
||||
final mapSocket = Get.find<MapSocketController>();
|
||||
|
||||
Log.print('📡 Simulating socket location update: '
|
||||
'(${position.latitude.toStringAsFixed(5)}, ${position.longitude.toStringAsFixed(5)})');
|
||||
|
||||
nearbyDrivers.driverCarsLocationToPassengerAfterApplied = [position];
|
||||
ride.driverCarsLocationToPassengerAfterApplied = [position];
|
||||
|
||||
ride.checkAndRecalculateIfDeviated(
|
||||
position,
|
||||
heading: heading,
|
||||
speed: 30.0,
|
||||
);
|
||||
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
if (mapEngine.mapController != null) {
|
||||
mapEngine.mapController!
|
||||
.animateCamera(CameraUpdate.newLatLngZoom(position, 16.5));
|
||||
}
|
||||
|
||||
ride.updateDriverMarker(position, heading);
|
||||
ride.updateRemainingRoute(position);
|
||||
ride.update();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_simulationTimer?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user