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 wiedeclareFamily()); - baut die Property
actionsauf, mit ihrer bereits gesetztenAtMostOne-Regel zwischenabortfocus/autofocus/loop— nichts in JSON zu deklarieren.autofocusist die vollständige Sequenz (Bewegungen + Messungen bis zum Optimum);loopist anders — siehe den Kasten unten; - baut auch
parmsundsignalsauf, 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 bereitsimage/imgparmsverfü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 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.
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.
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.