10.8. Kaksisuuntainen ohjaus WebSocketeilla¶
Server-Sent Events on vain työntösuuntainen. Kun omistaja napauttaa kojelaudalla ”tallenna tilannekuva juuri nyt” tai ”nollaa laukaisulaskuri”, kojelaudan täytyy lähettää viesti kameralle. Tähän tarvitaan WebSocket-tekniikkaa – yksi TCP-soketti, kehystetyt viestit virtaavat molempiin suuntiin.
10.8.1. /control-reitti¶
microdot.websocket.with_websocket() suorittaa WebSocket-päivityksen kättelyn ja antaa käsittelijälle WebSocket-olion. Käsittelijä silmukoi loputtomasti, lukien komentoja ja lähettäen kuittauksia:
from microdot.websocket import with_websocket
from microdot.websocket import WebSocketError
import json
@app.get('/control')
@with_websocket
async def control(request, ws):
while True:
try:
msg = await ws.receive()
except WebSocketError:
break
try:
cmd = json.loads(msg)
except ValueError:
await ws.send({'error': 'bad json'})
continue
if cmd.get('cmd') == 'snapshot_now':
if latest_jpeg:
path = '/sdcard/snaps/manual-{}.jpg'.format(
int(time.time()))
with open(path, 'wb') as f:
f.write(latest_jpeg)
await ws.send({'ok': True, 'saved': path})
else:
await ws.send({'ok': False, 'reason': 'no frame yet'})
elif cmd.get('cmd') == 'reset':
state['trigger_count'] = 0
await ws.send({'ok': True, 'counters': 'reset'})
else:
await ws.send({'error': 'unknown command'})
receive() palauttaa merkkijonon tekstikehyksille ja tavuja binäärikehyksille. Selainpuolen WebSocket.send(...) lähettää oletuksena tekstiä, joten JSON-koodatut komennot ovat luonteva valinta.
send() hyväksyy merkkijonoja, tavuja tai mitä tahansa JSON-serialisoitavaa – sanakirja lähetetään JSON-tekstikehyksenä.
WebSocketError nostetaan, kun asiakas katkaisee yhteyden (puhdas sulkeminen, verkkokatkos tai protokollavirhe). Käsittelijä poistuu silmukasta ja palaa; microdot siivoaa soketin.
10.8.2. Kojelauta lähettää komentoja¶
Kaksi painiketta lisätään tiedostoon index.html liukusäätimen viereen:
<button id="snap-btn">Save snapshot</button>
<button id="reset-btn">Reset counter</button>
ja app.js avaa WebSocketin kerran ja kytkee molemmat painikkeet funktioon send:
const proto = location.protocol === 'https:' ? 'wss://' : 'ws://';
const ws = new WebSocket(proto + location.host + '/control');
document.getElementById('snap-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'snapshot_now'})));
document.getElementById('reset-btn').addEventListener('click', () =>
ws.send(JSON.stringify({cmd: 'reset'})));
ws.addEventListener('message', (e) => {
console.log('cam:', JSON.parse(e.data));
});
Valinta ws:// vastaan wss:// heijastaa valintaa http:// vastaan https:// – WebSocket perii saman TLS-käsittelyn. Kun HTTPS on käytössä, kojelauta yhdistää automaattisesti wss://-protokollalla.
10.8.3. Milloin valita SSE ja milloin WebSocketit¶
Käytä SSE:tä, kun kamera työntää ja selain vain kuuntelee – ilmoitukset, telemetria, tilamuutokset. Siirtotie on tavallista HTTP:tä, asiakaspuoli on yksi rivi (new EventSource), ja uudelleenyhdistäminen on automaattista.
Käytä WebSocketeja, kun selaimen täytyy myös työntää – painikkeet, liukusäätimet jotka lähettävät näppäilytaajuisia päivityksiä, mikä tahansa missä seuraavaa pyyntöä odottaminen olisi liian hidasta. Yhteys on kaksisuuntainen ja kehystetty, mutta rajapinta on monimutkaisempi molemmilla puolilla.
Kamerasta on nyt tullut interaktiivinen laite – katsele, työnnä tapahtumia ulos, ota komentoja vastaan.