11.7. عمليات GATT¶
تقبع الخاصية ببساطة في قاعدة بيانات GATT بوصفها قيمة مسمّاة. وما يجعلها مفيدة هو المجموعة الصغيرة المحدّدة جيدًا من العمليات التي يمكن للعميل تشغيلها عليها. تُعلن كل خاصية عن العمليات التي تدعمها على هيئة قناع بتات للخصائص -- فالخادم الذي ليس لديه ما يكشفه يمكنه نشر قيمة للقراءة فقط، وقد يكون سجل تحكم للكتابة فقط، أما المستشعر الذي يبث التحديثات فيضبط بتة الإشعار. يكتشف العميل قناع البتات أثناء الاكتشاف ويحترمه.
العمليات الخمس هي القراءة والكتابة والكتابة دون استجابة والإشعار والإشارة. وتنقسم إلى مجموعتين -- السحب (العميل يطلب) والدفع (الخادم يرسل).
11.7.1. السحب: القراءة والكتابة¶
هاتان العمليتان هما الأبسط وتبدوان تمامًا كاستدعاءات دوال.
القراءة. يطلب العميل القيمة الحالية، فيرسلها الخادم إليه. رحلة ذهاب وإياب واحدة، يحصل العميل على أي بايتات ضبطها الخادم لتلك الخاصية، ولا يحصل الخادم على أي معلومة عمّن قرأ.
الكتابة. يرسل العميل بايتات جديدة، فيخزّنها الخادم (ويشغّل اختياريًا منطق التطبيق على القيمة الجديدة). يوجد نوعان:
الكتابة مع استجابة -- يقرّ الخادم بالاستلام، مثيرًا أي خطأ في التطبيق عند حالة غير صفرية. موثوقة، رحلة ذهاب وإياب واحدة.
الكتابة دون استجابة -- يخزّن الخادم البايتات بصمت؛ ولا يحصل العميل على أي إقرار على الإطلاق. أسرع (لا انتظار لرحلة ذهاب وإياب على الإقرار) ومفيدة للبث، مقابل أن اكتشاف الأخطاء لا يتم إلا عبر قراءة جانبية لاحقة.
في aioble، تخفي الواجهة على جانب العميل هذا الاختيار خلف دالة واحدة هي aioble.ClientCharacteristic.write() مع المعامل المفتاحي response (True / False / None للاختيار التلقائي بناءً على ما يعلن عنه النظير).
11.7.2. الدفع: الإشعار والإشارة¶
نموذج السحب غير ملائم لبيانات المستشعر. فحزام قياس معدل ضربات القلب الذي يتعين على الهاتف استطلاعه كل ثانية سيستنزف البطارية في مئة حدث راديو لا لزوم لها؛ أما الحزام الذي يدفع القيمة فقط عندما تكون لديه قراءة جديدة فهو مغزى BLE من الأساس.
يحل GATT ذلك بعمليات يبادر بها الخادم. يقوم العميل بـالاشتراك في خاصية؛ ومن تلك اللحظة فصاعدًا، في كل مرة يحدّث الخادم القيمة، تُدفع القيمة الجديدة عبر الارتباط إلى العميل. يوجد نوعان:
الإشعار. أطلق وانسَ. يضع الخادم إشعارًا في قائمة الانتظار، فتنقله طبقة الارتباط أثناء حدث الاتصال التالي، فيستلمه العميل. لا يوجد إقرار على مستوى GATT؛ تتولى إعادة الإرسال الاعتيادية في طبقة الارتباط معالجة الفقدان على جانب الراديو، لكن التطبيق لا يرى أي تأكيد على أن القيمة قد عُولجت.
الإشارة. يرسل الخادم إشعارًا وينتظر تأكيد العميل على مستوى GATT قبل إرسال التالي. إشارة واحدة في كل مرة. تُستخدم عندما يحتاج الخادم إلى معرفة أن العميل قد رأى القيمة فعلًا -- خاصية تنبيه حرج، أو إقرار تكوين.
السحب (القراءة) مقابل الدفع (الإشعار). مع الإشعارات، يشترك العميل مرة واحدة ويدفع الخادم القيم الجديدة كلما تغيرت.¶
يتم الاشتراك بالكتابة إلى واصف ملحق بالخاصية -- واصف تكوين خاصية العميل (CCCD، 0x2902). كتابة 0x0001 تمكّن الإشعارات، وكتابة 0x0002 تمكّن الإشارات، وكتابة 0x0000 تعطّل كليهما. تنفّذ الدالة aioble.ClientCharacteristic.subscribe() الكتابة نيابةً عنك، مع المعاملين المفتاحيين notify=True وindicate=True.
بمجرد الاشتراك، ينتظر العميل عمليات الدفع الواردة باستخدام notified() وindicated() -- وكلاهما كوروتينات غير متزامنة تتعلق حتى وصول عملية الدفع التالية.
11.7.3. يحكم MTU حجم الحمولة¶
كل عملية مقيّدة بقيمة MTU المتفاوض عليها التي استقر عليها الاتصال وقت قيام الارتباط. قيمة MTU الافتراضية هي 23 بايتًا، مما يترك 20 بايتًا لبايتات قيمة الخاصية بعد ترويسة GATT. وأي شيء أكبر من ذلك يجب إما أن يُلائَم في قيمة MTU أكبر (يُتفاوض على رفعها عبر aioble.DeviceConnection.exchange_mtu()، حتى 512 بايتًا على الكاميرا) أو أن يُقسَّم إلى خصائص متعددة أو إشعارات متعددة.
تتولى إجراءات GATT الطويلة خلف الكواليس معالجة عمليات القراءة والكتابة التي يبادر بها العميل لقيم أكبر من MTU (القراءة الطويلة / التحضير للكتابة + تنفيذ الكتابة)؛ يشغّل aioble هذه الإجراءات بشفافية، بحيث إن استدعاء read() / write() بقيمة أكبر من الحجم يكلّف فقط مزيدًا من رحلات الذهاب والإياب. أما الإشعارات والإشارات التي يبادر بها الخادم فهي لا تُجزّأ -- فكل عملية دفع محدودة بقيمة MTU، ويقسّم التطبيق أي شيء أكبر إلى إشعارات متعددة أو يبتعد عن GATT كليًا.
بالنسبة للنقل كبير الحجم فعلًا -- إطار ملتقط، أو دفعة من القياسات، أو كتلة برنامج ثابت -- يكون الجواب الصحيح عادةً هو الابتعاد عن GATT كليًا واستخدام قناة L2CAP بدلًا منه (انظر قنوات L2CAP).
11.7.4. الجانبان في لمحة¶
تظهر العمليات الخمس بشكل مختلف على كل جانب من جانبي الاتصال:
على الخادم (الجهاز الطرفي، في التخطيط الشائع):
aioble.Characteristic.read()-- قراءة القيمة المحلية الحالية من قاعدة بيانات GATT (جانب الخادم من "ما الذي سيراه العميل").aioble.Characteristic.write()-- تحديث القيمة المحلية، مع الدفع اختياريًا لكل عميل مشترك.aioble.Characteristic.notify()/indicate()-- إرسال عملية دفع إلى عميل محدد بعينه.aioble.Characteristic.written()-- انتظار عملية الكتابة الواردة التالية من أي عميل.aioble.Characteristic.on_read()-- دالة رد نداء تُستدعى بشكل متزامن عندما يقرأ عميل، مفيدة لحساب قيمة عند الطلب.
على العميل (الجهاز المركزي، في التخطيط الشائع):
aioble.ClientCharacteristic.read()-- طلب القيمة الحالية من الخادم.aioble.ClientCharacteristic.write()-- إرسال قيمة جديدة، مع استجابة أو دونها.aioble.ClientCharacteristic.subscribe()-- تمكين / تعطيل الإشعارات والإشارات.aioble.ClientCharacteristic.notified()/indicated()-- انتظار عملية الدفع التالية.