Anatomie d'un module
Un module OST est une bibliothèque partagée (.so), chargée dynamiquement par le serveur, qui expose des données structurées (des propriétés, contenant des éléments) et réagit aux écritures envoyées par les clients.
Deux classes de base
Basemodule— le strict nécessaire : propriétés, éléments, événements, persistance en profil. Aucune dépendance à INDI. C’est la classe à utiliser pour tout module qui ne pilote pas directement un device INDI (logique métier, agrégation, traitement…).IndiModule(dérive deBasemodule) — ajoute toute la mécanique de connexion à un serveur INDI, de gestion de devices, et les propriétés dérivées communément utiles (optique, équipements, localisation…). Voir le chapitre Modules INDI.
Ce chapitre ne couvre que Basemodule — tout ce qu’il décrit s’applique aussi à IndiModule, qui en hérite.
Le triplet de fichiers
Un module minimal tient en trois fichiers, dans son propre sous-dossier :
<nom>.h— déclare la classe (dérivée deBasemodule), son constructeur/destructeur, et les méthodes qu’elle surcharge (le plus souventonExternalEvent()).<nom>.cpp— implémente le constructeur (qui charge la structure du module) et les méthodes surchargées. Rien d’autre n’est obligatoire : un module qui n’a aucune action à traiter n’a besoin de surcharger aucune méthode.<nom>.json— déclare la structure du module : ses propriétés et leurs éléments. Embarqué dans le binaire via un fichier<nom>.qrc(ressource Qt) et chargé au démarrage.
// <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()est la fonction d’export, en liaison C (extern "C") pour être retrouvable par son nom brut une fois le.sochargé dynamiquement. Sa signature est toujours la même ; elle se contente d’instancier la classe.loadOstPropertiesFromFile(":nom.json")lit le fichier JSON embarqué et construit les propriétés/éléments qu’il décrit — voir Le plus petit module.
Un module renseigne en général sa propre identité (hash/date/message du commit, version) via setMetadata() dans son constructeur, à partir des constantes générées par le build (Version::GIT_SHA1, etc.) :
setMetadata("description", "Ce que fait ce module");
setMetadata("thisGithash", QString::fromStdString(Version::GIT_SHA1));setMetadata() est une métadonnée libre, sans déclaration JSON préalable.
La chaîne d’événements
Quand un client écrit une valeur (ou déclenche une action), le serveur appelle, dans cet ordre, trois méthodes virtuelles — jamais directement, toujours via un point d’entrée final que le développeur de module ne touche ni ne surcharge jamais :
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() applique déjà seule la valeur écrite au datastore si l’élément (ou sa propriété) a autoupdate:true — dans ce cas, onExternalEvent() n’a souvent rien à faire du tout, ou juste à relire la valeur appliquée pour réagir. Voir Réagir à une écriture pour le détail et un piège fréquent.
Un module qui n’a aucune action propre à gérer (comme l’exemple hello ci-dessus) n’a tout simplement rien à surcharger : l’implémentation vide par défaut de onExternalEvent() suffit.