12.6. Nimetyt kanavat

Kunkin paketin otsakkeessa oleva kanavatunnus mahdollistaa jopa 32 itsenäisen virran jakamisen samaan fyysiseen siirtotiehen. Kanavakerros muuntaa nämä numeeriset tunnukset nimetyiksi, sovellukselle näkyviksi päätepisteiksi, joihin isäntäkoodi voi viitata merkkijonolla.

Vasemmalla yksi siirtotiejohto, joka haarautuu neljäksi nimetyksi kanavaksi kameran puolella -- stdin, stdout, stream ja käyttäjän rekisteröimä tilakanava -- kukin esitettynä itsenäisenä laatikkona.

12.6.1. Neljä sisäänrakennettua kanavaa

Kamera rekisteröi käynnistyksessä neljä kanavaa, ennen kuin mikään sovelluskoodi suoritetaan:

  • stdin – skriptitavut, jotka isäntä työntää kameralle suoritettavaksi. IDE käyttää tätä kanavaa lähettääkseen muokattavaa skriptiä; isäntä-SDK:n exec() on vastaava kutsu Python-ohjelmasta.

  • stdout – tavut kameran print()-kutsuista ja käsittelemättömistä poikkeusten jäljitystiedoista. IDE:n sarjakonsoli lukee tätä kanavaa.

  • stream – reaaliaikaisen esikatselun kanava. IDE hakee siitä JPEG-kehyksiä; mikä tahansa isäntäskripti voi tehdä saman read_frame() -metodilla.

  • profile – profilointitapahtumat, läsnä vain kun kamera on käännetty profilointi käytössä. Useimmat julkaisukäännökset jättävät sen pois.

Sovelluskoodin tarvitsee harvoin koskea mihinkään sisäänrakennetuista; mielenkiintoinen työ tapahtuu kanavilla, jotka sovellus rekisteröi itse.

12.6.2. Kanavan rekisteröinti

Kameran puolen skripti rekisteröi uuden kanavan kutsumalla protocol.register() -funktiota nimellä ja Python-backend-objektilla:

import json
import protocol
import time

trigger_count = 0

class StatusChannel:
    def size(self):
        # Refresh the snapshot on every host query.
        self._buf = json.dumps({
            'uptime_s': time.ticks_ms() // 1000,
            'triggers': trigger_count,
        }).encode()
        return len(self._buf)

    def read(self, offset, size):
        return self._buf[offset:offset + size]

protocol.register(name='status', backend=StatusChannel())

Backend-objektin metodit päättävät, mitä kanava voi tehdä. Backend, jossa on vain size ja read, on vain luettava datakanava; lisää write, ja siitä tulee kaksisuuntainen; lisää poll, ja isäntä voi kysyä, onko uutta dataa valmiina, ennen kuin maksaa luvusta. Datan näytteenotto size-metodin sisällä on yksinkertaisin malli, kun hyötykuorma on tarpeeksi pieni mahtuakseen yhteen fragmenttiin – puskuri luodaan pyynnöstä, sitä ei koskaan välimuistiteta eikä siihen tule kilpailutilannetta. Suuremmat hyötykuormat – kuvakehykset, sensorijäljet – tarvitsevat lukitsevan mallin, joka pitää puskurin, kunnes isäntä on saanut usean fragmentin lukunsa valmiiksi; tämä käsitellään kehyskanavan yhteydessä.

Pieni määrä kirjanpitoa tapahtuu automaattisesti:

  • Kirjasto määrittää seuraavan vapaan kanavatunnuksen (välillä 0–31).

  • Ominaisuuslippu johdetaan läsnä olevista metodeista: CHANNEL_FLAG_READ, jos read on määritelty, CHANNEL_FLAG_WRITE, jos write on määritelty, CHANNEL_FLAG_LOCK, jos lock / unlock on määritelty.

  • CHANNEL_REGISTERED-tapahtumapaketti lähetetään jokaiselle yhdistetylle isännälle, jotta sen kanavalista päivittyy.

Paluuarvo on protocol.ProtocolChannel -kahva, jonka sovellus voi pitää tallessa. Kahvan send_event() -metodi on kameran puolen koukku, jolla kerrotaan isännälle, että ”tällä kanavalla tapahtui jotain muuttamatta luettavaa dataa” – liipaisin laukesi, painiketta painettiin, näytemäärän virstanpylväs saavutettiin.

