12.7. دوال رد النداء للقناة

الكائن الخلفي (backend) الذي يُمرَّر إلى protocol.register() هو صنف Python. لا تسأل مكتبة البروتوكول الصنفَ عن الدوال التي ينفّذها؛ بل تفحص النسخة وتربط ما تجده منها. هذا الفحص الاستبطاني هو ما يجعل واجهة الكائن الخلفي مرنة: أصغر كائن خلفي مفيد يتكوّن من دالتين، وأكثرها تفصيلًا من اثنتي عشرة دالة، ويختار التطبيق كل قدرة دالةً تلو الأخرى.

12.7.1. قواعد الفحص الاستبطاني

عند تشغيل protocol.register()، تمرّ المكتبة على قائمة ثابتة من أسماء الدوال القابلة للاستدعاء وتربط كل اسم تجده على نسخة الكائن الخلفي:

  • إضافة read إلى الصنف تفعّل CHANNEL_FLAG_READ. ولا يصل أي استدعاء من المضيف إلى channel_read() ليبلغ الكائن الخلفي إلا إذا كانت هذه الراية مفعّلة.

  • إضافة write تفعّل CHANNEL_FLAG_WRITE، مما يتيح channel_write().

  • إضافة lock و unlock تفعّل CHANNEL_FLAG_LOCK، مما يتيح للمضيف قفل القناة من أجل قراءة ذرّية متعددة الحزم.

  • إضافة poll تتيح للمضيف أن يسأل "هل هناك أي شيء جاهز؟" بتكلفة زهيدة، دون إجبار قراءة كاملة.

الدوال المفقودة ليست أخطاء -- فمكتبة البروتوكول تترك القدرة المقابلة معطّلة فحسب. الكائن الخلفي الذي يحتوي على size و read فقط صالح تمامًا؛ فهو قناة بيانات للقراءة فقط.

12.7.2. قناة مستشعر للقراءة فقط

قناة مستشعر تنشر قراءة جديدة في كل مرة يطلب فيها المضيف، وترفض كتابات المضيف، تستخدم أربعًا من دوال رد النداء:

import protocol
import struct

class TempChannel:
    def __init__(self, read_sensor):
        self._read_sensor = read_sensor
        self._buf = b''
        self._fresh = False

    def poll(self):
        # Tell the host whether a reading is waiting.
        return self._fresh

    def size(self):
        # Sample fresh data on every host-side size query.
        value = self._read_sensor()
        self._buf = struct.pack('<f', value)
        self._fresh = True
        return len(self._buf)

    def read(self, offset, size):
        end = offset + size
        if end >= len(self._buf):
            self._fresh = False
        return self._buf[offset:end]

protocol.register(name='temp', backend=TempChannel(read_temperature))

استعراض ما تفعله كل دالة:

  • poll تُرجع راية الحداثة. يستدعيها المضيف قبل القراءة ويتخطى القراءة كليًا عندما تُرجع False. هذا يوفّر تكلفة الذهاب والإياب في حالة "لا توجد بيانات جديدة بعد."

  • size تعيد توليد المخزن المؤقت عند الطلب وتبلّغ عن طوله. القيام بأخذ العينات هنا يعني أن الكائن الخلفي لا يحتاج إلى مهمة في الخلفية -- فاستدعاء من المضيف يقود كل عملية قياس.

  • read تُرجع شريحة من المخزن المؤقت. قد تستدعيها مكتبة البروتوكول أكثر من مرة عندما يكون المخزن المؤقت أكبر من أقصى حمولة متفاوض عليها؛ تتنقل وسيطة offset عبر الأجزاء.

  • عدم وجود write يعني أن كتابات المضيف تُرفض في طبقة التأطير، قبل أن يتدخل الكائن الخلفي.

12.7.3. مجموعة دوال رد النداء الكاملة

للرجوع إليها، كل دالة تبحث عنها المكتبة في الكائن الخلفي:

الدالة

تُرجع

الغرض

init(self)

كائن

تهيئة اختيارية تُنفَّذ مرة واحدة عند ارتباط القناة بمضيف لأول مرة. أرجع أي قيمة غير None عند النجاح.

poll(self)

bool

أرجع True عندما تكون البيانات متاحة.

lock(self)

bool

الاستحواذ على القناة من أجل نقل ذرّي متعدد الحزم.

unlock(self)

bool

تحرير lock سابق.

size(self)

int

عدد البايتات القابلة للقراءة حاليًا من القناة.

shape(self)

tuple

ما يصل إلى أربعة أعداد صحيحة تصف بنية البيانات (مثل ارتفاع الصورة وعرضها وعدد البايتات). يستخدمها المضيف لفك حزم المخازن المؤقتة المُصنَّفة بالنوع.

read(self, offset, size)

bytes

أرجع ما يصل إلى size بايت ابتداءً من offset. تُستدعى مرة واحدة لكل جزء عندما تتجاوز الحمولة الحد الأقصى المتفاوض عليه.

readp(self, offset, size)

bytes

نسخة بدون نسخ (zero-copy) من read: يجب أن تبقى ذاكرة المخزن المؤقت صالحة طوال مدة النقل.

write(self, offset, data)

int

كتب المضيف data عند offset. data هي عرض bytearray داخل مخزن الاستقبال المؤقت لطبقة البروتوكول -- انسخ ما تريد الاحتفاظ به قبل الإرجاع.

ioctl(self, cmd, length, arg)

int

رمز عملية مُعرَّف من التطبيق خارج نموذج القراءة/الكتابة. القيمة المُرجَعة السالبة تعني خطأ.

flush(self)

كائن

إسقاط أي بيانات مخزّنة مؤقتًا. تُستدعى عندما يريد المضيف إعادة تعيين القناة.

is_active(self)

bool

ذات معنى فقط على الكائنات الخلفية التي تمثّل ناقلًا فيزيائيًا (قنوات USB المدمجة). قنوات التطبيق لا تحتاج إلى هذه.

هذه هي واجهة الكائن الخلفي بأكملها. اثنا عشر اسم دالة، جميعها اختيارية، وتقرر مكتبة البروتوكول ما يمكن لكل قناة فعله بناءً على أيّها موجود.