Auf das Device einwirken

Beispielmodul: helloindiactions.

In Properties dynamisch erzeugen änderte der Aktions-Button den internen Zustand des Moduls (hellodyn). Hier ändert die Aktion tatsächlich etwas auf dem Device — das Device selbst speichert es dauerhaft, nicht das Modul.

HelloIndiActions::HelloIndiActions(...)
    : IndiModule(...)
{
    giveMeADevice("device", "Device", INDI::BaseDevice::GENERAL_INTERFACE);
    giveMeAnActions();

    OST::ElementBool *saveconfig = new OST::ElementBool("saveconfig", "Save device config", "1", "");
    saveconfig->setDirectEdit(true);
    getProperty("actions")->addElt(saveconfig);
}

giveMeAnActions() baut die Property actions (zu Beginn leer); addElt() fügt ihr das Element saveconfig hinzu, in C++, wie in Properties dynamisch erzeugen — dieses Kapitel ändert nichts an der Art, eine Property aufzubauen, nur an dem, was passiert, wenn sie ausgelöst wird.

void HelloIndiActions::onExternalEvent(OST::ExtEvent event)
{
    if (event.ev != OST::ExtEvType::SV || event.prpkey != "actions" || event.eltkey != "saveconfig") return;

    sendModNewSwitch(getString("devices", "device"), "CONFIG_PROCESS", "CONFIG_SAVE", ISS_ON);
    getEltBool("actions", "saveconfig")->setValue(false, true);
}

CONFIG_PROCESS/CONFIG_SAVE ist ein INDI-Standardswitch, auf jedem Treiber vorhanden (die aktuelle Konfiguration speichern) — das macht dieses Beispiel gegen jedes beliebige verbundene Device testbar, ohne von einer Funktion abhängig zu sein, die nur einem bestimmten Simulator eigen ist.

sendModNewSwitch/Number/String sind tückisch — nicht immer das richtige Werkzeug

Diese Helfer schreiben jeweils nur ein einziges Element: sie holen die Property so, wie sie derzeit auf Client-Seite bekannt ist, ändern den Wert des betroffenen Elements und senden dann den vollständigen Vektor zurück — die anderen Elemente gehen mit ihrem zuletzt bekannten Wert mit, nicht notwendigerweise 0, nicht notwendigerweise dem, was der Treiber erwartet.

Konkretes Beispiel: einen Guiding-Impuls an eine Montierung senden. TELESCOPE_TIMED_GUIDE_NS hat zwei Elemente, TIMED_GUIDE_N und TIMED_GUIDE_S — man pulst immer nur in eine Richtung zur selben Zeit, daher erwartet der Treiber, bei jedem Senden beide Werte zu erhalten, wobei einer von ihnen explizit 0 sein muss. Schreibt man nur TIMED_GUIDE_N über sendModNewNumber(), geht TIMED_GUIDE_S mit seinem zuletzt bekannten Wert mit — potenziell ungleich null, was der Treiber einfach ignorieren oder falsch interpretieren kann.

In solchen Fällen ist es besser, direkt auf die Ebene von INDI::BaseClient herabzusteigen (siehe die Übersicht: ein IndiModule ist ein echter INDI-Client): den vollständigen INDI::PropertyNumber selbst aufbauen, jedes Element explizit festlegen und dann direkt sendNewNumber() aufrufen — statt einem Helfer zu vertrauen, der für den einfachen Fall einer Property mit nur einem relevanten Element gedacht ist.