12.5. الموثوقية -- التسلسلات وإشعارات الاستلام (ACK) وإعادة الإرسال

تكتشف طبقة التأطير التلف عبر اختبارات CRC الخاصة بها. وتحوّل طبقة الموثوقية "التلف المكتشف" إلى "عدم رؤية التطبيق لأي بيانات معطوبة على الإطلاق" من خلال التفاوض على إعادة الإرسال كلما لم تصل حزمة سليمة.

12.5.1. أرقام التسلسل

يحمل كل رأس حزمة رقم تسلسل بحجم بايت واحد، منفصلاً لكل اتجاه نقل. يزيد المرسِل العدّاد قبل الإرسال؛ ويتحقق المستقبِل من أن تسلسل كل حزمة مستلَمة هو التسلسل السابق زائد واحد (بنظام modulo 256).

ثلاثة أشياء قد تظهر لدى المستقبِل بدلاً من حزمة نظيفة ومرتبة:

  • رقم التسلسل المتوقَّع، مع CRC صحيح. تُسلَّم الحزمة إلى الطبقة التالية.

  • رقم التسلسل المتوقَّع، مع CRC خاطئ. يُسقط المستقبِل الحزمة، ويرسل (إذا جرى التفاوض على إشعارات الاستلام) إشعار NAK يطلب إعادة الإرسال.

  • رقم تسلسل أعلى بمقدار واحد من المتوقَّع، مع CRC صحيح. يعرف المستقبِل عندئذٍ أن الحزمة السابقة فُقدت؛ فيرسل NAK يشير إلى التسلسل المفقود ويحتفظ بالحزمة الجديدة.

تُعالَج حالة التكرار (وصول إعادة إرسال بعد أن نجح الأصل أخيراً في العبور) عبر المقارنة مع العدّاد المتوقَّع: إذا كان التسلسل متأخراً عن المتوقَّع، فالحزمة مكرَّرة ويتجاهلها المستقبِل بعد إرسال إشعار الاستلام الذي من الواضح أن المرسِل لم يتلقَّه في المرة الأولى.

12.5.2. إشعار الاستلام (ACK) وإشعار الرفض (NAK)

تحمل خانتان من خانات العلَم في رأس الحزمة حركة الموثوقية نفسها:

  • تعيين ACK_REQ على حزمة صادرة يعني "أريد إشعار استلام يُعاد إليّ." تعيّن حزم البيانات هذا عادةً؛ أما نبضات الحالة والأحداث العابرة فقد لا تفعل.

  • تعيين ACK على حزمة يعني "هذه الحزمة هي إشعار الاستلام لرقم التسلسل المذكور في الرأس." وهي لا تحمل أي حمولة خاصة بها.

  • تعيين NAK يعني "هذه الحزمة ترفض حزمة سابقة" -- عادةً بسبب CRC خاطئ أو فجوة في رقم التسلسل. ويوجّه الرأس المرسِل إلى أي تسلسل ينبغي إعادة إرساله.

يشغّل المرسِل حلقة توقّف-وانتظر: فيرسل حزمة واحدة تتطلب إشعار استلام، ثم ينتظر إشعار الاستلام المطابق (أو NAK) قبل إرسال التالية. ويبقي نموذج "حزمة واحدة قيد الطيران" حالة المرسِل محدودة -- بضع مئات من البايتات على أصغر الكاميرات -- ويتلاءم مع دور البروتوكول كقناة تحكم بين طرفين بدلاً من كونه أنبوباً مُحسَّناً لأجل الإنتاجية. وعند NAK يعيد المرسِل إرسال الحزمة نفسها مع تعيين العلَم RTX كي يعرف المستقبِل أنها محاولة إعادة.

12.5.3. توقيت إعادة الإرسال

إذا لم يصل ACK ولا NAK خلال مهلة إعادة الإرسال، يعيد المرسِل من تلقاء نفسه إرسال الحزمة قيد الطيران. وتبلغ المهلة افتراضياً 500 ms وتتضاعف مع كل محاولة متتالية (1 s، 2 s، ...). وبعد العدد المُهيَّأ من المحاولات -- ثلاث افتراضياً -- يستسلم المرسِل ويبلّغ التطبيق بخطأ نقل.

