Anatomie eines Moduls
Ein OST-Modul ist eine shared library (.so), die dynamisch vom Server geladen wird und strukturierte Daten (Properties, die Elemente enthalten) bereitstellt und auf Schreibzugriffe von Clients reagiert.
Zwei Basisklassen
Basemodule— das absolut Notwendige: Properties, Elemente, Ereignisse, Profil-Persistenz. Keine Abhängigkeit von INDI. Dies ist die Klasse, die für jedes Modul zu verwenden ist, das kein INDI-Device direkt steuert (Geschäftslogik, Aggregation, Verarbeitung…).IndiModule(leitet vonBasemoduleab) — fügt die gesamte Mechanik für die Verbindung zu einem INDI-Server, die Device-Verwaltung sowie die üblichen, abgeleiteten Properties hinzu (Optik, Ausrüstung, Standort…). Siehe das Kapitel INDI-Module.
Dieses Kapitel behandelt nur Basemodule — alles, was hier beschrieben wird, gilt auch für IndiModule, das davon erbt.
Das Datei-Tripel
Ein minimales Modul besteht aus drei Dateien in seinem eigenen Unterordner:
<nom>.h— deklariert die Klasse (abgeleitet vonBasemodule), ihren Konstruktor/Destruktor und die Methoden, die sie überschreibt (meistonExternalEvent()).<nom>.cpp— implementiert den Konstruktor (der die Struktur des Moduls lädt) und die überschriebenen Methoden. Sonst ist nichts erforderlich: Ein Modul, das keine Aktion zu verarbeiten hat, muss keine einzige Methode überschreiben.<nom>.json— deklariert die Struktur des Moduls: seine Properties und deren Elemente. Wird über eine Datei<nom>.qrc(Qt-Ressource) ins Binary eingebettet und beim Start geladen.
// <nom>.h
#ifndef HELLO_H
#define HELLO_H
#include <basemodule.h>
class Hello : public Basemodule
{
Q_OBJECT
public:
Hello(QString name, QString label, QString profile, QVariantMap availableModuleLibs);
~Hello();
};
extern "C" Hello* initialize(QString name, QString label, QString profile, QVariantMap availableModuleLibs);
#endif// <nom>.cpp
#include "hello.h"
Hello* initialize(QString name, QString label, QString profile, QVariantMap availableModuleLibs)
{
return new Hello(name, label, profile, availableModuleLibs);
}
Hello::Hello(QString name, QString label, QString profile, QVariantMap availableModuleLibs)
: Basemodule(name, label, profile, availableModuleLibs)
{
Q_INIT_RESOURCE(hello);
loadOstPropertiesFromFile(":hello.json");
}
Hello::~Hello() {}initialize()ist die Export-Funktion, mit C-Bindung (extern "C"), damit sie nach dem dynamischen Laden der.sounter ihrem rohen Namen auffindbar ist. Ihre Signatur ist immer dieselbe; sie instanziiert lediglich die Klasse.loadOstPropertiesFromFile(":nom.json")liest die eingebettete JSON-Datei und baut die darin beschriebenen Properties/Elemente auf — siehe Das kleinste Modul.
Ein Modul gibt in der Regel seine eigene Identität (Commit-Hash/Datum/Message, Version) über setMetadata() in seinem Konstruktor an, ausgehend von den vom Build generierten Konstanten (Version::GIT_SHA1 usw.):
setMetadata("description", "Ce que fait ce module");
setMetadata("thisGithash", QString::fromStdString(Version::GIT_SHA1));setMetadata() ist ein freies Metadatenfeld, ohne vorherige JSON-Deklaration.
Die Ereigniskette
Wenn ein Client einen Wert schreibt (oder eine Aktion auslöst), ruft der Server in dieser Reihenfolge drei virtuelle Methoden auf — nie direkt, immer über einen finalen Einstiegspunkt, den der Modulentwickler niemals berührt oder überschreibt:
onExternalEventRoot() (final — jamais surchargé)
→ onExternalEventBase() (validation commune : propriété/élément existe,
permission, directedit, mise à jour automatique)
→ onExternalEventIndi() (hook IndiModule — vide pour un Basemodule)
→ onExternalEvent() (le module répond ici, s'il a besoin de réagir)onExternalEventBase() wendet den geschriebenen Wert bereits von sich aus auf den Datastore an, wenn das Element (oder seine Property) autoupdate:true hat — in diesem Fall hat onExternalEvent() oft überhaupt nichts zu tun, oder muss nur den angewendeten Wert erneut lesen, um zu reagieren. Siehe Auf einen Schreibzugriff reagieren für Details und eine häufige Falle.
Ein Modul, das keine eigene Aktion zu verarbeiten hat (wie das obige Beispiel hello), muss schlicht nichts überschreiben: die leere Standardimplementierung von onExternalEvent() reicht aus.