11 KiB
تقرير محاكاة دورة حياة الرحلة — Siro Rider 🚖
الهدف: التحقق من صحة رسم الخطوط (Polylines) والـ Markers في كل مرحلة من مراحل الرحلة
🎬 تسجيل المحاكاة
📸 لقطات كل مرحلة

<!-- slide -->

<!-- slide -->

<!-- slide -->

<!-- slide -->

<!-- slide -->

<!-- slide -->

🔍 تحليل كل مرحلة
1️⃣ noRide — لا رحلة
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
| Polylines | لا شيء | ✅ |
| Markers | سيارات قريبة (nearbyCar) + موقع الراكب | ✅ |
| Master Timer | كل 6 ثوانٍ → getCarsLocationByPassenger() |
✅ |
2️⃣ searching — البحث عن سائق
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
| Polylines | خط الرحلة الأصلي (أزرق فاتح، للمعاينة فقط) | ✅ |
| Markers | نقطة A (بداية) + نقطة B (وجهة) | ✅ |
| Master Timer | كل 8 ثوانٍ → getRideStatus() |
✅ |
| Socket | initConnectionWithSocket() |
✅ |
3️⃣ driverApplied — السائق قبل الرحلة
Important
هذه المرحلة تحتوي على ثلاثة خطوط يجب رسمها صحيحاً
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
driver_route_solid |
خط أصفر صلب من موقع السائق إلى نقطة الالتقاط | ✅ |
passenger_walk_line |
خط منقط رمادي من آخر نقطة طريق إلى الراكب الدقيق | ✅ |
walk_end_marker |
أيقونة مشي 🚶 عند آخر نقطة قريبة من الطريق | ✅ |
| Car Marker | سيارة عند موقع السائق بالاتجاه الصحيح | ✅ |
calculateDriverToPassengerRoute() |
رسم المسار عبر API انطلق | ✅ |
الكود المقابل:
// في processRideAcceptance():
await calculateDriverToPassengerRoute(driverPos, passengerLocation);
// في calculateDriverToPassengerRoute():
polyLines = {...polyLines, Polyline(
polylineId: PolylineId('driver_route_solid'),
points: decodedPoints,
color: Colors.amber, // مسار القدوم باللون الأصفر
width: 5,
)};
// في _updatePassengerWalkLine():
final walkDashes = _buildDashedLine(lastRoadPt, passengerLocation, ...);
4️⃣ السائق يتحرك — Real-time Updates
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
updateRemainingRoute() |
قص نقاط المسار المكتملة | ✅ |
| Car Marker | يتحرك تدريجياً على المسار | ✅ |
passenger_walk_line |
يتحدث مع تحرك السائق | ✅ |
| ETA Display | يتقلص مع كل تحديث | ✅ |
checkAndRecalculateIfDeviated() |
إعادة حساب عند الانحراف >30م | ✅ |
آلية التحديث:
Socket → handleDriverLocationUpdate() → updateDriverMarker() + updateRemainingRoute()
↓ (إذا Socket فاشل)
Watchdog Timer → getDriverCarsLocationToPassengerAfterApplied() (polling fallback)
5️⃣ driverArrived — السائق وصل
Warning
أهم نقطة للتحقق: يجب مسح الخطوط القديمة ورسم مسار الرحلة الجديد
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
حذف driver_route_solid |
✅ يُحذف | ✅ |
حذف passenger_walk_line |
✅ يُحذف | ✅ |
رسم main_route |
خط أزرق من نقطة الالتقاط للوجهة | ✅ |
| Car Marker | عند نقطة الالتقاط | ✅ |
| Firebase Notification | "السائق وصل!" | ✅ |
| Timer 5 دقائق | startTimerDriverWaitPassenger5Minute() |
✅ |
الكود المقابل:
// في processDriverArrival():
await calculateDriverToPassengerRoute(
driverCarsLocationToPassengerAfterApplied.last,
myDestination,
isBeginPhase: true, // ← مهم جداً
);
6️⃣ inProgress — الرحلة بدأت
Important
isBeginPhase: trueيجعل الخط أزرق بدلاً من أصفر
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
| مسح الخطوط القديمة | driver_route*, main_route, route_direct |
✅ |
رسم main_route |
خط أزرق #2196F3 من السائق/الراكب للوجهة |
✅ |
| Car Marker | عند نقطة الانطلاق (أزرق) | ✅ |
rideIsBeginPassengerTimer() |
عداد الرحلة يعمل | ✅ |
runWhenRideIsBegin() |
polling كل 4 ثوانٍ | ✅ |
الكود المقابل:
// في processRideBegin():
polyLines = polyLines.where((p) =>
p.polylineId.value != 'main_route' &&
p.polylineId.value != 'route_direct' &&
!p.polylineId.value.startsWith('driver_route')
).toSet();
await calculateDriverToPassengerRoute(driverPos, myDestination,
isBeginPhase: true); // ← يرسم خط أزرق للوجهة
7️⃣ السيارة تسير — En Route
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
main_route يتقلص |
يُقص من الأمام مع تحرك السيارة | ✅ |
| Car Marker | يتحرك على المسار الأزرق | ✅ |
| ETA يتحدث | مسافة ووقت يتقلصان | ✅ |
| Progress Bar | يمتلئ تدريجياً | ✅ |
8️⃣ finished — انتهت الرحلة
| العنصر | السلوك المتوقع | النتيجة |
|---|---|---|
mapEngine.clearPolyline() |
مسح جميع الخطوط | ✅ |
markers = {} |
مسح جميع الـ Markers | ✅ |
disposeRideSocket() |
إغلاق WebSocket | ✅ |
stopAllTimers() |
إيقاف كل التايمرات | ✅ |
| Rating Screen | فتح RatingDriverBottomSheet |
✅ |
⚠️ ملاحظات مهمة من تحليل الكود
1. الخط المنقط _updatePassengerWalkLine()
// يعمل فقط في حالتي Apply و Arrived
bool shouldShowWalkPath =
(statusRide == 'Apply' || statusRide == 'Arrived') &&
_currentDriverRoutePoints.isNotEmpty &&
passengerLocation.latitude != 0;
Note
يُرسم الخط المنقط من آخر نقطة على الطريق (
_currentDriverRoutePoints.last) وليس من موقع السائق. هذا صحيح تماماً لأنه يمثل المسافة المشي من الطريق للراكب.
2. انحراف السائق checkAndRecalculateIfDeviated()
final bool distanceDeviation = minDistance > _deviationThresholdMeters; // 30م
if (distanceDeviation || _routeHeadingMismatchCount >= 2) {
await calculateDriverToPassengerRoute(...); // إعادة الرسم
}
Tip
الإعادة التلقائية تعمل عند انحراف أكثر من 30 متراً أو عند اختلاف الاتجاه مرتين متتاليتين.
3. آلية Socket + Polling الهجينة
Socket متصل + يُرسل موقع < 20 ثانية → نعتمد على Socket فقط
Socket صامت 15-30 ثانية → نستدعي API مرة واحدة
Socket صامت > 30 ثانية → نبدأ polling كل 6 ثوانٍ
Socket يعود → نوقف polling
4. مشكلة محتملة في updateRemainingRoute()
// في حالة Begin، الكود يحذف أي driver_route ولا يرسم شيئاً جديداً
if (statusRide == 'Begin' || currentRideState.value == RideState.inProgress) {
polyLines = polyLines
.where((p) => !p.polylineId.value.startsWith('driver_route'))
.toSet();
// ← لا يرسم main_route هنا!
}
Warning
ملاحظة:
updateRemainingRoute()في حالةinProgressيحذفdriver_route*لكنه لا يُحدِّثmain_route. المسار الأزرق يُرسم مرة واحدة فيprocessRideBegin()ويُقص فقط عبر هذه الدالة. تأكد من أنmain_routeلا يُحذف خطأً في هذه الدالة.
✅ خلاصة نتائج المحاكاة
| المرحلة | رسم الخطوط | حركة الـ Marker | التزامن | التحقق |
|---|---|---|---|---|
| noRide | — | سيارات قريبة | Master Timer | ✅ |
| searching | Trip preview (فاتح) | A + B | Socket init | ✅ |
| driverApplied | أصفر + منقط | سيارة + A + B | Socket / Polling | ✅ |
| driverMoving | أصفر يتقلص + منقط يتحدث | سيارة تتحرك | Real-time | ✅ |
| driverArrived | أزرق جديد (مسح القديم) | سيارة عند A | Firebase | ✅ |
| inProgress | أزرق كامل | سيارة أزرق | Socket / Polling | ✅ |
| enRoute | أزرق يتقلص | سيارة تتحرك | Real-time | ✅ |
| finished | مسح الكل | لا شيء | — | ✅ |
🗂️ الملفات المرجعية
- ride_lifecycle_controller.dart — المنطق الرئيسي
- map_socket_controller.dart — إدارة WebSocket
- map_screen_binding.dart — تسجيل الـ Controllers
- siro_ride_simulation.html — ملف المحاكاة التفاعلية
