13.3.1.4. Özel kanallar

Kanal, kamera tarafı bir betik ile ana makine arasında adlandırılmış, çift yönlü bir bayt akışıdır. Kamera bir kanal kaydeder ve veri üreten ya da tüketen geri çağırmalar (callback) sağlar; ana makine bu kanaldan ada göre okur ve yazar. Paketin çerçeveleri taşıyan stream kanalı, betik çıktısını taşıyan stdout kanalı ve betik yüklemesini taşıyan stdin kanalı için dahili olarak kullandığı mekanizmanın aynısı kullanıcı betiklerine de açıktır; böylece ana makinenin ihtiyaç duyduğu uygulamaya özgü herhangi bir veri, ikinci bir protokol icat etmeden aynı USB bağlantısı üzerinden taşınabilir.

Bu, paketin en kullanışlı özelliğidir ve standart belgelerin en az iyi ele aldığı özelliktir; bu yüzden bu sayfa onu uçtan uca işler.

13.3.1.4.1. İki yarı

Özel bir kanal, her iki tarafta da iş birliği yapan kod gerektirir. Kamera tarafı betik protocol modülünü içe aktarır, üç metodu (size(), read(), poll()) ve isteğe bağlı bir write() metodunu tanımlayan bir sınıf belirler ve kanalı seçilen bir adla yayımlamak için protocol.register(name=..., backend=...) çağırır:

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() metodu, kanalın o anda kaç bayt kullanılabilir olduğunu döndürür. read() üreticidir: ana makine tarafından istenen bir offset ve size verildiğinde, baytları (veya protokol katmanının kodladığı bir dizeyi) döndürür. poll(), okunacak bir şey olduğunda True döndürür; protokol katmanı bunu, kanalı read_status() içinde hazır olarak işaretlemek için kullanır.

Ana makine tarafı program dört openmv.Camera metodu kullanır: kanalın var olup olmadığını denetlemek için has_channel(), ne kadar veri beklediğini sormak için channel_size(), baytları çekip almak için channel_read() ve baytları içeri itmek için channel_write(). read_status() tüm kanalları aynı anda yoklar:

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

Ana makine döngüsü read_status() metodunu yoklar; ticks kanalı hazır olduğunda, kullanılabilir olan her şeyi çekmek için size belirtmeden channel_read() çağırır. Kameranın TicksChannel.poll() metodu her denetlemede True döndürür; bu yüzden kanal her zaman “hazır” durumdadır ve ana makine her yoklamada taze bir tik değeri alır.

13.3.1.4.2. Çift yönlü bir kanal

Veri geri itmesi gereken bir ana makine için, kamera tarafı sınıf gelen baytları kabul eden bir write() metodu ekler:

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

Ana makine kanala channel_write() ile yazar ve yanıtı her zamanki read_status() / channel_read() örüntüsüyle geri okur:

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. Bu, uygulamaya ne kazandırır

Bir uygulama, çerçeve olmayan ve yazdırma olmayan verileri (telemetri sayaçları, ana makinedeki bir kullanıcı arayüzünden canlı akıtılan yapılandırma düğmeleri, ters yönde gönderilen denetim komutları, kameranın hesapladığı ve stream kanalının varsaydığı “görüntü” çerçevelemesine sığmayan bir ölçüm sonucu) mevcut USB bağlantısı üzerinden taşımak istediğinde özel kanallar doğru araçtır. Protokol katmanı çerçevelemeyi, parçalamayı, onaylamayı ve yeniden denemeyi yönetir; betiğin yalnızca dört metotlu arka ucu uygulaması gerekir ve ana makinenin yalnızca kanal adını ve veri biçimini bilmesi yeterlidir.

CLI’ın --channel NAME bayrağı, ana makine tarafı bir program yazmadan terminalden özel bir kanalı doğrulamanın hızlı bir yoludur: CLI, adlandırılmış kanalı yoklar ve her güncellemenin ilk on baytını yazdırır.

Tek bir channel_read() veya channel_write() çağrısındaki boyut sınırı, protokolün pazarlıkla belirlediği max_payload değeridir; varsayılan olarak 4096 bayt. Ana makine tarafı metotlar daha büyük yazmaları otomatik olarak doğru sayıda pakete böler; böylece uygulama keyfi büyüklükte arabellekler geçirebilir ve parçalama görünmezdir.