Ein Bild aufnehmen
Beispielmodul: helloindicapture.
giveMeAnImage(): die Anzeige ohne jeglichen Code
HelloIndiCapture::HelloIndiCapture(...)
: IndiModule(...)
{
giveMeADevice("camera", "Camera", INDI::BaseDevice::CCD_INTERFACE);
giveMeAnImage();
}giveMeAnImage() baut allein schon die Properties image (nur lesbar, Element ElementImg) und imgparms (Stretch-Modus, JPEG-Qualität) — dafür ist nichts in JSON zu deklarieren. Was das Modul noch liefern muss, ist die Aufnahme selbst und das Zurückgeben des Bildes nach dessen Empfang.
Die Aufnahme anfordern
void HelloIndiCapture::onExternalEvent(OST::ExtEvent event)
{
if (event.ev != OST::ExtEvType::SV || event.prpkey != "actions" || event.eltkey != "capture") return;
requestCapture(getString("devices", "camera"),
getFloat("shot", "exposure"),
getFloat("shot", "gain"),
getFloat("shot", "offset"));
getEltBool("actions", "capture")->setValue(false, true);
}requestCapture(device, exposition, gain, offset) sendet die Anfrage an das Device; shot und actions sind zwei klassische, in JSON deklarierte Properties (siehe Das kleinste Modul), nichts INDI-Spezifisches in ihrer Deklaration. requestCapture() aktiviert bereits selbst den Empfang der BLOBs auf diesem Device (setBLOBMode(B_ALSO, ...)) — es ist nicht nötig, das vorher selbst aufzurufen.
Das BLOB empfangen
void HelloIndiCapture::updateProperty(INDI::Property property)
{
IndiModule::updateProperty(property);
if (QString(property.getDeviceName()) == getString("devices", "camera") && property.getType() == INDI_BLOB)
{
newBLOB(property);
}
}
void HelloIndiCapture::newBLOB(INDI::PropertyBlob pblob)
{
loadImageFromBlob(pblob, 64, [this]() { refreshPreview(); });
}Ein Bild aufzunehmen ist der einzige Fall in dieser Beispielreihe, der updateProperty() (protected) statt onUpdateProperty() (öffentlich) überschreibt — siehe die Übersicht. Die Verkettung IndiModule::updateProperty(property) als erste Zeile ist nicht optional: sie ist es, die die Werteliste von devices.* bei einer Verbindung aktuell hält. Sie zu vergessen bricht diese Aktualisierung stillschweigend, ohne sichtbaren Fehler.
Ein Modul, das eine Kamera und einen Focuser steuert, muss sich nicht zwischen den beiden Mechanismen entscheiden: IndiModule::updateProperty(property) ruft selbst onUpdateProperty(property) am Ende auf, für jede Property, nicht nur die des Devices, das zum Überschreiben geführt hat. updateProperty() zu überschreiben, um die BLOBs der Kamera abzufangen (wie oben), und onUpdateProperty() zu überschreiben, um die Position des Focusers zu verfolgen (wie helloindiwrite), bestehen also ohne Konflikt im selben Modul nebeneinander, solange die obige Verkettung korrekt erfolgt.
In neueren Versionen von libindi kommt ein BLOB wie jede andere Property über updateProperty() an, typisiert als INDI_BLOB — es gibt keinen eigenen Callback mehr, den man überschreiben müsste. newBLOB() oben ist eine gewöhnliche private Methode des Moduls, keine Überschreibung; das manuelle Dispatching (property.getType() == INDI_BLOB) ist derzeit der einzige Weg, es auszulösen, genau wie es dummy.cpp im Code des Core macht. Dies zu vereinfachen, damit ein Modul updateProperty() nicht mehr selbst überschreiben muss, wird im Issue ost/ostserver#271 angesprochen, aber noch nicht entschieden.
Das Bild erneut veröffentlichen
void HelloIndiCapture::refreshPreview()
{
if (!_image) return;
QImage rawImage = _image->getRawQImage();
atomicSaveJpeg(rawImage, getWebroot() + "/" + getModuleName() + ".jpeg");
OST::ImgData data = _image->ImgStats();
data.mUrlJpeg = getModuleName() + ".jpeg";
getEltImg("image", "image")->setValue(data, true);
}loadImageFromBlob() befüllt _image (den residenten Frame des Moduls, immer nur einen auf einmal) und wendet bereits den aktuellen Stretch an; refreshPreview() muss nur noch ein JPEG im Webroot speichern und seine URL in das Element image pushen. refreshPreview() wird außerdem automatisch vom Framework erneut aufgerufen, wann immer der Benutzer imgparms.stretch ändert — ein Modul, das das benötigt, schreibt diese Methode nur ein einziges Mal.
ImgStats() befüllt nur die Felder von OST::ImgData, die sich direkt aus den rohen Pixeln berechnen lassen: min/max/mean/stddev/median pro Kanal, histogram, Abmessungen. Die Felder HFRavg, HFRavgDev und starsCount existieren zwar in der Struktur, aber ImgStats() lässt sie auf null.
Jede weiterführende Bildanalyse (Sternerkennung, HFR, Plate Solving…) ist ein fachliches Thema, das jedem Modul selbst zukommt und nicht in den Rahmen dieses Frameworks fällt — es ist nicht Aufgabe dieser Dokumentation, das zu behandeln. Als Inspiration für eine reale Implementierung siehe zum Beispiel das Focus-Modul des Core (src/modules/focus/focus.cpp), das dies über seine eigene Instanz der Klasse Solver (libs/solver.cpp) handhabt.