GPSD
معمارية تطبيقات المصادر المفتوحة (المجلد الثاني)GPSD
معمارية تطبيقات المصادر المفتوحة (المجلد الثاني) GPSD
Eric Raymond
إذا استمتعت بهذه الكتب، فقد يعجبك أيضًا التصميم البرمجي بالأمثلة في Python والتصميم البرمجي بالأمثلة في JavaScript.
GPSD حزمة أدوات لإدارة مجموعات من أجهزة GPS والمستشعرات الأخرى المتعلقة بالملاحة وحفظ الوقت الدقيق، ومنها أجهزة الراديو البحرية AIS (نظام التعريف التلقائي) والبوصلة الرقمية. والبرنامج الرئيسي، وهو خدمي خدمة (service daemon) اسمه gpsd، يدير مجموعة من المستشعرات ويجعل تقارير جميعها متاحة في هيئة تدفّق من كائنات JSON على منفذ TCP/IP معروف. وتشمل البرامج الأخرى في الحزمة عملاء عرض تجريبيين يمكن استعمالهم كنماذج برمجية، إضافة إلى مجموعة متنوّعة من أدوات التشخيص.
GPSD منشور على نطاق واسع في الحواسيب المحمولة والهواتف الذكية والمركبات ذاتية القيادة، ومنها السيارات ذاتية القيادة والغواصات الآلية. وهو يظهر في الأنظمة المدمجة المستخدَمة في الملاحة والزراعة الدقيقة وجمع البيانات العلمية الحسّاسة للموقع وخدمة الوقت الشبكي. بل إنه مستخدَم في نظام التعريف بالصديق أو العدو (Identification-Friend-or-Foe) في مركبات القتال المدرّعة، ومنها دبابة المعركة الرئيسية M1 «أبرامز».
GPSD مشروع متوسط الحجم—نحو 43 ألف سطر من الشيفرة (KLOC)، في معظمه بلغة C و Python—وتاريخه في عهده الحالي يعود إلى عام 2005، أما ما قبله فيعود إلى 1997. وقد ظلّ الفريق الأساسي مستقرًّا عند نحو ثلاثة مطوّرين، مع مساهمات شبه دورية من نحو اثنين وعشرين مطوّرًا آخر، إضافة إلى التصحيحات المتفرّقة المعتادة من مئات الأشخاص الآخرين.
تاريخيًا، تمتع GPSD بمعدّل عيوب استثنائي الانخفاض، سواء ما قيس بواسطة أدوات التدقيق مثل splint و valgrind و Coverity، أو ما قيس بمعدّل بلاغات الأخطاء في نظام تتبّعه وفي أماكن أخرى. ولم يحدث هذا من باب المصادفة؛ فقد كان المشروع شديدًا في تبنّي تقنيات الاختبار الآلي، وقد أثمر ذلك بشكل كبير.
إن جودة ما يؤديه GPSD عالية إلى حدّ أنه استقطب أو محا عمليًا جميع أسلافه التقريبيين، وعلى الأقل محاولة مباشرة واحدة لمنافسته. وفي عام 2010، فاز GPSD بأول منحة «الشيفرة الجيدة» (Good Code Grant) من تحالف تميّز الشيفرة (Alliance for Code Excellence). وحينما تنتهي من قراءة هذا الفصل، ينبغي أن تفهم السبب.
7.1. لماذا وُجد GPSD
يوجد GPSD لأن بروتوكولات التطبيقات التي تشحنها أجهزة GPS والمستشعرات الأخرى المتعلقة بالملاحة مصمَّمة تصميمًا سيئًا، وموثَّقة توثيقًا رديئًا، وشديدة التنوّع بحسب نوع المستشعر وطرازه. انظر Ray لمناقشة مفصَّلة؛ وستتعلّم هناك خصوصًا عن غرائب NMEA 0183 (المعيار شبه الرسمي لحِزَم تقارير GPS) وعن الفوضى المتراكمة من بروتوكولات المورّدين ضعيفة التوثيق التي تنافسه.
ولو كان على التطبيقات أن تتولّى هذه التعقيدية كلها بنفسها، لانتجت كميات هائلة من شيفرة هشّة ومكرَّرة، ما يؤدي إلى معدّلات مرتفعة من العيوب الظاهرة للمستخدم ومشكلات مستمرة مع تغيّر العتاد تدريجيًا خارج ما تفترضه التطبيقات.
يعزل GPSD التطبيقاتَ الواعية بالموقع عن تفاصيل واجهات العتاد، من خلال معرفته بنفسه بكل البروتوكولات (وندعم في وقت كتابة هذا الكتاب نحو 20 بروتوكولًا مختلفًا)، وإدارته لأجهزة المنفذ التسلسلي (serial) و USB حتى لا تضطر التطبيقات إلى ذلك، والإبلاغ عن بيانات حمولة المستشعرات بصيغة JSON بسيطة لا تعتمد على نوع الجهاز. ويضيف GPSD إلى ذلك تيسيرًا إضافيًا من خلال توفير مكتبات للعميل، حتى لا تحتاج تطبيقات العميل حتى إلى معرفة صيغة الإبلاغ هذه. وبدلًا من ذلك يصبح جلب معلومات المستشعر مجرّد استدعاء دالة.
يدعم GPSD أيضًا حفظ الوقت الدقيق؛ إذ يمكن أن يعمل مصدرًا للوقت بالنسبة إلى ntpd (خادم بروتوكول الوقت الشبكي) إذا كانت أيٌّ من مستشعراته الموصولة تملك قدرة PPS (نبضة في الثانية). ويتعاون مطوّرو GPSD عن قرب مع مشروع ntpd في تحسين خدمة الوقت الشبكي.
نعمل حاليًا (في منتصف عام 2011) على إكمال دعم شبكة AIS لمستقبِلات الملاحة البحرية. وفي المستقبل نتوقّع دعم أنواع جديدة من المستشعرات الواعية بالموقع—مثل مُجيبات الطائرات (transponders) من الجيل الثاني—مع توفّر وثائق البروتوكولات وأجهزة الاختبار.
ولخّصنا القول: إن الموضوع الأهم على الإطلاق في تصميم GPSD هو إخفاء كل قبحه المعتمد على الجهاز خلف واجهة عميل بسيطة تتحدّث إلى خدمة لا تحتاج إلى أي إعداد (zero-configuration).
7.2. المنظور الخارجي
البرنامج الرئيسي في حزمة GPSD هو خدمي الخدمة gpsd. ويستطيع جمع البيانات الصادرة عن مجموعة من أجهزة المستشعرات الموصولة عبر وصلات RS232 و USB و Bluetooth و TCP/IP و UDP. وتُرسَل التقارير عادةً إلى المنفذ 2947 من بروتوكول TCP/IP، لكنها قد تخرج أيضًا عبر واجهة الذاكرة المشتركة أو واجهة D-BUS.
تشحن حزمة توزيع GPSD مع مكتبات عميل لـ C و C++ و Python. وتتضمن عملاء نموذجيين بـ C و C++ و Python و PHP. ويتوفّر ربط (binding) لعميل Perl عبر CPAN. وهذه مكتبات العميل ليست مجرّد وسيلة راحة لمطوّري التطبيقات؛ فهي توفّر على مطوّري GPSD آلام الرأس أيضًا، إذ تعزل التطبيقات عن تفاصيل بروتوكول الإبلاغ بصيغة JSON الخاص بـ GPSD. وبذلك يمكن أن تبقى الواجهة البرمجية (API) المعروضة للعملاء كما هي، حتى مع نمو البروتوكول بميزات جديدة لأنواع مستشعرات جديدة.
وتشمل بقية البرامج في الحزمة أداة لمراقبة الأجهزة على مستوى منخفض (gpsmon)، ومنمِّطًا (profiler) يُنتج تقارير عن إحصاءات الأخطاء وتوقيت الأجهزة (gpsprof)، وأداة لضبط إعدادات الأجهزة (gpsctl)، وبرنامجًا لتحويل سجلات المستشعرات على دفعات إلى JSON مقروء (gpsdecode). وتُعين هذه الأدوات مجتمعةً المستخدمين ذوي الخبرة التقنية على الغوص في تشغيل المستشعرات الموصولة بقدر ما يريدون.
وبطبيعة الحال تساعد هذه الأدوات مطوّري GPSD أنفسهم أيضًا على التحقّق من التشغيل السليم لـ gpsd. وأهم أدوات الاختبار على الإطلاق هي gpsfake، وهي بيئة اختبار (test harness) لـ gpsd تستطيع ربطه بأي عدد من سجلات المستشعرات، وكأنها أجهزة حيّة. وباستخدام gpsfake يمكننا إعادة تشغيل سجل مستشعر مرفق مع بلاغ خطأ لإعادة إنتاج مشكلات بعينها. وgpsfake هو أيضًا محرّك مجموعة اختبارات الانحدار (regression test) الواسعة لدينا، ما يخفّض تكلفة تعديل البرمجيات بجعل من السهل اكتشاف التغييرات التي تُفسد الأمور.
ومن أهم الدروس التي نعتقد أن تعلّمنا إياها للمشاريع المستقبلية، أنه لا يكفي أن تكون حزمة برمجية صحيحة، بل ينبغي لها أيضًا أن تستطيع إثبات صحتها بنفسها. وقد اكتشفنا أنه عند السعي وراء هذا الهدف على الوجه الصحيح، لا يكون الأمر قميصًا من الخشب (hair shirt) بل زوجًا من الأجنحة—فالوقت الذي أنفقناه في كتابة بيئات الاختبار واختبارات الانحدار قد عاد بأضعاف ثمنه في الحرية التي منحها إياه أن نعدّل الشيفرة دون الخوف من إتلاف الوظائف القائمة بشكل خفي.
7.3. طبقات البرمجيات
هناك الكثير مما يجري داخل GPSD ممّا قد توحي به تجربة «وصّل المستشعر فيعمل فورًا». وتنقسم باطن gpsd طبيعيًا إلى أربع قطع: مشغّلات الأجهزة (drivers)، وقاطع الحِزَم (packet sniffer)، والمكتبة الأساسية (core library)، والمُوجِّه المتعدّد (multiplexer). وسنصفها من الأسفل إلى الأعلى.
الشكل 7.1: طبقات البرمجيات
مشغّلات الأجهزة هي في جوهرها محرّكات أجهزة تعمل في فضاء المستخدم (user space)، لكل نوع من مجموعات شرائح المستشعرات التي ندعمها. ونقاط الدخول الأساسية هي طرائق (methods) لتحليل حزمة بيانات إلى معلومات زمنية ومكانية وسرعية، أو إلى معلومات حالة، وتغيير وضعها أو معدل إيقاعها التسلسلي (baud rate)، والاستفسار عن النوع الفرعي للجهاز، وغيرها. وقد تدعم طرائق مساعِدة عمليات التحكّم بالمشغّل، مثل تغيير سرعة الاتصال التسلسلي للجهاز. والواجهة الكاملة مع أي مشغّل هي بنية C (structure) مليئة بمؤشرات بيانات ومؤشرات طرائق، صُمِّمت عمدًا على نمط بنية محرّك أجهزة في يونكس.
قاطع الحِزَم مسؤول عن استخراج حِزَم البيانات من تدفقات الإدخال التسلسلية. وهو في الأساس آلة حالة (state machine) تراقب أي شيء يشبه أحد أنواع الحِزَم المعروفة لدينا وعددها نحو 20 نوعًا (معظمها مزوّد بمجموع اختباري (checksum)، لذا نتمكّن من الثقة العالية حين نظن أننا قد تعرّفنا على حزمة). ولأن الأجهزة قد تُوصَل وتُفصل أثناء التشغيل (hotplug) أو تغيّر أوضاعها، فإن نوع الحزمة التي ستأتي عبر السلك من منفذ تسلسلي أو منفذ USB ليس بالضرورة مثبَّتًا إلى الأبد بمجرد التعرّف على أول حزمة.
المكتبة الأساسية تدير جلسة مع جهاز مستشعر. ونقاط الدخول الأساسية هي:
- بدء جلسة بفتح الجهاز وقراءة البيانات منه، مع تجريب معدلات الإيقاع التسلسلي ومجموعات التكافؤ/بتات الإيقاف حتى يبلغ قاطع الحِزَم قفل تزامن (synchronization lock) مع نوع حزمة معروف؛
- استطلاع الجهاز بطلب حزمة؛ و
- إغلاق الجهاز وإنهاء الجلسة.
من السمات الجوهرية للمكتبة الأساسية أنها مسؤولة عن تبديل كل اتصال GPS لاستخدام مشغّل الجهاز الصحيح تبعًا لنوع الحزمة الذي يعيده القاطع. وهذا الأمر لا يُعدّ مسبقًا وقد يتغيّر مع الزمن، ولا سيما إذا انتقل الجهاز بين بروتوكولات إبلاغ مختلفة. (تدعم معظم شرائح GPS بروتوكول NMEA وبروتوكولًا ثنائيًا أو أكثر للمورّدين، وقد تُبلّغ أجهزة مثل مستقبِلات AIS بالحِزَم عبر بروتوكولين مختلفين على السلك نفسه.)
وأخيرًا، فإن المُوجِّه المتعدّد (multiplexer) هو جزء الخدمي الذي يتعامل مع جلسات العملاء وإسناد الأجهزة. وهو مسؤول عن تمرير التقارير إلى العملاء، وقبول أوامر العملاء، والردّ على إشعارات التوصيل والفصل أثناء التشغيل. وهو محصور عمليًا في ملف مصدر واحد، هو gpsd.c، ولا يتحدث أبدًا مع مشغّلات الأجهزة مباشرة.
المكونات الثلاثة الأولى (عدا المُوجِّه المتعدّد) مرتبطة معًا في مكتبة اسمها libgpsd، ويمكن استخدامها بمعزل عن المُوجِّه المتعدّد. أما أدواتنا الأخرى التي تتحدّث مع المستشعرات مباشرة، مثل gpsmon و gpsctl، فتصل إلى ذلك بالاتصال المباشر بالمكتبة الأساسية وبطبقة المشغّلات.
أعقد المكوّنات منفردًا هو قاطع الحِزَم بما يقارب ألفي سطر من الشيفرة. وهذا حجم لا يمكن تقليصه؛ فالآلة الحالة القادرة على التعرّف على هذا العدد الكبير من البروتوكولات المختلفة لا بدّ أن تكون كبيرة ومعقّدة. لحسن الحظّ، من السهل أيضًا عزل قاطع الحِزَم واختباره؛ فالمشكلات فيه لا تميل إلى الارتباط بأجزاء أخرى من الشيفرة.
طبقة المُوجِّه المتعدّد بحجم مماثل تقريبًا، لكنها أقلّ تعقيدًا بعض الشيء. وتشكّل مشغّلات الأجهزة الجزء الأكبر من شيفرة الخدمي بما يقارب 15 ألف سطر. أما بقية الشيفرة كلها—جميع أدوات الدعم والمكتبات والعملاء الاختباريين مجتمعةً—فتساوي حجم الخدمي تقريبًا (وهناك جزء من الشيفرة، ولا سيما محلّل JSON، مشترك بين الخدمي ومكتبات العميل).
يتبيّن نجاح نهج التقسيم إلى طبقات هذا بطريقتين مختلفتين. إحدىاهما أن مشغّلات الأجهزة الجديدة سهلة الكتابة إلى حدّ أن عدة منها تبرّع بها أشخاص ليسوا أعضاء في الفريق الأساسي: فواجهة مشغّل الأجهزة موثَّقة، والمشغّلات الفردية مرتبطة بالمكتبة الأساسية عبر مؤشرات في جدول رئيسي لأنواع الأجهزة لا غير.
والفائدة الأخرى أن مُكامِلي الأنظمة (system integrators) يستطيعون تقليص البصمة (footprint) التي يتركها GPSD للنشر المدمج تقليصًا كبيرًا بمجرد الامتناع عن ترجمة المشغّلات غير المستخدَمة. والخدمي ليس كبيرًا في أصله، وبنية مبنيّة (build) مخلّصة بما يكفي تعمل بكل ارتياح على أجهزة ARM منخفضة الاستهلاك ومنخفضة السرعة وصغيرة الذاكرة. (ARM هي بنية مجموعة تعليمات RISC بعرض 32 بت تُستخدم في الإلكترونيات المحمولة والمدمجة. انظر http://en.wikipedia.org/wiki/ARM_architecture.)
والفائدة الثالثة من التقسيم إلى طبقات أن مُوجِّه الخدمي يمكن فصله من فوق المكتبة الأساسية واستبداله بمنطق أبسط، مثل التحويل المباشر على دفعات لملفات سجلات المستشعرات إلى تقارير JSON كما تفعل أداة gpsdecode.
لا شيء في هذا الجانب من بنية GPSD جديد. والدرس المستفاد منه أن التوصيف الواعي والمنضبط لنمط التصميم الخاص بمعالجة أجهزة يونكس مفيد لا في نوى أنظمة التشغيل فحسب، بل أيضًا في برامج فضاء المستخدم التي تُلزَم، على نحو مماثل، بالتعامل مع عتاد وبروتوكولات متنوّعة.
7.4. منظور تدفّق البيانات
لننظر الآن إلى بنية GPSD من منظور تدفّق البيانات. وفي التشغيل الطبيعي، يدور gpsd في حلقة انتظارًا للإدخال من أحد هذه المصادر:
- مجموعة من العملاء يُصدرون الطلبات عبر منفذ TCP/IP.
- مجموعة من مستشعرات الملاحة الموصولة عبر أجهزة تسلسلية أو أجهزة USB.
- مقبس التحكّم الخاص الذي تستخدمه سكربتات التوصيل والفصل أثناء التشغيل وبعض أدوات الإعداد.
- مجموعة من الخوادم التي تُصدر تحديثات تصحيحية دورية لـ GPS التفاضلي (DGPS و NTRIP). وتُعامَل كما لو كانت مستشعرات ملاحة.
حين يصبح منفذ USB نشطًا مع جهاز قد يكون مستشعر ملاحة، ترسل سكربتة توصيل وفصل أثناء التشغيل (والتي تشحنها حزمة GPSD) إشعارًا إلى مقبس التحكّم. وهذه هي الإشارة التي تجعل طبقة المُوجِّه المتعدّد تضع الجهاز في قائمتها الداخلية للمستشعرات. وعلى العكس، فإن حدث إزالة جهاز يمكن أن يزيل جهازًا من تلك القائمة.
حين يُصدر عميل طلب مراقبة (watch)، تفتح طبقة المُوجِّه المتعدّد مستشعرات الملاحة في قائمتها وتبدأ بقبول البيانات منها (بإضافة واصفات ملفاتها إلى المجموعة في الاستدعاء الرئيسي select). وفي غير ذلك، تُغلق جميع أجهزة GPS (لكن تبقى في القائمة) ويكون الخدمي في حالة سكون. أما الأجهزة التي تتوقف عن إرسال البيانات فتُحذف من قائمة الأجهزة بعد انتهاء المهلة.
الشكل 7.2: تدفّق البيانات
حين تصل البيانات من مستشعر ملاحة، تُغذّى إلى قاطع الحِزَم، وهو آلة حالات منتهية (finite-state machine) تعمل كمحلّل القواعد (lexical analyzer) في المُصرِّف. ومهمّة قاطع الحِزَم هي تجميع البيانات من كل منفذ (على حدة)، مع التعرّف على اللحظة التي يكون فيها قد جمع حزمة من نوع معروف.
قد تحتوي الحزمة على محدِّد موقع (position fix) صادر من GPS، أو مخطط بيانات AIS بحري، أو قراءة مستشعر من بوصلة مغناطيسية، أو حزمة بث DGPS (GPS التفاضلي)، أو أيًّا من أمور أخرى عديدة. ولا يهمّ قاطع الحِزَم بمحتوى الحزمة؛ وكل ما يفعله هو إخبار المكتبة الأساسية حين يكون قد جمع حزمة، وإعادة الحمولة ونوع الحزمة.
ثم تسلّم المكتبة الأساسية الحزمة إلى المشغّل المرتبط بنوعها. ومهمّة المشغّل هي استخراج البيانات من حمولة الحزمة إلى بنية جلسة خاصة بكل جهاز، وضبط بعض بتات الحالة التي تخبر طبقة المُوجِّه المتعدّد بنوع البيانات التي حصل عليها.
من هذه البتات واحدة تُشير إلى أن الخدمي قد جمع بيانات كافية لإرسال تقرير إلى عملائه. وحين يُرفع هذا البت بعد قراءة بيانات من جهاز مستشعر، فذلك يعني أننا رأينا نهاية حزمة، ونهاية مجموعة حِزَم (قد تكون حزمة واحدة أو أكثر)، وأن البيانات الموجودة في بنية جلسة الجهاز ينبغي تمريرها إلى أحد المصدّرين (exporters).
المصدّر الرئيسي هو مصدّر «المقبس» (socket)؛ فهو يولّد كائن تقرير بصيغة JSON ويرسله إلى جميع العملاء المراقبين للجهاز. وهناك مصدّر للذاكرة المشتركة ينسخ البيانات إلى مقطع من الذاكرة المشتركة بدلًا من ذلك. وفي كلتا الحالتين، يُتوقّع أن تقوم مكتبة عميل بفكّ ترميز البيانات (unmarshal) إلى بنية داخل فضاء ذاكرة برنامج العميل. ويتوفّر أيضًا مصدّر ثالث يرسل تحديثات الموقع عبر DBUS.
شيفرة GPSD مقسَّمة أفقيًا بعناية لا تقلّ عن تقسيمها رأسيًا. فقاطع الحِزَم لا يعرف ولا يحتاج إلى معرفة أي شيء عن حمولات الحِزَم، ولا يهمّه إن كان مصدر إدخاله منفذ USB، أو جهاز RS232، أو وصلة راديو Bluetooth، أو طرفًا زائفًا (pseudo-tty)، أو اتصالًا بمقبس TCP، أو تدفّق حِزَم UDP. أما المشغّلات فتعرف كيف تحلّل حمولات الحِزَم، لكنها لا تعرف شيئًا عن باطن قاطع الحِزَم ولا عن المصدّرين. والمصدّرون لا ينظرون إلا إلى بنية بيانات الجلسة التي يحدّثها المشغّلات.
وقد خدم هذا الفصل بين الوظائف GPSD خدمةً عظيمة. فمثلًا، حين تسلّمنا في مطلع عام 2010 طلبًا لتكييف الشيفرة بحيث تقبل بيانات المستشعرات القادمة في صورة حِزَم UDP من نظام الملاحة المثبَّت على متن غواصة آلية، كان من السهل تنفيذ ذلك في حفنة من أسطر الشيفرة من دون المساس بالمراحل اللاحقة في خط أنابيب البيانات.
وبشكل أعمّ، جعل التقسيم الدقيق إلى طبقات والوحدات (modularization) إضافة أنواع جديدة من المستشعرات نسبيًا سهلة. ونُدمج مشغّلات جديدة كل ستة أشهر أو نحو ذلك؛ وبعضها من كتابة أشخاص ليسوا مطوّرين أساسيين.
7.5. الدفاع عن البنية
مع تطوّر برنامج مفتوح المصدر مثل gpsd، يتكرّر موضوع واحد هو أن كل مساهم يقوم بأشياء لحل حالة المشكلة الخاصة به أو بها، وهذه تتسرّب معلومات أكثر تدريجيًا بين الطبقات أو المراحل التي صُمّمت في الأصل بفصل نظيف.
ومن الأمور التي تقلقنا في وقت كتابة هذا الكتاب أن بعض المعلومات عن نوع مصدر الإدخال (USB و RS232 و pty و Bluetooth و TCP و UDP) يبدو أنها تحتاج إلى تمريرها إلى الأعلى إلى طبقة المُوجِّه المتعدّد، لإخبارها مثلًا بما إذا كان ينبغي إرسال سلاسل الاستفسار (probe strings) إلى جهاز لم يُعرَّف بعد. فسلاسل الاستفسار هذه أحيانًا لازمة لإيقاظ مستشعرات RS232C، لكن هناك أسباب وجيهة تجعلنا لا نرسلها إلى عدد من الأجهزة أكبر مما نحتاج إليه بالضبط. فكثير من أجهزة GPS وغيرها من أجهزة المستشعرات مصمَّمة على ميزانيات ضيقة وفي عجلة من الأمر؛ وبعضها يمكن أن يصل به الإرباك بسبب سلاسل تحكّم غير متوقّعة إلى حدّ الجمود الكامل.
ولسبب مماثل، لدى الخدمي خيار -b يمنعه من محاولة تغيير معدل الإيقاع التسلسلي أثناء حلقة البحث في قاطع الحِزَم. فبعض أجهزة Bluetooth سيئة الصنع تتعامل مع هذه التغييرات تعاملًا سيئًا لدرجة أنه يلزم فصل التيار عنها وتشغيلها من جديد كي تعمل؛ وفي حالة متطرفة واحدة، اضطر مستخدم فعليًا إلى فكّ لحام البطارية الاحتياطية ليعيد جهازه إلى العمل!
كلتا الحالتين استثناءان ضروريان لقواعد تصميم المشروع. لكن في معظم الغالب، تكون هذه الاستثناءات أمرًا سيئًا. فمثلًا، تبرّع لنا بعض التصحيحات التي كان الغرض منها تحسين عمل خدمة الوقت PPS، لكنها أخربت التقسيم الرأسي للطبقات، وجعلت من المستحيل أن يعمل PPS بصورة صحيحة مع أكثر من المشغّل الوحيد الذي كان يُفترض أن تساعدوه. وقد رفضنا هذه التصحيحات لصالح العمل بجهد أكبر على تحسين مستقل عن نوع الجهاز.
وفي مناسبة قبل بضع سنوات، تسلّمنا طلبًا لدعم جهاز GPS له خاصية غريبة: قد تكون المجاميع الاختبارية (checksums) في حِزَم NMEA الخاصة به غير صحيحة حين لا يملك الجهاز محدِّد موقع. ولدعم هذا الجهاز، كان علينا إما أن (أ) نتنازل عن التحقّق من المجموع الاختباري في أي بيانات واردة تبدو كحزمة NMEA، مع خطر أن يسلّم قاطع الحِزَم بيانات تالفة إلى مشغّل NMEA، أو (ب) أن نضيف خيارًا في سطر الأوامر يفرض نوع المستشعر.
ورفض قائد المشروع (وهو مؤلف هذا الفصل) أن يفعل أيًّا منهما. فالتنازل عن التحقّق من حِزَم NMEA كانت فكرة سيئة واضحة. أما مفتاح يفرض نوع المستشعر فهو في واقع الأمر دعوة إلى أن نتكاهل في الإعداد التلقائي السليم، وهو ما سيجد مشكلات في امتداده حتى تطبيقات عملاء GPSD ومستخدميها. والخطوة التالية في ذلك الطريق المعبَّد بالنوايا الحسنة كانت ستكون بلا شك مفتاحًا لمعدل الإيقاع التسلسلي. وبدلًا من ذلك، رفضنا دعم هذا الجهاز المعطوب.
ومن أهم ما على مهندس البنية القائد في المشروع أن يدافع عن البنية في مواجهة «إصلاحات» عارضة تكسرها وتسبّب مشكلات وظيفية أو صداعًا شديدًا في الصيانة على المدى البعيد. وقد تصل الخلافات حول هذا إلى حدّ الحميّة، ولا سيما عند الدفاع عن تعارضات البنية في مواجهة أمر يعتبره مطوّر أو مستخدم ميزة لا يمكن التنازل عنها. لكن هذه الخلافات ضرورية، لأن الخيار الأسهل غالبًا هو الخيار الخطأ على المدى الأطول.
7.6. صفر إعداد، صفر متاعب
من أبرز سمات gpsd أنه خدمة لا تحتاج إلى أي إعداد (zero-configuration) (مع استثناء طفيف واحد لأجهزة Bluetooth التي تحمل برمجيات ثابتة معطوبة). فهو لا يملك أي ملف إعدادات نقطي (dotfile)! ويستنتج الخدمي أنواع المستشعرات التي يتحدث إليها من خلال استكشاف (sniffing) البيانات الواردة. بل إن gpsd يكتشف معدل الإيقاع تلقائيًا (autobauds) لأجهزة RS232 و USB (أي أنه يكتشف تلقائيًا سرعة الخط التسلسلي)، لذا ليس من الضروري أن يعرف الخدمي مسبقًا السرعة/التكافؤ/بتات الإيقاف التي يرسل بها المستشعر معلوماته.
وحين يملك نظام التشغيل المضيف قدرة على التوصيل والفصل أثناء التشغيل، يمكن لسكربتات التوصيل والفصل إرسال رسائل تفعيل وتعطيل الأجهزة إلى مقبس التحكّم لإعلام الخدمي بالتغيّر في بيئته. وتوفّر حزمة توزيع GPSD هذه السكربتات لنظام Linux. والنتيجة أن يستطيع المستخدم النهائي توصيل جهاز GPS بمنفذ USB إلى حاسوبه المحمول وتوقّع أن يبدأ فورًا في تزويد التقارير التي تستطيع التطبيقات الواعية بالموقع قراءتها—دون فوضى ولا زحام ودون تحرير أي ملف إعدادات نقطي أو سجل تفضيلات.
وتتدرّج فوائد ذلك صعودًا في كامل طبقة التطبيقات. ومن بين أمور أخرى، يعني هذا أن التطبيقات الواعية بالموقع لا تحتاج إلى لوحة إعداد مخصَّصة لضبط إعدادات GPS والمنافذ حتى تعمل الصفيحة بأكملها. وهذا يوفّر جهدًا كبيرًا على كتّاب التطبيقات وعلى المستخدمين معًا: إذ يتعاملون مع الموقع وكأنه خدمة لا تكاد تختلف في بساطها عن ساعة النظام.
ومن نتائج فلسفة «صفر إعداد» أننا لا نستقبل بترحيب المقترحات التي تهدف إلى إضافة ملف إعدادات أو خيارات إضافية في سطر الأوامر. والمشكلة في ذلك أن الإعداد الذي يمكن تحريره لا بدّ أن يُحرَّر. وهذا يعني إضافة متاعب إعداد على المستخدم النهائي، وهو تحديدًا ما ينبغي أن يتجنّبه خدمي خدمة مصمَّم بعناية.
مطوّرو GPSD هم هاكرز يونكس يعملون من أعماق تقليد يونكس، حيث إن قابلية الإعداد وامتلاك كثير من مفاتيح الضبط يكاد يكون دينًا. ومع ذلك، نعتقد أن مشاريع المصادر المفتوحة تستطيع أن تحاول بحدّ أعمق للتخلّص من ملفاتها النقطية وتهيئة نفسها تلقائيًا وفق ما تفعله بيئة التشغيل فعلًا.
7.7. القيود المدمجة التي نعدّها مفيدة
كان التصميم من أجل النشر المدمج هدفًا رئيسيًا لـ GPSD منذ عام 2005. وكان ذلك في الأصل لأننا لقينا اهتمامًا كبيرًا من مُكامِلي الأنظمة الذين يعملون مع الحواسيب ذات اللوحة الواحدة (single-board computers)، لكنه عاد بالفائدة منذ ذلك الحين بطريقة غير متوقّعة: النشر على الهواتف الذكية المزوّدة بـ GPS. (مع أن تقارير النشر المدمج المفضّلة لدينا إلى هذا اليوم هي ما يصلنا من الغواصات الآلية.)
أثّر التصميم من أجل النشر المدمج في GPSD تأثيرات مهمة. نحن نفكّر كثيرًا في طرائق لإبقاء البصمة الذاكرية (memory footprint) واستهلاك المعالج منخفضين، كي تعمل الشيفرة جيدًا على الأنظمة بطيئة المعالجة وصغيرة الذاكرة والمقيّدة في الطاقة.
ومن المبادرات المهمّة في معالجة هذه المسألة، كما ذكرنا سابقًا، ضمان ألّا تحمل بناءات gpsd أي وزن زائد على مجموعة بروتوكولات المستشعرات المحدّدة التي يحتاج مُكامل الأنظمة إلى دعمها. وفي يونيو 2011، بلغت البصمة الذاكرية لأصغر بناء ساكن (static build) لـ gpsd على نظام x86 نحو 69 كيلوبايت (أي مع ربط كل مكتبات C القياسية المطلوبة) على معالج x86 عريض 64 بت. وللمقارنة، فإن البناء الساكن مع كل المشغّلات يبلغ نحو 418 كيلوبايت.
وأخرى هي أننا نستعمل منمِّط الأداء (profiler) للبحث عن نقاط سخونة المعالج (CPU hotspots) بأيّز مختلف قليلًا عن معظم المشاريع. فبما أن مستشعرات الموقع تميل إلى الإبلاغ عن كميات صغيرة من البيانات على فترات في حدود الثانية الواحدة، فإن الأداء بالمفهوم المعتاد ليس مسألة شغل بـ GPSD—فحتى الشيفرة شديدة الهدر في الأداء من المرجّح ألّا تضيف تأخيرًا كافيًا ليظهر على مستوى التطبيق. بدلًا من ذلك، يتركّز جهدنا على تقليل استهلاك المعالج واستهلاك الطاقة. وقد نجحنا في ذلك إلى حدٍّ كبير: فحتى على أنظمة ARM منخفضة الاستهلاك وبلا وحدة معالجة عَشَرية (FPU)، انخفضت حصة gpsd من المعالج إلى مستوى قريب من ضجيج المنمِّط.
وفيما كان تصميم الشيفرة الأساسية لتكون منخفضة البصمة وعالية الكفاءة في استهلاك الطاقة مسألة محلولة إلى حدٍّ كبير في الوقت الحالي، فإن هناك جانبًا واحدًا لا يزال استهداف النشر المدمج يولّد فيه توترًا في بنية GPSD: استخدام لغات السكربتات. فمن جهة نريد تقليل العيوب الناتجة عن إدارة الموارد في المستويات الدنيا بنقل أكبر قدر ممكن من الشيفرة خارج C. ومن جهة أخرى، فإن Python (وهي لغتنا المفضّلة في كتابة السكربتات) ثقيلة وبطيئة إلى حدّ بعيد بالنسبة إلى معظم تطبيقات النشر المدمج.
وقد قسّمنا الفرق بالطريقة البديهية: خدمي الخدمة gpsd مكتوب بـ C، بينما إطار الاختبار وعدد من أدوات الدعم مكتوبة بـ Python. ومع مرور الوقت، نأمل أن ننقل المزيد من الشيفرة المساعدة من C إلى Python، لكن النشر المدمج يجعل هذه الاختيارات مصدرًا مستمرًا للخلاف والقلق.
ومع ذلك، فإننا في المجمل نجد ضغوط النشر المدمج منشِّطة. والشعور بالاكتفاء عند كتابة شيفرة نحيلة ومضبوطة وموفِّرة لموارد المعالج أمر ممتع. وقد قيل إن الفنّ يولد من الإبداع في ظلّ القيود؛ وإلى حدّ ما فإن هذا صحيح، فـ GPSD فنٌّ أفضل بفضل هذا الضغط.
وهذا الشعور لا ينتقل مباشرةً إلى نصيحة للمشاريع الأخرى، لكن أمرًا آخر ينتقل إليها قطعًا: لا تخمّن بل قِس! فلا شيء يضاهي التنميط المنتظم وقياسات البصمة في تنبيهك حين تنجرف مرهقًا إلى الالتزام بعبء زائد—وفي طمأنتك إلى أنك لست كذلك.
7.8. JSON ومستكشفي البنى
كان أحد أهم التحولات في تاريخ المشروع هو حين انتقلنا من بروتوكول الإبلاغ الأصلي إلى استخدام JSON بوصفه بروتوكولًا ميتا (metaprotocol)، وتحويل التقارير المُمرَّرة إلى العملاء إلى كائنات JSON. وكان البروتوكول الأصلي يستخدم مفاتيح من حرف واحد للأوامر والاستجابات، وقد نفد فضاء المفاتيح حرفيًا مع اتساع قدرات الخدمي تدريجيًا.
كان الانتقال إلى JSON مكسبًا كبيرًا جدًا. فهو يجمع مزايا يونكس التقليدية لصيغة نصية خالصة—سهلة الفحص بمجرد النظر، وسهلة التحرير بأدوات معيارية، وسهلة التوليد برمجيًا—مع القدرة على تمرير معلومات مهيكلة بطرق ثرية ومرنة.
وبجعل أنواع التقارير تُقابَل بكائنات JSON، ضمنّا أن يصحّ لأي تقرير أن يحتوي مزيجًا من بيانات نصية ورقمية ومنطقية ضمن بنية (وهي قدرة كان البروتوكول القديم يفتقر إليها). وبتعريف أنواع التقارير بسمة "class"، ضمنّا أننا سنتمكّن دائمًا من إضافة أنواع تقارير جديدة من غير أن تتداخل مع القديمة.
وكان لهذا القرار ثمن. فمحلّل JSON أكثر كلفة حسابًا قليلًا من المحلّل البسيط جدًا والمحدود الذي حلّ محله، وهو يحتاج بلا شك إلى عدد أكبر من أسطر الشيفرة (بما يعني أماكن أكثر لحدوث العيوب). كما أن محلّلات JSON التقليدية تتطلّب تخصيص تخزين ديناميكي للتعامل مع المصفوفات والقواميس متغيّرة الطول التي يصفها JSON، وتخصيص التخزين الديناميكي جاذب شهير للعيوب.
وتعاملنا مع هذه المشكلات بعدة طرق. كانت الخطوة الأولى كتابة محلّل بالغة C لجزء كبير بما يكفي من JSON، يستخدم تخزينًا ساكنًا بالكامل. وقد تطلّب ذلك قبول بعض القيود الطفيفة؛ فمثلًا لا يمكن للكائنات في لهجتنا (dialect) أن تحتوي القيمة null الخاصة بـ JSON، وللمصفوفات دائمًا طول أقصى ثابت. وقد سمح قبول هذه القيود بإدخال المحلّل في 600 سطر من C.
ثم بنينا مجموعة شاملة من اختبارات الوحدات (unit tests) للمحلّل للتحقّق من عمله بلا أخطاء. وأخيرًا، من أجل تطبيقات النشر المدمج شديدة التقييد حيث قد يكون عبء JSON مرتفعًا أكثر مما ينبغي، كتبنا مصدّرًا للذاكرة المشتركة يتجاوز الحاجة إلى إرسال JSON وتحليله كليًا إذا كان للخدمي وعميله وصول إلى ذاكرة مشتركة.
لم تعد JSON مخصّصة لتطبيقات الويب وحدها. ونعتقد أن كل من يصمّم بروتوكول تطبيق ينبغي أن يفكّر في نهج كمُهج GPSD. وبالطبع فإن فكرة بناء بروتوكولك فوق بروتوكول ميتا معياري ليست جديدة؛ فقد دافع عنها أنصار XML منذ سنوات عديدة، وذلك منطقي بالنسبة إلى البروتوكولات ذات البنية الشبيهة بالمستندات. وتتمتع JSON بميزتي انخفاض العبء مقارنة بـ XML، وملاءمة أكبر لتمرير بنى المصفوفات والسجلات.
7.9. التصميم من أجل صفر عيوب
نظرًا لاستخدامها في أنظمة الملاحة، فإن أي برمجية تقع بين المستخدم وجهاز GPS أو غيره من مستشعرات الموقع تنطوي على خطر يخصّ الأرواح، ولا سيما في البحر أو في الجو. وتميل برمجيات الملاحة مفتوحة المصدر إلى محاولة التملص من هذه المشكلة بأن تأتي مصحوبةً بتنويهات تقول: «لا تعتمد على هذا إذا كان ما ستقوم به قد عرّض الأرواح للخطر.»
ونحن نرى أن هذه التنويهات عديمة الأثر وخطرة: عديمة الأثر لأن مُكامِلي الأنظمة من المرجّح جدًا أن يتعاملوا معها على أنها شكلية ويتجاهلوها، وخطرة لأنها تشجّع المطوّرين على خداع أنفسهم بأن عيوب الشيفرة لن يكون لها عواقب بالغة، وأن التقصير في ضمان الجودة مقبول.
يصدّق مطوّرو مشروع GPSD أن السياسة المقبولة وحدها هي التصميم من أجل صفر عيوب. ولأن تعقيد البرمجيات هو ما هو، لم نصل إلى ذلك تمامًا—لكنه، بالنظر إلى حجم مشروع GPSD وعمره وتعقيده، نقترب منه كثيرًا.
استراتيجيتنا في ذلك هي مزيج من سياسات معمارية وسياسات برمجية ترمي إلى استبعاد احتمال وجود عيوب في الشيفرة المُسلَّمة.
ومن السياسات المهمّة ما يلي: لا يستخدم خدمي gpsd تخصيص تخزين ديناميكيًا أبدًا—لا malloc ولا calloc، ولا استدعاءات لأي دوال أو مكتبات تتطلبه. وبضربة واحدة، هذا يُنفي أشهر جاذب للعيوب في برمجة C. ولنا لا تسريبات ذاكرة ولا أخطاء تخصيص مزدوج أو تحرير مزدوج، ولن يكون لدينا ذلك أبدًا.
ونحن نفلت من هذا لأن جميع المستشعرات التي نتعامل معها تُصدر حِزَمًا ذات أطوال قصوى ثابتة نسبيًا وصغيرة، ومهمّة الخدمي أن يستوعبها ويرسلها إلى العملاء بأقل قدر ممكن من التخزين المؤقت. ومع ذلك، فإن التنازل عن malloc يتطلّب انضباطًا في البرمجة وبعض التنازلات في التصميم، عددًا منها سبق أن أشرنا إليه عند مناقشة محلّل JSON. ونحن ندفع هذه التكاليف راضين من أجل خفض معدّل عيوبنا.
ومن الآثار الجانبية المفيدة لهذه السياسة أنها تزيد فاعلية أدوات الفحص الساكن للشيفرة مثل splint و cppcheck و Coverity. وهذا يغذّي خيارًا سياسيًا آخر مهمًّا؛ فنحن نستعمل أدوات تدقيق الشيفرة هذه بكثافة شديدة، ونستعمل كذلك إطارًا مخصَّصًا لاختبارات الانحدار. (ولا نعرف أي حزمة برمجية أكبر من GPSD موسومة بالكامل بتعليقات splint، ونرجّح بقوة أنه لم يوجد شيء من ذلك بعد.)
وتساعدنا هنا البنية شديدة الوحداتية لـ GPSD أيضًا. فحدود الوحدات (modules) تعمل كنقاط قطع نركّب عندها بيئات الاختبار، وقد فعلنا ذلك بمنهجية شديدة. ويتحقق اختبار الانحدار المعتاد لدينا من كل شيء، بدءًا من سلوك الفاصلة العائمة في عتاد المضيف، مرورًا بتحليل JSON، وصولًا إلى سلوك الإبلاغ الصحيح على أكثر من سبعين سجل مستشعرات مختلفًا.
ولنا أن نعترف بأن مهمة الالتزام بالصرامة أسهل لدينا قليلًا مما هي عليه في كثير من التطبيقات، لأن الخدمي لا واجهات موجَّهة للمستخدم؛ فالبيئة المحيطة به ليست سوى مجموعة من تدفقات بيانات تسلسلية، ومن ثمّ فهي سهلة المحاكاة نسبيًا. ومع ذلك، فإن استغلال تلك الميزة فعليًا، تمامًا كالتنازل عن malloc، يتطلّب الموقف الصحيح، وهذا يعني تحديدًا الاستعداد لإنفاق وقت تصميم وبرمجة على أدوات الاختبار وبيئاته بقدر ما ننفقه على شيفرة الإنتاج. وهذه سياسة نعتقد أن مشاريع المصادر المفتوحة الأخرى تستطيع أن تحاكيها ويجب أن تفعل.
وأنا أكتب هذا (يوليو 2011)، يكون نظام تتبّع أخطاء مشروع GPSD خاليًا تمامًا. وقد ظلّ خاليًا لأسابيع، واستنادًا إلى معدلات إرسال الأخطاء في الماضي، نتوقّع أن يبقى خاليًا مدة طويلة أخرى. ولم نسلّم أي شيفرة فيها خطأ انهيار منذ ست سنوات. وحين تبقى لدينا أخطاء، فإنها تميل إلى أن تكون من نوع ميزة ناقصة طفيفة أو عدم مطابقة للمواصفة، من النوع الذي يسهل إصلاحه خلال دقائق عمل معدودة.
وهذا لا يعني أن المشروع كان عيشًا متواصلًا بلا انقطاع. وفي الفصل التالي، سنستعرض بعض أخطاءنا…
7.10. الدروس المستفادة
تصميم البرمجيات صعب؛ فالأخطاء والطرق المسدودة جزء طبيعي منه إلى حدّ كبير، ولم يكن GPSD استثناءً لهذه القاعدة. وقد كان أكبر خطأ في تاريخ هذا المشروع هو تصميم البروتوكول السابق لـ JSON، المخصّص لطلب معلومات GPS والإبلاغ عنها. وقد استغرق التعافي منه سنوات من العمل، وفي سوء التصميم الأصلي وفي التعافي عنه على حدٍّ سواء دروسٌ مفيدة.
كانت هناك مشكلتان جادّتان في البروتوكول الأصلي:
- ضعف القابلية للتوسّع. كان يستخدم وسومَ طلب واستجابة مؤلَّفة من حرف واحد لكلٍّ منها، مع تجاهل حالة الأحرف. وهكذا، على سبيل المثال، كان طلب الإبلاغ عن خط الطول ودائرة العرض هو
"P"، وكان الاستجابة تبدو هكذا:"P -75.32 40.05". فضلًا عن ذلك، كان المحلّل يفسّر طلبًا مثل"PA"على أنه طلب"P"يليه طلب"A"(الارتفاع). ومع اتساع قدرات الخدمي تدريجيًا، نفد فضاء الأوامر حرفيًا. - عدم تطابق بين النموذج الضمني الذي يفترضه البروتوكول لسلوك المستشعرات وبين سلوكها الفعلي. كان البروتوكول القديم قائمًا على الطلب/الاستجابة: أرسل طلبًا عن الموقع (أو الارتفاع، أو أي شيء آخر) لتحصل على تقرير بعد لحظات. لكن في الواقع، لا يمكن عادةً طلب تقرير من GPS أو من مستشعرات ملاحية أخرى؛ فهي تبثّ تقاريرها باستمرار، وأقصى ما يمكن أن يفعله أي طلب هو استعلام ذاكرة تخزين مؤقت (cache). وقد شجّع هذا عدم التطابق التطبيقاتَ على معالجة بيانات متسرّبة؛ فكثيرًا ما يطلبون بيانات الموقع دون أن يطلبوا كذلك ختمًا زمنيًا (timestamp) أو أي معلومات عن جودة محدِّد الموقع، وهي ممارسة يسهل أن تؤدي إلى عرض بيانات قديمة أو غير صحيحة على المستخدم.
أصبح واضحًا في وقت مبكر من عام 2006 أن تصميم البروتوكول القديم غير كافٍ، لكن تصميم بروتوكول بديل استغرق قرابة ثلاث سنوات من مخططات التصميم والبدايات الخاطئة. واستغرق الانتقال بعد ذلك سنتين، وتسبّب في بعض الألم لمطوّري تطبيقات العملاء. وكان سيكلّف أكثر بكثير لو لم يكن المشروع قد شحن مكتبات على جانب العميل تحمي المستخدمين من معظم تفاصيل البروتوكول—لكننا لم نضبط أيضًا واجهة تلك المكتبات البرمجية على الوجه الصحيح في البداية.
ولو كنّا نعرف حينها ما نعرفه اليوم، لأُدخل البروتوكول القائم على JSON قبل خمس سنوات، ولما احتاج تصميم واجهة مكتبات العملاء إلى هذا العدد الكبير من المراجعات. لكن بعض أنواع الدروس لا يعلّمها إلا الخبرة والتجريب.
هناك على الأقل مبدآن تصميميان يمكن لخدميات الخدمة في المستقبل أن يأخذاهما في الحسبان كي لا يكرّرا أخطاءنا:
- صمّم من أجل القابلية للتوسّع. إذا كان بروتوكول تطبيق خدميك قادرًا على نفاد فضاء الأسماء بالطريقة التي نفد بها بروتوكولنا القديم، فأنت على خطأ. المبالغة في تقدير التكاليف قصيرة المدى والتقليل من تقدير الفوائد بعيدة المدى للبروتوكولات الميتا مثل XML و JSON خطأ يتكرّر إلى حدّ كبير.
- مكتبات جانب العميل فكرة أفضل من كشف تفاصيل بروتوكول التطبيق. فقد تتمكّن المكتبة من تكييف باطنها مع إصدارات متعددة من بروتوكول التطبيق، بما يخفّض إلى حدّ كبير تعقيد الواجهة ومعدّل العيوب معًا، مقارنةً بالبديل الذي يضطر فيه كل كاتب تطبيق إلى تطوير ربط خاص به (ad hoc binding). وسيترجم هذا الفرق مباشرةً إلى عدد أقل من بلاغات الأخطاء في نظام تتبّع مشروعك.
ومن الردود الممكنة على تأكيدنا على القابلية للتوسّع، لا في بروتوكول تطبيق GPSD وحده بل في جوانب أخرى من بنية المشروع مثل واجهة مشغّل الحِزَم، أن يُردّ عليها بأنها نمط من الإفراط في التصميم سبّبه تمدّد المهمة (mission creep). فقد يسأل مبرمجو يونكس المربَّون على تقليد «افعل شيئًا واحدًا جيدًا» ما إذا كانت مجموعة أوامر gpsd تحتاج حقًا لأن تكون أكبر في 2011 منها في 2006، ولماذا يعالج gpsd الآن مستشعرات غير خاصة بـ GPS مثل البوصلات المغناطيسية ومستقبِلات AIS البحرية، ولماذا ندرس احتمالات مثل تتبّع الطائرات عبر ADS-B.
هذه أسئلة مشروعة. ويمكننا مقاربة الإجابة إليها بالنظر إلى التكلفة الفعلية بالتعقيد لإضافة نوع جهاز جديد. فلأسباب وجيهة جدًا، منها صغر حجم البيانات نسبيًا وارتفاع مستويات الضجيج الكهربائي التي ارتبطت تاريخيًا بالأسلاك التسلسلية المتصلة بالمستشعرات، تبدو بروتوكولات الإبلاغ لأجهزة GPS وغيرها من المستشعرات المتعلقة بالملاحة متشابهة في جوهرها إلى حدٍّ كبير: حِزَم صغيرة تحمل نوعًا ما من مجمّع التحقّق (checksum). وهذه البروتوكولات معقّدة المعالجة، لكنها ليست صعبة التمييز عن بعضها وتحليلها إلى حدٍّ كبير، وتكلفة الإضافة لواحد جديد تميل إلى أن تكون أقل من ألف سطر. وحتى أكثر بروتوكولاتنا دعمًا تعقيدًا، مع مولّدات التقارير الخاصة بها، مثل AIS البحرية، لا تكلّف سوى نحو 3 آلاف سطر لكل منها. وفي المجموع، تبلغ المشغّلات مع قاطع الحِزَم ومولّدات تقارير JSON المرتبطة بها نحو 18 ألف سطر إجمالًا.
وبمقارنة ذلك بـ 43 ألف سطر للمشروع ككل، نرى أن معظم تكلفة تعقيد GPSD تقع في شيفرة إطار العمل المحيطة بالمشغّلات—وفي (وهذا مهم) أدوات الاختبار والإطار الذي يتحقّق من سلامة الخدمي. ولو أُعيد إنتاج هذه الشيفرة، لكان مشروعًا أكبر بكثير من كتابة أي محلّل حِزَم مفرد. لذلك فإن كتابة نسخة مكافئة لـ GPSD لبروتوكول حِزَم لا يعالجه GPSD سيكون عملًا أكبر بكثير من إضافة مشغّل آخر ومجموعة اختبارات أخرى إلى GPSD نفسه. وعلى العكس، فإن أكثر النتائج اقتصادية (وهي النتيجة ذات أدنى معدّل تراكمي متوقّع للعيوب) هي أن ينمو GPSD ليضم مشغّلات حِزَم للعديد من أنواع المستشعرات المختلفة.
أما «الشيء الواحد» الذي تطوّر GPSD ليجيده فهو التعامل مع أي مجموعة مستشعرات تُصدر حِزَمًا متمايزة تحمل مجاميع اختبارية. فما يبدو تمدّدًا في المهمة هو في الواقع ما يمنع اضطرارنا إلى كتابة خدميات معالجة مختلفة ومكرَّرة. وبدلًا من ذلك، يحصل مطوّرو التطبيقات على واجهة برمجية واحدة نسبيًا البساطة، وعلى فوائد خبرتنا المكتسبة بشقّ الأنفس في التصميم والاختبار عبر مدى متنامٍ من أنواع المستشعرات.
وما الذي يميّز GPSD عن مجرد كومة من السمات ناتجة عن تمدّد المهمة ليس الحظ أو السحر الأسود، بل التطبيق الدقيق للممارسات الأفضل المعروفة في هندسة البرمجيات. وتبدأ العائدات من ذلك بمعدّل عيوب منخفض في الحاضر، وتستمر مع القدرة على دعم سمات جديدة بجهد قليل أو بأثر متوقّع ضئيل على معدّل العيوب في المستقبل.
وربما كان أهم درس تعلّمناه لمشاريع المصادر المفتوحة الأخرى هو هذا: إن خفض معدّلات العيوب مقتربًا إلى حدّ كبير من الصفر أمر صعب، لكنه ليس مستحيلًا—ولا حتى لمشروع مُنشر على نطاق واسع وبتنوّع كبير مثل GPSD. فالبنية السليمة، وممارسة البرمجة الجيدة، والتركيز الحازم على الاختبار، هي ما يحقّق ذلك—وأهم متطلب سابق هو الانضباط في السعي وراء هذه الثلاثة.