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