Le famiglie pronte all'uso
Modulo di esempio: helloindifamily — un vero modulo focuser, utilizzabile come slave da hellomaster o dal sequencer.
Relazione master/slave mostrava declareFamily("hello") — una famiglia arbitraria, propria alla coppia di esempi. IndiModule propone, per i ruoli comuni di un modulo pilotato da INDI, una versione pronta all’uso che fa tutto in un colpo:
HelloIndiFamily::HelloIndiFamily(...)
: IndiModule(...)
{
giveMeADevice("focuser", "Focuser", INDI::BaseDevice::FOCUSER_INTERFACE);
defineMeAsFocuser();
}Una sola chiamata, defineMeAsFocuser():
- dichiara la famiglia
focus(stesso meccanismo dideclareFamily()); - costruisce la proprietà
actions, con la sua regolaAtMostOnegià posta traabortfocus/autofocus/loop— nulla da dichiarare in JSON.autofocusè la sequenza completa (movimenti + misure fino all’ottimo);loopè diverso — vedi il box qui sotto; - costruisce anche
parmsesignals, ma vuote al di là del loro guscio generico — vedi sotto; - chiama internamente
defineMeAsImager(), che aggiunge in più le capacità ottiche (giveMeAnOptic()) e immagine (giveMeAnImage(), vedi Catturare un’immagine) — un modulo focuser ha quindi giàimage/imgparmsdisponibili senza chiedere nulla di più; - rende
isFocuser()vero da quel momento.
defineMeAsGuider(), defineMeAsSequencer(), defineMeAsImager(), defineMeAsNavigator() e defineMeAsPlanner() seguono lo stesso principio per gli altri ruoli; ciascuno ha il proprio is...() associato (isGuider(), isSequencer()…).
parms viene creata vuota (giveMeAParms()): un focuser reale deve aggiungervi da solo, con addElt() (Creare proprietà dinamicamente), gli elementi che gli sono propri — passo, backlash, ritardo tra misure… nulla è fornito per default.
signals (e non state — è lo stesso nome creato da giveMeAState(), visto in Relazione master/slave) arriva con un insieme fisso di elementi generici: state (luce), statedescription, event, eventdescription, e tre elementi “cassetta delle lettere” senza semantica imposta — val_num (float), val_int, val_str — che un modulo può dirottare per esporre un valore puntuale senza creare una proprietà dedicata.
autofocus e loop non fanno la stessa cosa, e nulla oltre al nome dell’azione lo dice. Nel vero modulo Focus del core, loop non muove mai il focuser stesso — è una misura ripetuta della metrica di nitidezza (HFR o equivalente) senza alcun movimento, pensata perché l’utente ruoti il focuser a mano e veda la metrica reagire in diretta. Entrambe le azioni girano fino a abortfocus, ma autofocus pilota il device, loop si limita a osservarlo.
void HelloIndiFamily::onExternalEvent(OST::ExtEvent event)
{
if (event.ev != OST::ExtEvType::SV || event.prpkey != "actions") return;
// abortfocus/autofocus/loop sont déjà mutuellement exclusifs
// (AtMostOne, posé par defineMeAsFocuser()) - rien à gérer pour ça.
if (event.eltkey == "autofocus")
{
setStateEvent(OST::Busy, "focusing", "autofocus", "");
QTimer::singleShot(2000, this, [this]()
{
setStateEvent(OST::Ok, "ready", "autofocus", "");
getEltBool("actions", "autofocus")->setValue(false, true);
});
}
if (event.eltkey == "abortfocus")
{
setStateEvent(OST::Ok, "ready", "abortfocus", "");
}
}Il modulo non deve più fare altro che reagire alle azioni già dichiarate — setStateEvent() (visto in Relazione master/slave) resta l’unico modo di far avanzare il proprio stato.
Durante una sequenza di autofocus reale, nulla impedisce all’utente di modificare parms a metà strada — salvo deciderlo esplicitamente. getProperty(nome)->disable() blocca tutta la proprietà in lettura lato client (in grigio lato interfaccia), ->enable() la sblocca:
getProperty("parms")->disable();
// ... séquence en cours ...
getProperty("parms")->enable();È il blocco — non lo stato OST::Busy — che impedisce realmente una modifica concorrente; setStateEvent() da solo si limita a informare, non blocca nulla lato server.