13.3.1.4. Mukautetut kanavat

Kanava on nimetty, kaksisuuntainen tavuvirta kameran skriptin ja isännän välillä. Kamera rekisteröi kanavan ja tarjoaa takaisinkutsut, jotka tuottavat tai kuluttavat dataa; isäntä lukee ja kirjoittaa kyseiseen kanavaan nimellä. Sama mekanismi, jota paketti käyttää sisäisesti kehyksiä kuljettavalle stream-kanavalle, skriptin tulostetta kuljettavalle stdout-kanavalle ja skriptin latausta kuljettavalle stdin-kanavalle, on käyttäjäskriptien käytettävissä, joten mikä tahansa sovelluskohtainen data, jota isäntä tarvitsee, voi kulkea samalla USB-yhteydellä ilman toisen protokollan keksimistä.

Tämä on paketin hyödyllisin ominaisuus ja se, jonka vakiodokumentaatio kattaa heikoimmin, joten tämä sivu käy sen läpi alusta loppuun.

13.3.1.4.1. Kaksi puoliskoa

Mukautettu kanava tarvitsee yhteistyössä toimivaa koodia molemmilla puolilla. Kameran skripti tuo protocol, määrittelee luokan, jossa on kolme metodia (size(), read(), poll()) sekä valinnainen write(), ja kutsuu protocol.register(name=..., backend=...) julkaistakseen kanavan valitulla nimellä:

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())

size()-metodi palauttaa, kuinka monta tavua kanavalla on tällä hetkellä saatavilla. read() on tuottaja: isännän pyytämällä offset- ja size-arvolla se palauttaa tavut (tai merkkijonon, jonka protokollakerros koodaa). poll() palauttaa True, kun luettavaa on – protokollakerros käyttää tätä merkitäkseen kanavan valmiiksi metodissa read_status().

Isäntäohjelma käyttää neljää openmv.Camera-metodia: has_channel() tarkistaakseen kanavan olemassaolon, channel_size() kysyäkseen, kuinka paljon dataa odottaa, channel_read() vetääkseen tavuja ulos ja channel_write() työntääkseen tavuja sisään. read_status() kyselee jokaista kanavaa kerralla:

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()}")

Isäntäsilmukka kyselee read_status(); kun ticks-kanava on valmis, se kutsuu channel_read() ilman size-arvoa vetääkseen kaiken saatavilla olevan. Kameran TicksChannel.poll() palauttaa True jokaisella tarkistuksella, joten kanava on aina ”valmis” ja isäntä saa tuoreen tick-arvon jokaisella kyselyllä.

13.3.1.4.2. Kaksisuuntainen kanava

Isännälle, jonka täytyy työntää dataa takaisin, kameran luokka lisää write()-metodin, joka ottaa vastaan saapuvat tavut:

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())

Isäntä kirjoittaa kanavaan kutsulla channel_write() ja lukee vastauksen takaisin tavanomaisella read_status() / channel_read() -mallilla:

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. Mitä tämä antaa sovellukselle

Mukautetut kanavat ovat oikea työkalu aina, kun sovellus haluaa hyödyntää olemassa olevaa USB-yhteyttä muulle kuin kehys- tai tulostedatalle: telemetrialaskurit, käyttöliittymästä isännällä reaaliajassa suoratoistetut konfiguraationupit, toiseen suuntaan lähetetyt ohjauskomennot, tulokset kameran laskemasta mittauksesta, joka ei sovi stream-kanavan olettamaan ”kuva”-kehystykseen. Protokollakerros hoitaa kehystyksen, fragmentoinnin, kuittauksen ja uudelleenyrityksen; skriptin tarvitsee vain toteuttaa nelimetodinen taustajärjestelmä, ja isännän tarvitsee vain tietää kanavan nimi ja datan muoto.

Komentorivityökalun --channel NAME -lippu on nopea tapa todentaa mukautettu kanava terminaalista ilman isäntäohjelman kirjoittamista: komentorivityökalu kyselee nimettyä kanavaa ja tulostaa jokaisen päivityksen ensimmäiset kymmenen tavua.

Yksittäisen channel_read()- tai channel_write()-kutsun kokoraja on protokollan neuvottelema max_payload – oletuksena 4096 tavua. Isäntäpuolen metodit jakavat automaattisesti suuremmat kirjoitukset oikeaan määrään paketteja, joten sovellus voi välittää mielivaltaisen suuria puskureita; fragmentointi on näkymätöntä.