تضاعُف المهلة هو نمط التراجع الأسي المعياري. فالمهلة الأولى القصيرة تلتقط الحزم المفقودة بسرعة؛ والمضاعفة تعني أن مضيفاً مشغولاً لبضع مئات من الميلي ثانية لن يطلق سيلاً من التكرارات التي تفاقم الحِمل.

12.5.4. تهيئة الموثوقية

يستطيع الطرفان إيقاف أجزاء من طبقة الموثوقية، بالاتفاق، عندما يكون بمقدور التطبيق تحمّل فقدان البيانات:

  • يعطّل protocol.init(ack=False) إشعارات الاستلام لكل حزمة. يطلق المرسِل وينسى؛ ويسلّم المستقبِل ما يصل أياً كان. وهو جيد لبث بيانات المستشعر حيث تكون العيّنة القديمة مقبولة.

  • يوقف protocol.init(seq=False) تتبّع أرقام التسلسل، وهو ما يستلزم إيقاف إشعارات الاستلام أيضاً. وهو مفيد فقط على وسائط النقل الموثوقة تماماً.

  • يوقف protocol.init(crc=False) التحقق عبر CRC لكنه يُبقي بقية التأطير سليمة. ولا يُجدي ذلك إلا عندما يكون وسيط النقل نفسه متيناً بما يكفي بحيث لا تقع أخطاء CRC.

الإعدادات الافتراضية -- وكل شيء مُفعَّل -- هي نقطة الانطلاق الصحيحة لأي جلسة تنقيح بين المضيف والكاميرا. وبمجرد أن يصبح التطبيق في الإنتاج، تغدو المفاضلات خاصة ببياناته ووسيط نقله.

12.5.5. رموز الحالة

عندما ينتشر خطأ نقل صاعداً إلى شيفرة التطبيق، فإنه يصل على هيئة رمز حالة. تعرّف مكتبة البروتوكول عشرة رموز:

  • SUCCESS -- اكتملت العملية.

  • FAILED -- فشل الأمر لسبب غير محدد.

  • INVALID -- رفض المستقبِل الأمر أو أحد وسائطه.

  • TIMEOUT -- نفد مؤقت إعادة المحاولة.

  • BUSY -- الكاميرا مشغولة (عادةً قناة مقفلة).

  • CHECKSUM -- لم يتطابق CRC الخاص بالرأس أو الحمولة.

  • SEQUENCE -- كان رقم التسلسل خارج الترتيب بما يتجاوز ما تستطيع الطبقة التعافي منه.

  • OVERFLOW -- تجاوزت حمولة الحد الأقصى المتفاوَض عليه.

  • FRAGMENT -- وصلت رسالة متعددة الأجزاء بأجزاء ناقصة.

  • UNKNOWN -- شامل دفاعي للحالات غير المتوقَّعة حقاً.

ترى شيفرة المضيف التي تستدعي channel_read() هذه على هيئة استثناءات Python؛ بينما تراها شيفرة التطبيق الجارية على الكاميرا والتي اختارت معالجة أخطاء مخصصة على هيئة قيم إرجاع من دوال رد النداء الخلفية. ولا تحتاج معظم تطبيقات الكاميرا إلى النظر في رموز الحالة على الإطلاق -- إذ تتولى المكتبة إعادة المحاولة، ولا يصل إلى التطبيق سوى حالات الفشل غير القابلة للتعافي حقاً (مثل اختفاء وسيط النقل نفسه).

بوجود التأطير لكشف التلف والموثوقية للتعافي منه، يكون العمل على مستوى السلك قد اكتمل. ترى شيفرة التطبيق حزماً مؤطَّرة ومرتبة وسليمة؛ والبايتات بداخلها حرة في أن تعني ما تشاء أن تعنيه القناة الأعلى منها.