12.6.3. Kanavien lukeminen isännältä

Isäntä-SDK toimitetaan openmv-pakettina PyPI:ssä (pip install openmv), ja se on rakennettu pyserial-kirjaston päälle siirtotietä varten. Sen openmv.camera.Camera -luokka tuo kameran nimetyt kanavat esiin korkean tason metodien kautta:

from openmv.camera import Camera

with Camera('/dev/ttyACM0', baudrate=921600) as cam:
    cam.update_channels()
    if cam.has_channel('status'):
        size = cam.channel_size('status')
        data = cam.channel_read('status', size)

Varoitus

openmv-paketti vaatii CPython 3.12:n tai uudemman. Vanhemmista tulkeista puuttuu ominaisuuksia, joista SDK riippuu; asenna 3.12+ -käännös ennen komentoa pip install openmv.

Muutama asetuksiin liittyvä huomio:

  • Sarjaporttimerkkijono – tässä /dev/ttyACM0 – on Windowsissa COM3-tyylinen, macOS:ssä /dev/cu.usbmodemXXXX ja Linuxissa /dev/ttyACM*. Varsinainen numero riippuu siitä, mihin porttiin kamera ilmaantui.

  • Siirtonopeus on protokollan taikaluku 921600, jonka kameran USB-CDC-pino tunnistaa merkitsevän ”tämä asiakas puhuu protokollaa, ei REPL:iä”. Mikä tahansa muu nopeus palautuu tavalliseksi sarjalinjaksi.

  • with Camera(...) as cam: -kontekstinhallinta avaa siirtotien, suorittaa PROTO_SYNC-toiminnon, vaihtaa ominaisuustiedot ja sulkee portin siististi poistuttaessa. Eksplisiittinen update_channels() -kutsu sisäänkirjautumisen jälkeen päivittää paikallisen kanavalistan kaikilla kanavilla, jotka sovellus rekisteröi käynnistyksen jälkeen.

channel_size() ja channel_read() ovat työjuhtametodit; channel_write() lähettää puskurin edestakaisin kameralle, jos backendissa on write-metodi; has_channel() on turvallinen tapa tarkistaa, että nimi on rekisteröity ennen sen käyttöä. Kanavan nimi haetaan kerran kanavatunnukseksi, jonka kamera määritti register-vaiheessa, ja sitä käytetään siitä eteenpäin jokaisessa paketissa.

Kukin channel_size() / channel_read() -pari maksaa kaksi edestakaista kierrosta: yksi paketti koon pyytämiseen, yksi tavujen pyytämiseen. USB-CDC:n kautta molemmat valmistuvat yhteensä noin millisekunnissa; UARTin kautta sama vaihto kestää pidempään suhteessa sarjalinjan siirtonopeuteen. Tiukassa silmukassa lukeva sovelluskoodi kutsukoon channel_size() -metodia vain silloin, kun koko voi todella muuttua – kiinteäkokoiselle datalle ensimmäisen kutsun koko voidaan välimuistittaa.

12.6.4. Kanavien riippumattomuus

Kolme asiaa kanavien vuorovaikutuksesta on hyvä tietää:

  • Riippumaton vuonohjaus. Kullakin kanavalla on oma keskeneräinen lukutilansa, oma datansa ja omat size / read / write -takaisinkutsunsa. Pitkäkestoinen luku stream-kanavalla ei estä lukuja sovelluksen config-kanavalla.

  • Peräkkäinen kanavakohtaisesti. Yhden kanavan sisällä paketit toimitetaan järjestyksessä. Luotettavuuskerros takaa tämän myös silloin, kun mukana on uudelleenlähetyksiä.

  • Jaettu siirtotie, jaettu uudelleenlähetysbudjetti. Kaikki kanavat jakavat saman fyysisen linkin, joten liikennetulva yhdellä kanavalla hidastaa muita varaamalla johdon itselleen. CHANNEL_LOCK-mekanismi antaa yhden kanavan varata johdon atomista usean paketin lukua varten; backend ottaa tämän käyttöön toteuttamalla lock / unlock -takaisinkutsut.

Kanava on pienin pinta-ala, jolla isäntäohjelma ja kameraohjelma sopivat yhteistyöstä. Nimi, suuntaisuus (luku tai kirjoitus tai molemmat), kameran puolen takaisinkutsumetodit ja niitä vastaavat metodikutsut isännän puolella muodostavat koko sopimuksen.