11.12. Peran bersamaan dan beberapa koneksi¶
Halaman periferal dan central masing-masing menampilkan satu peran yang melayani satu koneksi pada satu waktu. Aplikasi nyata jarang sesederhana itu. Sebuah kamera mungkin mempublikasikan layanan sensor ke ponsel sambil juga membaca nilai dari tali detak jantung, atau menerima koneksi dari dua ponsel yang dipasangkan secara bersamaan. API aioble mendukung kedua pola tersebut karena radio melakukan multiplexing di bawahnya dan setiap operasi sudah merupakan coroutine -- jalankan lebih banyak coroutine, dan pekerjaan terjadi secara paralel pada satu event loop.
Halaman ini mengumpulkan pola-pola yang sering muncul.
11.12.1. Beberapa klien terhubung ke satu periferal¶
Loop periferal sederhana di Bertindak sebagai periferal melayani satu central yang terhubung pada satu waktu:
async def serve():
while True:
connection = await aioble.advertise(...)
async with connection:
await connection.disconnected()
Pola yang memungkinkannya menerima lebih dari satu klien adalah dengan mengaktifkan task per koneksi dan segera kembali ke aioble.advertise() sehingga klien berikutnya juga dapat terhubung:
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))
Setiap koneksi berjalan dalam task-nya sendiri. Database GATT dibagikan -- semua klien melihat layanan dan karakteristik yang sama -- tetapi status per koneksi ada di dalam task. Notifikasi dikirim ke setiap klien yang berlangganan saat write() dipanggil dengan send_update=True; push terarah yang hanya perlu menjangkau satu klien menggunakan notify() / indicate() dengan argumen DeviceConnection yang spesifik.
Jaga agar fan-out tetap kecil. Setiap koneksi yang dipegang menghabiskan waktu radio, RAM, dan slot dalam tabel koneksi controller, dan kamera tidak dirancang untuk menjadi hub bagi puluhan klien. Dua atau tiga central (ponsel, tablet, mikrokontroler pendamping) masih dalam jangkauan; desain yang membutuhkan lebih banyak sebaiknya menggunakan gateway BLE yang tepat daripada kamera.
11.12.2. Periferal dan central secara bersamaan¶
Sebuah kamera dapat mengiklankan layanannya sendiri ke ponsel sambil juga bertindak sebagai central ke perangkat yang dapat dipakai. aioble tidak memiliki sakelar "mode" -- loop iklan dan loop scan-and-connect hanyalah coroutine yang independen:
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())
Radio berbagi waktu antara dua peran -- jendela scan di sini, burst iklan di sana, event koneksi ketika salah satu koneksi dari kedua sisi sedang aktif. Throughput pada setiap peran berkurang ketika keduanya aktif karena radio tidak dapat secara harfiah melakukan dua hal sekaligus, tetapi untuk percakapan bandwidth rendah yang dirancang untuk BLE, biayanya biasanya tidak terasa.
Dua hal praktis yang perlu diingat:
Kedua peran perlu berada dalam coroutine-nya sendiri. Memanggil
aioble.scan()dari dalam task per klien yang menangani central yang terhubung memang berfungsi, tetapi memblokir notifikasi klien tersebut hingga pemindaian selesai -- jalankan pemindaian di task-nya sendiri.Hanya satu pemindaian berjalan pada satu waktu. Jika Anda perlu memindai dari dua tempat berbeda, bagikan iterator scan atau koordinasikan akses; jangan masukkan dua context manager
aioble.scan()secara paralel.
11.12.3. Mengkoordinasikan beberapa koneksi dari satu task¶
Ketika beberapa koneksi perlu digabungkan menjadi satu operasi logis -- misalnya, kamera berkomunikasi dengan dua sensor sekaligus dan hanya melaporkan hasil setelah keduanya menjawab -- primitif asyncio standar berlaku secara langsung. asyncio.gather() menjalankan coroutine per koneksi secara bersamaan dan kembali saat semua selesai; asyncio.wait_for() menambahkan deadline.
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
Pola yang sama yang digunakan bab asyncio (Asyncio) untuk jaringan -- coroutine BLE dapat dimasukkan ke dalam gather / wait_for / Event / Lock dengan cara yang sama seperti coroutine TCP.
11.12.4. Ketika satu peran selesai per siklus dan yang lain tidak¶
Sebuah siklus pada kamera bertenaga baterai mungkin terlihat seperti:
Bangun.
Sebagai central, baca nilai terbaru dari tali sensor yang dipasangkan.
Sebagai periferal, iklankan agar ponsel dapat mengunduh pengukuran hari ini.
Ketika keduanya idle, panggil
aioble.stop()dan tidur.
Pengurutannya mudah dilakukan dengan dua task dan sebuah asyncio.Event
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