לתפוס רוצח (נתונים) סדרתי

מי שעוקב אחרי הבלוג יודע שאני לא מחבב ספריות קוד מוכנות. יש לכך שתי סיבות. ראשית, כשמשתמשים בקוד מוכן של מישהו אחר, הנטיה היא פשוט ליישם אותו, וכך מתפספסת כל הלמידה של האופן בו הדברים נעשים באמת. שנית, אין שום אחריות שקוד של מישהו אחר יעבוד בצורה יעילה, או בכל התנאים האפשריים, או בהתאמה מיטבית ליישום הספציפי שלנו – או בכלל!

כמובן, לעתים קרובות אין ברירה אלא להשתמש בקוד מוכן, מפני שהוא מבצע משימה מסובכת מכדי שנכתוב אותה לבד, או שפשוט אין לנו זמן לכך. כשבחרתי בספריות של TurboPower Async Professional למימוש תקשורת טורית בין יישומים שאני כותב בדלפי לבין הארדואינו, אלה היו השיקולים שהנחו אותי. זהו גם קוד מוכר וותיק, כך שהיו לי כל הסיבות לסמוך עליו… עד שעשיתי זאת בפועל.

המשימה היתה, למראית עין, פשוטה למדי. בזיכרון ה-EEPROM של הארדואינו שמורים פרמטרים מסוימים שעשויים להשתנות עם הזמן, והייתי צריך תוכנת Desktop שתהיה מסוגלת לקרוא אותם מהארדואינו דרך ה-Serial, להראותם למשתמש, לאפשר לו לבצע שינויים – ואז לשלוח את השינויים הללו בחזרה לארדואינו.

מכיוון שהעברת הנתונים הזו מתבצעת לעתים רחוקות ואין לה שום דרישות מיוחדות של מהירות או תזמון, השתמשתי בהגדרות התקשורת הנפוצות והפשוטות ביותר, שכבר הוכיחו את עצמן בנסיבות אחרות. תוכנת ה-Desktop שולחת תו מוסכם לארדואינו, שאומר "שלח לי את הפרמטרים השמורים אצלך". לאחר מכן היא מאזינה בלולאה לחיבור הטורי, ובכל פעם שהיא מזהה הגעה של בייט חדש (בעזרת הפונקציה המקבילה ל-Serial.available של הארדואינו), היא קוראת אותו (כמו Serial.read) ומוסיפה לתוך העותק המקומי של המידע עד שזה מתמלא.

לא היתה אמורה להיות שום בעיה, והנתונים אכן הגיעו למחשב – אבל לעתים קרובות היו בהם שגיאות. הקוד אמנם לא היה בנוי לאיתור אקטיבי של שגיאות, אך אני הזנתי מראש לארדואינו נתוני דמה (בסגנון "123456789"), ולכן כשהסתכלתי במה שהתקבל במחשב הבחנתי מיד בכפילויות מוזרות ("123445677"). מה גורם לכפילות הזו? מי משבש לי את הנתונים?

קודם כל, בדקתי את החשודים המיידיים: באג בקוד השולח ובאג בקוד המקבל. כאמור, הקוד הרלוונטי בשני הצדדים היה פשוט מאד, כך שלא היו הרבה אפשרויות לתקלה, ואכן הכל נראה תקין. התחלתי "לירות באוויר" ולשחק עם פרמטרים שונים ומשונים בתוכנה. התמונה התחילה להתבהר כשהוספתי עיכובים קטנים לשידור ולקליטה, ששיפרו או הרעו את המצב, ובמהרה הבנתי – לפחות כך אני חושב – מה קורה.

בקוד המקורי, הארדואינו זרק את הנתונים בייט אחרי בייט ברצף, ללא הפסקה. עדיין, המהירות שלו היא בדיחה בשביל המחשב, ובגלל זה הכנסתי מראש את הבדיקות החוזרות אם הגיע בייט חדש. אך מסתבר שהבדיקות האלה, כאשר הן מתרחשות בזמן השידור, פוגעות איכשהו בקריאה הנכונה של המידע: כביכול אין סינכרון מושלם בין התשובה שהן נותנות לבין ההגעה של המידע בפועל.

רוצח הנתונים הסדרתי עוד לא נתפס. יכול להיות שזהו אכן באג כלשהו ברכיב התוכנה, וייתכן גם שלמרות הכל משהו חמק מעיניי והטעות היא שלי. כך או אחרת, עם הבנת הבעיה הגיע גם הפתרון, שימנע שיבושי נתונים נוספים: אחרי שהיישום במחשב שולח את הבקשה לנתונים, הוא מחכה כמה עשיריות שניה בלי לעשות שום דבר. בינתיים הנתונים מהארדואינו מגיעים ומצטברים להם בבטחה בחוצץ (buffer) המקומי, שכמובן וידאתי מראש שהוא מספיק גדול כדי להכיל את כולם. משך ההמתנה חושב כך שיספיק כדי לכסות את כל השידור, ואז אין עוד התנגשויות: היישום קורא את הנתונים כפי שקרא קודם, אך הפעם זה לא מפריע לתקשורת.

בפוסט הבא אספר על פרוטוקול זיהוי שגיאות פשוט שהוספתי לתוכנה במסגרת הדיבוג, קצת על זיהוי שגיאות באופן כללי – ומה קורה כשכל העברת הנתונים הזאת צריכה פתאום לעבור באלחוט!

להרשמה
הודע לי על
0 תגובות
Inline Feedbacks
הראה את כל התגובות