Moduli INDI

È qui che tutto si gioca

class IndiModule : public Basemodule, public INDI::BaseClient

Una sola riga, eppure tutto il framework è lì: IndiModule è il punto di incontro tra due mondi.

  • Basemodule parla all’utente.
  • INDI::BaseClient parla all’hardware.
  • IndiModule non eredita un po’ di ciascuno: eredita realmente da entrambi insieme, ed è precisamente questa posizione, esattamente tra i due, che ne fa il luogo dove scrivere l’intelligenza reale di un device — un focuser, un sequencer, un polar alignment non sono altro che questo: la traduzione di una richiesta utente in un comando hardware, e di una misura hardware in informazione per l’utente.

E non è un modo di dire: un IndiModule è un client INDI a pieno titolo, esattamente come Ekos o qualsiasi altro client. Tutti i metodi di INDI::BaseClient sono lì, utilizzabili direttamente (connectServer(), watchDevice(), setBLOBMode()…) — un modulo non è mai rinchiuso nei soli helper giveMeA*()/defineMeAsXxx() visti più avanti, sono solo una scorciatoia per i casi più comuni.

Il funzionamento del client INDI in sé — protocollo, ciclo di vita device/proprietà, BLOB — è compito della documentazione di sviluppo INDI coprirlo, non di questa: qui si parla solo di ciò che IndiModule aggiunge al di sopra.

Due livelli di callback

IndiModule intercetta da solo i callback grezzi di INDI::BaseClient (newDevice, newProperty, removeProperty, updateProperty) per fare la propria contabilità interna (aggiornare la lista di valori dei device, la connessione automatica all’avvio…), poi chiama una versione pubblica, virtuale, pensata per essere sovrascritta dal modulo concreto:

newDevice()       (privé, IndiModule)  →  onNewDevice()       (virtuel, à surcharger)
newProperty()     (privé, IndiModule)  →  onNewProperty()     (virtuel, à surcharger)
removeProperty()  (privé, IndiModule)  →  onRemoveProperty()  (virtuel, à surcharger)

updateProperty() è un’eccezione: è protetto, non privato, precisamente perché un modulo che ha bisogno di un accesso più diretto (capture d’immagine, vedi più avanti) possa sovrascriverlo da solo — a condizione di richiamare IndiModule::updateProperty(property) per primo, sotto pena di perdere l’aggiornamento automatico di devices.* alla connessione.

onUpdateProperty() basta nella grande maggioranza dei casi

Sovrascrivere onUpdateProperty() (pubblico, virtuale) piuttosto che updateProperty() (protetto) direttamente — è ciò che fanno helloindi e helloindiwrite più avanti. Solo un modulo che ha bisogno di distinguere un tipo di proprietà molto presto nella catena, come una capture d’immagine (BLOB), ha una ragione per sovrascrivere updateProperty() da solo.

Il device-picker: giveMeADevice()

giveMeADevice("device", "Device", INDI::BaseDevice::GENERAL_INTERFACE);

Aggiunge un elemento di selezione nella proprietà devices (creata al volo), la cui lista di valori propone solo i device realmente presenti sul bus INDI ed esponenti l’interfaccia richiesta (GENERAL_INTERFACE, TELESCOPE_INTERFACE, CCD_INTERFACE, FOCUSER_INTERFACE…). Il nome passato come primo argomento ("device" qui) è la chiave dell’elemento in devices — getString("devices", "device") ritrova il device scelto dall’utente.

Alcuni vettori INDI standard frequenti

Il protocollo INDI in sé resta fuori perimetro (vedi sopra), ma alcuni vettori ricorrono nella quasi totalità dei moduli che pilotano una camera o un focuser — tanto vale non riscoprirli ogni volta:

NecessitàVettoreElementoCome
Spostare un focuser in posizione assolutaABS_FOCUS_POSITIONFOCUS_ABSOLUTE_POSITIONsendModNewNumber() per spostarsi lì, onUpdateProperty() o getModNumber() per seguire la posizione corrente
Ritagliare una cameraCCD_FRAMEX/Y/WIDTH/HEIGHTframeSet(device, x, y, w, h) — helper IndiModule, non serve il vettore grezzo
Reinizializzare il frame di una cameraCCD_FRAME_RESET— (switch)frameReset(device) — helper IndiModule, idem
Annullare un’esposizione in corsoCCD_ABORT_EXPOSUREABORTsendModNewSwitch(device, "CCD_ABORT_EXPOSURE", "ABORT", ISS_ON)
abortfocus (defineMeAsFocuser) non invia nulla al device per default

L’azione abortfocus posta da defineMeAsFocuser() (Le famiglie pronte all’uso) arresta la sequenza lato OST — il modulo Focus del core non comanda mai FOCUS_ABORT_MOTION al device, si limita a non inviare più una nuova posizione. Da verificare se il driver utilizzato necessita di un abort esplicito durante il movimento.

I cinque esempi

Ogni esempio aggiunge un solo concetto al precedente: