Modules INDI
C’est ici que tout se joue
class IndiModule : public Basemodule, public INDI::BaseClientUne seule ligne, et pourtant tout le framework est là : IndiModule est le point de rencontre entre deux mondes.
Basemoduleparle à l’utilisateur.INDI::BaseClientparle au matériel.IndiModulen’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.
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 :
| Besoin | Vecteur | Élément | Comment |
|---|---|---|---|
| Déplacer un focuser en position absolue | ABS_FOCUS_POSITION | FOCUS_ABSOLUTE_POSITION | sendModNewNumber() pour s’y déplacer, onUpdateProperty() ou getModNumber() pour suivre la position courante |
| Recadrer une caméra | CCD_FRAME | X/Y/WIDTH/HEIGHT | frameSet(device, x, y, w, h) — helper IndiModule, pas besoin du vecteur brut |
| Réinitialiser le cadre d’une caméra | CCD_FRAME_RESET | — (switch) | frameReset(device) — helper IndiModule, idem |
| Annuler une exposition en cours | CCD_ABORT_EXPOSURE | ABORT | sendModNewSwitch(device, "CCD_ABORT_EXPOSURE", "ABORT", ISS_ON) |
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 :
- Se connecter à un device
giveMeADevice, connexion automatique, refléter l'état CONNECTION dans une propriété
- Refléter une propriété du device
Lire et écrire une vraie propriété INDI dans les deux sens
- Capturer une image
giveMeAnImage, la réception d'un BLOB, et la chaîne loadImageFromBlob/refreshPreview
- Agir sur le device
giveMeAnActions, envoyer un switch INDI standard au device plutôt qu'à l'état interne du module
- Les familles prêtes à l'emploi
defineMeAsFocuser() et les autres defineMeAsXxx() : la version prête à l'emploi de declareFamily()