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 di declareFamily());
  • costruisce la proprietà actions, con la sua regola AtMostOne già posta tra abortfocus/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 parms e signals, 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/imgparms disponibili 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 e signals: un guscio, non un contenuto

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.

loop: una misura continua, non un autofocus che si ripete

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.

Bloccare una proprietà durante un’operazione lunga

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.