22 KiB
22 KiB
تقرير فني: دورة حياة الرحلة وتتبع نظام الموقع في تطبيق السائق (Siro Driver)
1. منطق الاتصال بالشبكة وفحوصات السلامة (Connection & Safety Logic)
يتم التحكم في حالة اتصال السائق بالشبكة وتفعيل استقبال الطلبات في ملف التحكم
من خلال الدالة
والتي تقوم بإجراء الفحوصات المتتالية التالية قبل السماح للسائق بالدخول في حالة النشاط (`isActive = true`):
أ. فحص عقوبة إلغاء الرحلات (Cancellation Penalty Check)
تتحقق الدالة
من وجود تاريخ حظر نشط مخزن في الذاكرة المحلية تحت المفتاح `blockUntilDate`. إذا كان الوقت الحالي قبل وقت انتهاء الحظر، يتم إجبار السائق على وضع عدم الاتصال وعرض نافذة حوار مانعة تعرض عداداً تنازلياً لوقت فك الحظر. تفرض هذه العقوبة تلقائياً لمدة 4 ساعات عند إلغاء السائق لـ 3 رحلات في اليوم الواحد.
ب. فحص حد الإرهاق اليومي (Fatigue Monitoring Check)
يقوم النظام بمراقبة ساعات القيادة المتواصلة للسائق لمنع الحوادث عبر الدالة
إذا بلغ مجموع ثواني النشاط المخزنة في `fatigue_total_seconds` ما يعادل 12 ساعة عمل، يتم استدعاء الدالة
والتي تقطع الاتصال فوراً وتمنع السائق من العمل. لا يتم تصفير هذا العداد إلا إذا بقي السائق في وضع عدم الاتصال بشكل مستمر لمدة لا تقل عن 6 ساعات متواصلة (يتم تتبعها عبر قراءة تاريخ `fatigue_last_offline`).
ج. فحص الحد الأدنى لنقاط المحفظة (Wallet Points Threshold)
يتم استدعاء خاصية
والتي تعتمد على الدولة الحالية المخزنة في إعدادات التطبيق. إذا كان الرصيد الحالي للنقاط أدنى من الحد المسموح به (والذي يساوي `-200` نقطة في سوريا ومصر، و `-3` نقاط في الأردن)، يتم منع السائق من استقبال الطلبات وإيقاف تحديثات الموقع فوراً.
2. نظام التتبع وتحسين استهلاك البطارية والمعالج (GPS & Performance Optimization)
لمعالجة مشاكل استنزاف البطارية وارتفاع حرارة الأجهزة الضعيفة، تم تطبيق استراتيجيات تحسين الأداء التالية في
و
أ. تجميع قنوات البث ومستمع الحركة الموحد (Centralized GPS Stream & 500ms Timer Polling)
بدلاً من فتح قنوات بث (Streams) متعددة ومستقلة للـ GPS، تم تركيز البث في كلاس مركزي موحد هو
حيث يقوم مستمع التوجيه والملاحة في الدالة
بعمل استعلام دوري خفيف (Polling) كل 500 ملي ثانية لقراءة إحداثيات `locationController.myLocation` الجاهزة مسبقاً، مما يمنع استدعاء العتاد المادي لجهاز الاستقبال عدة مرات متزامنة.
ب. فلترة ضجيج الإحداثيات (Jitter Noise Filtering)
لمنع التحديثات المتكررة وغير المفيدة التي تنتج عن عدم دقة حساس الـ GPS عند الوقوف، يقوم الكود بمقارنة إحداثيات الموقع الجديد مع آخر موقع تم تسجيله عبر الدالة
Geolocator.distanceBetween
فإذا كانت المسافة المقطوعة أقل من 3 أمتار، يتم تجاهل التحديث بالكامل وعدم تعديل المسار أو إرسال بيانات للسيرفر.
ج. التحكم الذكي في حركة الكاميرا والـ UI Throttling
يتم التحكم في حركة الكاميرا لمتابعة حركة السائق على الخريطة الرئيسية عبر مؤقت دوري يعمل كل 8 ثوانٍ:
_cameraFollowTimer = Timer.periodic(const Duration(seconds: 8), (timer) { ... });
وتشترط الدالة تحرك السائق لمسافة تزيد عن 15 متراً عن آخر موقع تحركت إليه الكاميرا لتنفيذ الحركة الدائرية والتقريب، مما يقلل بشكل كبير من استهلاك معالج الرسوميات (GPU) في عمليات إعادة رسم الخريطة (Re-rendering) أثناء الوقوف. كذلك، تم تفعيل وسيلة
_uiThrottleMs = 400
لكبح تكرار استدعاء الدالة `update()` المسؤولة عن تحديث الواجهات.
3. استقبال وإدارة الطلبات ونافذة الواجهة العائمة (Order Requests & Overlay System)
يتم استقبال إشعارات الرحلات الجديدة إما عبر سوكيت الويب (WebSockets) أو إشعارات Firebase (FCM). عند وصول إشعار والبرنامج في الخلفية، يتم تفعيل شاشة الواجهة العائمة المفتوحة عبر حزمة
FlutterOverlayWindow
والتي تظهر للسائق تفاصيل الطلب بشكل مباشر.
أ. تهيئة البيانات ودعم الصيغ المتعددة (Smart Data Handling)
يقوم الكلاس
بتحليل البيانات المستقبلة في الدالة
حيث تدعم بشكل مرن استقبال البيانات سواء كانت على شكل قائمة مرتبة (List) قادمة من إشعارات Firebase، أو على شكل خريطة مفاتيح (Map) قادمة من سوكيت الويب.
ب. مؤقت قبول الطلب وصوت التنبيه
عند فتح شاشة الطلب، يتم تشغيل صوت تنبيه متكرر وتفعيل مؤقت تنازلي مدته 15 ثانية عبر الدالة
إذا انتهت الـ 15 ثانية دون استجابة السائق، يتم إيقاف الصوت وإغلاق الشاشة تلقائياً.
ج. فحص القبول المسبق للطلب (Socket ride_taken Listening)
لتفادي قبول طلب تم أخذه بالفعل من قبل كابتن آخر، يقوم الكنترولر في الدالة
بالاستماع لحدث السوكيت `ride_taken`. عند استقبال الحدث ومطابقة معرف الرحلة، يتم إلغاء إشعار النظام فوراً، وإغلاق شاشة الطلب وعرض تنبيه للسائق بأن "الطلب تم قبوله من قبل سائق آخر".
4. نظام الملاحة التفاعلي ورسم المسارات (Interactive Navigation & Mapping)
يعتمد تطبيق السائق على خرائط انطلق المبنية على محرك مابليبرا (MapLibre)، ويتم استدعاء ورسم مسارات الملاحة التفاعلية في تطبيق السائق بدقة وتفصيل عالية عبر الفئات والأساليب التالية:
أ. رسم المسارات المزدوجة ونوافذ المعلومات في شاشة طلب الرحلة (Dual-Route & Info Windows in Order Request)
في شاشة استقبال الطلب
يقوم التطبيق بالاستعلام ورسم مسارين جغرافيين في نفس الوقت عبر الدالة
حيث يستدعي:
1. مسار الانطلاق (Pickup Route): من موقع السائق الحالي إلى موقع الراكب (يرسم باللون الأصفر/الذهبي).
2. مسار الرحلة الرئيسي (Trip Route): من موقع الراكب إلى الوجهة النهائية (يرسم باللون الأسود/الأزرق).
ولعرض تفاصيل المسافة والوقت كصندوق معلومات عائم (Info Window) مباشرة فوق الخريطة، يتم استدعاء الدالة
والتي تقوم بطلب مولد الماركرز
MarkerGenerator.createCustomMarkerBitmap
لتوليد صور ماركر مخصصة ديناميكياً تحتوي على الوقت والمسافة كصندوق معلومات يعلو الخريطة فوق نقطة الركوب (أقرب مسافة وزمن وصول للسائق) ونقطة الوصول (المسافة والزمن المقدرين للرحلة الكلية للراكب).
ب. تسلسل رسم وحذف المسارات أثناء دورة حياة الرحلة (Lifecycle Route Transitions)
تخضع مسارات الخريطة لعملية تحديث وحذف دورية أثناء الرحلة في الكنترولر
وفق التسلسل التالي:
1. **عند قبول الطلب**: يتم مسح خط الوجهة، ورسم خط الملاحة الجاري باتجاه الراكب (باللون الأصفر) عبر استدعاء
2. **عند وصول السائق لموقع الراكب**: بمجرد ضغط السائق على زر "وصلت" وتأكيده، يتم استدعاء الدالة
والتي تقوم بمسح وحذف المسار الجاري الأول (الخط الأصفر الموصل للراكب) بالكامل من الخريطة لتنظيف الشاشة.
3. **عند بدء الرحلة الفعلي**: يتم الاستعلام ورسم المسار الأزرق/الأسود الجديد المؤدي للوجهة النهائية مباشرة باتجاه وجهة الراكب عبر إعادة استدعاء دالة المسار `getRoute` للوجهة.
ج. تفادي انهيار الخرائط عند المسافات الصفرية (Same-Device Crash Protection)
عند تشغيل اختبارات الرحلة وكون موقع السائق والراكب متطابقين تماماً (مسافة أقل من 10 أمتار)، ينهار محرك الملاحة المكتوب بلغة C++ بسبب إحداثيات الصندوق المحيط (Bounds) ذات العرض الصفرى مطلقةً استثناء `std::domain_error`. لمنع ذلك، يقوم الكود بفحص المسافة، وفي حال كانت متطابقة يقوم بإظهار نافذة تنبيه
ثم الانتقال قسرياً لتطبيق زوم تقريبي آمن بدلاً من احتواء الحدود الصفرية.
د. تحديث المسار المقطوع بنظام النافذة المنزلقة (Bidirectional Sliding Window)
لمنع إعادة رسم كامل خط المسار (Polyline) عند كل إرسال للموقع، يتم استخدام نافذة بحث منزلقة ثنائية الاتجاه تتكون من 60 نقطة (30 للخلف و 30 للأمام) في الدالة
تحدد الدالة أقرب نقطة لموقع السائق الحالي على المسار المخزن، وتقوم بقطع الـ Polyline إلى جزأين: مسار مقطوع بلون رمادي ومسار متبقي بلون أزرق/أصفر، وتحديث الخريطة فقط عند تجاوز إزاحة تزيد عن 50 متراً.
هـ. رسم خطوط المشي المنقطة (Passenger Walk Dotted Line)
عندما يكون موقع الراكب الفعلي بعيداً عن أقرب طريق إسفلتي متاح للسيارات، يتم استدعاء الدالة
والتي تقوم برسم خط منقط بلون أزرق رمادي مميز يمتد من نهاية طريق السيارات الإسفلتي إلى موقع الراكب الحقيقي لتوجيه السائق سيراً على الأقدام إذا لزم الأمر.
5. محرك تسعير الرحلة الديناميكي (Dynamic Pricing Engine)
أثناء سير الرحلة، يعمل مؤقت دوري كل ثانية لحساب السعر الفعلي بشكل لحظي وعرضه في واجهة السائق عبر الدالة
ويعتمد الحساب على القواعد البرمجية التالية:
أ. تجميد الأسعار للرحلات الثابتة (Fixed Price Protection)
إذا كان نوع الرحلة من الفئات ذات السعر الثابت مثل `Speed` أو `Fixed Price` أو `Awfar Car`، يتم إيقاف الحساب الديناميكي وتثبيت السعر المعروض على القيمة المتفق عليها مسبقاً في عرض السعر الأولي للراكب.
ب. تسعير الفئات المتغيرة (Comfort / Electric / Van / Delivery)
للفئات المتغيرة، يتم حساب السعر التراكمي عبر دمج المسافة الفعلية المقطوعة مع وقت الرحلة الفعلي طبقاً للمعادلة:
Price = (Distance_KM * Per_KM_Rate) + (Duration_Minutes * Per_Minute_Rate)
حيث يتم تطبيق تسعيرة الدقيقة بناءً على ساعة الرحلة الحالية لمراعاة أوقات الذروة (طبيعي، متأخر، أو حركة مرورية كثيفة)، بالإضافة إلى ضرب الناتج في عمولة السيرفر (كازان) المحددة بنسبة مئوية.
ج. تخفيضات المسافات الطويلة (Long Distance Reduction Rules)
إذا تجاوزت المسافة المقطوعة 35 كم أو 40 كم، يطبق محرك التسعير قواعد خاصة:
- يتم تجميد تسعيرة الدقيقة وتثبيتها على قيمة ثابتة للرحلات الطويلة تعادل 600 ل.س/دقيقة.
- يتم تطبيق نسبة خصم ديناميكية (تصل إلى 35%) على تسعيرة الكيلومتر لتخفيض الأعباء على الراكب مع الحفاظ على ربحية السائق.
- يضمن الكود دائماً عدم نزول السعر الفعلي النهائي عن السعر المتفق عليه مسبقاً (Quoted Price).
6. حماية الرحلة من الإنهاء المبكر الخاطئ (Anti-Fraud & Exit Validation)
عندما يسحب السائق شريط إنهاء الرحلة، يمر الطلب بفحص أمني دقيق للتأكد من عدم وجود تلاعب أو إنهاء وهمي للرحلة.
أ. شرط الإزاحة الأمنية (Displacement Validation Check)
يتم استدعاء الدالة
والتي تقوم بحساب المسافة المستقيمة الفاصلة بين موقع انطلاق الرحلة (موقع الراكب الأصلي) والموقع الجغرافي الحالي للسائق. يشترط النظام أن تتجاوز هذه المسافة قيمة **خُمس المسافة الإجمالية المخططة للرحلة** (`plannedDistance / 5`).
ب. التنبيه الصوتي المانع والتراجع (TTS Rejection Alert)
إذا حاول السائق إنهاء الرحلة قبل قطع حد الخُمس المسموح، يتم رفض الطلب فوراً وإغلاق أي نوافذ تحميل، وتفعيل قارئ النصوص الصوتي لإصدار تنبيه صوتي باللغة الإنجليزية عبر الهاتف:
"You haven't moved sufficiently!"
مع إظهار رسالة خطأ تحذيرية في الواجهة وإعادة زر إنهاء الرحلة لوضعه النشط للسماح بإكمال الرحلة.
ج. إتمام المعاملة المالية الموازية (Parallel Transaction Completion)
عند اجتياز فحص المسافة بنجاح، يتم تفعيل دالة الإنهاء الفعلي
والتي تقوم بإرسال طلبات التحديث المالي وإغلاق الرحلة في آن واحد إلى سيرفر العمليات وسيرفر المحفظة المالي بالتوازي عبر استدعاء
Future.wait([...])
مما يقلل وقت المعاملة على أجهزة السائقين ويمنع تعليق التطبيق. بعد نجاح المعاملات، يتم توجيه السائق تلقائياً إلى صفحة تقييم الراكب وإرسال تقرير السلوك الفني.