Anatomia di un modulo
Un modulo OST è una libreria condivisa (.so), caricata dinamicamente dal server, che espone dati strutturati (delle proprietà, contenenti degli elementi) e reagisce alle scritture inviate dai client.
Due classi di base
Basemodule— lo stretto necessario: proprietà, elementi, eventi, persistenza in profilo. Nessuna dipendenza da INDI. È la classe da usare per qualsiasi modulo che non pilota direttamente un device INDI (logica di business, aggregazione, elaborazione…).IndiModule(deriva daBasemodule) — aggiunge tutta la meccanica di connessione a un server INDI, di gestione dei device, e le proprietà derivate comunemente utili (ottica, equipaggiamenti, localizzazione…). Vedi il capitolo Moduli INDI.
Questo capitolo copre solo Basemodule — tutto ciò che descrive si applica anche a IndiModule, che ne eredita.
La tripletta di file
Un modulo minimale sta in tre file, nella sua propria sottocartella:
<nome>.h— dichiara la classe (derivata daBasemodule), il suo costruttore/distruttore, e i metodi che sovrascrive (il più spessoonExternalEvent()).<nome>.cpp— implementa il costruttore (che carica la struttura del modulo) e i metodi sovrascritti. Nulla di più è obbligatorio: un modulo che non ha alcuna azione da gestire non ha bisogno di sovrascrivere alcun metodo.<nome>.json— dichiara la struttura del modulo: le sue proprietà e i loro elementi. Incorporato nel binario tramite un file<nome>.qrc(risorsa Qt) e caricato all’avvio.
// <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()è la funzione di export, con linkage C (extern "C") per essere rintracciabile dal suo nome grezzo una volta che il.soè caricato dinamicamente. La sua firma è sempre la stessa; si limita a istanziare la classe.loadOstPropertiesFromFile(":nom.json")legge il file JSON incorporato e costruisce le proprietà/elementi che descrive — vedi Il modulo più piccolo.
Un modulo in genere dichiara la propria identità (hash/data/messaggio del commit, versione) tramite setMetadata() nel suo costruttore, a partire dalle costanti generate dalla build (Version::GIT_SHA1, ecc.):
setMetadata("description", "Ce que fait ce module");
setMetadata("thisGithash", QString::fromStdString(Version::GIT_SHA1));setMetadata() è un metadato libero, senza dichiarazione JSON preliminare.
La catena di eventi
Quando un client scrive un valore (o attiva un’azione), il server chiama, in questo ordine, tre metodi virtuali — mai direttamente, sempre tramite un punto di ingresso finale che lo sviluppatore del modulo non tocca né sovrascrive mai:
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() applica già da sola il valore scritto al datastore se l’elemento (o la sua proprietà) ha autoupdate:true — in questo caso, onExternalEvent() spesso non ha nulla da fare, o solo da rileggere il valore applicato per reagire. Vedi Reagire a una scrittura per il dettaglio e un’insidia frequente.
Un modulo che non ha alcuna azione propria da gestire (come l’esempio hello sopra) semplicemente non ha nulla da sovrascrivere: l’implementazione vuota predefinita di onExternalEvent() è sufficiente.