Moduli INDI
È qui che tutto si gioca
class IndiModule : public Basemodule, public INDI::BaseClientUna sola riga, eppure tutto il framework è lì: IndiModule è il punto di incontro tra due mondi.
Basemoduleparla all’utente.INDI::BaseClientparla all’hardware.IndiModulenon 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.
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à | Vettore | Elemento | Come |
|---|---|---|---|
| Spostare un focuser in posizione assoluta | ABS_FOCUS_POSITION | FOCUS_ABSOLUTE_POSITION | sendModNewNumber() per spostarsi lì, onUpdateProperty() o getModNumber() per seguire la posizione corrente |
| Ritagliare una camera | CCD_FRAME | X/Y/WIDTH/HEIGHT | frameSet(device, x, y, w, h) — helper IndiModule, non serve il vettore grezzo |
| Reinizializzare il frame di una camera | CCD_FRAME_RESET | — (switch) | frameReset(device) — helper IndiModule, idem |
| Annullare un’esposizione in corso | CCD_ABORT_EXPOSURE | ABORT | sendModNewSwitch(device, "CCD_ABORT_EXPOSURE", "ABORT", ISS_ON) |
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:
- Connettersi a un device
giveMeADevice, connessione automatica, riflettere lo stato CONNECTION in una proprietà
- Riflettere una proprietà del device
Leggere e scrivere una vera proprietà INDI in entrambi i sensi
- Catturare un'immagine
giveMeAnImage, la ricezione di un BLOB, e la catena loadImageFromBlob/refreshPreview
- Agire sul device
giveMeAnActions, inviare uno switch INDI standard al device piuttosto che allo stato interno del modulo
- Le famiglie pronte all'uso
defineMeAsFocuser() e gli altri defineMeAsXxx(): la versione pronta all'uso di declareFamily()