11.12. Eşzamanlı roller ve çoklu bağlantılar¶
Peripheral ve central sayfalarının her biri, tek seferde tek bir bağlantıya hizmet veren tek bir rolü gösterir. Gerçek uygulamalar nadiren bu kadar basittir. Bir kamera, bir telefona sensör hizmeti yayınlarken aynı zamanda bir kalp atışı bandından değerler okuyabilir ya da eş zamanlı eşleştirilmiş iki telefondan bağlantı kabul edebilir. aioble API’si her iki deseni de destekler; çünkü radyo alttan çoklama yapar ve her işlem zaten bir eşyordamdır (coroutine) – daha fazla eşyordam çalıştırın, iş tek bir olay döngüsünde paralel olarak gerçekleşir.
Bu sayfa, karşımıza çıkan desenleri bir araya getirir.
11.12.1. Tek bir peripheral’a bağlanan birden çok istemci¶
Çevre birimi olarak davranma sayfasındaki basit peripheral döngüsü, tek seferde tek bir bağlı central’a hizmet verir:
async def serve():
while True:
connection = await aioble.advertise(...)
async with connection:
await connection.disconnected()
Birden fazla istemci kabul etmesini sağlayan desen, bağlantı başına bir görev başlatıp hemen aioble.advertise() çağrısına geri dönmektir; böylece bir sonraki istemci de bağlanabilir:
async def handle_client(connection):
async with connection:
# ... per-client work: subscribe their CCCDs,
# push notifications, await writes ...
await connection.disconnected()
async def serve():
while True:
connection = await aioble.advertise(
interval_us=250000,
name="openmv-env",
services=[ENV_SERVICE],
)
asyncio.create_task(handle_client(connection))
Her bağlantı kendi görevinde çalışır. GATT veritabanı paylaşılır – tüm istemciler aynı hizmetleri ve karakteristikleri görür – ancak bağlantı başına durum görevin içinde yaşar. write() çağrısı send_update=True ile yapıldığında bildirimler her abone olmuş istemciye gider; yalnızca tek bir istemciye ulaşması gereken yönlendirilmiş gönderimler ise belirli DeviceConnection bağımsız değişkeniyle notify() / indicate() kullanır.
Dağıtımı küçük tutun. Tutulan her bağlantı radyo zamanına, RAM’e ve denetleyicinin bağlantı tablosundaki bir yuvaya mal olur ve kamera onlarca istemci için bir merkez (hub) olacak şekilde tasarlanmamıştır. İki veya üç central (bir telefon, bir tablet, bir yardımcı mikrodenetleyici) rahatlıkla erişilebilirdir; daha fazlasına ihtiyaç duyan tasarımlar kamerada değil, uygun bir BLE ağ geçidinde yer almalıdır.
11.12.2. Aynı anda hem peripheral hem central¶
Bir kamera, kendi hizmetini bir telefona yayınlarken aynı zamanda giyilebilir bir cihaza karşı central olarak davranabilir. aioble‘da bir “mod” anahtarı yoktur – yayın (advertise) döngüsü ile tara-ve-bağlan döngüsü yalnızca bağımsız eşyordamlardır:
async def be_peripheral():
while True:
connection = await aioble.advertise(
interval_us=250000,
name="openmv-hub",
services=[ENV_SERVICE],
)
asyncio.create_task(handle_client(connection))
async def be_central():
while True:
sensor = await find_sensor()
if sensor is None:
await asyncio.sleep(5)
continue
try:
async with await sensor.connect() as conn:
await stream_from_sensor(conn)
except aioble.DeviceDisconnectedError:
pass
async def main():
await asyncio.gather(be_peripheral(), be_central())
asyncio.run(main())
Radyo, iki rol arasında zaman paylaşımı yapar – burada bir tarama penceresi, orada bir yayın patlaması, iki taraftan birinin bağlantısı aktif olduğunda bir bağlantı olayı. Her iki rol de aktifken her bir rolün iş hacmi düşer; çünkü radyo gerçek anlamda aynı anda iki iş yapamaz, ancak BLE’nin tasarlandığı düşük bant genişlikli iletişimler için bu maliyet genellikle fark edilmez.
Akılda tutulması gereken iki pratik nokta:
Her iki rol de kendi eşyordamında olmalıdır. Bağlı bir central’ı işleyen istemci başına görevin içinden
aioble.scan()çağırmak işe yarar, ancak tarama bitene kadar o istemcinin bildirimlerini engeller – bunun yerine taramayı kendi görevinde çalıştırın.Aynı anda yalnızca bir tarama çalışır. İki farklı yerden tarama yapmanız gerekiyorsa, tarama yineleyicisini paylaşın veya erişimi eşgüdümleyin; paralel olarak iki
aioble.scan()bağlam yöneticisine girmeyin.
11.12.3. Tek bir görevden birden çok bağlantının eşgüdümlenmesi¶
Birden fazla bağlantının tek bir mantıksal işlemde birleştirilmesi gerektiğinde – örneğin kamera aynı anda iki sensörle konuşur ve sonucu yalnızca her ikisi de yanıt verdikten sonra raporlar – standart asyncio ilkelleri doğrudan geçerlidir. asyncio.gather() bağlantı başına eşyordamları eşzamanlı olarak çalıştırır ve hepsi bittiğinde döner; asyncio.wait_for() bir son tarih ekler.
async def read_pair():
async with await sensor_a.connect() as a:
async with await sensor_b.connect() as b:
value_a, value_b = await asyncio.gather(
read_value(a, A_SERVICE, A_CHAR),
read_value(b, B_SERVICE, B_CHAR),
)
return value_a, value_b
asyncio bölümünün (Asyncio) ağ iletişimi için kullandığı desenin aynısı – BLE eşyordamları, TCP olanların yaptığı gibi gather / wait_for / Event / Lock ile birleşir.
11.12.4. Bir rol döngü başına bitip diğeri bitmediğinde¶
Pil ile çalışan bir kameradaki bir döngü şöyle görünebilir:
Uyan.
Central olarak, eşleştirilmiş bir sensör bandından taze değerler oku.
Peripheral olarak, bir telefonun günün ölçümlerini indirmesi için yayın yap.
Her ikisi de boştayken
aioble.stop()çağır ve uyu.
Sıralama, iki görev ve bir asyncio.Event ile basittir:
phone_done = asyncio.Event()
async def serve_phone():
connection = await aioble.advertise(
interval_us=250000,
name="openmv-hub",
services=[ENV_SERVICE],
)
async with connection:
await stream_measurements(connection)
phone_done.set()
async def read_strap():
async with await strap.connect() as conn:
await pull_fresh_values(conn)
async def cycle():
await asyncio.gather(read_strap(), serve_phone())
aioble.stop() # radio off until next wake