WebSocket الملاحة
WebSocket الملاحة
بدلًا من إرسال PATCH كل ثانية أو ثانيتين، أبقِ اتصال WebSocket واحدًا مفتوحًا لكل جلسة ملاحة. تُرسَل عبره المواقع، وتصل عبره بيانات التقدم والمسارات الجديدة والمسارات البديلة وتنبيهات المخاطر.
أنشئ الجلسة أولًا عبر POST /navigation/sessions. ويبقى إنهاؤها عبر REST أيضًا. واحتفظ بـ PATCH /navigation/sessions/{id} خيارًا احتياطيًا عند انقطاع الاتصال.
الاتصال
wss://navvo.io/api/v1/navigation/sessions/{sessionId}/live
صادِق أثناء المصافحة ببيانات الاعتماد نفسها التي تستخدمها مع REST.
| الطريقة | الكيفية |
|---|---|
| مستخدم مسجّل الدخول | الترويسة Authorization: Bearer <JWT> |
| مفتاح API | الترويسة X-Api-Key: <key> |
| تطبيق لا يستطيع ضبط الترويسات | معامل الاستعلام ?token=<JWT> |
استخدم الترويسة كلما أمكن، لأن الرمز الموجود في عنوان URL قد يصل إلى السجلات.
أرسل ping كل 25 ثانية. وإذا لم يصل pong خلال 10 ثوانٍ، فأغلق الاتصال وأعد الاتصال. ويقطع الخادم الاتصال الصامت لمدة 90 ثانية.
لكل جلسة اتصال واحد في الوقت نفسه، ويحلّ الاتصال الجديد محل السابق.
صيغة الرسائل
كل رسالة إطار نصي بصيغة JSON وترميز UTF-8.
{ "type": "<string>", "timestamp": "2026-11-10T11:05:00.123Z", "payload": { } }
الحدود: 16 كيلوبايت لكل رسالة و20 رسالة في الثانية. وتُهمل الإطارات التي تتجاوز المعدل.
الرسائل التي ترسلها
location_update
أرسل رسالة لكل موقع GPS صالح، بمعدل مرة إلى مرتين في الثانية.
{
"type": "location_update",
"timestamp": "2026-11-10T11:05:01.450Z",
"payload": {
"lat": 31.953949,
"lng": 35.910635,
"bearing": 215.4,
"speed": 18.2,
"routeRevision": 2,
"capturedAt": "2026-11-10T11:05:01.400Z"
}
}
speed لا تقل عن 2 متر في الثانية.تُتجاهل قيمة bearing أو speed غير الصالحة ويُحتسب الموقع رغم ذلك. وأثناء انشغال الخادم يُحتفظ بأحدث موقع فقط.
request_reroute
اطلب مسارًا جديدًا من موقع المركبة، مثلًا من زر إعادة التوجيه. لا يحتاج اكتشاف الخروج عن المسار إلى هذه الرسالة، لأن الخادم يستنتجه من مواقعك. ويمكنك إرسال طلب واحد كل 5 ثوانٍ.
{
"type": "request_reroute",
"timestamp": "2026-11-10T11:05:03.000Z",
"payload": {
"reason": "manual",
"currentLocation": { "lat": 31.9542, "lng": 35.9101 },
"routeRevision": 2
}
}
select_alternative
انتقل إلى مسار عرضته الرسالة alternative_available.
{
"type": "select_alternative",
"timestamp": "2026-11-10T11:05:10.000Z",
"payload": { "alternativeId": "alt_3fa91c0b27de", "routeRevision": 2 }
}
الرسائل التي تستقبلها
progress
رسالة لكل موقع يعالجه الخادم.
{
"type": "progress",
"timestamp": "2026-11-10T11:05:01.490Z",
"payload": {
"routeRevision": 2,
"staleFix": false,
"offRoute": false,
"wrongWay": false,
"arrived": false,
"shapeIndex": 184,
"snapped": { "lat": 31.95394, "lng": 35.91062 },
"distanceToRouteMeters": 2,
"distanceRemainingMeters": 8450,
"durationRemainingSeconds": 580,
"distanceToNextManeuverMeters": 350,
"progressFraction": 0.15,
"nextManeuver": {
"instruction": "Keep left toward Zahran St",
"verbalAlert": "In 350 meters, keep left toward Zahran Street",
"type": "fork",
"modifier": "left",
"maneuverCode": 24
}
}
}
عندما تكون staleFix بقيمة true تجاهل قيم التقدم في هذه الرسالة. وقد تكون nextManeuver بقيمة null.
تكون type إحدى القيم depart وarrive وcontinue وturn وuturn وramp وexit وfork وmerge وroundabout وferry. وتكون modifier إحدى القيم left وright وslight_left وslight_right وsharp_left وsharp_right وstraight وenter وexit أو null.
route_updated
تغيّر المسار. استبدل المسار المعروض واحفظ قيمة routeRevision الجديدة. وتجاهل أي رسالة بمراجعة أقل مما لديك.
{
"type": "route_updated",
"timestamp": "2026-11-10T11:05:04.120Z",
"payload": {
"routeRevision": 3,
"reason": "off_route_auto_recovery",
"totalDistanceMeters": 8920,
"totalDurationSeconds": 610,
"routeGeometry": [[31.9542, 35.9101], [31.95435, 35.91001]],
"routeShapeEncoded": "…",
"maneuvers": [],
"trafficSegments": []
}
}
reason | المعنى |
|---|---|
off_route_auto_recovery | خرجت المركبة عن المسار |
wrong_way_auto_recovery | سارت المركبة عكس المسار لمدة 3 ثوانٍ |
reroute_requested | رد على request_reroute أو إعادة حساب عبر REST |
alternative_selected | رد على select_alternative |
resync | كانت routeRevision لديك متأخرة، مثلًا بعد إعادة الاتصال |
تتكون routeGeometry من أزواج [lat, lng]، وrouteShapeEncoded هو الخط نفسه بصيغة polyline6. وفي maneuvers تكون type هي القيمة النصية أعلاه وmaneuverCode هو الرمز الرقمي. أما trafficSegments فتكون فارغة دائمًا حاليًا.
alternative_available
طريق آخر للوصول إلى الوجهة. يتحقق الخادم مرة كل دقيقة تقريبًا ويعرض المسارات التي لا تزيد مدتها على 5 دقائق.
{
"type": "alternative_available",
"timestamp": "2026-11-10T11:06:00.000Z",
"payload": {
"alternativeId": "alt_3fa91c0b27de",
"routeRevision": 3,
"name": "Via Zahran St",
"summary": "Save 4 min",
"durationDifferenceSeconds": -240,
"distanceDifferenceMeters": 350,
"forkPoint": { "lat": 31.9551, "lng": 35.9082 },
"routeGeometry": [[31.9551, 35.9082]],
"routeShapeEncoded": "…",
"expiresAt": "2026-11-10T11:08:00.000Z"
}
}
توقف عن عرض البديل عند expiresAt أو عند وصول route_updated. والقيمة نفسها في alternativeId تعني البديل نفسه.
hazard_alert
تُرسل مرة واحدة لكل خطر عندما يصبح ضمن 600 متر أمامك على المسار.
{
"type": "hazard_alert",
"timestamp": "2026-11-10T11:06:30.000Z",
"payload": {
"alertId": "inc_77492",
"category": "speed_camera",
"severity": "medium",
"distanceMeters": 600,
"location": { "lat": 31.9582, "lng": 35.9015 },
"voiceAlert": "Speed camera reported ahead in 600 meters",
"speedLimitKmh": 80
}
}
تكون category إما speed_camera أو نوع حادث مثل accident أو closure أو construction أو jam. ولا تظهر speedLimitKmh إلا عند معرفة الحد.
error
تعذّر استخدام الرسالة. يبقى الاتصال مفتوحًا.
{ "type": "error", "timestamp": "…", "payload": { "code": "INVALID_MESSAGE", "detail": "…" } }
الرموز: INVALID_JSON وINVALID_MESSAGE وUNKNOWN_TYPE وALTERNATIVE_UNAVAILABLE وRATE_LIMITED وINTERNAL.
مراجعات المسار
تُبقي routeRevision تطبيقك والخادم متوافقين، سواء عبر WebSocket أو عبر PATCH.
- احفظ أعلى مراجعة طبّقتها.
- أرسلها مع كل موقع.
- بعد أي انقطاع، أرسل موقعًا مع المراجعة المحفوظة. وإذا كنت متأخرًا فستصلك
route_updatedبالسببresync.
انقطاع الاتصال
| رمز الإغلاق | المعنى | ما يجب فعله |
|---|---|---|
1000 | انتهت الرحلة | توقف |
4001 | الجلسة غير موجودة أو منتهية | توقف. وابدأ جلسة جديدة إذا كانت الملاحة مستمرة |
4003 | بيانات اعتماد مفقودة أو غير صالحة | توقف. جدّد تسجيل الدخول ثم أعد الاتصال مرة واحدة |
4009 | حلّ محله اتصال أحدث | توقف |
1011 | مشكلة مؤقتة في الخادم | أعد الاتصال |
1012 | الخدمة تعيد التشغيل | أعد الاتصال |
1013 | الخدمة متوقفة مؤقتًا | أعد الاتصال واستخدم PATCH في هذه الأثناء |
| أي رمز آخر | فقدان الشبكة أو إعادة تشغيل الخادم | أعد الاتصال |
قد تفشل المصافحة أيضًا برمز HTTP 503 أثناء توقف الخدمة. وتعامل معه مثل 1013.
لإعادة الاتصال، أعد المحاولة بعد ثانية ثم ثانيتين ثم 4 ثوانٍ، ثم كل 4 ثوانٍ. وبعد انقطاع يتجاوز 5 ثوانٍ، أرسل PATCH /navigation/sessions/{id} مع routeRevision وcapturedAt. وعند عودة الاتصال أوقف PATCH وأرسل location_update فورًا.
إنهاء الرحلة
POST /api/v1/navigation/sessions/{id}/complete
{ "rating": "up", "message": "Great route", "distanceKm": 12.4, "durationSec": 900 }
جميع الحقول اختيارية. تنتهي الرحلة ويُغلق الاتصال بالرمز 1000. ولإلغاء رحلة استخدم DELETE /navigation/sessions/{id}.