13.1.10. Ferestre de terminal independente¶
Tools → Open Terminal deschide ferestre de terminal independente – fiecare fiind o sesiune OpenMV IDE în miniatură în propria fereastră, cu un vizualizator al tamponului de cadre (frame buffer), o histogramă și un terminal interactiv, conectate printr-un transport ales de tine. Conexiunea ferestrei principale nu este afectată, astfel încât un terminal independent îți permite să urmărești o a doua cameră în timp ce prima rămâne conectată sau să depanezi o cameră aflată la celălalt capăt al unei rețele.
O fereastră de terminal independentă printr-un port serial: terminalul interactiv în stânga cu bara sa de instrumente run / stop / reset, tamponul de cadre (frame buffer) și histograma în dreapta – acestea se activează atunci când camera transmite cadre in-band.¶
New Terminal solicită unul dintre cele trei transporturi:
Serial port – orice port serial la orice rată baud (implicit 115.200). Acesta acoperă portul USB al unei a doua camere, o cameră conectată printr-o punte USB-to-UART, o legătură serială Bluetooth (care apare ca un port serial obișnuit) sau orice dispozitiv serial non-OpenMV care necesită un terminal. La un port USB rata baud nu limitează viteza – datele se deplasează întotdeauna la viteza legăturii USB – dar la portul USB al unei camere evită valorile 921.600 și 12.000.000, care comută camera de la REPL în protocolul de depanare al IDE-ului.
TCP – conectează-te la un server la o gazdă și un port aleși sau ascultă ca server pe un port ales.
UDP – aceeași pereche de roluri, prin UDP.
IDE-ul reține ultimele zece configurații și le listează în submeniul Open Terminal pentru redeschidere cu un singur clic; Clear Menu le uită.
Spre deosebire de panoul exclusiv de ieșire al ferestrei principale, un terminal independent este pe deplin interactiv: este un REPL. Tastează la prompt și Python execută pe camera conectată linie cu linie, cu istoric și completare cu tab oferite chiar de MicroPython. Bara de instrumente adaugă echivalente cu un singur clic ale secvențelor uzuale de control – rularea scriptului curent din editor, oprirea scriptului în execuție și resetarea soft – și aceleași comenzi de ștergere, salvare și încadrare ca panoul principal al terminalului.
Tamponul de cadre (frame buffer) de deasupra terminalului este și el activ. Atunci când camera conectată transmite cadre comprimate in-band – încorporate în același flux ca ieșirea sa print – terminalul le decodează și le afișează, iar butoanele Record și Zoom funcționează exact ca în fereastra principală. Această combinație reprezintă povestea de depanare prin rețea a IDE-ului: o cameră care își expune REPL-ul prin Wi-Fi obține întreaga buclă editare-rulare-previzualizare fără niciun cablu USB implicat.
13.1.10.1. Transmiterea cadrelor in-band¶
Codificarea pe care o înțelege terminalul este simplă: un octet 0xFE deschide un cadru, un al doilea 0xFE îl închide, iar fiecare octet de date dintre ei are bitul cel mai semnificativ setat și transportă șase biți din imaginea comprimată – trei octeți de imagine devin patru octeți de date. Textul simplu nu folosește niciodată acele valori de octeți, astfel încât cadrele și ieșirea print() partajează fluxul fără a se ciocni: terminalul afișează textul și prezintă cadrele.
Scriptul de mai jos captează, convertește fiecare cadru în JPEG și îl tipărește în această formă. Împachetarea pe biți rulează prin ulab (care nu are operatori de deplasare, așa că deplasările sunt scrise ca înmulțiri și împărțiri) și este suficient de rapidă pentru a ține pasul cu camera:
import csi
import sys
import time
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
clock = time.clock()
def encode_for_ide(data):
n = len(data)
n3 = n - (n % 3)
m = (n3 // 3) * 4
out = bytearray(((n * 8) + 5) // 6 + 2)
out[0] = 0xFE
out[-1] = 0xFE
if n3:
src = np.frombuffer(data, dtype=np.uint8)
dst = np.frombuffer(out, dtype=np.uint8)
b0 = src[0:n3:3]
b1 = src[1:n3:3]
b2 = src[2:n3:3]
dst[1:m + 1:4] = (b0 & 0x3F) | 0x80
dst[2:m + 2:4] = (b0 // 64) | ((b1 & 0x0F) * 4) | 0x80
dst[3:m + 3:4] = (b1 // 16) | ((b2 & 0x03) * 16) | 0x80
dst[4:m + 4:4] = (b2 // 4) | 0x80
if n % 3 == 2:
x = data[n - 2] | (data[n - 1] << 8)
out[m + 1] = 0x80 | (x & 0x3F)
out[m + 2] = 0x80 | ((x >> 6) & 0x3F)
out[m + 3] = 0x80 | ((x >> 12) & 0x3F)
elif n % 3 == 1:
out[m + 1] = 0x80 | (data[n - 1] & 0x3F)
out[m + 2] = 0x80 | (data[n - 1] >> 6)
return out
while True:
clock.tick()
img = csi0.snapshot().to_jpeg(quality=80)
sys.stdout.write(encode_for_ide(img.bytearray()))
print(clock.fps())
Acest lucru funcționează într-un terminal independent deoarece tipărirea în REPL așteaptă: camera se blochează până când terminalul a preluat datele, astfel încât fiecare octet al cadrului ajunge, iar un JPEG se deplasează rapid printr-o legătură USB full-speed sau high-speed. Același script funcționează neschimbat printr-un terminal TCP, care este calea de depanare prin rețea de mai sus. Singurul loc în care nu funcționează este conexiunea principală de depanare a IDE-ului, al cărei canal de ieșire se resetează la depășire în loc să aștepte – acolo vizualizatorul tamponului de cadre (frame buffer) afișează deja nativ cadrele camerei, deci nu se pierde nimic.