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.