Update: 2026-06-19 01:47:48
This commit is contained in:
252
knowledge/ride_simulation_report.md
Normal file
252
knowledge/ride_simulation_report.md
Normal file
@@ -0,0 +1,252 @@
|
||||
|
||||
# تقرير محاكاة دورة حياة الرحلة — Siro Rider 🚖
|
||||
|
||||
> **الهدف:** التحقق من صحة رسم الخطوط (Polylines) والـ Markers في كل مرحلة من مراحل الرحلة
|
||||
|
||||
---
|
||||
|
||||
## 🎬 تسجيل المحاكاة
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📸 لقطات كل مرحلة
|
||||
|
||||
````carousel
|
||||

|
||||
<!-- 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 انطلق | ✅ |
|
||||
|
||||
**الكود المقابل:**
|
||||
```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) — ملف المحاكاة التفاعلية
|
||||
|
||||
Reference in New Issue
Block a user