Files
Siro/knowledge/ride_simulation_report.md
2026-06-19 01:47:48 +03:00

253 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# تقرير محاكاة دورة حياة الرحلة — Siro Rider 🚖
> **الهدف:** التحقق من صحة رسم الخطوط (Polylines) والـ Markers في كل مرحلة من مراحل الرحلة
---
## 🎬 تسجيل المحاكاة
![محاكاة Siro Rider](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/siro_ride_simulation_1781817864105.webp)
---
## 📸 لقطات كل مرحلة
````carousel
![noRide Phase](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/phase_noride_1781818077016.png)
<!-- slide -->
![driverApplied Phase](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/phase_driverapplied_manual_1781818140247.png)
<!-- slide -->
![driverMoving Phase](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/phase_drivermoving_manual_1781818175395.png)
<!-- slide -->
![driverArrived Phase](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/phase_driverarrived_manual_1781818207978.png)
<!-- slide -->
![inProgress Phase](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/phase_inprogress_manual_1781818237176.png)
<!-- slide -->
![enRoute Phase](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/phase_enroute_manual_1781818269918.png)
<!-- slide -->
![finished Phase](/Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/phase_finished_manual_1781818301463.png)
````
---
## 🔍 تحليل كل مرحلة
### 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 انطلق | ✅ |
**الكود المقابل:**
```dart
// في 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()` | ✅ |
**الكود المقابل:**
```dart
// في 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 ثوانٍ | ✅ |
**الكود المقابل:**
```dart
// في 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()`
```dart
// يعمل فقط في حالتي Apply و Arrived
bool shouldShowWalkPath =
(statusRide == 'Apply' || statusRide == 'Arrived') &&
_currentDriverRoutePoints.isNotEmpty &&
passengerLocation.latitude != 0;
```
> [!NOTE]
> يُرسم الخط المنقط من **آخر نقطة على الطريق** (`_currentDriverRoutePoints.last`) وليس من موقع السائق. هذا صحيح تماماً لأنه يمثل المسافة المشي من الطريق للراكب.
### 2. انحراف السائق `checkAndRecalculateIfDeviated()`
```dart
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()`
```dart
// في حالة 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](file:///Users/hamzaaleghwairyeen/development/App/Siro/siro_rider/lib/controller/home/map/ride_lifecycle_controller.dart) — المنطق الرئيسي
- [map_socket_controller.dart](file:///Users/hamzaaleghwairyeen/development/App/Siro/siro_rider/lib/controller/home/map/map_socket_controller.dart) — إدارة WebSocket
- [map_screen_binding.dart](file:///Users/hamzaaleghwairyeen/development/App/Siro/siro_rider/lib/controller/home/map/map_screen_binding.dart) — تسجيل الـ Controllers
- [siro_ride_simulation.html](file:///Users/hamzaaleghwairyeen/.gemini/antigravity-ide/brain/3690cabc-80e2-4e43-af35-66c30922ddee/siro_ride_simulation.html) — ملف المحاكاة التفاعلية