11.12. Istovremene uloge i višestruke veze¶
Stranice o periferiji i centralnom uređaju svaka prikazuju jednu ulogu koja istovremeno poslužuje jednu vezu. Stvarne aplikacije rijetko su tako jednostavne. Kamera može objavljivati senzorsku uslugu telefonu dok istovremeno čita vrijednosti s remena za mjerenje pulsa, ili prihvaćati veze s dva istovremeno uparena telefona. aioble API podržava oba obrasca jer se radio multipleksira u pozadini, a svaka operacija već je korutina – pokrenite više korutina, i posao se odvija paralelno na jednoj petlji događaja.
Ova stranica okuplja obrasce koji se pojavljuju.
11.12.1. Više klijenata koji se povezuju s jednom periferijom¶
Jednostavna petlja periferije na Djelovanje kao periferija poslužuje jedan povezani centralni uređaj istovremeno:
async def serve():
while True:
connection = await aioble.advertise(...)
async with connection:
await connection.disconnected()
Obrazac koji joj omogućuje prihvaćanje više od jednog klijenta je pokretanje zadatka po vezi i trenutno vraćanje natrag na aioble.advertise() kako bi se i sljedeći klijent mogao povezati:
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))
Svaka veza izvodi se u vlastitom zadatku. GATT baza podataka je dijeljena – svi klijenti vide iste usluge i karakteristike – ali stanje pojedine veze živi unutar zadatka. Obavijesti odlaze svakom pretplaćenom klijentu kada se write() pozove s send_update=True; usmjereni potisci koji bi trebali doprijeti samo do jednog klijenta koriste notify() / indicate() s određenim DeviceConnection argumentom.
Držite razgranavanje malim. Svaka održavana veza troši radijsko vrijeme, RAM i mjesto u tablici veza kontrolera, a kamera nije dizajnirana da bude središte za desetke klijenata. Dva ili tri centralna uređaja (telefon, tablet, prateći mikrokontroler) lako su u dosegu; dizajni kojima treba više pripadaju na pravom BLE pristupniku, a ne na kameri.
11.12.2. Periferija i centralni uređaj istovremeno¶
Kamera može oglašavati vlastitu uslugu telefonu dok istovremeno djeluje kao centralni uređaj prema nosivom uređaju. aioble nema prekidač za „način rada” – petlja oglašavanja i petlja skeniranja-i-povezivanja samo su neovisne korutine:
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 dijeli vrijeme između dviju uloga – prozor skeniranja ovdje, prasak oglašavanja ondje, događaj veze kada je jedna od veza bilo koje strane aktivna. Propusnost svake uloge pada kada su obje aktivne jer radio doslovno ne može raditi dvije stvari odjednom, ali za razgovore niske propusnosti za koje je BLE dizajniran, trošak je obično neprimjetan.
Dvije praktične stvari koje treba imati na umu:
Obje uloge moraju biti u vlastitoj korutini. Pozivanje
aioble.scan()iz zadatka po klijentu koji obrađuje povezani centralni uređaj radi, ali blokira obavijesti tog klijenta dok se skeniranje ne završi – umjesto toga skeniranje pokrenite u vlastitom zadatku.Istovremeno se izvodi samo jedno skeniranje. Ako trebate skenirati s dva različita mjesta, dijelite iterator skeniranja ili koordinirajte pristup; ne ulazite u dva
aioble.scan()upravitelja konteksta paralelno.
11.12.3. Koordiniranje više veza iz jednog zadatka¶
Kada se nekoliko veza treba kombinirati u jednu logičku operaciju – na primjer, kamera istovremeno komunicira s dva senzora i rezultat prijavljuje tek nakon što su oba odgovorila – standardni asyncio primitivi izravno se primjenjuju. asyncio.gather() istovremeno izvodi korutine po vezi i vraća se kada su sve završile; asyncio.wait_for() dodaje rok.
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
Isti obrazac koji poglavlje o asyncio (Asyncio) koristi za umrežavanje – BLE korutine uključuju se u gather / wait_for / Event / Lock na isti način kao TCP korutine.
11.12.4. Kada jedna uloga završi po ciklusu, a druga ne¶
Ciklus kamere napajane baterijom mogao bi izgledati ovako:
Buđenje.
Kao centralni uređaj, čitanje svježih vrijednosti s uparenog senzorskog remena.
Kao periferija, oglašavanje kako bi telefon preuzeo mjerenja toga dana.
Kada su oboje neaktivni, pozovite
aioble.stop()i spavajte.
Sljedovanje je jednostavno s dva zadatka i 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