File indexing completed on 2024-04-21 14:47:25

0001 /*
0002     Helper class of KStars UI tests
0003 
0004     SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "config-kstars.h"
0010 #include "test_ekos.h"
0011 #include "test_ekos_debug.h"
0012 #include "test_ekos_simulator.h"
0013 #include "ekos/guide/guide.h"
0014 #include "ekos/manager/meridianflipstate.h"
0015 
0016 #include "indi/indidevice.h"
0017 #include "indi/indigroup.h"
0018 #include "indi/indidome.h"
0019 #include "indi/indiproperty.h"
0020 #include "indi/indielement.h"
0021 #include "indi/guimanager.h"
0022 #include "oal/oal.h"
0023 #include "ekos/scheduler/scheduler.h"
0024 
0025 #include <QObject>
0026 
0027 #pragma once
0028 
0029 /**
0030   * @brief Helper function to verify the execution of a statement.
0031   *
0032   * If the result is false, this function immediately returns with false,
0033   * otherwise simply continues.
0034   * Use this function in subroutines of test cases which should return bool.
0035   * @param statement expression to be verified
0036   * @return false if statement equals false, otherwise continuing
0037   */
0038 #define KVERIFY_SUB(statement) \
0039 do {\
0040     if (!QTest::qVerify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__))\
0041         return false;\
0042 } while (false)
0043 
0044 /**
0045   * @brief Subroutine version of QVERIFY2
0046   * @return false if statement equals false, otherwise continuing
0047   */
0048 #define KVERIFY2_SUB(statement, description) \
0049 do {\
0050     if (statement) {\
0051         if (!QTest::qVerify(true, #statement, (description), __FILE__, __LINE__))\
0052             return false;\
0053     } else {\
0054         if (!QTest::qVerify(false, #statement, (description), __FILE__, __LINE__))\
0055             return false;\
0056     }\
0057 } while (false)
0058 
0059 /**
0060   * @brief Helper macro to wrap statements with test macros that returns false if
0061   * the given statement preliminary invokes return (due to a test failure inside).
0062   * @return false if statement equals false, otherwise continuing
0063   */
0064 #define KWRAP_SUB(statement) \
0065 { bool passed = false; \
0066     [&]() { statement; passed = true;}(); \
0067     if (!passed) return false; \
0068 } while (false);
0069 
0070 /**
0071   * @brief Subroutine version of QTRY_TIMEOUT_DEBUG_IMPL
0072   * @return false if expression equals false, otherwise continuing
0073   */
0074 #define KTRY_TIMEOUT_DEBUG_IMPL_SUB(expr, timeoutValue, step)\
0075     if (!(expr)) { \
0076         QTRY_LOOP_IMPL((expr), (2 * timeoutValue), step);\
0077         if (expr) { \
0078             QString msg = QString::fromUtf8("QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) was too short, %3 ms would have been sufficient this time."); \
0079             msg = msg.arg(QString::fromUtf8(#expr)).arg(timeoutValue).arg(timeoutValue + qt_test_i); \
0080             KVERIFY2_SUB(false, qPrintable(msg)); \
0081         } \
0082     }
0083 
0084 /**
0085   * @brief Subroutine version of QTRY_IMPL
0086   * @return false if expression equals false, otherwise continuing
0087   */
0088 #define KTRY_IMPL_SUB(expr, timeout)\
0089     const int qt_test_step = 50; \
0090     const int qt_test_timeoutValue = timeout; \
0091     QTRY_LOOP_IMPL((expr), qt_test_timeoutValue, qt_test_step); \
0092     KTRY_TIMEOUT_DEBUG_IMPL_SUB((expr), qt_test_timeoutValue, qt_test_step)\
0093 
0094 /**
0095   * @brief Subroutine version of QTRY_VERIFY_WITH_TIMEOUT
0096   * @param expr expression to be verified
0097   * @param timeout max time until the expression must become true
0098   * @return false if statement equals false, otherwise continuing
0099   */
0100 #define KTRY_VERIFY_WITH_TIMEOUT_SUB(expr, timeout) \
0101     do { \
0102         KTRY_IMPL_SUB((expr), timeout);\
0103         KVERIFY_SUB(expr); \
0104     } while (false)
0105 
0106 /** @brief Helper to retrieve a gadget from a certain module view.
0107  * @param module KStars module that holds the checkox
0108  * @param klass is the class of the gadget to look for.
0109  * @param name is the gadget name to look for in the UI configuration.
0110  * @warning Fails the test if the gadget "name" of class "klass" does not exist in the Mount module
0111  */
0112 #define KTRY_GADGET(module, klass, name) klass * const name = module->findChild<klass*>(#name); \
0113     QTRY_VERIFY2_WITH_TIMEOUT(name != nullptr, QString(#klass " '%1' does not exist and cannot be used").arg(#name).toStdString().c_str(), 10000)
0114 
0115 /** @brief Helper to retrieve a gadget from a certain module view (subroutine version).
0116  * @param module KStars module that holds the checkox
0117  * @param klass is the class of the gadget to look for.
0118  * @param name is the gadget name to look for in the UI configuration.
0119  * @warning Fails the test if the gadget "name" of class "klass" does not exist in the Mount module
0120  */
0121 #define KTRY_GADGET_SUB(module, klass, name) klass * const name = module->findChild<klass*>(#name); \
0122     KVERIFY2_SUB(name != nullptr, QString(#klass " '%1' does not exist and cannot be used").arg(#name).toStdString().c_str())
0123 
0124 /** @brief Retrieve an INDI element from the INDI controller
0125  *  @param device INDI device name
0126  *  @param group INDI group within the device
0127  *  @param name INDI property name within the group
0128  *  @param property resulting INDI property (@see #INDI_P)
0129  */
0130 #define KTRY_INDI_PROPERTY(device, group, name, property) INDI_P * property = nullptr; \
0131     do { \
0132     INDI_D *indi_device = GUIManager::Instance()->findGUIDevice(device); \
0133     QVERIFY(indi_device != nullptr); \
0134     INDI_G *indi_group = indi_device->getGroup(group); \
0135     QVERIFY(indi_group != nullptr); \
0136     property = indi_group->getProperty(name); \
0137     } while (false); \
0138     QVERIFY(property != nullptr);
0139 
0140 /** @brief Helper to find a top level window with a given title
0141  *  @param result resulting object name
0142  *  @param name window name
0143  */
0144 #define KTRY_FIND_WINDOW(result, name) \
0145     QWindow * result = nullptr; \
0146     for (QWindow *window : qApp->topLevelWindows()) { \
0147     if (window->title() == name) { \
0148         result = window; break; } \
0149     } \
0150     QVERIFY(result != nullptr); QTRY_VERIFY_WITH_TIMEOUT(result->isVisible(), 1000); \
0151 
0152 
0153 /** @brief Helper to click a button from a certain module view.
0154  * @param module KStars module that holds the checkox
0155  * @param button is the gadget name of the button to look for in the UI configuration.
0156  * @warning Fails the test if the button is not currently enabled.
0157  */
0158 #define KTRY_CLICK(module, button) do { \
0159     QTimer::singleShot(100, Ekos::Manager::Instance(), [&]() { \
0160         KTRY_GADGET(module, QPushButton, button); \
0161         QVERIFY2(button->isEnabled(), QString("QPushButton '%1' is disabled and cannot be clicked").arg(#button).toStdString().c_str()); \
0162         QTest::mouseClick(button, Qt::LeftButton); }); \
0163     QTest::qWait(200); } while(false)
0164 
0165 /** @brief Helper to click a button from a certain module view (subroutine version for KTRY_CLICK).
0166  * @param module KStars module that holds the checkox
0167  * @param button is the gadget name of the button to look for in the UI configuration.
0168  * @warning Fails the test if the button is not currently enabled.
0169  */
0170 #define KTRY_CLICK_SUB(module, button) do { \
0171     bool success = false; \
0172     QTimer::singleShot(100, Ekos::Manager::Instance(), [&]() { \
0173         KTRY_GADGET(module, QPushButton, button); \
0174         QVERIFY2(button->isEnabled(), QString("QPushButton '%1' is disabled and cannot be clicked").arg(#button).toStdString().c_str()); \
0175         QTest::mouseClick(button, Qt::LeftButton); success = true;}); \
0176         KTRY_VERIFY_WITH_TIMEOUT_SUB(success, 1000);} while(false)
0177 
0178 /** @brief Helper to set a checkbox and verify whether it succeeded
0179  * @param module KStars module that holds the checkox
0180  * @param checkbox object name of the checkbox
0181  * @param value value the checkbox should be set
0182  */
0183 #define KTRY_SET_CHECKBOX(module, checkbox, value) \
0184     KTRY_GADGET(module, QCheckBox, checkbox); checkbox->setChecked(value); QVERIFY(checkbox->isChecked() == value)
0185 
0186 /** @brief Subroutine version of @see KTRY_SET_CHECKBOX
0187  * @param module KStars module that holds the checkox
0188  * @param checkbox object name of the checkbox
0189  * @param value value the checkbox should be set
0190  */
0191 #define KTRY_SET_CHECKBOX_SUB(module, checkbox, value) \
0192     KWRAP_SUB(KTRY_SET_CHECKBOX(module, checkbox, value))
0193 
0194 /** @brief Helper to set a radiobutton and verify whether it succeeded
0195  * @param module KStars module that holds the radiobutton
0196  * @param checkbox object name of the radiobutton
0197  * @param value value the radiobutton should be set
0198  */
0199 #define KTRY_SET_RADIOBUTTON(module, radiobutton, value) \
0200     KTRY_GADGET(module, QRadioButton, radiobutton); radiobutton->setChecked(value); QVERIFY(radiobutton->isChecked() == value)
0201 
0202 /** @brief Subroutine version of @see KTRY_SET_RADIOBUTTON
0203  * @param module KStars module that holds the radiobutton
0204  * @param checkbox object name of the radiobutton
0205  * @param value value the radiobutton should be set
0206  */
0207 #define KTRY_SET_RADIOBUTTON_SUB(module, radiobutton, value) \
0208     KWRAP_SUB(KTRY_SET_RADIOBUTTON(module, radiobutton, value))
0209 
0210 /** @brief Helper to set a spinbox and verify whether it succeeded
0211  * @param module KStars module that holds the spinbox
0212  * @param spinbox object name of the spinbox
0213  * @param value value the spinbox should be set
0214  */
0215 #define KTRY_SET_SPINBOX(module, spinbox, x) \
0216     KTRY_GADGET(module, QSpinBox, spinbox); spinbox->setValue(x); QVERIFY(spinbox->value() == x)
0217 
0218 /** @brief Subroutine version of @see KTRY_SET_SPINBOX
0219  * @param module KStars module that holds the spinbox
0220  * @param spinbox object name of the spinbox
0221  * @param value value the spinbox should be set
0222  */
0223 #define KTRY_SET_SPINBOX_SUB(module, spinbox, x) \
0224     KWRAP_SUB(KTRY_SET_SPINBOX(module, spinbox, x))
0225 
0226 /** @brief Helper to set a doublespinbox and verify whether it succeeded
0227  * @param module KStars module that holds the spinbox
0228  * @param spinbox object name of the spinbox
0229  * @param value value the spinbox should be set
0230  */
0231 #define KTRY_SET_DOUBLESPINBOX(module, spinbox, x) \
0232     KTRY_GADGET(module, QDoubleSpinBox, spinbox); spinbox->setValue(x); QVERIFY((spinbox->value() - x < 0.001))
0233 
0234 /** @brief Subroutine version of @see KTRY_SET_DOUBLESPINBOX_SUB
0235  * @param module KStars module that holds the spinbox
0236  * @param spinbox object name of the spinbox
0237  * @param value value the spinbox should be set
0238  */
0239 #define KTRY_SET_DOUBLESPINBOX_SUB(module, spinbox, x) \
0240     KWRAP_SUB(KTRY_SET_DOUBLESPINBOX(module, spinbox, x))
0241 
0242 /** @brief Helper to set a combo box and verify whether it succeeded
0243  * @param module KStars module that holds the combo box
0244  * @param combo object name of the combo box
0245  * @param value value the combo box should be set
0246  */
0247 #define KTRY_SET_COMBO(module, combo, value) \
0248     KTRY_GADGET(module, QComboBox, combo); combo->setCurrentText(value); QTRY_VERIFY_WITH_TIMEOUT(combo->currentText() == value, 1000)
0249 
0250 /** @brief Helper to set a combo box by index and verify whether it succeeded
0251  * @param module KStars module that holds the combo box
0252  * @param combo object name of the combo box
0253  * @param value index the combo box should be selected
0254  */
0255 #define KTRY_SET_COMBO_INDEX(module, combo, value) \
0256     KTRY_GADGET(module, QComboBox, combo); combo->setCurrentIndex(value); QVERIFY(combo->currentIndex() == value)
0257 
0258 /** @brief Subroutine version of @see KTRY_SET_COMBO
0259  * @param module KStars module that holds the combo box
0260  * @param combo object name of the combo box
0261  * @param value value the combo box should be set
0262  */
0263 #define KTRY_SET_COMBO_SUB(module, combo, value) \
0264     KWRAP_SUB(KTRY_GADGET(module, QComboBox, combo); combo->setCurrentText(value); QVERIFY(combo->currentText() == value))
0265 
0266 /** @brief Helper to set a combo box by index and verify whether it succeeded
0267  * @param module KStars module that holds the combo box
0268  * @param combo object name of the combo box
0269  * @param value index the combo box should be selected
0270  */
0271 #define KTRY_SET_COMBO_INDEX_SUB(module, combo, value) \
0272     KWRAP_SUB(KTRY_SET_COMBO_INDEX(module, combo, value))
0273 
0274 /** @brief Helper to set a line edit box and verify whether it succeeded
0275  * @param module KStars module that holds the line edit box
0276  * @param spinbox object name of the line edit box
0277  * @param value value the line edit box should be set
0278  */
0279 #define KTRY_SET_LINEEDIT(module, lineedit, value) \
0280     KTRY_GADGET(module, QLineEdit, lineedit); lineedit->setText(value); QVERIFY((lineedit->text() == value))
0281 
0282 /** @brief Subroutine version of @see KTRY_SET_LINEEDIT
0283  * @param module KStars module that holds the line edit box
0284  * @param spinbox object name of the line edit box
0285  * @param value value the line edit box should be set
0286  */
0287 #define KTRY_SET_LINEEDIT_SUB(module, lineedit, value) \
0288     KWRAP_SUB(KTRY_GADGET(module, QLineEdit, lineedit); lineedit->setText(value); QVERIFY((lineedit->text() == value)))
0289 
0290 /**
0291   * @brief Helper to check whether a state queue is empty after the given delay
0292   * @param queue event queue
0293   * @param delay in milliseconds
0294   */
0295 #define KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(queue, delay) \
0296     if (! QTest::qWaitFor([&](){return queue.isEmpty();}, delay)) { \
0297     QString result("States not reached: "); \
0298     QTextStream stream(&result); \
0299     while (!(queue).isEmpty()) stream << (queue).dequeue(); \
0300     QFAIL(qPrintable(result));}
0301 
0302 /**
0303   * @brief Helper to check whether a state queue is empty after the given delay
0304   * @param queue event queue
0305   * @param delay in milliseconds
0306   */
0307 #define KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(queue, delay) \
0308     if (! QTest::qWaitFor([&](){return queue.isEmpty();}, delay)) { \
0309     QString result("States not reached: "); \
0310     QTextStream stream(&result); \
0311     while (!(queue).isEmpty()) stream << (queue).dequeue(); \
0312     QWARN(qPrintable(result)); return false;}
0313 
0314 /**
0315   * @brief Helper to verify if the text in the text field starts with the given text
0316   * @param field UI Text field
0317   * @param text Text the text field should start with
0318   * @param timeout in ms
0319   */
0320 #define KTRY_VERIFY_TEXTFIELD_STARTS_WITH_TIMEOUT_SUB(field, title, timeout) \
0321             KTRY_VERIFY_WITH_TIMEOUT_SUB(field->text().length() >= QString(title).length() && \
0322                                          field->text().left(QString(title).length()).compare(title) == 0, timeout)
0323 
0324 /**
0325   * @brief Helper function for switching to a certain module
0326   * @param module target module
0327   * @param timeout in ms
0328   */
0329 #define KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(module, timeout) do {\
0330     KTRY_EKOS_GADGET(QTabWidget, toolsWidget); \
0331     toolsWidget->setCurrentWidget(module); \
0332     QTRY_COMPARE_WITH_TIMEOUT(toolsWidget->currentWidget(), module, timeout);} while (false)
0333 
0334 #define SET_INDI_VALUE_DOUBLE(device, group, property, value) do {\
0335     int result = QProcess::execute(QString("indi_setprop"), {QString("-n"), QString("%1.%2.%3=%4").arg(device).arg(group).arg(property).arg(value)});\
0336     qCInfo(KSTARS_EKOS_TEST) << "Process result code: " << result;\
0337     } while (false)
0338 
0339 #define SET_INDI_VALUE_SWITCH(device, group, property, value) do {\
0340     int result = QProcess::execute(QString("indi_setprop"), {QString("-s"), QString("%1.%2.%3=%4").arg(device).arg(group).arg(property).arg(value==true?"On":"Off")});\
0341     qCInfo(KSTARS_EKOS_TEST) << "Process result code: " << result;\
0342     } while (false)
0343 
0344 #define CLOSE_MODAL_DIALOG(button_nr) do { \
0345     QTimer::singleShot(1000, capture, [&]() { \
0346         QDialog * const dialog = qobject_cast <QDialog*> (QApplication::activeModalWidget()); \
0347         if (dialog) \
0348         { \
0349             QList<QPushButton*> pb = dialog->findChildren<QPushButton*>(); \
0350             QTest::mouseClick(pb[button_nr], Qt::MouseButton::LeftButton); \
0351             qCInfo(KSTARS_EKOS_TEST) << "Button clicked:" << button_nr; \
0352         } \
0353         else \
0354             qCWarning(KSTARS_EKOS_TEST) << "No active modal widget found!" ; \
0355     }); \
0356     } while (false)
0357 
0358 
0359 
0360 class TestEkosHelper : public QObject
0361 {
0362         Q_OBJECT
0363     public:
0364         explicit TestEkosHelper(QString guider = nullptr);
0365 
0366         // main optical train
0367         QString m_primaryTrain = i18n("Primary");
0368         QString m_guidingTrain = i18n("Secondary");
0369 
0370         // predefined set of scopes
0371         OAL::Scope *fsq85, *newton_10F4, *takfinder10x50;
0372         enum ScopeType {SCOPE_FSQ85, SCOPE_NEWTON_10F4, SCOPE_TAKFINDER10x50};
0373         // map scope type to predefined scope
0374         OAL::Scope *getScope(ScopeType type);
0375 
0376         // Mount device
0377         QString m_MountDevice = "";
0378         // CCD device
0379         QString m_CCDDevice = "";
0380         // Rotator device
0381         QString m_RotatorDevice = "";
0382         // Guiding device
0383         QString m_GuiderDevice = "";
0384         // Focusing device
0385         QString m_FocuserDevice = "";
0386         // Flat light panel device
0387         QString m_LightPanelDevice = "";
0388         // Dust cap device
0389         QString m_DustCapDevice = "";
0390         // Dome device
0391         QString m_DomeDevice = "";
0392 
0393         // Guider (PHD2 or Internal)
0394         QString m_Guider = "Internal";
0395 
0396         // PHD2 setup (host and port)
0397         QProcess *phd2 { nullptr };
0398         QString const phd2_guider_host = "localhost";
0399         QString const phd2_guider_port = "4400";
0400 
0401         // guiding used?
0402         bool use_guiding = false;
0403         // did a dithering start?
0404         bool dithered = false;
0405 
0406         // sequence of alignment states that are expected
0407         QQueue<Ekos::AlignState> expectedAlignStates;
0408         // sequence of capture states that are expected
0409         QQueue<Ekos::CaptureState> expectedCaptureStates;
0410         // sequence of focus states that are expected
0411         QQueue<Ekos::FocusState> expectedFocusStates;
0412         // sequence of guiding states that are expected
0413         QQueue<Ekos::GuideState> expectedGuidingStates;
0414         // sequence of mount states that are expected
0415         QQueue<ISD::Mount::Status> expectedMountStates;
0416         // sequence of dome states that are expected
0417         QQueue<ISD::Dome::Status> expectedDomeStates;
0418         // sequence of meridian flip states that are expected
0419         QQueue<Ekos::MeridianFlipState::MeridianFlipMountState> expectedMeridianFlipStates;
0420         // sequence of scheduler states that are expected
0421         QQueue<Ekos::SchedulerState> expectedSchedulerStates;
0422 
0423         /**
0424          * @brief Initialization ahead of executing the test cases.
0425          */
0426         void virtual init();
0427 
0428         /**
0429          * @brief Cleanup after test cases have been executed.
0430          */
0431         void virtual cleanup();
0432 
0433         /**
0434          * @brief Fill mount, guider, CCD and focuser of an EKOS profile
0435          * @param isDone will be true if everything succeeds
0436          */
0437         void fillProfile(bool *isDone);
0438 
0439         /**
0440          * @brief create a new EKOS profile
0441          * @param name name of the profile
0442          * @param isPHD2 use internal guider or PHD2
0443          * @param isDone will be true if everything succeeds
0444          */
0445         void createEkosProfile(QString name, bool isPHD2, bool *isDone);
0446 
0447         /**
0448          * @brief Configure the EKOS profile
0449          * @param name of the profile
0450          * @param isPHD2 use internal guider or PHD2
0451          */
0452         bool setupEkosProfile(QString name, bool isPHD2);
0453 
0454         /**
0455          * @brief Start a test EKOS profile.
0456          */
0457         bool startEkosProfile();
0458 
0459         /**
0460          * @brief Shutdown the current test EKOS profile.
0461          */
0462         bool shutdownEkosProfile();
0463 
0464 
0465         /**
0466          * @brief Helper function starting PHD2
0467          */
0468         void startPHD2();
0469 
0470         /**
0471          * @brief Helper function stopping PHD2
0472          */
0473         void stopPHD2();
0474 
0475         /**
0476          * @brief Helper function for preparing the PHD2 test configuration
0477          */
0478         void preparePHD2();
0479 
0480         /**
0481          * @brief Helper function for cleaning up PHD2 test configuration
0482          */
0483         void cleanupPHD2();
0484 
0485         /**
0486          * @brief Prepare two optical trains for test purposes, one for capturing and one for guiding
0487          */
0488         void prepareOpticalTrains();
0489 
0490         /**
0491          * @brief Helper function that ensures that alignment works in a test environment
0492          */
0493         void prepareAlignmentModule();
0494 
0495         /**
0496          * @brief Helper function that ensures that capturing works in a test environment
0497          */
0498         void prepareCaptureModule();
0499 
0500         /**
0501          * @brief Helper function that ensures that focusing works in a test environment
0502          */
0503         void prepareFocusModule();
0504 
0505         /**
0506          * @brief Helper function that ensures that guiding works in a test environment
0507          */
0508         void prepareGuidingModule();
0509 
0510         /**
0511          * @brief Prepare the mount module with the given mount parameters
0512          */
0513         void prepareMountModule(ScopeType primary = SCOPE_FSQ85, ScopeType guiding = SCOPE_TAKFINDER10x50);
0514 
0515         /**
0516          * @brief Slew to a given position
0517          * @param fast set to true if slewing should be fast using sync first close to the position
0518          */
0519         bool slewTo(double RA, double DEC, bool fast);
0520 
0521         /**
0522          * @brief Helper function for start of guiding
0523          * @param guiding exposure time
0524          */
0525         bool startGuiding(double expTime);
0526 
0527         /**
0528          * @brief Helper function to stop guiding
0529          */
0530         bool stopGuiding();
0531 
0532         /**
0533          * @brief Start alignment process
0534          * @param expTime exposure time
0535          * @return true iff starting alignment succeeds
0536          */
0537         bool startAligning(double expTime);
0538 
0539         /**
0540          * @brief Check if astrometry files exist.
0541          * @return true iff astrometry files found
0542          */
0543         bool checkAstrometryFiles();
0544 
0545         /**
0546          * @brief Returns true iff astrometry is available and checks if necessary.
0547          */
0548         bool isAstrometryAvailable();
0549 
0550         /**
0551          * @brief Helper function for focusing
0552          * @param initialFocusPosition starting point for focusing
0553          */
0554         bool executeFocusing(int initialFocusPosition = 40000);
0555 
0556         /**
0557          * @brief Helper function to stop focusing
0558          */
0559         bool stopFocusing();
0560 
0561         /**
0562          * @brief Determine the number of seconds until the meridian flip should take place by reading
0563          *        the displayed meridian flip status.
0564          * @param message text containing the seconds to the meridian flip
0565          */
0566         int secondsToMF(QString message);
0567 
0568         /**
0569          * @brief update J2000 coordinates
0570          */
0571         void updateJ2000Coordinates(SkyPoint *target);
0572 
0573         /**
0574          * @brief Set a tree view combo to a given value
0575          * @param combo box with tree view
0576          * @param lookup target value
0577          */
0578         void setTreeviewCombo(QComboBox *combo, const QString lookup);
0579 
0580         /**
0581          * @brief Simple write-string-to-file utility.
0582          * @param filename name of the file to be created
0583          * @param lines file content
0584          */
0585         bool writeFile(const QString &filename, const QStringList &lines,
0586                        QFileDevice::Permissions permissions = QFileDevice::ReadOwner | QFileDevice::WriteOwner);
0587 
0588         /**
0589          * @brief createCountingScript create a script that reads the number from its log file,
0590          * increases it by 1 and outputs it to the logfile. With this script we can count
0591          *  how often it has been executed.
0592          * @param scriptname full path to the script file
0593          * @param logfilename full path to the log file the script will produce
0594          * @return true iff file creation succeeds
0595          */
0596         bool createCountingScript(Ekos::ScriptTypes, const QString scriptname);
0597 
0598         /**
0599          * @brief createAllCaptureScripts Create all pre-/post job/capture scripts
0600          * @param directory where the scripts should be located
0601          */
0602         bool createAllCaptureScripts(QTemporaryDir *destination);
0603 
0604         /**
0605          * @brief countScriptRuns Utility that extracts the number of test script runs from its log file.
0606          * @param scripttype script type, its name will be extracted from {@see #scripts}
0607          * @return counter from the log file
0608          */
0609         int countScriptRuns(Ekos::ScriptTypes scripttype);
0610 
0611         /**
0612          * @brief checkScriptRuns Check if the configured pre- and post scripts have been running correctly
0613          * @param captures_per_sequence number of captures per sequence (all sequences must have the identical
0614          * number)
0615          * @param sequences number of capture sequences
0616          */
0617         bool checkScriptRuns(int captures_per_sequence, int sequences);
0618 
0619         /**
0620          * @brief Retrieve the current alignment status.
0621          */
0622         inline Ekos::AlignState getAlignStatus()
0623         {
0624             return m_AlignStatus;
0625         }
0626         /**
0627          * @brief Retrieve the current capture status.
0628          */
0629         inline Ekos::CaptureState getCaptureStatus()
0630         {
0631             return m_CaptureStatus;
0632         }
0633         /**
0634          * @brief Retrieve the current focus status.
0635          */
0636         inline Ekos::FocusState getFocusStatus()
0637         {
0638             return m_FocusStatus;
0639         }
0640         /**
0641          * @brief Retrieve the current guiding status.
0642          */
0643         Ekos::GuideState getGuidingStatus()
0644         {
0645             return m_GuideStatus;
0646         }
0647         /**
0648          * @brief Retrieve the current guide deviation
0649          */
0650         double getGuideDeviation()
0651         {
0652             return m_GuideDeviation;
0653         }
0654         /**
0655          * @brief Retrieve the current mount meridian flip status.
0656          */
0657         Ekos::MeridianFlipState::MeridianFlipMountState getMeridianFlipStatus()
0658         {
0659             return m_MFStatus;
0660         }
0661         /**
0662          * @brief Retrieve the current scheduler status.
0663          */
0664         Ekos::SchedulerState getSchedulerStatus()
0665         {
0666             return m_SchedulerStatus;
0667         }
0668         /**
0669          * @brief Retrieve the current dome status.
0670          */
0671         ISD::Dome::Status getDomeStatus()
0672         {
0673             return m_DomeStatus;
0674         }
0675 
0676         const QMap<Ekos::ScriptTypes, QString> &getScripts() const
0677         {
0678             return scripts;
0679         }
0680         /**
0681          * @brief Connect to read all modules state changes
0682          */
0683         void connectModules();
0684 
0685         /**
0686          * @brief Search for a certain scope in the database and create it if necessary
0687          * @param model scope model
0688          * @param vendor scope vendor
0689          * @param type scope type (refractor, newton, ...
0690          * @param aperture scope aperture in mm
0691          * @param focallenght focal length in mm
0692          * @return scope object
0693          */
0694         OAL::Scope *createScopeIfNecessary(QString model, QString vendor, QString type, double aperture, double focallenght);
0695 
0696 private:
0697         // current mount status
0698         ISD::Mount::Status m_MountStatus { ISD::Mount::MOUNT_IDLE };
0699 
0700         // current mount meridian flip status
0701         Ekos::MeridianFlipState::MeridianFlipMountState m_MFStatus { Ekos::MeridianFlipState::MOUNT_FLIP_NONE };
0702 
0703         // current alignment status
0704         Ekos::AlignState m_AlignStatus { Ekos::ALIGN_IDLE };
0705 
0706         // current capture status
0707         Ekos::CaptureState m_CaptureStatus { Ekos::CAPTURE_IDLE };
0708 
0709         // current focus status
0710         Ekos::FocusState m_FocusStatus { Ekos::FOCUS_IDLE };
0711 
0712         // current guiding status
0713         Ekos::GuideState m_GuideStatus { Ekos::GUIDE_IDLE };
0714 
0715         // current dome status
0716         ISD::Dome::Status m_DomeStatus { ISD::Dome::DOME_IDLE };
0717 
0718         // current guiding deviation
0719         double m_GuideDeviation { -1 };
0720 
0721         // current scheduler status
0722         Ekos::SchedulerState m_SchedulerStatus { Ekos::SCHEDULER_IDLE };
0723 
0724         // pre- and post sequence / capture execution scripts
0725         QMap<Ekos::ScriptTypes, QString> scripts;
0726 
0727         /**
0728          * @brief Slot to track the align status of the mount
0729          * @param status new align state
0730          */
0731         void alignStatusChanged(Ekos::AlignState status);
0732 
0733         /**
0734          * @brief Slot to track the mount status
0735          * @param status new mount status
0736          */
0737         void mountStatusChanged(ISD::Mount::Status status);
0738 
0739         /**
0740          * @brief Slot to track the meridian flip stage of the mount
0741          * @param status new meridian flip state
0742          */
0743         void meridianFlipStatusChanged(Ekos::MeridianFlipState::MeridianFlipMountState status);
0744 
0745         /**
0746          * @brief Slot to track the focus status
0747          * @param status new focus status
0748          */
0749         void focusStatusChanged(Ekos::FocusState status);
0750 
0751         /**
0752          * @brief Slot to track the guiding status
0753          * @param status new guiding status
0754          */
0755         void guidingStatusChanged(Ekos::GuideState status);
0756 
0757         /**
0758          * @brief Slot to track guiding deviation events
0759          * @param delta_ra RA deviation
0760          * @param delta_dec DEC deviation
0761          */
0762         void guideDeviationChanged(double delta_ra, double delta_dec);
0763 
0764         /**
0765          * @brief Slot to track the capture status
0766          * @param status new capture status
0767          */
0768         void captureStatusChanged(Ekos::CaptureState status);
0769 
0770         /**
0771          * @brief Slot to track the scheduler status
0772          * @param status new scheduler status
0773          */
0774         void schedulerStatusChanged(Ekos::SchedulerState status);
0775 
0776         /**
0777          * @brief Slot to track the dome status
0778          * @param status new scheduler status
0779          */
0780         void domeStatusChanged(ISD::Dome::Status status);
0781 
0782     private:
0783         bool m_astrometry_available;
0784 
0785 };