12.7. פונקציות callback של ערוצים#
אובייקט ה-backend המועבר אל protocol.register() הוא מחלקת Python. ספריית הפרוטוקול אינה שואלת את המחלקה אילו מתודות היא ממשת; היא בוחנת את המופע ומחברת את אלו שהיא מוצאת. אינטרוספקציה זו היא מה שמקנה לממשק ה-backend את גמישותו: ה-backend השימושי הקטן ביותר מורכב משתי מתודות, המורכב ביותר משתים-עשרה, והאפליקציה מצטרפת לכל יכולת בנפרד, מתודה אחת בכל פעם.
12.7.1. כללי האינטרוספקציה#
כאשר protocol.register() רץ, הספרייה עוברת על רשימה קבועה של שמות הניתנים לקריאה ומקשרת כל אחד שהיא מוצאת על מופע ה-backend:
הוספת
readלמחלקה מדליקה אתCHANNEL_FLAG_READ. קריאה מהמארח אלchannel_read()מגיעה אל ה-backend רק אם דגל זה מוגדר.הוספת
writeמדליקה אתCHANNEL_FLAG_WRITE, ומאפשרת אתchannel_write().הוספת
lockו-unlockמדליקה אתCHANNEL_FLAG_LOCK, ומאפשרת למארח לנעול את הערוץ לצורך קריאה אטומית מרובת מנות (packets).הוספת
pollמאפשרת למארח לשאול ”האם משהו מוכן?“ בזול, בלי לכפות קריאה מלאה.
מתודות חסרות אינן שגיאות – ספריית הפרוטוקול פשוט משאירה את היכולת המתאימה מושבתת. backend עם size ו-read בלבד הוא תקין לחלוטין; זהו ערוץ נתונים לקריאה בלבד.
12.7.2. ערוץ חיישן לקריאה בלבד#
ערוץ חיישן המפרסם קריאה טרייה בכל פעם שהמארח מבקש, ומסרב לכתיבות מהמארח, עושה שימוש בארבע מפונקציות ה-callback:
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מייצרת מחדש את החוצץ (buffer) לפי דרישה ומדווחת על אורכו. ביצוע הדגימה כאן משמעו שה-backend אינו זקוק למשימת רקע – קריאה מהמארח מניעה כל מדידה.readמחזירה פלח של החוצץ (buffer). ספריית הפרוטוקול עשויה לקרוא לה יותר מפעם אחת כאשר החוצץ גדול מהמטען המרבי שסוכם; הארגומנטoffsetעובר דרך הקטעים.היעדר
writeמשמעו שכתיבות מהמארח נדחות בשכבת המסגור (framing), לפני שה-backend מעורב.
12.7.3. ערכת ה-callback המלאה#
לעיון, כל מתודה שהספרייה מחפשת על backend:
מתודה | מחזירה | מטרה |
|---|---|---|
| object | אתחול חד-פעמי אופציונלי כאשר הערוץ נקשר לראשונה למארח. החזר כל ערך שאינו |
| bool | החזר |
| bool | תפוס את הערוץ לצורך העברה אטומית מרובת מנות (packets). |
| bool | שחרר |
| int | מספר הבייטים הניתנים כעת לקריאה מהערוץ. |
| tuple | עד ארבעה מספרים שלמים המתארים את מבנה הנתונים (למשל גובה התמונה, רוחב, מספר בייטים). משמש את המארח כדי לפרוס חוצצים מטופסים. |
| bytes | החזר עד size בייטים החל מ-offset. נקרא פעם אחת לכל קטע כאשר המטען חורג מהמרבי שסוכם. |
| bytes | וריאנט ללא העתקה (zero-copy) של |
| int | המארח כתב data ב-offset. |
| int | אופקוד מוגדר-אפליקציה מחוץ למודל הקריאה/כתיבה. ערך החזרה שלילי הוא שגיאה. |
| object | השלך כל נתון מאוחסן בחוצץ. נקרא כאשר המארח רוצה לאפס את הערוץ. |
| bool | משמעותי רק על backends המייצגים תעבורה (transport) פיזית (ערוצי ה-USB המובנים). ערוצי אפליקציה אינם זקוקים לכך. |
זהו ממשק ה-backend בשלמותו. שנים-עשר שמות מתודות, כולם אופציונליים, וספריית הפרוטוקול מחליטה מה כל ערוץ יכול לעשות על סמך אילו מהם נוכחים.