13.3.1.4. Prilagođeni kanali

Kanal je imenovani, dvosmjerni tok bajtova između skripte na strani kamere i domaćina. Kamera registrira kanal i pruža povratne pozive koji proizvode ili troše podatke; domaćin čita iz i piše u taj kanal po nazivu. Isti mehanizam koji paket koristi interno za kanal stream koji prenosi sličice, kanal stdout koji prenosi izlaz skripte i kanal stdin koji prenosi prijenos skripte izložen je korisničkim skriptama, pa svi podaci specifični za aplikaciju koji su domaćinu potrebni mogu putovati istom USB vezom bez izmišljanja drugog protokola.

Ovo je najkorisnija značajka paketa i ona koju standardna dokumentacija najlošije pokriva, pa je ova stranica obrađuje od početka do kraja.

13.3.1.4.1. Dvije polovice

Prilagođeni kanal treba kod koji surađuje na obje strane. Skripta na strani kamere uvozi protocol, definira klasu s tri metode (size(), read(), poll()) plus opcionalnom write() i poziva protocol.register(name=..., backend=...) da objavi kanal pod odabranim nazivom:

import protocol
import time

class TicksChannel:
    def size(self):
        return 10

    def read(self, offset, size):
        return f'{time.ticks_ms():010d}'

    def poll(self):
        return True

protocol.register(name='ticks', backend=TicksChannel())

Metoda size() vraća koliko bajtova kanal trenutno ima dostupno. read() je proizvođač: na temelju offset i size koje zatraži domaćin, vraća bajtove (ili niz znakova koji sloj protokola kodira). poll() vraća True kada postoji nešto za čitanje – sloj protokola to koristi za označavanje kanala kao spremnog u read_status().

Program na strani domaćina koristi četiri metode klase openmv.Camera: has_channel() za provjeru postoji li kanal, channel_size() za upit koliko podataka čeka, channel_read() za izvlačenje bajtova i channel_write() za guranje bajtova. read_status() istovremeno ispituje svaki kanal:

from openmv import Camera

with Camera('/dev/ttyACM0') as cam:
    cam.stop()
    cam.exec(open('ticks_cam.py').read())

    while True:
        status = cam.read_status()

        if status.get('ticks'):
            data = cam.channel_read('ticks')
            print(f"ticks: {data.decode()}")

Petlja domaćina ispituje read_status(); kada je kanal ticks spreman, poziva channel_read() bez size da izvuče sve što je dostupno. TicksChannel.poll() kamere vraća True pri svakoj provjeri, pa je kanal uvijek „spreman”, a domaćin pri svakom ispitivanju dobiva svježu vrijednost otkucaja.

13.3.1.4.2. Dvosmjerni kanal

Za domaćina koji treba gurati podatke natrag, klasa na strani kamere dodaje metodu write() koja prihvaća dolazne bajtove:

import protocol

class CommandChannel:
    def __init__(self):
        self.last_command = b''
        self.replied = False

    def size(self):
        return len(self.last_command)

    def read(self, offset, size):
        self.replied = True
        return self.last_command

    def write(self, offset, data):
        self.last_command = b'echo: ' + bytes(data)
        self.replied = False

    def poll(self):
        return not self.replied and len(self.last_command) > 0

protocol.register(name='echo', backend=CommandChannel())

Domaćin piše u kanal pomoću channel_write() i čita odgovor natrag uobičajenim obrascem read_status() / channel_read()

with Camera('/dev/ttyACM0') as cam:
    cam.stop()
    cam.exec(open('echo_cam.py').read())

    cam.channel_write('echo', b'hello')

    while True:
        if cam.read_status().get('echo'):
            print(cam.channel_read('echo').decode())
            break

13.3.1.4.3. Što ovo donosi aplikaciji

Prilagođeni kanali pravi su alat kad god aplikacija želi iskoristiti postojeću USB vezu za podatke koji nisu sličice ni ispis: telemetrijski brojači, konfiguracijske postavke koje se prenose uživo s korisničkog sučelja na domaćinu, upravljačke naredbe poslane u drugom smjeru, rezultati mjerenja koje je kamera izračunala, a koji ne pristaju u „slikovni” okvir koji kanal toka pretpostavlja. Sloj protokola brine o uokvirivanju, fragmentaciji, potvrdi i ponovnom pokušaju; skripta treba samo implementirati pozadinu s četiri metode, a domaćin treba samo znati naziv kanala i oblik podataka.

Zastavica --channel NAME u CLI-ju brz je način provjere prilagođenog kanala iz terminala bez pisanja programa na strani domaćina: CLI ispituje imenovani kanal i ispisuje prvih deset bajtova svakog ažuriranja.

Ograničenje veličine za jedan poziv channel_read() ili channel_write() je dogovoreni max_payload protokola – prema zadanim postavkama 4096 bajtova. Metode na strani domaćina automatski dijele veće zapise u odgovarajući broj paketa, pa aplikacija može proslijediti proizvoljno velike međuspremnike; fragmentacija je nevidljiva.