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. مجموعة دوال رد النداء الكاملة¶
للرجوع إليها، كل دالة تبحث عنها المكتبة في الكائن الخلفي:
الدالة |
تُرجع |
الغرض |
|---|---|---|
|
كائن |
تهيئة اختيارية تُنفَّذ مرة واحدة عند ارتباط القناة بمضيف لأول مرة. أرجع أي قيمة غير |
|
bool |
أرجع |
|
bool |
الاستحواذ على القناة من أجل نقل ذرّي متعدد الحزم. |
|
bool |
تحرير |
|
int |
عدد البايتات القابلة للقراءة حاليًا من القناة. |
|
tuple |
ما يصل إلى أربعة أعداد صحيحة تصف بنية البيانات (مثل ارتفاع الصورة وعرضها وعدد البايتات). يستخدمها المضيف لفك حزم المخازن المؤقتة المُصنَّفة بالنوع. |
|
bytes |
أرجع ما يصل إلى size بايت ابتداءً من offset. تُستدعى مرة واحدة لكل جزء عندما تتجاوز الحمولة الحد الأقصى المتفاوض عليه. |
|
bytes |
نسخة بدون نسخ (zero-copy) من |
|
int |
كتب المضيف data عند offset. |
|
int |
رمز عملية مُعرَّف من التطبيق خارج نموذج القراءة/الكتابة. القيمة المُرجَعة السالبة تعني خطأ. |
|
كائن |
إسقاط أي بيانات مخزّنة مؤقتًا. تُستدعى عندما يريد المضيف إعادة تعيين القناة. |
|
bool |
ذات معنى فقط على الكائنات الخلفية التي تمثّل ناقلًا فيزيائيًا (قنوات USB المدمجة). قنوات التطبيق لا تحتاج إلى هذه. |
هذه هي واجهة الكائن الخلفي بأكملها. اثنا عشر اسم دالة، جميعها اختيارية، وتقرر مكتبة البروتوكول ما يمكن لكل قناة فعله بناءً على أيّها موجود.