11.7. GATT işlemleri

Bir karakteristik (characteristic), GATT veritabanında yalnızca adlandırılmış bir değer olarak durur. Onu kullanışlı kılan, bir istemcinin üzerinde çalıştırabileceği küçük, iyi tanımlanmış işlemler kümesidir. Her karakteristik, hangi işlemleri desteklediğini bir özellik bit maskesi (property bitmask) olarak bildirir – açığa çıkaracak hiçbir şeyi olmayan bir sunucu salt okunur bir değer yayınlayabilir, bir kontrol yazmacı yalnızca yazılabilir olabilir, güncellemeleri akıtan bir sensör ise notify bitini ayarlardı. İstemci, bit maskesini keşif (discovery) sırasında öğrenir ve ona uyar.

Beş işlem şunlardır: read, write, write without response, notify ve indicate. Bunlar iki gruba ayrılır – çekme (istemci talep eder) ve itme (sunucu gönderir).

11.7.1. Çekme: read ve write

Bu ikisi en basit olanlardır ve tam olarak işlev çağrılarına benzer.

  • Read. İstemci geçerli değeri ister, sunucu onu geri gönderir. Tek bir gidiş-dönüş; istemci, sunucunun o karakteristik için ayarladığı baytları alır, sunucu ise kimin okuduğuna dair hiçbir şey öğrenmez.

  • Write. İstemci yeni baytlar gönderir, sunucu bunları saklar (ve isteğe bağlı olarak yeni değer üzerinde uygulama mantığı çalıştırır). İki çeşidi vardır:

    • Yanıtlı yazma (Write with response) – sunucu onaylar ve sıfır olmayan bir durumda herhangi bir uygulama hatasını yükseltir. Güvenilir, tek gidiş-dönüş.

    • Yanıtsız yazma (Write without response) – sunucu baytları sessizce saklar; istemci hiçbir onay almaz. Daha hızlıdır (onayı bekleyen gidiş-dönüş yoktur) ve akış için kullanışlıdır; bunun bedeli ise hataları yalnızca yan kanaldan geri okuma yoluyla öğrenmektir.

aioble modülünde, istemci tarafı API’si bu seçimi tek bir aioble.ClientCharacteristic.write() yöntemi ve bir response anahtar sözcüğü (eşin ne reklamladığına göre otomatik seçim için True / False / None) arkasında gizler.

11.7.2. İtme: notify ve indicate

Çekme modeli sensör verileri için yanlıştır. Telefonun her saniye yoklaması gereken bir kalp atış hızı bandı, yüzlerce gereksiz radyo olayında pil tüketirdi; yalnızca yeni bir okuması olduğunda bir değer iten bir cihaz ise zaten BLE’nin asıl amacıdır.

GATT bunu sunucu tarafından başlatılan (server-initiated) işlemlerle çözer. İstemci bir karakteristiğe abone olur (subscribe); o noktadan itibaren, sunucu değeri her güncellediğinde yeni değer bağlantı üzerinden istemciye itilir. İki çeşidi vardır:

  • Notify. Gönder ve unut. Sunucu bir bildirim sıraya alır, bağlantı katmanı bunu bir sonraki bağlantı olayı sırasında iletir, istemci onu alır. GATT düzeyinde herhangi bir onay yoktur; bağlantı katmanının normal yeniden iletimi radyo tarafındaki kaybı ele alır, ancak uygulama değerin işlendiğine dair hiçbir doğrulama görmez.

  • Indicate. Sunucu bir bildirim gönderir ve bir sonrakini göndermeden önce istemcinin GATT düzeyindeki onayını bekler. Her seferinde tek bir indikasyon olur. Sunucunun, istemcinin değeri gerçekten gördüğünü bilmesi gerektiğinde kullanılır – kritik bir alarm karakteristiği, bir yapılandırma onayı gibi.

Bir sunucu ve bir istemcinin yan yana iki diyagramı. Solda istemci "read" gönderir, sunucu değerle yanıt verir. Üst üste üç okuma, her biri bir ok çifti. Sağda istemci tek bir "subscribe" gönderir, ardından sunucu aralarında herhangi bir istemci talebi olmadan seçtiği zamanlarda üç "notify" paketi iter.

Çekme (read) ile itme (notify) karşılaştırması. Bildirimlerle, istemci bir kez abone olur ve sunucu değerler değiştikçe yeni değerleri iter.

Abone olmak, karakteristiğe iliştirilmiş bir tanımlayıcıya – İstemci Karakteristik Yapılandırma Tanımlayıcısı (Client Characteristic Configuration Descriptor) (CCCD, 0x2902) – yazılarak gerçekleşir. 0x0001 yazmak bildirimleri (notifications) etkinleştirir, 0x0002 indikasyonları (indications) etkinleştirir, 0x0000 her ikisini de devre dışı bırakır. aioble.ClientCharacteristic.subscribe() yöntemi, notify=True ve indicate=True anahtar sözcüğü bayraklarıyla yazma işlemini sizin için gerçekleştirir.

Abone olduktan sonra istemci, gelen itmeleri notified() ve indicated() ile bekler – her ikisi de bir sonraki itme gelene kadar askıya alan asenkron eşyordamlardır (coroutine).

11.7.3. MTU, yük boyutunu yönetir

Her işlem, bağlantının bağlantı kurulurken üzerinde karar kıldığı, müzakere edilmiş MTU tarafından kısıtlanır. Varsayılan MTU 23 bayttır; bu da GATT başlığından sonra karakteristik değer baytları için 20 bayt bırakır. Bundan daha büyük herhangi bir şeyin ya daha büyük bir MTU’ya sığması (kamerada 512 bayta kadar aioble.DeviceConnection.exchange_mtu() aracılığıyla yukarı müzakere edilir) ya da birden fazla karakteristiğe veya birden fazla bildirime bölünmesi gerekir.

MTU’dan büyük değerlerin istemci tarafından başlatılan okumaları ve yazmaları, GATT’ın uzun (long) prosedürleri tarafından arka planda ele alınır (Read Long / Prepare-Write + Execute-Write); aioble bunları şeffaf biçimde çalıştırır, dolayısıyla aşırı büyük bir değerle read() / write() çağırmak yalnızca daha fazla gidiş-dönüşe mal olur. Sunucu tarafından başlatılan bildirimler ve indikasyonlar parçalanmaz (fragmented) – bir itme MTU ile sınırlandırılmıştır ve uygulama, daha büyük olan herhangi bir şeyi birden fazla bildirime böler veya tamamen GATT’tan çıkar.

Gerçekten büyük aktarımlar için – yakalanmış bir çerçeve, bir grup ölçüm, bir aygıt yazılımı (firmware) ikili nesnesi – doğru cevap genellikle tamamen GATT’tan çıkıp bunun yerine bir L2CAP kanalı (L2CAP channel) kullanmaktır (bkz. L2CAP kanalları).

11.7.4. İki tarafa bir bakış

Beş işlem, bağlantının her iki tarafında farklı biçimde açığa çıkar: