10.13. Laukaistujen kehysten lataaminen pilveen

Kun liike laukaisee, kamera sytyttää nyt kojelaudan. Se riittää reaaliaikaiseen käyttöön, mutta omistaja haluaa myös pysyvän arkiston jokaisesta laukaistusta kehyksestä, tallennettuna jonnekin kameran ulkopuolelle. Tämä on lähtevä HTTP-kutsu – kamera toimii asiakkaana.

10.13.1. Kamera asiakkaana

requests-moduuli on kameran lähtevä HTTP-asiakas. Sen rajapinta on tarkoituksellinen kopio CPythonin requests-moduulista – samat verbinimiset moduulifunktiot, samat files=-, json=-, headers=- ja auth=-avainsana-argumentit. Jos olet tehnyt HTTP-kutsuja CPythonista, tunnet jo rajapinnan:

import requests
import io

ARCHIVE_URL = 'https://api.backyard-cloud.com/frames'
ARCHIVE_TOKEN = load_archive_token()

async def archive_frame(jpeg, ts):
    try:
        r = requests.post(
            ARCHIVE_URL,
            files={'image': (
                'frame-{}.jpg'.format(ts),
                io.BytesIO(jpeg),
            )},
            headers={'Authorization': 'Bearer ' + ARCHIVE_TOKEN},
        )
    except OSError as e:
        print('upload failed:', e)
        return False
    if r.status_code >= 400:
        print('archive rejected:', r.status_code, r.reason)
        return False
    return True

requests.post() avaa TCP-yhteyden, lähettää pyynnön ja palauttaa Response-olion, jolla on status_code, reason, headers, content, json() ja loput tutuista ominaisuuksista.

files={...} rakentaa multipart/form-data-rungon. Arvo on (filename, file-like)-monikko; requests.post() lukee tiedostomaisen olion paloissa, jotta koko JPEG-kuvaa ei tarvitse ensin puskuroida uudelleen merkkijonoksi. io.BytesIO kietoo jo muistissa olevat JPEG-tavut niin, että ne tarjoavat tiedostona luettavan rajapinnan.

headers={...} on suora sanakirja, joka lähetetään pyynnön otsakkeina – tässä bearer-token vakiomuotoisessa Authorization-paikassa. Arkistopalvelun tarjoaja dokumentoi haluamansa token-muodon; esimerkki on yleisin muoto.

10.13.2. Kytkeminen liiketunnistimeen

Aiemmin esitelty liiketunnistimen korutiini suoritetaan jo jokaiselle uudelle kehykselle ja laukeaa, kun change > state['threshold']. Lisää lataus siihen, mutta laukaise se taustatehtävänä, jotta tunnistin ei lopeta tarkkailua latauksen ollessa käynnissä:

async def motion_detector():
    global last_motion
    prev = None
    while True:
        await new_frame.wait()
        change = compute_change(prev, latest_jpeg)
        if change > state['threshold']:
            state['trigger_count'] += 1
            ts = int(time.time())
            last_motion = {'ts': ts,
                           'count': state['trigger_count'],
                           'change': change}
            motion_event.set()
            asyncio.create_task(archive_frame(latest_jpeg, ts))
        prev = latest_jpeg
        await asyncio.sleep_ms(50)

asyncio.create_task() ajoittaa latauskorutiinin ja palaa välittömästi. Tunnistin jatkaa kehysten kaappaamista; lataus ajetaan sen rinnalla; kamera ei koskaan jumiudu.

10.13.3. Vikatilanteet

Verkkokoodi epäonnistuu. Kamera voi olla offline-tilassa, arkisto voi olla alhaalla, bearer-token voi olla vanhentunut. Kannattaa napata seuraavat luokat:

  • OSError – TCP-yhteyttä ei voitu avata tai se suljettiin kesken siirron. DNS-virhe, ei reittiä, yhteyden nollaus. requests nostaa juuri tämän poikkeuksen.

  • status_code >= 400 – palvelin vastaanotti pyynnön ja hylkäsi sen. 401 vanhentuneelle tokenille, 403 peruutetulle, 413 liian suurelle rungolle, 5xx arkiston ollessa epäterveessä tilassa.

  • Hiljainen aikakatkaisu – requests käyttää oletusarvoista sokettiaikakatkaisua (muutama sekunti); sen jälkeen se nostaa OSError-poikkeuksen arvolla errno.ETIMEDOUT.

Arkistolle, jolla on todella merkitystä, jonottaisit hylätyt kehykset hakemistoon /sdcard/pending/ ja yrittäisit uudelleen hitaammassa silmukassa – se on muutama rivi lisää tapausta kohden näytetyn päälle.

10.13.4. Mitä requests ei tee

MicroPython-portti on tarkoituksella pieni. Muutama asia, jonka CPythonin requests tekee mutta tämä ei:

  • Yhteyksien poolaus. Jokainen kutsu avaa uuden TCP-yhteyden.

  • Automaattiset uudelleenyritykset ohimenevissä virheissä. Kääri kutsu itse.

  • Vastausten suoratoisto. r.content luetaan kokonaan RAM-muistiin; stream=True-vastinetta ei ole.

  • Gzip-pakattujen vastausten automaattinen purku. Aseta Accept-Encoding-otsake nimenomaisesti vain, jos palvelin on konfiguroitu sitä varten.

Katso requests — HTTP-asiakas täydellisen metodiluettelon sekä laajuuden sisä- ja ulkopuolelle jäävien asioiden osalta.

HTTPS toimii suoraan – URL-skeema ohjaa sitä, ja oletusarvoinen SSL-konteksti luodaan lennossa. Arkiston varmenteen tarkistamiseksi kameralle lataamaasi CA-pakettia vastaan katso Julkisen palvelimen varmentaminen (kamera asiakkaana)-sivun asiakkaana-osio.

Sovellus on täysin valmis: reaaliaikainen esikatselu, liiketunnistus, kojelauta kirjautumisella, HTTPS, CORS/CSRF, pilviarkisto.