Die vorgefertigten Familien

Beispielmodul: helloindifamily — ein echtes Focuser-Modul, nutzbar als Slave von hellomaster oder vom Sequencer.

Master/Slave-Beziehung zeigte declareFamily("hello") — eine beliebige Familie, eigens für das Beispielpaar. IndiModule bietet für die üblichen Rollen eines von INDI gesteuerten Moduls eine vorgefertigte Version an, die alles auf einmal erledigt:

HelloIndiFamily::HelloIndiFamily(...)
    : IndiModule(...)
{
    giveMeADevice("focuser", "Focuser", INDI::BaseDevice::FOCUSER_INTERFACE);
    defineMeAsFocuser();
}

Ein einziger Aufruf, defineMeAsFocuser():

  • deklariert die Familie focus (derselbe Mechanismus wie declareFamily());
  • baut die Property actions auf, mit ihrer bereits gesetzten AtMostOne-Regel zwischen abortfocus/autofocus/loop — nichts in JSON zu deklarieren. autofocus ist die vollständige Sequenz (Bewegungen + Messungen bis zum Optimum); loop ist anders — siehe den Kasten unten;
  • baut auch parms und signals auf, aber leer über ihre generische Hülle hinaus — siehe unten;
  • ruft intern defineMeAsImager() auf, das zusätzlich die Fähigkeiten Optik (giveMeAnOptic()) und Bild (giveMeAnImage(), siehe Ein Bild aufnehmen) hinzufügt — ein Focuser-Modul hat also bereits image/imgparms verfügbar, ohne etwas weiter anzufordern;
  • lässt isFocuser() von da an wahr zurückgeben.

defineMeAsGuider(), defineMeAsSequencer(), defineMeAsImager(), defineMeAsNavigator() und defineMeAsPlanner() folgen demselben Prinzip für die anderen Rollen; jede hat ihr zugehöriges is...() (isGuider(), isSequencer()…).

parms und signals: eine Hülle, kein Inhalt

parms wird leer erzeugt (giveMeAParms()): ein echter Focuser muss selbst, mit addElt() (Properties dynamisch erzeugen), die ihm eigenen Elemente hinzufügen — Schrittweite, Backlash, Verzögerung zwischen Messungen… nichts wird standardmäßig bereitgestellt.

signals (und nicht state — es ist derselbe Name wie der von giveMeAState() erzeugte, gesehen in Master/Slave-Beziehung) kommt mit einem festen Satz generischer Elemente: state (Licht), statedescription, event, eventdescription, und drei “Briefkasten”-Elemente ohne vorgegebene Semantik — val_num (float), val_int, val_str — die ein Modul zweckentfremden kann, um einen punktuellen Wert bereitzustellen, ohne eine eigene Property zu erstellen.

loop: eine fortlaufende Messung, kein sich wiederholender Autofokus

autofocus und loop tun nicht dasselbe, und nichts außer dem Namen der Aktion sagt das. Im echten Focus-Modul des Core bewegt loop den Focuser selbst niemals — es ist eine wiederholte Messung der Schärfe-Metrik (HFR oder Äquivalent) ohne jegliche Bewegung, gedacht dafür, dass der Benutzer den Focuser manuell dreht und die Metrik live reagieren sieht. Beide Aktionen laufen bis abortfocus, aber autofocus steuert das Device, loop beschränkt sich darauf, es zu beobachten.

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", "");
    }
}

Das Modul muss nur noch auf die bereits deklarierten Aktionen reagieren — setStateEvent() (gesehen in Master/Slave-Beziehung) bleibt das einzige Mittel, um seinen Zustand voranzubringen.

Eine Property während einer langen Operation verriegeln

Während einer echten Autofokus-Sequenz hindert nichts den Benutzer daran, parms mittendrin zu bearbeiten — es sei denn, man entscheidet sich explizit dafür. getProperty(nom)->disable() sperrt die gesamte Property auf Client-Seite auf Lesen (ausgegraut auf der Oberfläche), ->enable() entsperrt sie:

getProperty("parms")->disable();
// ... séquence en cours ...
getProperty("parms")->enable();

Es ist die Sperre — nicht der Zustand OST::Busy —, die eine gleichzeitige Bearbeitung tatsächlich verhindert; setStateEvent() allein informiert nur, es blockiert serverseitig nichts.