10.1. Ihr erster Endpunkt

Bevor die Kamera etwas Interessantes tun kann, muss der Rest des Netzwerks sie erreichen können. Das Billigste, was beweist, dass der Server läuft, ist ein HTTP-Endpunkt mit einer einzigen Route, der etwas JSON zurückgibt:

from microdot import Microdot

app = Microdot()

frame_count = 0
trigger_count = 0

@app.get('/status')
async def status(request):
    return {'frames': frame_count, 'triggers': trigger_count}

app.run(host='0.0.0.0', port=80)

Führen Sie es in der IDE aus. Öffnen Sie von einem beliebigen anderen Rechner im LAN aus http://<cam-ip>/status. Der Browser zeigt:

{"frames": 0, "triggers": 0}

Die Zähler sind Platzhalter – bisher rührt sie nichts an –, aber die Anfrage hat das Netzwerk durchquert, die Kamera hat sie geroutet, einen Handler ausgeführt und JSON zurückgesendet.

10.1.1. Was jede Zeile bewirkt

Eine microdot.Microdot-Instanz pro Skript. Die Instanz besitzt die Routing-Tabelle, die Fehler-Handler und den Lebenszyklus (Start, Bedienen, Stopp). Große Anwendungen werden in mehrere Python-Module aufgeteilt, teilen sich aber dennoch ein einziges app-Objekt.

@app.get('/status') ist der Routen-Dekorator. Wir verwenden hier nur microdot.Microdot.get(); post(), put() und delete() tauchen auf späteren Seiten auf, wenn die Kamera beginnt, Schreibvorgänge anzunehmen.

Jeder Routen-Handler ist eine asyncio-Koroutine und erhält die Anfrage als erstes Argument. Der Handler muss request nicht verwenden – dieser ignoriert es –, aber der Parameter ist immer vorhanden, sodass die Signatur einheitlich bleibt.

Das Zurückgeben eines dict ist der kürzeste Weg, JSON zu senden. Microdot serialisiert das dict automatisch zu JSON und setzt Content-Type: application/json in der Antwort. Das Zurückgeben einer Zeichenkette sendet text/plain. Das explizite Zurückgeben einer microdot.Response ist die ausführliche Form – nötig, wenn der Body binär ist oder wenn die Antwort benutzerdefinierte Header haben soll.

app.run(host='0.0.0.0', port=80) startet den Server. 0.0.0.0 bedeutet auf jeder Schnittstelle lauschen, die die Kamera hat – sowohl das kabelgebundene Ethernet als auch der WLAN-STA, falls beide aktiv sind. Port 80 ist der HTTP-Standard, sodass Browser keine Portnummer eingeben müssen.

10.1.2. Eine Anfrage, von Anfang bis Ende

Das Telefon öffnet eine TCP-Verbindung zur Kamera, sendet eine HTTP- Anfrage, die Kamera parst, routet, führt den Handler aus und schreibt dann eine Antwort zurück.

Das Telefon öffnet eine TCP-Verbindung, schreibt die Request-Zeile und die Header und wartet. Die Kamera liest die Bytes vom Socket, parst sie zu einem microdot.Request-Objekt, gleicht Pfad und Methode mit der Routing-Tabelle ab, wartet auf die Handler-Koroutine, serialisiert, was auch immer sie zurückgegeben hat, schreibt eine Statuszeile, Header und Body zurück über den Socket und schließt dann die Verbindung (HTTP/1.0-Standard) oder verwendet sie wieder (HTTP/1.1 mit Connection: keep-alive). Der gesamte Austausch dauert ungefähr so lange wie die Netzwerk-Umlaufzeit plus das, was der Handler getan hat.

10.1.3. Ein Hinweis zum Blockieren

run() ist blockierend – es kehrt erst zurück, wenn der Server stoppt. Das ist für einen Server mit einem einzigen Zweck in Ordnung. Eine Anwendung, die auch Einzelbilder erfasst oder andere Koroutinen ausführt, verwendet stattdessen start_server() innerhalb eines asyncio.run(), damit der HTTP-Server die Schleife mit allem anderen teilen kann.

Die Anwendung beantwortet eine URL.