File indexing completed on 2024-04-21 14:47:24
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 "test_ekos_helper.h" 0010 #include "ksutils.h" 0011 #include "Options.h" 0012 #include "ekos/profileeditor.h" 0013 #include "ekos/guide/internalguide/gmath.h" 0014 #include "ekos/auxiliary/opticaltrainmanager.h" 0015 #include "ekos/align/align.h" 0016 #include "ekos/capture/capture.h" 0017 #include "ekos/scheduler/scheduler.h" 0018 #include "ekos/auxiliary/filtermanager.h" 0019 #include "kstarsdata.h" 0020 0021 TestEkosHelper::TestEkosHelper(QString guider) 0022 { 0023 m_Guider = guider; 0024 m_MountDevice = "Telescope Simulator"; 0025 m_CCDDevice = "CCD Simulator"; 0026 if (guider != nullptr) 0027 m_GuiderDevice = "Guide Simulator"; 0028 m_astrometry_available = false; 0029 } 0030 0031 0032 void TestEkosHelper::createEkosProfile(QString name, bool isPHD2, bool *isDone) 0033 { 0034 ProfileEditor* profileEditor = Ekos::Manager::Instance()->findChild<ProfileEditor*>("profileEditorDialog"); 0035 0036 // Disable Port Selector 0037 KTRY_SET_CHECKBOX(profileEditor, portSelectorCheck, false); 0038 // Set the profile name 0039 KTRY_SET_LINEEDIT(profileEditor, profileIN, name); 0040 // select the guider type 0041 KTRY_SET_COMBO(profileEditor, guideTypeCombo, isPHD2 ? "PHD2" : "Internal"); 0042 if (isPHD2) 0043 { 0044 // Write PHD2 server specs 0045 KTRY_SET_LINEEDIT(profileEditor, externalGuideHost, "localhost"); 0046 KTRY_SET_LINEEDIT(profileEditor, externalGuidePort, "4400"); 0047 } 0048 0049 qCInfo(KSTARS_EKOS_TEST) << "Ekos profile " << name << " created."; 0050 // and now continue with filling the profile 0051 fillProfile(isDone); 0052 } 0053 0054 void TestEkosHelper::fillProfile(bool *isDone) 0055 { 0056 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: starting..."; 0057 ProfileEditor* profileEditor = Ekos::Manager::Instance()->findChild<ProfileEditor*>("profileEditorDialog"); 0058 0059 // Select the mount device 0060 if (m_MountDevice != "") 0061 { 0062 KTRY_PROFILEEDITOR_GADGET(QComboBox, mountCombo); 0063 setTreeviewCombo(mountCombo, m_MountDevice); 0064 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Mount selected."; 0065 } 0066 0067 // Selet the CCD device 0068 if (m_CCDDevice != "") 0069 { 0070 KTRY_PROFILEEDITOR_GADGET(QComboBox, ccdCombo); 0071 setTreeviewCombo(ccdCombo, m_CCDDevice); 0072 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: CCD selected."; 0073 } 0074 0075 // Select the focuser device 0076 if (m_FocuserDevice != "") 0077 { 0078 KTRY_PROFILEEDITOR_GADGET(QComboBox, focuserCombo); 0079 setTreeviewCombo(focuserCombo, m_FocuserDevice); 0080 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Focuser selected."; 0081 } 0082 0083 // Select the guider device, if not empty and different from CCD 0084 if (m_GuiderDevice != "" && m_GuiderDevice != m_CCDDevice) 0085 { 0086 KTRY_PROFILEEDITOR_GADGET(QComboBox, guiderCombo); 0087 setTreeviewCombo(guiderCombo, m_GuiderDevice); 0088 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Guider selected."; 0089 } 0090 0091 // Select the light panel device for flats capturing 0092 if (m_LightPanelDevice != "") 0093 { 0094 KTRY_PROFILEEDITOR_GADGET(QComboBox, aux1Combo); 0095 setTreeviewCombo(aux1Combo, m_LightPanelDevice); 0096 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Light panel selected."; 0097 } 0098 0099 // Select the dome device 0100 if (m_DomeDevice != "") 0101 { 0102 KTRY_PROFILEEDITOR_GADGET(QComboBox, domeCombo); 0103 setTreeviewCombo(domeCombo, m_DomeDevice); 0104 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Dome selected."; 0105 } 0106 0107 // Select the rotator device 0108 if (m_RotatorDevice != "") 0109 { 0110 KTRY_PROFILEEDITOR_GADGET(QComboBox, aux2Combo); 0111 setTreeviewCombo(aux2Combo, m_RotatorDevice); 0112 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Rotator selected."; 0113 } 0114 0115 // wait a short time to make the setup visible 0116 QTest::qWait(1000); 0117 // Save the profile using the "Save" button 0118 QDialogButtonBox* buttons = profileEditor->findChild<QDialogButtonBox*>("dialogButtons"); 0119 QVERIFY(nullptr != buttons); 0120 QTest::mouseClick(buttons->button(QDialogButtonBox::Save), Qt::LeftButton); 0121 0122 qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Selections saved."; 0123 0124 *isDone = true; 0125 } 0126 0127 bool TestEkosHelper::setupEkosProfile(QString name, bool isPHD2) 0128 { 0129 qCInfo(KSTARS_EKOS_TEST) << "Setting up Ekos profile..."; 0130 bool isDone = false; 0131 Ekos::Manager * const ekos = Ekos::Manager::Instance(); 0132 // check if the profile with the given name exists 0133 KTRY_GADGET_SUB(ekos, QComboBox, profileCombo); 0134 if (profileCombo->findText(name) >= 0) 0135 { 0136 KTRY_GADGET_SUB(ekos, QPushButton, editProfileB); 0137 0138 // edit Simulators profile 0139 KWRAP_SUB(KTRY_EKOS_SELECT_PROFILE(name)); 0140 0141 // edit only editable profiles 0142 if (editProfileB->isEnabled()) 0143 { 0144 // start with a delay of 1 sec a new thread that edits the profile 0145 QTimer::singleShot(1000, ekos, [&] {fillProfile(&isDone);}); 0146 KTRY_CLICK_SUB(ekos, editProfileB); 0147 } 0148 else 0149 { 0150 qCInfo(KSTARS_EKOS_TEST) << "Profile " << name << " not editable, setup finished."; 0151 isDone = true; 0152 return true; 0153 } 0154 } 0155 else 0156 { 0157 // start with a delay of 1 sec a new thread that edits the profile 0158 qCInfo(KSTARS_EKOS_TEST) << "Creating new profile " << name << " ..."; 0159 QTimer::singleShot(1000, ekos, [&] {createEkosProfile(name, isPHD2, &isDone);}); 0160 // create new profile addProfileB 0161 KTRY_CLICK_SUB(ekos, addProfileB); 0162 } 0163 0164 // Cancel the profile editor if test did not close the editor dialog within 10 sec 0165 QTimer * closeDialog = new QTimer(this); 0166 closeDialog->setSingleShot(true); 0167 closeDialog->setInterval(10000); 0168 ekos->connect(closeDialog, &QTimer::timeout, this, [&] 0169 { 0170 ProfileEditor* profileEditor = ekos->findChild<ProfileEditor*>("profileEditorDialog"); 0171 if (profileEditor != nullptr) 0172 { 0173 profileEditor->reject(); 0174 qWarning(KSTARS_EKOS_TEST) << "Editing profile aborted."; 0175 } 0176 }); 0177 0178 0179 // Click handler returned, stop the timer closing the dialog on failure 0180 closeDialog->stop(); 0181 delete closeDialog; 0182 0183 // Verification of the first test step 0184 return isDone; 0185 0186 } 0187 0188 void TestEkosHelper::connectModules() 0189 { 0190 Ekos::Manager * const ekos = Ekos::Manager::Instance(); 0191 0192 // wait for modules startup 0193 if (m_MountDevice != "") 0194 QTRY_VERIFY_WITH_TIMEOUT(ekos->mountModule() != nullptr, 10000); 0195 if (m_CCDDevice != "") 0196 { 0197 QTRY_VERIFY_WITH_TIMEOUT(ekos->captureModule() != nullptr, 10000); 0198 QTRY_VERIFY_WITH_TIMEOUT(ekos->alignModule() != nullptr, 10000); 0199 } 0200 if (m_GuiderDevice != "") 0201 QTRY_VERIFY_WITH_TIMEOUT(ekos->guideModule() != nullptr, 10000); 0202 if (m_FocuserDevice != "") 0203 QTRY_VERIFY_WITH_TIMEOUT(ekos->focusModule() != nullptr, 10000); 0204 0205 // connect to the alignment process to receive align status changes 0206 connect(ekos->alignModule(), &Ekos::Align::newStatus, this, &TestEkosHelper::alignStatusChanged, 0207 Qt::UniqueConnection); 0208 0209 if (m_MountDevice != "") 0210 { 0211 // connect to the mount process to rmount status changes 0212 connect(ekos->mountModule(), &Ekos::Mount::newStatus, this, 0213 &TestEkosHelper::mountStatusChanged, Qt::UniqueConnection); 0214 0215 // connect to the mount process to receive meridian flip status changes 0216 connect(ekos->mountModule()->getMeridianFlipState().get(), &Ekos::MeridianFlipState::newMountMFStatus, this, 0217 &TestEkosHelper::meridianFlipStatusChanged, Qt::UniqueConnection); 0218 } 0219 0220 if (m_GuiderDevice != "") 0221 { 0222 // connect to the guiding process to receive guiding status changes 0223 connect(ekos->guideModule(), &Ekos::Guide::newStatus, this, &TestEkosHelper::guidingStatusChanged, 0224 Qt::UniqueConnection); 0225 0226 connect(ekos->guideModule(), &Ekos::Guide::newAxisDelta, this, &TestEkosHelper::guideDeviationChanged, 0227 Qt::UniqueConnection); 0228 } 0229 0230 // connect to the capture process to receive capture status changes 0231 if (m_CCDDevice != "") 0232 connect(ekos->captureModule(), &Ekos::Capture::newStatus, this, &TestEkosHelper::captureStatusChanged, 0233 Qt::UniqueConnection); 0234 0235 // connect to the scheduler process to receive scheduler status changes 0236 connect(ekos->schedulerModule(), &Ekos::Scheduler::newStatus, this, &TestEkosHelper::schedulerStatusChanged, 0237 Qt::UniqueConnection); 0238 0239 // connect to the focus process to receive focus status changes 0240 if (m_FocuserDevice != "") 0241 connect(ekos->focusModule(), &Ekos::Focus::newStatus, this, &TestEkosHelper::focusStatusChanged, 0242 Qt::UniqueConnection); 0243 0244 // connect to the dome process to receive dome status changes 0245 // connect(ekos->domeModule(), &Ekos::Dome::newStatus, this, &TestEkosHelper::domeStatusChanged, 0246 // Qt::UniqueConnection); 0247 } 0248 0249 bool TestEkosHelper::startEkosProfile() 0250 { 0251 Ekos::Manager * const ekos = Ekos::Manager::Instance(); 0252 0253 KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(ekos->setupTab, 1000)); 0254 0255 if (m_Guider == "PHD2") 0256 { 0257 // Start a PHD2 instance 0258 startPHD2(); 0259 // setup the EKOS profile 0260 KWRAP_SUB(QVERIFY(setupEkosProfile("Test profile (PHD2)", true))); 0261 } 0262 else 0263 KWRAP_SUB(QVERIFY(setupEkosProfile("Test profile", false))); 0264 0265 // start the profile 0266 KTRY_EKOS_CLICK(processINDIB); 0267 // wait for the devices to come up 0268 KWRAP_SUB(QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->indiStatus() == Ekos::Success, 10000)); 0269 // receive status updates from all devices 0270 connectModules(); 0271 0272 // Everything completed successfully 0273 return true; 0274 } 0275 0276 bool TestEkosHelper::shutdownEkosProfile() 0277 { 0278 // disconnect to the dome process to receive dome status changes 0279 // disconnect(Ekos::Manager::Instance()->domeModule(), &Ekos::Dome::newStatus, this, 0280 // &TestEkosHelper::domeStatusChanged); 0281 // disconnect to the focus process to receive focus status changes 0282 disconnect(Ekos::Manager::Instance()->focusModule(), &Ekos::Focus::newStatus, this, 0283 &TestEkosHelper::focusStatusChanged); 0284 // disconnect the guiding process to receive the current guiding status 0285 disconnect(Ekos::Manager::Instance()->guideModule(), &Ekos::Guide::newStatus, this, 0286 &TestEkosHelper::guidingStatusChanged); 0287 // disconnect the mount process to receive mount status changes 0288 disconnect(Ekos::Manager::Instance()->mountModule(), &Ekos::Mount::newStatus, this, 0289 &TestEkosHelper::mountStatusChanged); 0290 // disconnect the mount process to receive meridian flip status changes 0291 disconnect(Ekos::Manager::Instance()->mountModule()->getMeridianFlipState().get(), 0292 &Ekos::MeridianFlipState::newMountMFStatus, this, 0293 &TestEkosHelper::meridianFlipStatusChanged); 0294 // disconnect to the scheduler process to receive scheduler status changes 0295 disconnect(Ekos::Manager::Instance()->schedulerModule(), &Ekos::Scheduler::newStatus, this, 0296 &TestEkosHelper::schedulerStatusChanged); 0297 // disconnect to the capture process to receive capture status changes 0298 disconnect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::newStatus, this, 0299 &TestEkosHelper::captureStatusChanged); 0300 // disconnect to the alignment process to receive align status changes 0301 disconnect(Ekos::Manager::Instance()->alignModule(), &Ekos::Align::newStatus, this, 0302 &TestEkosHelper::alignStatusChanged); 0303 0304 if (m_Guider == "PHD2") 0305 { 0306 KTRY_GADGET_SUB(Ekos::Manager::Instance()->guideModule(), QPushButton, externalDisconnectB); 0307 // ensure that PHD2 is connected 0308 if (externalDisconnectB->isEnabled()) 0309 { 0310 // click "Disconnect" 0311 KTRY_CLICK_SUB(Ekos::Manager::Instance()->guideModule(), externalDisconnectB); 0312 // Stop PHD2 0313 stopPHD2(); 0314 } 0315 } 0316 0317 qCInfo(KSTARS_EKOS_TEST) << "Stopping profile ..."; 0318 KWRAP_SUB(KTRY_EKOS_STOP_SIMULATORS()); 0319 qCInfo(KSTARS_EKOS_TEST) << "Stopping profile ... (done)"; 0320 // Wait until the profile selection is enabled. This shows, that all devices are disconnected. 0321 KTRY_VERIFY_WITH_TIMEOUT_SUB(Ekos::Manager::Instance()->profileGroup->isEnabled() == true, 10000); 0322 // Everything completed successfully 0323 return true; 0324 } 0325 0326 void TestEkosHelper::startPHD2() 0327 { 0328 phd2 = new QProcess(this); 0329 QStringList arguments; 0330 // Start PHD2 with the proper configuration 0331 phd2->start(QString("phd2"), arguments); 0332 QVERIFY(phd2->waitForStarted(3000)); 0333 QTest::qWait(2000); 0334 QTRY_VERIFY_WITH_TIMEOUT(phd2->state() == QProcess::Running, 1000); 0335 0336 // Try to connect to the PHD2 server 0337 QTcpSocket phd2_server(this); 0338 phd2_server.connectToHost(phd2_guider_host, phd2_guider_port.toUInt(), QIODevice::ReadOnly, QAbstractSocket::IPv4Protocol); 0339 if(!phd2_server.waitForConnected(5000)) 0340 { 0341 QWARN(QString("Cannot continue, PHD2 server is unavailable (%1)").arg(phd2_server.errorString()).toStdString().c_str()); 0342 return; 0343 } 0344 phd2_server.disconnectFromHost(); 0345 if (phd2_server.state() == QTcpSocket::ConnectedState) 0346 QVERIFY(phd2_server.waitForDisconnected(1000)); 0347 } 0348 0349 void TestEkosHelper::stopPHD2() 0350 { 0351 phd2->terminate(); 0352 QVERIFY(phd2->waitForFinished(5000)); 0353 } 0354 0355 void TestEkosHelper::preparePHD2() 0356 { 0357 QString const phd2_config_name = ".PHDGuidingV2"; 0358 QString const phd2_config_bak_name = ".PHDGuidingV2.bak"; 0359 QString const phd2_config_orig_name = ".PHDGuidingV2_mf"; 0360 QStandardPaths::setTestModeEnabled(false); 0361 QFileInfo phd2_config_home(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), phd2_config_name); 0362 QFileInfo phd2_config_home_bak(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), phd2_config_bak_name); 0363 QFileInfo phd2_config_orig(phd2_config_orig_name); 0364 QStandardPaths::setTestModeEnabled(true); 0365 QWARN(QString("Writing PHD configuration file to '%1'").arg(phd2_config_home.filePath()).toStdString().c_str()); 0366 if (phd2_config_home.exists()) 0367 { 0368 // remove existing backup file 0369 if (phd2_config_home_bak.exists()) 0370 QVERIFY(QFile::remove(phd2_config_home_bak.filePath())); 0371 // rename existing file to backup file 0372 QVERIFY(QFile::rename(phd2_config_home.filePath(), phd2_config_home_bak.filePath())); 0373 } 0374 QVERIFY2(phd2_config_orig.exists(), phd2_config_orig_name.toLocal8Bit() + " not found in current directory!"); 0375 QVERIFY(QFile::copy(phd2_config_orig_name, phd2_config_home.filePath())); 0376 } 0377 0378 void TestEkosHelper::cleanupPHD2() 0379 { 0380 QString const phd2_config = ".PHDGuidingV2"; 0381 QString const phd2_config_bak = ".PHDGuidingV2.bak"; 0382 QStandardPaths::setTestModeEnabled(false); 0383 QFileInfo phd2_config_home(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), phd2_config); 0384 QFileInfo phd2_config_home_bak(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), phd2_config_bak); 0385 // remove PHD2 test config 0386 if (phd2_config_home.exists()) 0387 QVERIFY(QFile::remove(phd2_config_home.filePath())); 0388 // restore the backup 0389 if (phd2_config_home_bak.exists()) 0390 QVERIFY(QFile::rename(phd2_config_home_bak.filePath(), phd2_config_home.filePath())); 0391 } 0392 0393 void TestEkosHelper::prepareOpticalTrains() 0394 { 0395 Ekos::OpticalTrainManager *otm = Ekos::OpticalTrainManager::Instance(); 0396 // close window, we change everything programatically 0397 otm->close(); 0398 // setup train with main scope and camera 0399 QVariantMap primaryTrain = otm->getOpticalTrain(m_primaryTrain); 0400 primaryTrain["mount"] = m_MountDevice; 0401 primaryTrain["camera"] = m_CCDDevice; 0402 primaryTrain["filterwheel"] = m_CCDDevice; 0403 primaryTrain["focuser"] = m_FocuserDevice == "" ? "-" : m_FocuserDevice; 0404 primaryTrain["rotator"] = m_RotatorDevice == "" ? "-" : m_RotatorDevice; 0405 primaryTrain["lightbox"] = m_LightPanelDevice == "" ? "-" : m_LightPanelDevice; 0406 primaryTrain["dustcap"] = m_DustCapDevice == "" ? "-" : m_DustCapDevice; 0407 KStarsData::Instance()->userdb()->UpdateOpticalTrain(primaryTrain, primaryTrain["id"].toInt()); 0408 if (m_GuiderDevice != "") 0409 { 0410 // setup guiding scope train 0411 QVariantMap guidingTrain = otm->getOpticalTrain(m_guidingTrain); 0412 bool isNew = guidingTrain.size() == 0; 0413 guidingTrain["mount"] = m_MountDevice; 0414 guidingTrain["camera"] = m_GuiderDevice; 0415 guidingTrain["filterwheel"] = "-"; 0416 guidingTrain["focuser"] = "-"; 0417 guidingTrain["guider"] = m_MountDevice; 0418 if (isNew) 0419 { 0420 // create guiding train if missing 0421 guidingTrain["name"] = m_guidingTrain; 0422 otm->addOpticalTrain(QJsonObject::fromVariantMap(guidingTrain)); 0423 } 0424 else 0425 KStarsData::Instance()->userdb()->UpdateOpticalTrain(guidingTrain, guidingTrain["id"].toInt()); 0426 } 0427 // ensure that the OTM initializes from the database 0428 otm->refreshModel(); 0429 otm->refreshTrains(); 0430 } 0431 0432 void TestEkosHelper::prepareAlignmentModule() 0433 { 0434 0435 // check if astrometry files exist 0436 QTRY_VERIFY(isAstrometryAvailable() == true); 0437 // switch to alignment module 0438 KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->alignModule(), 1000); 0439 // select the primary train for alignment 0440 KTRY_SET_COMBO(Ekos::Manager::Instance()->alignModule(), opticalTrainCombo, m_primaryTrain); 0441 // select local solver 0442 Ekos::Manager::Instance()->alignModule()->setSolverMode(Ekos::Align::SOLVER_LOCAL); 0443 // select internal SEP method 0444 Options::setSolveSextractorType(SSolver::EXTRACTOR_BUILTIN); 0445 // select StellarSolver 0446 Options::setSolverType(SSolver::SOLVER_STELLARSOLVER); 0447 // select fast solve profile option 0448 Options::setSolveOptionsProfile(SSolver::Parameters::SINGLE_THREAD_SOLVING); 0449 // select the "Slew to Target" mode 0450 KTRY_SET_RADIOBUTTON(Ekos::Manager::Instance()->alignModule(), slewR, true); 0451 // disable rotator check in alignment 0452 Options::setAstrometryUseRotator(false); 0453 // select the Luminance filter 0454 KTRY_SET_COMBO(Ekos::Manager::Instance()->alignModule(), alignFilter, "Luminance"); 0455 // set the exposure time to a standard 0456 KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->alignModule(), alignExposure, 3.0); 0457 // reduce the accuracy to avoid testing problems 0458 KTRY_SET_SPINBOX(Ekos::Manager::Instance()->alignModule(), alignAccuracyThreshold, 300); 0459 // disable re-alignment 0460 Options::setAlignCheckFrequency(0); 0461 } 0462 0463 void TestEkosHelper::prepareCaptureModule() 0464 { 0465 Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); 0466 // clear refocusing limits 0467 KTRY_SET_CHECKBOX(capture, limitRefocusS, false); 0468 KTRY_SET_CHECKBOX(capture, limitFocusHFRS, false); 0469 KTRY_SET_CHECKBOX(capture, limitFocusDeltaTS, false); 0470 // clear the guiding limits 0471 KTRY_SET_CHECKBOX(capture, startGuiderDriftS, false); 0472 KTRY_SET_CHECKBOX(capture, limitGuideDeviationS, false); 0473 0474 } 0475 0476 void TestEkosHelper::prepareFocusModule() 0477 { 0478 // set focus mode defaults 0479 KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->focusModule(), 1000); 0480 // select the primary train for focusing 0481 KTRY_SET_COMBO(Ekos::Manager::Instance()->focusModule(), opticalTrainCombo, m_primaryTrain); 0482 // use full field 0483 KTRY_SET_RADIOBUTTON(Ekos::Manager::Instance()->focusModule(), focusUseFullField, true); 0484 //initial step size 5000 0485 KTRY_SET_SPINBOX(Ekos::Manager::Instance()->focusModule(), focusTicks, 5000); 0486 // max travel 100000 0487 KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->focusModule(), focusMaxTravel, 100000.0); 0488 // focus tolerance 20% - make focus fast and robust, precision does not matter 0489 KTRY_GADGET(Ekos::Manager::Instance()->focusModule(), QDoubleSpinBox, focusTolerance); 0490 focusTolerance->setMaximum(20.0); 0491 focusTolerance->setValue(20.0); 0492 // use single pass linear algorithm 0493 KTRY_SET_COMBO(Ekos::Manager::Instance()->focusModule(), focusAlgorithm, "Linear 1 Pass"); 0494 // select star detection 0495 KTRY_SET_COMBO(Ekos::Manager::Instance()->focusModule(), focusSEPProfile, "1-Focus-Default"); 0496 // set annulus to 0% - 100% 0497 KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->focusModule(), focusFullFieldInnerRadius, 0.0); 0498 KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->focusModule(), focusFullFieldOuterRadius, 100.0); 0499 // try to make focusing fast, precision is not relevant here 0500 KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->focusModule(), focusOutSteps, 3.0); 0501 // select the Luminance filter 0502 KTRY_SET_COMBO(Ekos::Manager::Instance()->focusModule(), focusFilter, "Luminance"); 0503 // select SEP algorithm for star detection 0504 KTRY_SET_COMBO(Ekos::Manager::Instance()->focusModule(), focusDetection, "SEP"); 0505 // select HFR a star focus measure 0506 KTRY_SET_COMBO(Ekos::Manager::Instance()->focusModule(), focusStarMeasure, "HFR"); 0507 // set exp time for current filter 0508 KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->focusModule(), focusExposure, 1.0); 0509 // set exposure times for all filters 0510 auto filtermanager = Ekos::Manager::Instance()->focusModule()->filterManager(); 0511 for (int pos = 0; pos < filtermanager->getFilterLabels().count(); pos++) 0512 { 0513 filtermanager->setFilterExposure(pos, 1.0); 0514 filtermanager->setFilterLock(pos, "Luminance"); 0515 } 0516 0517 // Eliminate the noise setting to ensure a working focusing and plate solving 0518 KTRY_INDI_PROPERTY(m_CCDDevice, "Simulator Config", "SIMULATOR_SETTINGS", ccd_settings); 0519 INDI_E *noise_setting = ccd_settings->getElement("SIM_NOISE"); 0520 QVERIFY(ccd_settings != nullptr); 0521 noise_setting->setValue(0.0); 0522 ccd_settings->processSetButton(); 0523 0524 // gain 100 0525 KTRY_SET_DOUBLESPINBOX(Ekos::Manager::Instance()->focusModule(), focusGain, 100.0); 0526 // suspend guiding while focusing 0527 KTRY_SET_CHECKBOX(Ekos::Manager::Instance()->focusModule(), focusSuspendGuiding, true); 0528 } 0529 0530 void TestEkosHelper::prepareGuidingModule() 0531 { 0532 // switch to guiding module 0533 KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->guideModule(), 1000); 0534 0535 // preserve guiding calibration as good as possible 0536 Options::setReuseGuideCalibration(true); 0537 Options::setResetGuideCalibration(false); 0538 // guide calibration captured with fsq-85 as guiding scope, clear if it creates problems 0539 // KTRY_CLICK(Ekos::Manager::Instance()->guideModule(), clearCalibrationB); 0540 Options::setSerializedCalibration("Cal v1.0,bx=1,by=1,pw=0.0024,ph=0.0024,fl=450,ang=268.349,angR=270.023,angD=176.674,ramspas=139.764,decmspas=134.438,swap=0,ra= 27:21:00,dec=00:25:52,side=0,when=2023-02-18 16:46:48,calEnd"); 0541 // 0.5 pixel dithering 0542 Options::setDitherPixels(0.5); 0543 // auto star select 0544 KTRY_SET_CHECKBOX(Ekos::Manager::Instance()->guideModule(), guideAutoStar, true); 0545 // set the guide star box to size 32 0546 KTRY_SET_COMBO(Ekos::Manager::Instance()->guideModule(), guideSquareSize, "32"); 0547 // use 1x1 binning for guiding 0548 KTRY_SET_COMBO(Ekos::Manager::Instance()->guideModule(), guideBinning, "1x1"); 0549 if (m_Guider == "PHD2") 0550 { 0551 KTRY_GADGET(Ekos::Manager::Instance()->guideModule(), QPushButton, externalConnectB); 0552 // ensure that PHD2 is connected 0553 if (externalConnectB->isEnabled()) 0554 { 0555 // click "Connect" 0556 KTRY_CLICK(Ekos::Manager::Instance()->guideModule(), externalConnectB); 0557 // wait max 60 sec that PHD2 is connected (sometimes INDI connections hang) 0558 QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->guideModule()->status() == Ekos::GUIDE_CONNECTED, 60000); 0559 qCInfo(KSTARS_EKOS_TEST) << "PHD2 connected successfully."; 0560 } 0561 } 0562 else 0563 { 0564 // select the secondary train for guiding 0565 KTRY_SET_COMBO(Ekos::Manager::Instance()->guideModule(), opticalTrainCombo, m_guidingTrain); 0566 // select multi-star 0567 Options::setGuideAlgorithm(SEP_MULTISTAR); 0568 // select small star profile 0569 Options::setGuideOptionsProfile(2); 0570 // set 2 sec guiding calibration pulse duration 0571 Options::setCalibrationPulseDuration(2000); 0572 // use three steps in each direction for calibration 0573 Options::setAutoModeIterations(3); 0574 // set the guiding aggressiveness 0575 Options::setRAProportionalGain(0.5); 0576 Options::setDECProportionalGain(0.5); 0577 // use simulator's guide head 0578 Options::setUseGuideHead(true); 0579 } 0580 } 0581 0582 Scope *TestEkosHelper::createScopeIfNecessary(QString model, QString vendor, QString type, double aperture, 0583 double focallenght) 0584 { 0585 QList<Scope *> scope_list; 0586 KStarsData::Instance()->userdb()->GetAllScopes(scope_list); 0587 0588 for (Scope *scope : scope_list) 0589 { 0590 if (scope->model() == model && scope->vendor() == vendor && scope->type() == type && scope->aperture() == aperture 0591 && scope->focalLength() == focallenght) 0592 return scope; 0593 } 0594 // no match found, create it again 0595 KStarsData::Instance()->userdb()->AddScope(model, vendor, type, aperture, focallenght); 0596 Ekos::OpticalTrainManager::Instance()->refreshOpticalElements(); 0597 // find it 0598 scope_list.clear(); 0599 KStarsData::Instance()->userdb()->GetAllScopes(scope_list); 0600 for (Scope *scope : scope_list) 0601 { 0602 if (scope->model() == model && scope->vendor() == vendor && scope->type() == type && scope->aperture() == aperture 0603 && scope->focalLength() == focallenght) 0604 return scope; 0605 } 0606 // this should never happen 0607 return nullptr; 0608 } 0609 0610 0611 0612 OAL::Scope *TestEkosHelper::getScope(TestEkosHelper::ScopeType type) 0613 { 0614 switch (type) 0615 { 0616 case SCOPE_FSQ85: 0617 return fsq85; 0618 case SCOPE_NEWTON_10F4: 0619 return newton_10F4; 0620 case SCOPE_TAKFINDER10x50: 0621 return takfinder10x50; 0622 } 0623 // this should never happen 0624 return fsq85; 0625 } 0626 0627 void TestEkosHelper::prepareMountModule(ScopeType primary, ScopeType guiding) 0628 { 0629 Ekos::OpticalTrainManager *otm = Ekos::OpticalTrainManager::Instance(); 0630 // set mount defaults 0631 QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule() != nullptr, 5000); 0632 QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule()->slewStatus() != IPState::IPS_ALERT, 1000); 0633 0634 // define a set of scopes 0635 fsq85 = createScopeIfNecessary("FSQ-85", "Takahashi", "Refractor", 85.0, 450.0); 0636 newton_10F4 = createScopeIfNecessary("ONTC", "Teleskop-Service", "Newtonian", 254.0, 1000.0); 0637 takfinder10x50 = createScopeIfNecessary("Finder 7x50", "Takahashi", "Refractor", 50.0, 170.0); 0638 0639 QVERIFY(fsq85 != nullptr); 0640 QVERIFY(newton_10F4 != nullptr); 0641 QVERIFY(takfinder10x50 != nullptr); 0642 0643 OAL::Scope *primaryScope = getScope(primary); 0644 OAL::Scope *guidingScope = getScope(guiding); 0645 0646 // setup the primary train 0647 QVariantMap primaryTrain = otm->getOpticalTrain(m_primaryTrain); 0648 primaryTrain["scope"] = primaryScope->name(); 0649 primaryTrain["reducer"] = 1.0; 0650 KStarsData::Instance()->userdb()->UpdateOpticalTrain(primaryTrain, primaryTrain["id"].toInt()); 0651 0652 // setup the guiding train 0653 QVariantMap guidingTrain = otm->getOpticalTrain(m_guidingTrain); 0654 guidingTrain["scope"] = guidingScope->name(); 0655 guidingTrain["reducer"] = 1.0; 0656 KStarsData::Instance()->userdb()->UpdateOpticalTrain(guidingTrain, guidingTrain["id"].toInt()); 0657 // ensure that the OTM initializes from the database 0658 otm->refreshModel(); 0659 otm->refreshTrains(); 0660 0661 // Set the scope parameters also in the telescope simulator to ensure that the CCD simulator creates the right FOW 0662 if (m_CCDDevice != nullptr) 0663 { 0664 KTRY_INDI_PROPERTY(m_CCDDevice, "Options", "SCOPE_INFO", ccd_scope_info); 0665 INDI_E *primary_aperture = ccd_scope_info->getElement("APERTURE"); 0666 INDI_E *primary_focallength = ccd_scope_info->getElement("FOCAL_LENGTH"); 0667 QVERIFY(primary_aperture != nullptr); 0668 QVERIFY(primary_focallength != nullptr); 0669 primary_aperture->setValue(primaryTrain["aperture"].toDouble()); 0670 primary_focallength->setValue(primaryTrain["focal_length"].toDouble() * primaryTrain["reducer"].toDouble()); 0671 ccd_scope_info->processSetButton(); 0672 } 0673 if (m_GuiderDevice != nullptr) 0674 { 0675 KTRY_INDI_PROPERTY(m_GuiderDevice, "Options", "SCOPE_INFO", guider_scope_info); 0676 INDI_E *guider_aperture = guider_scope_info->getElement("APERTURE"); 0677 INDI_E *guider_focallength = guider_scope_info->getElement("FOCAL_LENGTH"); 0678 QVERIFY(guider_aperture != nullptr); 0679 QVERIFY(guider_focallength != nullptr); 0680 guider_aperture->setValue(guidingTrain["aperture"].toDouble()); 0681 guider_focallength->setValue(guidingTrain["focal_length"].toDouble() * guidingTrain["reducer"].toDouble()); 0682 guider_scope_info 0683 ->processSetButton(); 0684 } 0685 0686 // select the primary train for the mount 0687 KTRY_SET_COMBO(Ekos::Manager::Instance()->mountModule(), opticalTrainCombo, m_primaryTrain); 0688 } 0689 0690 bool TestEkosHelper::slewTo(double RA, double DEC, bool fast) 0691 { 0692 if (fast) 0693 { 0694 // reset mount model 0695 Ekos::Manager::Instance()->mountModule()->resetModel(); 0696 // sync to a point close before the meridian to speed up slewing 0697 Ekos::Manager::Instance()->mountModule()->sync(RA + 0.002, DEC); 0698 } 0699 // now slew very close before the meridian 0700 Ekos::Manager::Instance()->mountModule()->slew(RA, DEC); 0701 // wait a certain time until the mount slews 0702 QTest::qWait(3000); 0703 // wait until the mount is tracking 0704 KTRY_VERIFY_WITH_TIMEOUT_SUB(Ekos::Manager::Instance()->mountModule()->status() == ISD::Mount::MOUNT_TRACKING, 10000); 0705 0706 // everything succeeded 0707 return true; 0708 } 0709 0710 bool TestEkosHelper::startGuiding(double expTime) 0711 { 0712 if (getGuidingStatus() == Ekos::GUIDE_GUIDING) 0713 { 0714 QWARN("Start guiding ignored, guiding already running!"); 0715 return false; 0716 } 0717 0718 // setup guiding 0719 prepareGuidingModule(); 0720 0721 //set the exposure time 0722 KTRY_SET_DOUBLESPINBOX_SUB(Ekos::Manager::Instance()->guideModule(), guideExposure, expTime); 0723 0724 // start guiding 0725 KTRY_GADGET_SUB(Ekos::Manager::Instance()->guideModule(), QPushButton, guideB); 0726 // ensure that the guiding button is enabled (after MF it may take a while) 0727 KTRY_VERIFY_WITH_TIMEOUT_SUB(guideB->isEnabled(), 10000); 0728 expectedGuidingStates.enqueue(Ekos::GUIDE_GUIDING); 0729 KTRY_CLICK_SUB(Ekos::Manager::Instance()->guideModule(), guideB); 0730 KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(expectedGuidingStates, 120000); 0731 qCInfo(KSTARS_EKOS_TEST) << "Guiding started."; 0732 qCInfo(KSTARS_EKOS_TEST) << "Guiding calibration: " << Options::serializedCalibration(); 0733 qCInfo(KSTARS_EKOS_TEST) << "Waiting 2sec for settle guiding ..."; 0734 QTest::qWait(2000); 0735 // all checks succeeded, remember that guiding is running 0736 use_guiding = true; 0737 0738 return true; 0739 } 0740 0741 bool TestEkosHelper::stopGuiding() 0742 { 0743 // check whether guiding is not running or already stopped 0744 if (use_guiding == false || getGuidingStatus() == Ekos::GUIDE_IDLE || getGuidingStatus() == Ekos::GUIDE_ABORTED) 0745 return true; 0746 0747 // switch to guiding module 0748 KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->guideModule(), 1000)); 0749 0750 // stop guiding 0751 KTRY_GADGET_SUB(Ekos::Manager::Instance()->guideModule(), QPushButton, stopB); 0752 // check if guiding could be stopped 0753 if (stopB->isEnabled() == false) 0754 return true; 0755 KTRY_CLICK_SUB(Ekos::Manager::Instance()->guideModule(), stopB); 0756 KTRY_VERIFY_WITH_TIMEOUT_SUB(getGuidingStatus() == Ekos::GUIDE_IDLE || getGuidingStatus() == Ekos::GUIDE_ABORTED, 15000); 0757 qCInfo(KSTARS_EKOS_TEST) << "Guiding stopped."; 0758 qCInfo(KSTARS_EKOS_TEST) << "Waiting 2sec for settle guiding stop..."; // Avoid overlapping with focus pausing 0759 QTest::qWait(2000); 0760 // all checks succeeded 0761 return true; 0762 } 0763 0764 /* ********************************************************************************* 0765 * 0766 * Alignment support 0767 * 0768 * ********************************************************************************* */ 0769 bool TestEkosHelper::startAligning(double expTime) 0770 { 0771 KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->alignModule(), 1000)); 0772 // set the exposure time to the given value 0773 KTRY_SET_DOUBLESPINBOX_SUB(Ekos::Manager::Instance()->alignModule(), alignExposure, expTime); 0774 // reduce the accuracy to avoid testing problems 0775 KTRY_SET_SPINBOX_SUB(Ekos::Manager::Instance()->alignModule(), alignAccuracyThreshold, 300); 0776 // select the Luminance filter 0777 KTRY_SET_COMBO_SUB(Ekos::Manager::Instance()->alignModule(), alignFilter, "Luminance"); 0778 0779 // start alignment 0780 KTRY_GADGET_SUB(Ekos::Manager::Instance()->alignModule(), QPushButton, solveB); 0781 // ensure that the solve button is enabled (after MF it may take a while) 0782 KTRY_VERIFY_WITH_TIMEOUT_SUB(solveB->isEnabled(), 60000); 0783 KTRY_CLICK_SUB(Ekos::Manager::Instance()->alignModule(), solveB); 0784 // alignment started 0785 return true; 0786 } 0787 0788 0789 bool TestEkosHelper::checkAstrometryFiles() 0790 { 0791 // first check the current configuration 0792 QStringList dataDirs = KSUtils::getAstrometryDataDirs(); 0793 0794 // search if configured directories contain some index files 0795 for(QString dirname : dataDirs) 0796 { 0797 QDir dir(dirname); 0798 dir.setNameFilters(QStringList("index*")); 0799 dir.setFilter(QDir::Files); 0800 if (! dir.entryList().isEmpty()) 0801 return true; 0802 } 0803 QWARN(QString("No astrometry index files found in %1").arg( 0804 KSUtils::getAstrometryDataDirs().join(", ")).toStdString().c_str()); 0805 return false; 0806 } 0807 0808 bool TestEkosHelper::isAstrometryAvailable() 0809 { 0810 // avoid double checks if already found 0811 if (! m_astrometry_available) 0812 m_astrometry_available = checkAstrometryFiles(); 0813 0814 return m_astrometry_available; 0815 } 0816 0817 bool TestEkosHelper::executeFocusing(int initialFocusPosition) 0818 { 0819 // check whether focusing is already running 0820 if (! (getFocusStatus() == Ekos::FOCUS_IDLE || getFocusStatus() == Ekos::FOCUS_COMPLETE 0821 || getFocusStatus() == Ekos::FOCUS_ABORTED)) 0822 return true; 0823 0824 // prepare for focusing tests 0825 prepareFocusModule(); 0826 0827 initialFocusPosition = 40000; 0828 KTRY_SET_SPINBOX_SUB(Ekos::Manager::Instance()->focusModule(), absTicksSpin, initialFocusPosition); 0829 // start focusing 0830 KTRY_CLICK_SUB(Ekos::Manager::Instance()->focusModule(), startGotoB); 0831 // wait one second for settling 0832 QTest::qWait(3000); 0833 // start focusing 0834 expectedFocusStates.append(Ekos::FOCUS_COMPLETE); 0835 KTRY_CLICK_SUB(Ekos::Manager::Instance()->focusModule(), startFocusB); 0836 // wait for successful completion 0837 KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(expectedFocusStates, 180000); 0838 qCInfo(KSTARS_EKOS_TEST) << "Focusing finished."; 0839 // all checks succeeded 0840 return true; 0841 } 0842 0843 bool TestEkosHelper::stopFocusing() 0844 { 0845 // check whether focusing is already stopped 0846 if (getFocusStatus() == Ekos::FOCUS_IDLE || getFocusStatus() == Ekos::FOCUS_COMPLETE 0847 || getFocusStatus() == Ekos::FOCUS_ABORTED) 0848 return true; 0849 0850 // switch to focus module 0851 KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->focusModule(), 1000)); 0852 // stop focusing if necessary 0853 KTRY_GADGET_SUB(Ekos::Manager::Instance()->focusModule(), QPushButton, stopFocusB); 0854 if (stopFocusB->isEnabled()) 0855 KTRY_CLICK_SUB(Ekos::Manager::Instance()->focusModule(), stopFocusB); 0856 KTRY_VERIFY_WITH_TIMEOUT_SUB(getFocusStatus() == Ekos::FOCUS_IDLE 0857 || getFocusStatus() == Ekos::FOCUS_COMPLETE || 0858 getFocusStatus() == Ekos::FOCUS_ABORTED || getFocusStatus() == Ekos::FOCUS_FAILED, 15000); 0859 0860 // all checks succeeded 0861 return true; 0862 } 0863 0864 int TestEkosHelper::secondsToMF(QString message) 0865 { 0866 QRegExp mfPattern("Meridian flip in (\\d+):(\\d+):(\\d+)"); 0867 0868 int pos = mfPattern.indexIn(message); 0869 if (pos > -1) 0870 { 0871 int hh = mfPattern.cap(1).toInt(); 0872 int mm = mfPattern.cap(2).toInt(); 0873 int sec = mfPattern.cap(3).toInt(); 0874 if (hh >= 0) 0875 return (((hh * 60) + mm) * 60 + sec); 0876 else 0877 return (((hh * 60) - mm) * 60 - sec); 0878 } 0879 0880 // unknown time 0881 return -1; 0882 0883 } 0884 0885 void TestEkosHelper::updateJ2000Coordinates(SkyPoint *target) 0886 { 0887 SkyPoint J2000Coord(target->ra(), target->dec()); 0888 J2000Coord.catalogueCoord(KStars::Instance()->data()->ut().djd()); 0889 target->setRA0(J2000Coord.ra()); 0890 target->setDec0(J2000Coord.dec()); 0891 } 0892 0893 void TestEkosHelper::init() 0894 { 0895 // initialize the recorded states 0896 m_AlignStatus = Ekos::ALIGN_IDLE; 0897 m_CaptureStatus = Ekos::CAPTURE_IDLE; 0898 m_FocusStatus = Ekos::FOCUS_IDLE; 0899 m_GuideStatus = Ekos::GUIDE_IDLE; 0900 m_MFStatus = Ekos::MeridianFlipState::MOUNT_FLIP_NONE; 0901 m_DomeStatus = ISD::Dome::DOME_IDLE; 0902 // initialize the event queues 0903 expectedAlignStates.clear(); 0904 expectedCaptureStates.clear(); 0905 expectedFocusStates.clear(); 0906 expectedGuidingStates.clear(); 0907 expectedMountStates.clear(); 0908 expectedDomeStates.clear(); 0909 expectedMeridianFlipStates.clear(); 0910 expectedSchedulerStates.clear(); 0911 0912 0913 // disable by default 0914 use_guiding = false; 0915 // reset dithering flag 0916 dithered = false; 0917 // disable dithering by default 0918 Options::setDitherEnabled(false); 0919 // clear the script map 0920 scripts.clear(); 0921 // do not open INDI window on Ekos startup 0922 Options::setShowINDIwindowInitially(false); 0923 } 0924 void TestEkosHelper::cleanup() 0925 { 0926 0927 } 0928 0929 /* ********************************************************************************* 0930 * 0931 * Helper functions 0932 * 0933 * ********************************************************************************* */ 0934 void TestEkosHelper::setTreeviewCombo(QComboBox *combo, QString lookup) 0935 { 0936 // Match the text recursively in the model, this results in a model index with a parent 0937 QModelIndexList const list = combo->model()->match(combo->model()->index(0, 0), Qt::DisplayRole, 0938 QVariant::fromValue(lookup), 1, Qt::MatchRecursive); 0939 QVERIFY(0 < list.count()); 0940 QModelIndex const &index = list.first(); 0941 QCOMPARE(list.value(0).data().toString(), lookup); 0942 QVERIFY(!index.parent().parent().isValid()); 0943 // Now set the combobox model root to the match's parent 0944 combo->setRootModelIndex(index.parent()); 0945 combo->setModelColumn(index.column()); 0946 combo->setCurrentIndex(index.row()); 0947 0948 // Now reset 0949 combo->setRootModelIndex(QModelIndex()); 0950 combo->view()->setCurrentIndex(index); 0951 0952 // Check, if everything went well 0953 QCOMPARE(combo->currentText(), lookup); 0954 } 0955 0956 0957 // Simple write-string-to-file utility. 0958 bool TestEkosHelper::writeFile(const QString &filename, const QStringList &lines, QFileDevice::Permissions permissions) 0959 { 0960 QFile qFile(filename); 0961 if (qFile.open(QIODevice::WriteOnly | QIODevice::Text)) 0962 { 0963 QTextStream out(&qFile); 0964 for (QStringList::const_iterator it = lines.begin(); it != lines.end(); it++) 0965 out << *it + QChar::LineFeed; 0966 qFile.close(); 0967 qFile.setPermissions(filename, permissions); 0968 return true; 0969 } 0970 return false; 0971 } 0972 0973 bool TestEkosHelper::createCountingScript(Ekos::ScriptTypes scripttype, const QString scriptname) 0974 { 0975 QString logfilename = scriptname; 0976 logfilename.replace(scriptname.lastIndexOf(".sh"), 3, ".log"); 0977 QStringList script_content({"#!/bin/sh", 0978 QString("nr=`head -1 %1|| echo 0` 2> /dev/null").arg(logfilename), 0979 QString("nr=$(($nr+1))\necho $nr > %1").arg(logfilename)}); 0980 // create executable script 0981 bool result = writeFile(scriptname, script_content, 0982 QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); 0983 scripts.insert(scripttype, scriptname); 0984 // clear log file 0985 QFile logfile(logfilename); 0986 result |= logfile.remove(); 0987 // ready 0988 return result; 0989 } 0990 0991 bool TestEkosHelper::createAllCaptureScripts(QTemporaryDir *destination) 0992 { 0993 KWRAP_SUB(QVERIFY2(createCountingScript(Ekos::SCRIPT_PRE_JOB, 0994 destination->path() + "/prejob.sh"), "Creating pre-job script failed!")); 0995 KWRAP_SUB(QVERIFY2(createCountingScript(Ekos::SCRIPT_PRE_CAPTURE, 0996 destination->path() + "/precapture.sh"), "Creating pre-capture script failed!")); 0997 KWRAP_SUB(QVERIFY2(createCountingScript(Ekos::SCRIPT_POST_CAPTURE, 0998 destination->path() + "/postcapture.sh"), "Creating post-capture script failed!")); 0999 KWRAP_SUB(QVERIFY2(createCountingScript(Ekos::SCRIPT_POST_JOB, 1000 destination->path() + "/postjob.sh"), "Creating post-job script failed!")); 1001 // everything succeeded 1002 return true; 1003 } 1004 1005 int TestEkosHelper::countScriptRuns(Ekos::ScriptTypes scripttype) 1006 { 1007 if (scripts.contains(scripttype)) 1008 { 1009 QString scriptname = scripts[scripttype]; 1010 QString logfilename = scriptname; 1011 logfilename.replace(scriptname.lastIndexOf(".sh"), 3, ".log"); 1012 QFile logfile(logfilename); 1013 logfile.open(QIODevice::ReadOnly | QIODevice::Text); 1014 QTextStream in(&logfile); 1015 QString countstr = in.readLine(); 1016 logfile.close(); 1017 return countstr.toInt(); 1018 } 1019 else 1020 // no script found 1021 return -1; 1022 } 1023 1024 bool TestEkosHelper::checkScriptRuns(int captures_per_sequence, int sequences) 1025 { 1026 // check the log file if it holds the expected number 1027 int runs = countScriptRuns(Ekos::SCRIPT_PRE_JOB); 1028 KWRAP_SUB(QVERIFY2(runs == sequences, 1029 QString("Pre-job script not executed as often as expected: %1 expected, %2 detected.") 1030 .arg(captures_per_sequence).arg(runs).toLocal8Bit())); 1031 runs = countScriptRuns(Ekos::SCRIPT_PRE_CAPTURE); 1032 KWRAP_SUB( QVERIFY2(runs == sequences * captures_per_sequence, 1033 QString("Pre-capture script not executed as often as expected: %1 expected, %2 detected.") 1034 .arg(captures_per_sequence).arg(runs).toLocal8Bit())); 1035 runs = countScriptRuns(Ekos::SCRIPT_POST_JOB); 1036 KWRAP_SUB(QVERIFY2(runs == sequences, 1037 QString("Post-job script not executed as often as expected: %1 expected, %2 detected.") 1038 .arg(captures_per_sequence).arg(runs).toLocal8Bit())); 1039 runs = countScriptRuns(Ekos::SCRIPT_POST_CAPTURE); 1040 KWRAP_SUB(QVERIFY2(runs == sequences * captures_per_sequence, 1041 QString("Post-capture script not executed as often as expected: %1 expected, %2 detected.") 1042 .arg(captures_per_sequence).arg(runs).toLocal8Bit())); 1043 // everything succeeded 1044 return true; 1045 } 1046 1047 /* ********************************************************************************* 1048 * 1049 * Slots for catching state changes 1050 * 1051 * ********************************************************************************* */ 1052 1053 void TestEkosHelper::alignStatusChanged(Ekos::AlignState status) 1054 { 1055 m_AlignStatus = status; 1056 // check if the new state is the next one expected, then remove it from the stack 1057 if (!expectedAlignStates.isEmpty() && expectedAlignStates.head() == status) 1058 expectedAlignStates.dequeue(); 1059 } 1060 1061 void TestEkosHelper::mountStatusChanged(ISD::Mount::Status status) 1062 { 1063 m_MountStatus = status; 1064 // check if the new state is the next one expected, then remove it from the stack 1065 if (!expectedMountStates.isEmpty() && expectedMountStates.head() == status) 1066 expectedMountStates.dequeue(); 1067 } 1068 1069 void TestEkosHelper::meridianFlipStatusChanged(Ekos::MeridianFlipState::MeridianFlipMountState status) 1070 { 1071 m_MFStatus = status; 1072 // check if the new state is the next one expected, then remove it from the stack 1073 if (!expectedMeridianFlipStates.isEmpty() && expectedMeridianFlipStates.head() == status) 1074 expectedMeridianFlipStates.dequeue(); 1075 } 1076 1077 void TestEkosHelper::focusStatusChanged(Ekos::FocusState status) 1078 { 1079 m_FocusStatus = status; 1080 // check if the new state is the next one expected, then remove it from the stack 1081 if (!expectedFocusStates.isEmpty() && expectedFocusStates.head() == status) 1082 expectedFocusStates.dequeue(); 1083 } 1084 1085 void TestEkosHelper::guidingStatusChanged(Ekos::GuideState status) 1086 { 1087 m_GuideStatus = status; 1088 // check if the new state is the next one expected, then remove it from the stack 1089 if (!expectedGuidingStates.isEmpty() && expectedGuidingStates.head() == status) 1090 expectedGuidingStates.dequeue(); 1091 // dithering detected? 1092 if (status == Ekos::GUIDE_DITHERING || status == Ekos::GUIDE_DITHERING_SUCCESS || status == Ekos::GUIDE_DITHERING_ERROR) 1093 dithered = true; 1094 1095 } 1096 1097 void TestEkosHelper::guideDeviationChanged(double delta_ra, double delta_dec) 1098 { 1099 m_GuideDeviation = std::hypot(delta_ra, delta_dec); 1100 } 1101 1102 void TestEkosHelper::captureStatusChanged(Ekos::CaptureState status) 1103 { 1104 m_CaptureStatus = status; 1105 // check if the new state is the next one expected, then remove it from the stack 1106 if (!expectedCaptureStates.isEmpty() && expectedCaptureStates.head() == status) 1107 expectedCaptureStates.dequeue(); 1108 } 1109 1110 void TestEkosHelper::schedulerStatusChanged(Ekos::SchedulerState status) 1111 { 1112 m_SchedulerStatus = status; 1113 // check if the new state is the next one expected, then remove it from the stack 1114 if (!expectedSchedulerStates.isEmpty() && expectedSchedulerStates.head() == status) 1115 expectedSchedulerStates.dequeue(); 1116 } 1117 1118 void TestEkosHelper::domeStatusChanged(ISD::Dome::Status status) 1119 { 1120 m_DomeStatus = status; 1121 // check if the new state is the next one expected, then remove it from the stack 1122 if (!expectedDomeStates.isEmpty() && expectedDomeStates.head() == status) 1123 expectedDomeStates.dequeue(); 1124 }