Modules INDI

C’est ici que tout se joue

class IndiModule : public Basemodule, public INDI::BaseClient

Une seule ligne, et pourtant tout le framework est là : IndiModule est le point de rencontre entre deux mondes.

  • Basemodule parle à l’utilisateur.
  • INDI::BaseClient parle au matériel.
  • IndiModule n’hérite pas un peu de chaque : il hérite réellement des deux à la fois, et c’est précisément cette position, pile entre les deux, qui en fait l’endroit où écrire l’intelligence réelle d’un device — un focuser, un sequencer, un polar alignment ne sont rien d’autre que ça : la traduction d’une demande utilisateur en ordre matériel, et d’une mesure matérielle en information utilisateur.

Et ce n’est pas une façon de parler : un IndiModule est un client INDI à part entière, exactement au même titre qu’Ekos ou n’importe quel autre client. Toutes les méthodes d’INDI::BaseClient sont là, utilisables directement (connectServer(), watchDevice(), setBLOBMode()…) — un module n’est jamais enfermé dans les seuls helpers giveMeA*()/defineMeAsXxx() vus plus bas, ils ne sont qu’un raccourci pour les cas les plus courants.

Le fonctionnement du client INDI en lui-même — protocole, cycle de vie device/propriété, BLOBs — c’est à la documentation de développement INDI de le couvrir, pas à celle-ci : ici, on ne parle que de ce qu’IndiModule ajoute par-dessus.

Deux couches de callbacks

IndiModule intercepte lui-même les callbacks bruts d’INDI::BaseClient (newDevice, newProperty, removeProperty, updateProperty) pour faire sa propre comptabilité interne (rafraîchir la liste de valeurs des devices, la connexion automatique au démarrage…), puis appelle une version publique, virtuelle, pensée pour être surchargée par le module concret :

newDevice()       (privé, IndiModule)  →  onNewDevice()       (virtuel, à surcharger)
newProperty()     (privé, IndiModule)  →  onNewProperty()     (virtuel, à surcharger)
removeProperty()  (privé, IndiModule)  →  onRemoveProperty()  (virtuel, à surcharger)

updateProperty() est une exception : il est protégé, pas privé, précisément pour qu’un module qui a besoin d’un accès plus direct (capture d’image, voir plus bas) puisse le surcharger lui-même — à condition de rappeler IndiModule::updateProperty(property) en premier, sous peine de perdre le rafraîchissement automatique de devices.* à la connexion.

onUpdateProperty() suffit dans l’immense majorité des cas

Surcharger onUpdateProperty() (public, virtuel) plutôt que updateProperty() (protégé) directement — c’est ce que font helloindi et helloindiwrite ci-dessous. Seul un module qui a besoin de distinguer un type de propriété très tôt dans la chaîne, comme une capture d’image (BLOB), a une raison de surcharger updateProperty() lui-même.

Le device-picker : giveMeADevice()

giveMeADevice("device", "Device", INDI::BaseDevice::GENERAL_INTERFACE);

Ajoute un élément de sélection dans la propriété devices (créée à la volée), dont la liste de valeurs ne propose que les devices réellement présents sur le bus INDI et exposant l’interface demandée (GENERAL_INTERFACE, TELESCOPE_INTERFACE, CCD_INTERFACE, FOCUSER_INTERFACE…). Le nom passé en premier argument ("device" ici) est la clé de l’élément dans devices — getString("devices", "device") retrouve le device choisi par l’utilisateur.

Quelques vecteurs INDI standards fréquents

Le protocole INDI lui-même reste hors périmètre (voir plus haut), mais quelques vecteurs reviennent dans la quasi-totalité des modules pilotant une caméra ou un focuser — autant ne pas les redécouvrir à chaque fois :

BesoinVecteurÉlémentComment
Déplacer un focuser en position absolueABS_FOCUS_POSITIONFOCUS_ABSOLUTE_POSITIONsendModNewNumber() pour s’y déplacer, onUpdateProperty() ou getModNumber() pour suivre la position courante
Recadrer une caméraCCD_FRAMEX/Y/WIDTH/HEIGHTframeSet(device, x, y, w, h) — helper IndiModule, pas besoin du vecteur brut
Réinitialiser le cadre d’une caméraCCD_FRAME_RESET— (switch)frameReset(device) — helper IndiModule, idem
Annuler une exposition en coursCCD_ABORT_EXPOSUREABORTsendModNewSwitch(device, "CCD_ABORT_EXPOSURE", "ABORT", ISS_ON)
abortfocus (defineMeAsFocuser) n’envoie rien au device par défaut

L’action abortfocus posée par defineMeAsFocuser() (Les familles prêtes à l’emploi) arrête la séquence côté OST — le module Focus du core ne commande jamais FOCUS_ABORT_MOTION au device, il se contente de ne plus envoyer de nouvelle position. À vérifier si le driver utilisé a besoin d’un abort explicite en cours de mouvement.

Les cinq exemples

Chaque exemple ajoute une seule notion à la précédente :