10.13. Carregar fotogramas despoletados para a nuvem¶
Quando é detetado movimento, a câmara ilumina agora o painel de controlo. Isso é suficiente para utilização em tempo real, mas o proprietário também quer um arquivo permanente de cada fotograma despoletado, armazenado fora da câmara. Trata-se de uma chamada HTTP de saída – a câmara a atuar como cliente.
10.13.1. A câmara como cliente¶
O módulo requests é o cliente HTTP de saída da câmara. A sua superfície é uma cópia deliberada do requests do CPython – as mesmas funções do módulo com nomes de verbos HTTP, os mesmos argumentos de palavra-chave files=, json=, headers=, auth=. Se já fez chamadas HTTP a partir do CPython, já conhece a API:
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() abre uma ligação TCP, envia o pedido e devolve um Response com status_code, reason, headers, content, json(), e as restantes propriedades familiares.
files={...} constrói um corpo multipart/form-data. O valor é um tuplo (filename, file-like); requests.post() lê o objeto file-like em partes para que o JPEG completo não tenha de ser re-armazenado numa string primeiro. io.BytesIO envolve os bytes JPEG já em memória para que estes exponham uma interface de leitura como ficheiro.
headers={...} é um dicionário simples que é enviado como cabeçalhos do pedido – aqui, um token bearer na posição Authorization padrão. O fornecedor de arquivo documenta o formato de token que pretende; o exemplo é a forma mais comum.
10.13.2. Integração no detetor de movimento¶
A corrotina do detetor de movimento introduzida anteriormente já é executada em cada novo fotograma e é ativada quando change > state['threshold']. Adicione o carregamento aí, mas ative-o como uma tarefa em background para que o detetor não pare de vigiar enquanto o carregamento está em curso:
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() agenda a corrotina de carregamento e retorna imediatamente. O detetor continua a capturar fotogramas; o carregamento é executado em paralelo; a câmara nunca para.
10.13.3. Modos de falha¶
O código de rede falha. A câmara pode estar offline, o arquivo pode estar indisponível, o token bearer pode ter expirado. As categorias que vale a pena tratar:
OSError– a ligação TCP não pôde ser aberta ou foi fechada a meio da transferência. Falha de DNS, sem rota, ligação reiniciada.requestslança exatamente esta exceção.status_code >= 400– o servidor recebeu o pedido e rejeitou-o. 401 para um token expirado, 403 para um token revogado, 413 para um corpo demasiado grande, 5xx para o arquivo em mau estado.Timeout silencioso –
requestsutiliza um timeout de socket predefinido (alguns segundos); ultrapassado esse limite, lançaOSErrorcomerrno.ETIMEDOUT.
Para um arquivo verdadeiramente importante, colocaria os fotogramas rejeitados em fila em /sdcard/pending/ e tentaria novamente num ciclo mais lento – são mais algumas linhas por caso, para além do que é mostrado.
10.13.4. O que o requests não faz¶
A versão MicroPython é deliberadamente pequena. Algumas coisas que o requests do CPython faz e esta versão não faz:
Pooling de ligações. Cada chamada abre uma nova ligação TCP.
Tentativas automáticas em erros transitórios. Envolva a chamada você mesmo.
Respostas em streaming.
r.contenté lido para a RAM na íntegra; não há equivalente astream=True.Descompressão automática de respostas gzip. Defina o cabeçalho
Accept-Encodingexplicitamente apenas se o servidor estiver configurado para isso.
Consulte requests — cliente HTTP para a lista completa de métodos e o que está dentro e fora do âmbito.
HTTPS funciona de imediato – o esquema do URL controla isso, e o contexto SSL predefinido é criado dinamicamente. Para verificar o certificado do arquivo em relação a um pacote de AC carregado na câmara, consulte a secção como cliente de Verificar um servidor público (câmara como cliente).
A aplicação está completamente pronta: pré-visualização em tempo real, deteção de movimento, painel de controlo com autenticação, HTTPS, CORS/CSRF, arquivo na nuvem.