13.4.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.4.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.4.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.4.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ä.