12.5. אמינות – רצפים, ACK ושידורים חוזרים¶
שכבת המסגור מזהה שיבושים באמצעות ה-CRC שלה. שכבת האמינות הופכת את ”שיבוש שזוהה“ ל-”היישום לעולם אינו רואה נתונים פגומים“ על ידי משא ומתן על שידורים חוזרים בכל פעם שחבילה אינה מגיעה שלמה.
12.5.1. מספרי רצף¶
כל כותרת חבילה נושאת מספר רצף בגודל בית אחד, נפרד לכל כיוון תנועה. השולח מגדיל את המונה לפני השידור; המקבל בודק שרצף כל חבילה שהתקבלה הוא הקודם ועוד אחד (מודולו 256).
שלושה דברים יכולים להופיע אצל המקבל במקום חבילה נקייה ובסדר הנכון:
מספר הרצף הצפוי, עם CRC תקין. החבילה נמסרת לשכבה הבאה.
מספר הרצף הצפוי, עם CRC שגוי. המקבל מפיל את החבילה ו(אם נעשה משא ומתן על ACK) שולח NAK המבקש שידור חוזר.
מספר רצף הגבוה באחד מהצפוי, עם CRC תקין. המקבל יודע שהחבילה הקודמת אבדה; הוא שולח NAK המתייחס לרצף החסר ומאחסן את החדש.
המקרה של כפילות (שידור חוזר שמגיע אחרי שהמקור סוף סוף עבר) מטופל על ידי בדיקה מול המונה הצפוי: אם הרצף מאחורי הצפוי, החבילה היא כפילות והמקבל משליך אותה לאחר ששלח את ה-ACK שהשולח כנראה לא קיבל בפעם הראשונה.
12.5.2. ACK ו-NAK¶
שני ביטי דגל בכותרת החבילה נושאים את תעבורת האמינות עצמה:
ACK_REQשמוגדר בחבילה יוצאת פירושו ”אני רוצה אישור בחזרה“. חבילות נתונים בדרך כלל מגדירות זאת; פינגים של סטטוס ואירועים חד-פעמיים עשויים שלא.ACKשמוגדר בחבילה פירושו ”חבילה זו היא האישור עבור מספר הרצף שבכותרת“. היא אינה נושאת מטען משלה.NAKשמוגדר פירושו ”חבילה זו דוחה חבילה קודמת“ – בדרך כלל בגלל CRC שגוי או פער במספר הרצף. הכותרת מצביעה לשולח על איזה רצף לשדר מחדש.
השולח מריץ לולאת עצור-והמתן: הוא משדר חבילה אחת הדורשת אישור, ואז ממתין ל-ACK (או NAK) התואם לפני שליחת הבאה. מודל החבילה-הבודדת-בתעופה שומר על מצב השולח חסום – כמה מאות בתים במצלמות הקטנות ביותר – ותואם את תפקיד הפרוטוקול כערוץ בקרה בין שתי נקודות קצה ולא כצינור ממוטב-תפוקה. בעת NAK השולח משדר מחדש את אותה חבילה עם דגל RTX מוגדר כך שהמקבל יודע שזהו ניסיון חוזר.
12.5.3. תזמון שידור חוזר¶
אם לא מגיע לא ACK ולא NAK בתוך פסק זמן השידור החוזר, השולח משדר מחדש את החבילה שבתעופה בעצמו. פסק הזמן מוגדר כברירת מחדל ל-500 ms ומכפיל את עצמו בכל ניסיון חוזר רצוף (1 ש«, 2 ש«, …). לאחר מספר הניסיונות שהוגדר – שלושה כברירת מחדל – השולח מוותר ומדווח על שגיאת תעבורה ליישום.
הכפלת פסק הזמן היא דפוס נסיגה אקספוננציאלית (exponential backoff) הסטנדרטי. פסק זמן ראשון קצר תופס חבילות אבודות במהירות; ההכפלה פירושה שמארח שעסוק לכמה מאות מילישניות אינו מפעיל סופה של כפילויות שמרכיבות את העומס.
12.5.4. הגדרת אמינות¶
שני הקצוות יכולים לכבות חלקים משכבת האמינות, בהסכמה, כאשר היישום יכול להרשות לעצמו לאבד נתונים:
protocol.init(ack=False)משבית אישורי ACK לכל חבילה. השולח יורה ושוכח; המקבל מוסר את מה שמגיע. טוב לזרימת נתוני חיישן שבה דגימה מיושנת קבילה.protocol.init(seq=False)מכבה מעקב אחר מספרי רצף, מה שמשתמע ממנו שגם ACK כבוי. שימושי רק בתעבורות אמינות לחלוטין.protocol.init(crc=False)מכבה את אימות ה-CRC אך משאיר את שאר המסגור שלם. כדאי לעשות זאת רק כאשר התעבורה עצמה חסונה מספיק כך ששגיאות CRC אינן קורות.
ברירות המחדל – הכל מופעל – הן נקודת ההתחלה הנכונה לכל מפגש איתור באגים בין מארח למצלמה. כשהיישום נכנס לייצור, הפשרות הופכות ספציפיות לנתונים שלו ולתעבורה שלו.
12.5.5. קודי הסטטוס¶
כששגיאת תעבורה אכן מתפשטת מעלה לקוד היישום, היא מגיעה כקוד סטטוס. ספריית הפרוטוקול מגדירה עשרה:
SUCCESS– הפעולה הושלמה.FAILED– הפקודה נכשלה מסיבה לא מוגדרת.INVALID– המקבל דחה את הפקודה או אחד מהארגומנטים שלה.TIMEOUT– טיימר ניסיון חוזר פג.BUSY– המצלמה עסוקה (בדרך כלל ערוץ נעול).CHECKSUM– ה-CRC של הכותרת או המטען לא התאים.SEQUENCE– מספר הרצף היה מחוץ לסדר מעבר למה שהשכבה יכולה להתאושש ממנו.OVERFLOW– מטען חרג מהמקסימום שעליו נעשה משא ומתן.FRAGMENT– הודעה רב-מקטעית הגיעה עם חלקים חסרים.UNKNOWN– מקרה-תפסן הגנתי למצבים בלתי צפויים באמת.
קוד מארח שקורא ל-channel_read() רואה אותם כחריגות (exceptions) של Python; קוד יישום בצד המצלמה שבחר בטיפול שגיאות מותאם אישית רואה אותם כערכי החזרה מפונקציות ה-callback של ה-backend. רוב יישומי המצלמה אינם צריכים להסתכל על קודי הסטטוס כלל – הספרייה מטפלת בניסיון החוזר, ורק כשלים שאינם ניתנים לשחזור באמת (למשל התעבורה עצמה נעלמה) מגיעים ליישום.
כשהמסגור במקומו לזיהוי שיבושים והאמינות במקומה להתאוששות מהם, העבודה ברמת החוט הסתיימה. קוד היישום רואה חבילות ממוסגרות, מסודרות ושלמות; הבתים שבתוכן חופשיים לציין כל מה שהערוץ שמעליהם רוצה שיציינו.