12#include "ekos/scheduler/framingassistantui.h"
13#include "ksnotification.h"
14#include "ksmessagebox.h"
16#include "kstarsdata.h"
19#include "scheduleradaptor.h"
20#include "schedulerjob.h"
21#include "schedulerprocess.h"
22#include "schedulermodulestate.h"
23#include "schedulerutils.h"
24#include "scheduleraltitudegraph.h"
25#include "skymapcomposite.h"
26#include "skycomponents/mosaiccomponent.h"
27#include "skyobjects/mosaictiles.h"
28#include "auxiliary/QProgressIndicator.h"
29#include "dialogs/finddialog.h"
30#include "ekos/manager.h"
31#include "ekos/capture/sequencejob.h"
32#include "ekos/capture/placeholderpath.h"
33#include "skyobjects/starobject.h"
34#include "greedyscheduler.h"
35#include "ekos/auxiliary/opticaltrainmanager.h"
36#include "ekos/auxiliary/solverutils.h"
37#include "ekos/auxiliary/stellarsolverprofile.h"
40#include <KConfigDialog>
41#include <KActionCollection>
46#include <ekos_scheduler_debug.h>
48#include "ekos/capture/sequenceeditor.h"
54#define INDEX_FOLLOWER 1
56#define BAD_SCORE -1000
57#define RESTART_GUIDING_DELAY_MS 5000
59#define DEFAULT_MIN_ALTITUDE 15
60#define DEFAULT_MIN_MOON_SEPARATION 0
65#define TEST_PRINT if (false) fprintf
89 setupScheduler(ekosPathString, ekosInterfaceString);
96 schedulerPathString = path;
97 kstarsInterfaceString = interface;
98 setupScheduler(ekosPathStr, ekosInterfaceStr);
101void Scheduler::setupScheduler(
const QString &ekosPathStr,
const QString &ekosInterfaceStr)
104 if (kstarsInterfaceString ==
"org.kde.kstars")
107 qRegisterMetaType<Ekos::SchedulerState>(
"Ekos::SchedulerState");
108 qDBusRegisterMetaType<Ekos::SchedulerState>();
110 m_moduleState.
reset(
new SchedulerModuleState());
111 m_process.reset(
new SchedulerProcess(moduleState(), ekosPathStr, ekosInterfaceStr));
116 QDateTime currentDateTime = SchedulerModuleState::getLocalTime();
117 QTime currentTime = currentDateTime.
time();
119 currentDateTime.
setTime(currentTime);
122 startupTimeEdit->setDateTime(currentDateTime);
123 schedulerUntilValue->setDateTime(currentDateTime);
134 leadFollowerSelectionCB->setModel(model);
136 sleepLabel->setPixmap(
138 changeSleepLabel(
"",
false);
141 bottomLayout->addWidget(pi, 0);
143 geo = KStarsData::Instance()->geo();
146 raBox->setUnits(dmsBox::HOURS);
155 queueTable->setToolTip(
156 i18n(
"Job scheduler list.\nClick to select a job in the list.\nDouble click to edit a job with the left-hand fields.\nShift click to view a job's altitude tonight."));
157 QTableWidgetItem *statusHeader = queueTable->horizontalHeaderItem(SCHEDCOL_STATUS);
158 QTableWidgetItem *altitudeHeader = queueTable->horizontalHeaderItem(SCHEDCOL_ALTITUDE);
159 QTableWidgetItem *startupHeader = queueTable->horizontalHeaderItem(SCHEDCOL_STARTTIME);
160 QTableWidgetItem *completionHeader = queueTable->horizontalHeaderItem(SCHEDCOL_ENDTIME);
161 QTableWidgetItem *captureCountHeader = queueTable->horizontalHeaderItem(SCHEDCOL_CAPTURES);
163 if (statusHeader !=
nullptr)
164 statusHeader->
setToolTip(
i18n(
"Current status of the job, managed by the Scheduler.\n"
165 "If invalid, the Scheduler was not able to find a proper observation time for the target.\n"
166 "If aborted, the Scheduler missed the scheduled time or encountered transitory issues and will reschedule the job.\n"
167 "If complete, the Scheduler verified that all sequence captures requested were stored, including repeats."));
168 if (altitudeHeader !=
nullptr)
169 altitudeHeader->
setToolTip(
i18n(
"Current altitude of the target of the job.\n"
170 "A rising target is indicated with an arrow going up.\n"
171 "A setting target is indicated with an arrow going down."));
172 if (startupHeader !=
nullptr)
173 startupHeader->
setToolTip(
i18n(
"Startup time of the job, as estimated by the Scheduler.\n"
174 "The altitude at startup, if available, is displayed too.\n"
175 "Fixed time from user or culmination time is marked with a chronometer symbol."));
176 if (completionHeader !=
nullptr)
177 completionHeader->
setToolTip(
i18n(
"Completion time for the job, as estimated by the Scheduler.\n"
178 "You may specify a fixed time to limit duration of looping jobs. "
179 "A warning symbol indicates the altitude at completion may cause the job to abort before completion.\n"));
180 if (captureCountHeader !=
nullptr)
181 captureCountHeader->
setToolTip(
i18n(
"Count of captures stored for the job, based on its sequence job.\n"
182 "This is a summary, additional specific frame types may be required to complete the job."));
188 removeFromQueueB->setToolTip(
189 i18n(
"Remove selected job from the observation list.\nJob properties are copied in the edition fields before removal."));
193 queueUpB->setToolTip(
i18n(
"Move selected job one line up in the list.\n"));
196 queueDownB->setToolTip(
i18n(
"Move selected job one line down in the list.\n"));
200 evaluateOnlyB->setToolTip(
i18n(
"Reset state and force reevaluation of all observation jobs."));
203 sortJobsB->setToolTip(
204 i18n(
"Reset state and sort observation jobs per altitude and movement in sky, using the start time of the first job.\n"
205 "This action sorts setting targets before rising targets, and may help scheduling when starting your observation.\n"
206 "Note the algorithm first calculates all altitudes using the same time, then evaluates jobs."));
211 positionAngleSpin->setSpecialValueText(
"--");
226 selectShutdownScriptB->setIcon(
241 schedulerRepeatEverything->setEnabled(Options::rememberJobProgress() ==
false);
242 executionSequenceLimit->setEnabled(Options::rememberJobProgress() ==
false);
243 executionSequenceLimit->setValue(Options::schedulerExecutionSequencesLimit());
246 leadFollowerSelectionCB->setEnabled(
false);
259 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated,
this, &Scheduler::refreshOpticalTrain);
263 mosaicB->setDown(checked);
298 pauseB->setCheckable(
false);
317 connect(moduleState().data(), &SchedulerModuleState::ekosStateChanged,
this, &Scheduler::ekosStateChanged);
318 connect(moduleState().data(), &SchedulerModuleState::indiStateChanged,
this, &Scheduler::indiStateChanged);
319 connect(moduleState().data(), &SchedulerModuleState::indiCommunicationStatusChanged,
this,
320 &Scheduler::indiCommunicationStatusChanged);
322 connect(moduleState().data(), &SchedulerModuleState::startupStateChanged,
this, &Scheduler::startupStateChanged);
323 connect(moduleState().data(), &SchedulerModuleState::shutdownStateChanged,
this, &Scheduler::shutdownStateChanged);
324 connect(moduleState().data(), &SchedulerModuleState::parkWaitStateChanged,
this, &Scheduler::parkWaitStateChanged);
325 connect(moduleState().data(), &SchedulerModuleState::profilesChanged,
this, &Scheduler::updateProfiles);
327 connect(moduleState().data(), &SchedulerModuleState::jobStageChanged,
this, &Scheduler::updateJobStageUI);
329 connect(moduleState().data(), &SchedulerModuleState::currentProfileChanged,
this, [&]()
331 schedulerProfileCombo->setCurrentText(moduleState()->currentProfile());
337 connect(process().data(), &SchedulerProcess::shutdownStarted,
this, &Scheduler::handleShutdownStarted);
339 connect(process().data(), &SchedulerProcess::jobsUpdated,
this, &Scheduler::handleJobsUpdated);
340 connect(process().data(), &SchedulerProcess::targetDistance,
this, &Scheduler::targetDistance);
345 connect(process().data(), &SchedulerProcess::jobStarted,
this, &Scheduler::jobStarted);
346 connect(process().data(), &SchedulerProcess::jobEnded,
this, &Scheduler::jobEnded);
347 connect(process().data(), &SchedulerProcess::syncGreedyParams,
this, &Scheduler::syncGreedyParams);
349 connect(process().data(), &SchedulerProcess::changeSleepLabel,
this, &Scheduler::changeSleepLabel);
352 connect(process().data(), &SchedulerProcess::newWeatherStatus,
this, &Scheduler::setWeatherStatus);
360 connect(errorHandlingButtonGroup,
static_cast<void (QButtonGroup::*)(QAbstractButton *)
>
365 Options::setErrorHandlingStrategy(strategy);
366 errorHandlingStrategyDelay->setEnabled(strategy != ERROR_DONT_RESTART);
370 Options::setErrorHandlingStrategyDelay(value);
374 if (Options::schedulerAlgorithm() != ALGORITHM_GREEDY)
376 process()->appendLogText(
377 i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
378 Options::setSchedulerAlgorithm(ALGORITHM_GREEDY);
382 setAlgorithm(Options::schedulerAlgorithm());
386 SkyPoint
center = SkyMap::Instance()->getCenterPoint();
395 const SkyPoint coords = process()->mountCoords();
398 setTargetCoords(coords.
ra(), coords.
dec(),
false);
400 copyMountTargetB->setIcon(QIcon(
":/icons/ekos_mount_simple.png"));
404 if (!m_SequenceEditor)
405 m_SequenceEditor.reset(
new SequenceEditor(
this));
407 m_SequenceEditor->show();
408 m_SequenceEditor->raise();
411 m_JobUpdateDebounce.setSingleShot(
true);
412 m_JobUpdateDebounce.setInterval(1000);
415 emit jobsUpdated(moduleState()->getJSONJobs());
418 moduleState()->calculateDawnDusk();
419 process()->loadProfiles();
423 loadGlobalSettings();
425 refreshOpticalTrain();
428QString Scheduler::getCurrentJobName()
430 return (activeJob() !=
nullptr ? activeJob()->getName() :
"");
441 m_OpsOffsetSettings =
new OpsOffsetSettings();
445 m_OpsAlignmentSettings =
new OpsAlignmentSettings();
446 page = dialog->
addPage(m_OpsAlignmentSettings,
i18n(
"Alignment"));
449 m_OpsJobsSettings =
new OpsJobsSettings();
450 page = dialog->
addPage(m_OpsJobsSettings,
i18n(
"Jobs"));
453 m_OpsScriptsSettings =
new OpsScriptsSettings();
454 page = dialog->
addPage(m_OpsScriptsSettings,
i18n(
"Scripts"));
460 if (enable == jobChangesAreWatched)
472 schedulerStartupScript,
473 schedulerShutdownScript
484 schedulerProfileCombo,
486 leadFollowerSelectionCB
492 errorHandlingButtonGroup,
494 constraintButtonGroup,
495 completionButtonGroup,
496 startupProcedureButtonGroup,
497 shutdownProcedureGroup
502 errorHandlingRescheduleErrorsCB,
503 schedulerMoonSeparation,
504 schedulerMoonAltitude,
512 schedulerExecutionSequencesLimit,
513 errorHandlingStrategyDelay
518 schedulerMoonSeparationValue,
519 schedulerMoonAltitudeMaxValue,
520 schedulerAltitudeValue,
535 for (
auto *
const control : lineEdits)
540 for (
auto *
const control : dateEdits)
545 for (
auto *
const control : comboBoxes)
547 if (control == leadFollowerSelectionCB)
549 this, [
this](
int pos)
551 setJobManipulation(queueUpB->isEnabled() || queueDownB->isEnabled(), removeFromQueueB->isEnabled(),
pos == INDEX_LEAD);
560 for (
auto *
const control : buttonGroups)
561#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
569 for (
auto *
const control : buttons)
574 for (
auto *
const control : spinBoxes)
579 for (
auto *
const control : dspinBoxes)
592 for (
auto *
const control : lineEdits)
594 for (
auto *
const control : dateEdits)
596 for (
auto *
const control : comboBoxes)
598 for (
auto *
const control : buttons)
600 for (
auto *
const control : buttonGroups)
601#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
606 for (
auto *
const control : spinBoxes)
608 for (
auto *
const control : dspinBoxes)
612 jobChangesAreWatched = enable;
617 schedulerRepeatEverything->
setEnabled(Options::rememberJobProgress() ==
false);
618 executionSequenceLimit->setEnabled(Options::rememberJobProgress() ==
false);
623 if (FindDialog::Instance()->execWithParent(Ekos::Manager::Instance()) ==
QDialog::Accepted)
625 SkyObject *
object = FindDialog::Instance()->targetObject();
630void Scheduler::addObject(
SkyObject *
object)
632 if (
object !=
nullptr)
636 if (object->
name() ==
"star")
644 nameEdit->setText(finalObjectName);
645 setTargetCoords(object->
ra0(), object->
dec0());
654 "FITS (*.fits *.fit);;XISF (*.xisf)");
658 processFITSSelection(url);
661void Scheduler::processFITSSelection(
const QUrl &url)
671 const QString filename = fitsEdit->text();
673 double ra = 0, dec = 0;
675 char comment[128], error_status[512];
676 fitsfile *fptr =
nullptr;
678 if (fits_open_diskfile(&fptr, filename.
toLatin1(), READONLY, &status))
680 fits_report_error(stderr, status);
681 fits_get_errstatus(status, error_status);
687 if (fits_movabs_hdu(fptr, 1, IMAGE_HDU, &status))
689 fits_report_error(stderr, status);
690 fits_get_errstatus(status, error_status);
696 char objectra_str[32] = {0};
697 if (fits_read_key(fptr, TSTRING,
"OBJCTRA", objectra_str, comment, &status))
699 if (fits_read_key(fptr, TDOUBLE,
"RA", &ra, comment, &status))
701 fits_report_error(stderr, status);
702 fits_get_errstatus(status, error_status);
703 process()->appendLogText(
i18n(
"FITS header: cannot find OBJCTRA (%1).", QString(error_status)));
715 char objectde_str[32] = {0};
716 if (fits_read_key(fptr, TSTRING,
"OBJCTDEC", objectde_str, comment, &status))
718 if (fits_read_key(fptr, TDOUBLE,
"DEC", &dec, comment, &status))
720 fits_report_error(stderr, status);
721 fits_get_errstatus(status, error_status);
722 process()->appendLogText(
i18n(
"FITS header: cannot find OBJCTDEC (%1).", QString(error_status)));
733 setTargetCoords(raDMS, deDMS);
735 char object_str[256] = {0};
736 if (fits_read_key(fptr, TSTRING,
"OBJECT", object_str, comment, &status))
738 QFileInfo info(filename);
739 nameEdit->setText(info.completeBaseName());
743 nameEdit->setText(object_str);
747bool Scheduler::processCoordinates(dms &ra, dms &dec)
750 bool raOk =
false, decOk =
false;
751 ra = raBox->createDms(&raOk);
752 dec = decBox->createDms(&decOk);
755 process()->appendLogText(
i18n(
"Warning: RA value %1 is invalid.", raBox->text()));
758 process()->appendLogText(
i18n(
"Warning: DEC value %1 is invalid.", decBox->text()));
761 return (raOk && decOk);
772 sequenceEdit->setText(sequenceURL.toLocalFile());
780 dirPath.toLocalFile(),
781 i18n(
"Ekos Sequence Queue (*.esq)"));
789 "Select Startup Script"),
791 i18n(
"Script (*)")));
792 if (moduleState()->startupScriptURL().isEmpty())
797 moduleState()->setDirty(
true);
798 schedulerStartupScript->setText(moduleState()->startupScriptURL().toLocalFile());
804 "Select Shutdown Script"),
806 i18n(
"Script (*)")));
807 if (moduleState()->shutdownScriptURL().isEmpty())
812 moduleState()->setDirty(
true);
813 schedulerShutdownScript->setText(moduleState()->shutdownScriptURL().toLocalFile());
818 if (0 <= jobUnderEdit)
821 job = moduleState()->jobs().at(jobUnderEdit);
831 int currentRow = moduleState()->currentPosition();
835 currentRow = queueTable->rowCount();
843 if (moduleState()->jobs().count() > currentRow)
844 moduleState()->setCurrentPosition(currentRow);
847 emit jobsUpdated(moduleState()->getJSONJobs());
854 auto job = moduleState()->jobs().at(index);
861 emit jobsUpdated(moduleState()->getJSONJobs());
868 if (nameEdit->text().isEmpty())
870 process()->appendLogText(
i18n(
"Warning: Target name is required."));
874 if (sequenceEdit->text().isEmpty())
876 process()->appendLogText(
i18n(
"Warning: Sequence file is required."));
881 if ((raBox->isEmpty() || decBox->isEmpty()) && fitsURL.isEmpty())
883 process()->appendLogText(
i18n(
"Warning: Target coordinates are required."));
888 if (readCoordsFromUI() ==
false)
897 if (asapConditionR->isChecked())
898 startCondition = START_ASAP;
901 if (schedulerCompleteSequences->isChecked())
902 stopCondition = FINISH_SEQUENCE;
903 else if (schedulerRepeatSequences->isChecked())
904 stopCondition = FINISH_REPEAT;
905 else if (schedulerUntilTerminated->isChecked())
906 stopCondition = FINISH_LOOP;
908 double altConstraint = SchedulerJob::UNDEFINED_ALTITUDE;
909 if (schedulerAltitude->isChecked())
910 altConstraint = schedulerAltitudeValue->value();
912 double moonSeparation = -1;
913 if (schedulerMoonSeparation->isChecked())
914 moonSeparation = schedulerMoonSeparationValue->value();
916 double moonMaxAltitude = 90;
917 if (schedulerMoonAltitude->isChecked())
918 moonMaxAltitude = schedulerMoonAltitudeMaxValue->value();
920 QString train = opticalTrainCombo->currentText() ==
"--" ?
"" : opticalTrainCombo->currentText();
924 SchedulerUtils::setupJob(*job, nameEdit->text(), leadFollowerSelectionCB->currentIndex() == INDEX_LEAD, groupEdit->text(),
925 train, targetCoords.ra0(), targetCoords.dec0(),
926 KStarsData::Instance()->ut().djd(),
927 positionAngleSpin->value(), sequenceURL, fitsURL,
929 startCondition, startupTimeEdit->dateTime(),
930 stopCondition, schedulerUntilValue->dateTime(), schedulerExecutionSequencesLimit->value(),
935 schedulerWeather->isChecked(),
936 schedulerTwilight->isChecked(),
937 schedulerHorizon->isChecked(),
939 schedulerTrackStep->isChecked(),
940 schedulerFocusStep->isChecked(),
941 schedulerAlignStep->isChecked(),
942 schedulerGuideStep->isChecked());
949bool Scheduler::readCoordsFromUI()
952 if (processCoordinates(ra, dec) ==
false)
960 setTargetCoords(ra, dec, epochCB->currentText() ==
"J2000");
969 int currentRow = moduleState()->currentPosition() + 1;
974 if (0 <= jobUnderEdit)
977 if (jobUnderEdit != currentRow - 1)
979 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"BUG: the observation job under edit does not match the selected row in the job table.";
983 job = moduleState()->jobs().at(jobUnderEdit);
996 job =
new SchedulerJob();
1006 moduleState()->mutlableJobs().insert(currentRow, job);
1012 job->setLeadJob(moduleState()->findLead(currentRow - 1));
1013 moduleState()->refreshFollowerLists();
1018 int numWarnings = 0;
1021 foreach (SchedulerJob *a_job, moduleState()->jobs())
1023 if (a_job == job || !a_job->isLead())
1027 else if (a_job->getName() == job->getName())
1029 int const a_job_row = moduleState()->jobs().indexOf(a_job);
1032 process()->appendLogText(
i18n(
"Warning: job '%1' at row %2 has a duplicate target at row %3, "
1033 "the scheduler may consider the same storage for captures.",
1034 job->getName(), currentRow, a_job_row));
1037 if (a_job->getSequenceFile() == job->getSequenceFile())
1039 if (a_job->getRepeatsRequired() == job->getRepeatsRequired() && Options::rememberJobProgress())
1040 process()->appendLogText(
i18n(
"Warning: jobs '%1' at row %2 and %3 probably require a different repeat count "
1041 "as currently they will complete simultaneously after %4 batches (or disable option 'Remember job progress')",
1042 job->getName(), currentRow, a_job_row, job->getRepeatsRequired()));
1046 if (++numWarnings >= 1)
1048 process()->appendLogText(
i18n(
"Skipped checking for duplicates."));
1058 queueSaveAsB->setEnabled(
true);
1059 queueSaveB->setEnabled(
true);
1060 startB->setEnabled(
true);
1061 evaluateOnlyB->setEnabled(
true);
1063 checkJobInputComplete();
1065 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' at row #%2 was saved.").
arg(job->getName()).
arg(currentRow + 1);
1069 if (SCHEDULER_LOADING != moduleState()->schedulerState())
1071 process()->evaluateJobs(
true);
1077 nameEdit->setText(job->getName());
1078 groupEdit->setText(job->getGroup());
1080 setTargetCoords(job->getTargetCoords().
ra0(), job->getTargetCoords().
dec0());
1083 fitsURL = job->getFITSFile().
isEmpty() ?
QUrl() : job->getFITSFile();
1084 fitsEdit->setText(fitsURL.toLocalFile());
1086 schedulerTrackStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_TRACK);
1087 schedulerFocusStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_FOCUS);
1088 schedulerAlignStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_ALIGN);
1089 schedulerGuideStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_GUIDE);
1091 switch (job->getFileStartupCondition())
1094 asapConditionR->setChecked(
true);
1098 startupTimeConditionR->setChecked(
true);
1099 startupTimeEdit->setDateTime(job->getStartupTime());
1103 if (job->getMinAltitude())
1105 schedulerAltitude->setChecked(
true);
1106 schedulerAltitudeValue->setValue(job->getMinAltitude());
1110 schedulerAltitude->setChecked(
false);
1111 schedulerAltitudeValue->setValue(DEFAULT_MIN_ALTITUDE);
1114 if (job->getMinMoonSeparation() > 0)
1116 schedulerMoonSeparation->setChecked(
true);
1117 schedulerMoonSeparationValue->setValue(job->getMinMoonSeparation());
1121 schedulerMoonSeparation->setChecked(
false);
1122 schedulerMoonSeparationValue->setValue(DEFAULT_MIN_MOON_SEPARATION);
1125 if (job->getMaxMoonAltitude() < 90)
1127 schedulerMoonAltitude->setChecked(
true);
1128 schedulerMoonAltitudeMaxValue->setValue(job->getMaxMoonAltitude());
1132 schedulerMoonAltitude->setChecked(
false);
1135 schedulerWeather->setChecked(job->getEnforceWeather());
1137 schedulerTwilight->blockSignals(
true);
1138 schedulerTwilight->setChecked(job->getEnforceTwilight());
1139 schedulerTwilight->blockSignals(
false);
1141 schedulerHorizon->blockSignals(
true);
1142 schedulerHorizon->setChecked(job->getEnforceArtificialHorizon());
1143 schedulerHorizon->blockSignals(
false);
1147 leadFollowerSelectionCB->setCurrentIndex(INDEX_LEAD);
1151 leadFollowerSelectionCB->setCurrentIndex(INDEX_FOLLOWER);
1154 if (job->getOpticalTrain().
isEmpty())
1155 opticalTrainCombo->setCurrentIndex(0);
1157 opticalTrainCombo->setCurrentText(job->getOpticalTrain());
1159 sequenceURL = job->getSequenceFile();
1160 sequenceEdit->setText(sequenceURL.toLocalFile());
1162 positionAngleSpin->setValue(job->getPositionAngle());
1164 switch (job->getCompletionCondition())
1166 case FINISH_SEQUENCE:
1167 schedulerCompleteSequences->setChecked(
true);
1171 schedulerRepeatSequences->setChecked(
true);
1172 schedulerExecutionSequencesLimit->setValue(job->getRepeatsRequired());
1176 schedulerUntilTerminated->setChecked(
true);
1180 schedulerUntil->setChecked(
true);
1181 schedulerUntilValue->setDateTime(job->getFinishAtTime());
1191 schedulerParkDome->setChecked(Options::schedulerParkDome());
1192 schedulerParkMount->setChecked(Options::schedulerParkMount());
1193 schedulerCloseDustCover->setChecked(Options::schedulerCloseDustCover());
1194 schedulerWarmCCD->setChecked(Options::schedulerWarmCCD());
1195 schedulerUnparkDome->setChecked(Options::schedulerUnparkDome());
1196 schedulerUnparkMount->setChecked(Options::schedulerUnparkMount());
1197 schedulerOpenDustCover->setChecked(Options::schedulerOpenDustCover());
1199 errorHandlingStrategyDelay->setValue(Options::errorHandlingStrategyDelay());
1200 errorHandlingRescheduleErrorsCB->setChecked(Options::rescheduleErrors());
1202 schedulerShutdownScript->setText(moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile));
1204 if (process()->captureInterface() !=
nullptr)
1206 QVariant hasCoolerControl = process()->captureInterface()->property(
"coolerControl");
1207 if (hasCoolerControl.
isValid())
1209 schedulerWarmCCD->setEnabled(hasCoolerControl.
toBool());
1210 moduleState()->setCaptureReady(
true);
1218 if (job ==
nullptr && moduleState()->jobs().
size() > 0)
1220 int const currentRow = moduleState()->currentPosition();
1221 if (0 <= currentRow && currentRow < moduleState()->jobs().
size())
1222 job = moduleState()->jobs().at(currentRow);
1226 qCWarning(KSTARS_EKOS_SCHEDULER()) <<
"Cannot update night time, no matching job found at line" << currentRow;
1231 QDateTime const dawn = job ? job->getDawnAstronomicalTwilight() : moduleState()->Dawn();
1232 QDateTime const dusk = job ? job->getDuskAstronomicalTwilight() : moduleState()->Dusk();
1234 QChar const warning(dawn == dusk ? 0x26A0 :
'-');
1238bool Scheduler::modifyJob(
int index)
1246 queueTable->selectRow(index);
1247 auto modelIndex = queueTable->model()->index(index, 0);
1254 if (jobUnderEdit == i.
row())
1257 SchedulerJob *
const job = moduleState()->jobs().at(i.
row());
1272 startB->setEnabled(
false);
1273 evaluateOnlyB->setEnabled(
false);
1278 jobUnderEdit = i.
row();
1279 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' at row #%2 is currently edited.").
arg(job->getName()).
arg(
1289 queueSaveB->setToolTip(
"Save schedule to " + schedulerURL.fileName());
1294 Q_UNUSED(deselected)
1297 if (jobChangesAreWatched ==
false || selected.
empty())
1303 if ((current.
row() + 1) > moduleState()->jobs().
size())
1305 qCWarning(KSTARS_EKOS_SCHEDULER()) <<
"Unexpected row number" << current.
row() <<
"- ignoring.";
1308 moduleState()->setCurrentPosition(current.
row());
1309 SchedulerJob *
const job = moduleState()->jobs().at(current.
row());
1313 if (jobUnderEdit < 0)
1315 else if (jobUnderEdit != current.
row())
1318 process()->appendLogText(
i18n(
"Stop editing of job #%1, resetting to original value.", jobUnderEdit + 1));
1323 else nightTime->setText(
"-");
1332 handleAltitudeGraph(index.
row());
1336 if (index.
isValid() && index.
row() < moduleState()->jobs().count())
1347 addToQueueB->setToolTip(
i18n(
"Use edition fields to create a new job in the observation list."));
1353 addToQueueB->setToolTip(
i18n(
"Apply job changes."));
1356 checkJobInputComplete();
1363 int const currentRow = moduleState()->currentPosition();
1364 if (currentRow >= 0)
1366 SchedulerJob *currentJob = moduleState()->jobs().at(currentRow);
1368 queueUpB->setEnabled(0 < currentRow &&
1369 (currentJob->isLead() || (currentRow > 1 && moduleState()->findLead(currentRow - 2) !=
nullptr)));
1371 queueDownB->setEnabled(currentRow < queueTable->rowCount() - 1 &&
1372 (moduleState()->findLead(currentRow + 1,
false) !=
nullptr));
1377 queueUpB->setEnabled(
false);
1378 queueDownB->setEnabled(
false);
1380 sortJobsB->setEnabled(can_reorder);
1381 removeFromQueueB->setEnabled(can_delete);
1383 nameEdit->setEnabled(is_lead);
1384 selectObjectB->setEnabled(is_lead);
1385 targetStarLabel->setVisible(is_lead);
1386 raBox->setEnabled(is_lead);
1387 decBox->setEnabled(is_lead);
1388 copySkyCenterB->setEnabled(is_lead);
1389 schedulerProfileCombo->setEnabled(is_lead);
1390 fitsEdit->setEnabled(is_lead);
1391 selectFITSB->setEnabled(is_lead);
1392 groupEdit->setEnabled(is_lead);
1393 schedulerTrackStep->setEnabled(is_lead);
1394 schedulerFocusStep->setEnabled(is_lead);
1395 schedulerAlignStep->setEnabled(is_lead);
1396 schedulerGuideStep->setEnabled(is_lead);
1397 startupGroup->setEnabled(is_lead);
1398 contraintsGroup->setEnabled(is_lead);
1401 leadFollowerSelectionCB->setEnabled(moduleState()->findLead(queueTable->currentRow()) !=
nullptr);
1402 if (leadFollowerSelectionCB->isEnabled() ==
false)
1403 leadFollowerSelectionCB->setCurrentIndex(INDEX_LEAD);
1409 foreach (SchedulerJob* job, moduleState()->jobs())
1410 if (!reordered_sublist.
contains(job))
1411 reordered_sublist.
append(job);
1413 if (moduleState()->jobs() != reordered_sublist)
1416 int const selectedRow = moduleState()->currentPosition();
1417 SchedulerJob *
const selectedJob = 0 <= selectedRow ? moduleState()->jobs().at(selectedRow) :
nullptr;
1420 moduleState()->setJobs(reordered_sublist);
1423 for (SchedulerJob *job : moduleState()->jobs())
1427 if (
nullptr != selectedJob)
1428 moduleState()->setCurrentPosition(moduleState()->jobs().indexOf(selectedJob));
1437 int const rowCount = queueTable->rowCount();
1438 int const currentRow = queueTable->currentRow();
1440 SchedulerJob *job = moduleState()->jobs().at(currentRow);
1442 if (moduleState()->jobs().at(currentRow)->isLead())
1444 int const rows = 1 + job->followerJobs().
count();
1446 if (currentRow - rows < 0)
1450 destinationRow = currentRow - 1 - moduleState()->jobs().at(currentRow - rows)->followerJobs().count();
1453 destinationRow = currentRow - 1;
1456 if (currentRow < 0 || rowCount <= 1 || destinationRow < 0)
1459 if (moduleState()->jobs().at(currentRow)->isLead())
1462 moduleState()->mutlableJobs().removeOne(job);
1463 for (
auto follower : job->followerJobs())
1464 moduleState()->mutlableJobs().removeOne(follower);
1467 moduleState()->mutlableJobs().insert(destinationRow++, job);
1469 for (
auto follower : job->followerJobs())
1470 moduleState()->mutlableJobs().insert(destinationRow++, follower);
1472 for (
int i = currentRow; i > destinationRow; i--)
1475 moduleState()->setCurrentPosition(destinationRow - job->followerJobs().
count() - 1);
1480#if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
1481 moduleState()->mutlableJobs().swapItemsAt(currentRow, destinationRow);
1483 moduleState()->jobs().swap(currentRow, destinationRow);
1491 moduleState()->setCurrentPosition(destinationRow);
1493 SchedulerJob *newLead = moduleState()->findLead(destinationRow,
true);
1494 if (newLead !=
nullptr)
1496 job->setLeadJob(newLead);
1497 moduleState()->refreshFollowerLists();
1501 setJobManipulation(
true,
true, leadFollowerSelectionCB->currentIndex() == INDEX_LEAD);
1504 moduleState()->setDirty(
true);
1505 process()->evaluateJobs(
true);
1510 int const rowCount = queueTable->rowCount();
1511 int const currentRow = queueTable->currentRow();
1513 SchedulerJob *job = moduleState()->jobs().at(currentRow);
1515 if (moduleState()->jobs().at(currentRow)->isLead())
1517 int const rows = 1 + job->followerJobs().
count();
1519 if (currentRow + rows >= moduleState()->jobs().count())
1523 destinationRow = currentRow + 1 + moduleState()->jobs().at(currentRow + rows)->followerJobs().count();
1526 destinationRow = currentRow + 1;
1529 if (currentRow < 0 || rowCount <= 1 || destinationRow >= rowCount)
1532 if (moduleState()->jobs().at(currentRow)->isLead())
1535 moduleState()->mutlableJobs().removeOne(job);
1536 for (
auto follower : job->followerJobs())
1537 moduleState()->mutlableJobs().removeOne(follower);
1540 moduleState()->mutlableJobs().insert(destinationRow++, job);
1542 for (
auto follower : job->followerJobs())
1543 moduleState()->mutlableJobs().insert(destinationRow++, follower);
1545 for (
int i = currentRow; i < destinationRow; i++)
1548 moduleState()->setCurrentPosition(destinationRow - job->followerJobs().
count() - 1);
1553#if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
1554 moduleState()->mutlableJobs().swapItemsAt(currentRow, destinationRow);
1556 moduleState()->mutlableJobs().swap(currentRow, destinationRow);
1562 moduleState()->setCurrentPosition(destinationRow);
1564 if (moduleState()->jobs().at(currentRow)->isLead())
1566 job->setLeadJob(moduleState()->jobs().at(currentRow));
1567 moduleState()->refreshFollowerLists();
1571 setJobManipulation(
true,
true, leadFollowerSelectionCB->currentIndex() == INDEX_LEAD);
1574 moduleState()->setDirty(
true);
1575 process()->evaluateJobs(
true);
1583 for (
auto onejob : moduleState()->jobs())
1589 const int row = moduleState()->jobs().indexOf(job);
1594 if (row >= queueTable->rowCount())
1597 QTableWidgetItem *nameCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_NAME));
1598 QTableWidgetItem *statusCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_STATUS));
1599 QTableWidgetItem *altitudeCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_ALTITUDE));
1600 QTableWidgetItem *startupCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_STARTTIME));
1601 QTableWidgetItem *completionCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_ENDTIME));
1602 QTableWidgetItem *captureCountCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_CAPTURES));
1605 if (!nameCell)
return;
1607 if (
nullptr != nameCell)
1609 nameCell->
setText(job->isLead() ? job->getName() :
"*");
1615 if (
nullptr != statusCell)
1618 static QString stateStringUnknown;
1629 stateStringUnknown =
i18n(
"Unknown");
1631 statusCell->
setText(stateStrings.
value(job->getState(), stateStringUnknown));
1638 if (
nullptr != startupCell)
1640 auto time = (job->getState() ==
SCHEDJOB_BUSY) ? job->getStateTime() : job->getStartupTime();
1645 .arg(job->getAltitudeAtStartup() < job->getMinAltitude() ?
QString(
QChar(0x26A0)) :
"")
1646 .arg(
QChar(job->isSettingAtStartup() ? 0x2193 : 0x2191))
1647 .
arg(job->getAltitudeAtStartup(), 0,
'f', 1)
1648 .
arg(time.toString(startupTimeEdit->displayFormat())));
1649 job->setStartupFormatted(startupCell->
text());
1651 switch (job->getFileStartupCondition())
1680 if (
nullptr != altitudeCell)
1683 bool is_setting =
false;
1684 double const alt = SchedulerUtils::findAltitude(job->getTargetCoords(),
QDateTime(), &is_setting);
1687 .arg(
QChar(is_setting ? 0x2193 : 0x2191))
1688 .arg(alt, 0,
'f', 1));
1690 job->setAltitudeFormatted(altitudeCell->
text());
1696 if (
nullptr != completionCell)
1699 if (job->getStopTime().
isValid())
1702 .arg(job->getAltitudeAtStop() < job->getMinAltitude() ?
QString(
QChar(0x26A0)) :
"")
1703 .arg(
QChar(job->isSettingAtStop() ? 0x2193 : 0x2191))
1704 .
arg(job->getAltitudeAtStop(), 0,
'f', 1)
1705 .
arg(job->getStopTime().
toString(startupTimeEdit->displayFormat())));
1706 job->setEndFormatted(completionCell->
text());
1708 switch (job->getCompletionCondition())
1714 case FINISH_SEQUENCE:
1733 if (
nullptr != captureCountCell)
1735 switch (job->getCompletionCondition())
1742 captureCountCell->
setText(
QString(
"%L1/-").arg(job->getCompletedCount()));
1745 case FINISH_SEQUENCE:
1749 captureCountCell->
setText(
QString(
"%L1/%L2").arg(job->getCompletedCount()).
arg(job->getSequenceCount()));
1753 QString tooltip = job->getProgressSummary();
1754 if (tooltip.
size() == 0)
1755 tooltip =
i18n(
"Count of captures stored for the job, based on its sequence job.\n"
1756 "This is a summary, additional specific frame types may be required to complete the job.");
1764 m_JobUpdateDebounce.start();
1769 const int pos = above ? row : row + 1;
1772 if (row > queueTable->rowCount())
1775 queueTable->insertRow(
pos);
1778 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_NAME), nameCell);
1783 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_STATUS), statusCell);
1788 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_CAPTURES), captureCount);
1793 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_STARTTIME), startupCell);
1798 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_ALTITUDE), altitudeCell);
1803 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_ENDTIME), completionCell);
1816void Scheduler::resetJobEdit()
1818 if (jobUnderEdit < 0)
1821 SchedulerJob *
const job = moduleState()->jobs().at(jobUnderEdit);
1822 Q_ASSERT_X(job !=
nullptr, __FUNCTION__,
"Edited job must be valid");
1824 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' at row #%2 is not longer edited.").
arg(job->getName()).
arg(
1834 setJobManipulation(
true,
true, leadFollowerSelectionCB->currentIndex() == INDEX_LEAD);
1837 evaluateOnlyB->setEnabled(
true);
1838 startB->setEnabled(
true);
1841 Q_ASSERT_X(jobUnderEdit == -1, __FUNCTION__,
"No more edited/selected job after exiting edit mode");
1846 int currentRow = moduleState()->currentPosition();
1849 if (moduleState()->
removeJob(currentRow) ==
false)
1857 queueTable->removeRow(currentRow);
1860 if (queueTable->rowCount() == 0)
1862 setJobManipulation(
false,
false, leadFollowerSelectionCB->currentIndex() == INDEX_LEAD);
1863 evaluateOnlyB->setEnabled(
false);
1864 queueSaveAsB->setEnabled(
false);
1865 queueSaveB->setEnabled(
false);
1866 startB->setEnabled(
false);
1867 pauseB->setEnabled(
false);
1874 queueTable->clearSelection();
1877 if (jobUnderEdit >= 0)
1881 moduleState()->refreshFollowerLists();
1882 process()->evaluateJobs(
true);
1885 setJobManipulation(
false,
false, leadFollowerSelectionCB->currentIndex() == INDEX_LEAD);
1890 moduleState()->setCurrentPosition(index);
1893void Scheduler::toggleScheduler()
1895 if (moduleState()->schedulerState() == SCHEDULER_RUNNING)
1897 moduleState()->disablePreemptiveShutdown();
1904void Scheduler::pause()
1906 moduleState()->setSchedulerState(SCHEDULER_PAUSED);
1907 process()->appendLogText(
i18n(
"Scheduler pause planned..."));
1908 pauseB->setEnabled(
false);
1911 startB->setToolTip(
i18n(
"Resume Scheduler"));
1914void Scheduler::syncGreedyParams()
1916 process()->getGreedyScheduler()->setParams(
1917 errorHandlingRestartImmediatelyButton->isChecked(),
1918 errorHandlingRestartQueueButton->isChecked(),
1919 errorHandlingRescheduleErrorsCB->isChecked(),
1920 errorHandlingStrategyDelay->value(),
1921 errorHandlingStrategyDelay->value());
1924void Scheduler::handleShutdownStarted()
1926 KSNotification::event(QLatin1String(
"ObservatoryShutdown"),
i18n(
"Observatory is in the shutdown process"),
1927 KSNotification::Scheduler);
1928 weatherLabel->hide();
1931void Ekos::Scheduler::changeSleepLabel(QString text,
bool show)
1933 sleepLabel->setToolTip(text);
1942 TEST_PRINT(stderr,
"%d Setting %s\n", __LINE__, timerStr(RUN_NOTHING).toLatin1().data());
1945 bool wasAborted =
false;
1946 for (
auto &oneJob : moduleState()->jobs())
1956 KSNotification::event(
QLatin1String(
"SchedulerAborted"),
i18n(
"Scheduler aborted."), KSNotification::Scheduler,
1957 KSNotification::Alert);
1959 startupB->setEnabled(
true);
1960 shutdownB->setEnabled(
true);
1963 if (moduleState()->preemptiveShutdown())
1965 changeSleepLabel(
i18n(
"Scheduler is in shutdown until next job is ready"));
1966 pi->stopAnimation();
1970 changeSleepLabel(
"",
false);
1973 startB->setToolTip(
i18n(
"Start Scheduler"));
1974 pauseB->setEnabled(
false);
1977 queueLoadB->setEnabled(
true);
1978 queueAppendB->setEnabled(
true);
1979 addToQueueB->setEnabled(
true);
1980 setJobManipulation(
false,
false, leadFollowerSelectionCB->currentIndex() == INDEX_LEAD);
1982 evaluateOnlyB->setEnabled(
true);
1988 return load(
true, path.toLocalFile());
1998 "Ekos Scheduler List (*.esl)");
2005 if (fileURL.
isValid() ==
false)
2008 KSNotification::sorry(message,
i18n(
"Invalid URL"));
2015 process()->removeAllJobs();
2017 const int row = moduleState()->jobs().count();
2022 const bool success = process()->appendEkosScheduleList(fileURL.
toLocalFile());
2029 if (moduleState()->jobs().count() > row)
2030 moduleState()->setCurrentPosition(row);
2033 process()->startJobEvaluation();
2043 if (jobUnderEdit >= 0)
2046 while (queueTable->rowCount() > 0)
2047 queueTable->removeRow(0);
2052 process()->clearLog();
2055void Scheduler::saveAs()
2057 schedulerURL.
clear();
2063 QUrl backupCurrent = schedulerURL;
2064 schedulerURL = path;
2070 schedulerURL = backupCurrent;
2075bool Scheduler::save()
2077 QUrl backupCurrent = schedulerURL;
2080 schedulerURL.
clear();
2083 if (moduleState()->dirty() ==
false && !schedulerURL.
isEmpty())
2090 "Ekos Scheduler List (*.esl)");
2094 schedulerURL = backupCurrent;
2104 if (schedulerURL.isValid())
2106 if ((process()->saveScheduler(schedulerURL)) ==
false)
2108 KSNotification::error(
i18n(
"Failed to save scheduler list"),
i18n(
"Save"));
2113 queueSaveB->setToolTip(
"Save schedule to " + schedulerURL.fileName());
2117 QString message =
i18n(
"Invalid URL: %1", schedulerURL.url());
2118 KSNotification::sorry(message,
i18n(
"Invalid URL"));
2125void Scheduler::checkJobInputComplete()
2128 bool const nameSelectionOK = !raBox->isEmpty() && !decBox->isEmpty() && !nameEdit->text().isEmpty();
2131 bool const fitsSelectionOK = !nameEdit->text().isEmpty() && !fitsURL.isEmpty();
2134 bool const seqSelectionOK = !sequenceEdit->text().isEmpty();
2137 bool const addingOK = (nameSelectionOK || fitsSelectionOK) && seqSelectionOK;
2139 addToQueueB->setEnabled(addingOK);
2145 checkJobInputComplete();
2148 if (jobUnderEdit < 0)
2151 moduleState()->setDirty(
true);
2153 if (
sender() == startupProcedureButtonGroup ||
sender() == shutdownProcedureGroup)
2157 if (
sender() == schedulerStartupScript)
2159 else if (
sender() == schedulerShutdownScript)
2160 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(schedulerShutdownScript->text()));
2166 if (moduleState()->jobs().isEmpty())
2175 using namespace std::placeholders;
2177 std::stable_sort(sortedJobs.
begin() + 1, sortedJobs.
end(),
2178 std::bind(SchedulerJob::decreasingAltitudeOrder, _1, _2, moduleState()->jobs().first()->getStartupTime()));
2183 for (SchedulerJob * job : moduleState()->jobs())
2186 process()->evaluateJobs(
true);
2193 TEST_PRINT(stderr,
"%d Setting %s\n", __LINE__, timerStr(RUN_SCHEDULER).toLatin1().data());
2194 moduleState()->setupNextIteration(RUN_SCHEDULER);
2200 if (errorHandlingRestartQueueButton->isChecked())
2201 return ERROR_RESTART_AFTER_TERMINATION;
2202 else if (errorHandlingRestartImmediatelyButton->isChecked())
2203 return ERROR_RESTART_IMMEDIATELY;
2205 return ERROR_DONT_RESTART;
2210 errorHandlingStrategyDelay->setEnabled(strategy != ERROR_DONT_RESTART);
2214 case ERROR_RESTART_AFTER_TERMINATION:
2215 errorHandlingRestartQueueButton->setChecked(
true);
2217 case ERROR_RESTART_IMMEDIATELY:
2218 errorHandlingRestartImmediatelyButton->setChecked(
true);
2221 errorHandlingDontRestartButton->setChecked(
true);
2229void Scheduler::setAlgorithm(
int algIndex)
2231 if (algIndex != ALGORITHM_GREEDY)
2233 process()->appendLogText(
2234 i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
2235 algIndex = ALGORITHM_GREEDY;
2237 Options::setSchedulerAlgorithm(algIndex);
2239 groupLabel->setDisabled(
false);
2240 groupEdit->setDisabled(
false);
2241 queueTable->model()->setHeaderData(START_TIME_COLUMN,
Qt::Horizontal,
tr(
"Next Start"));
2242 queueTable->model()->setHeaderData(END_TIME_COLUMN,
Qt::Horizontal,
tr(
"Next End"));
2251 process()->appendLogText(
2252 i18n(
"Turning off astronomical twilight check may cause the observatory to run during daylight. This can cause irreversible damage to your equipment!"));
2256void Scheduler::updateProfiles()
2258 schedulerProfileCombo->blockSignals(
true);
2259 schedulerProfileCombo->clear();
2260 schedulerProfileCombo->addItems(moduleState()->profiles());
2261 schedulerProfileCombo->setCurrentText(moduleState()->currentProfile());
2262 schedulerProfileCombo->blockSignals(
false);
2265void Scheduler::updateJobStageUI(SchedulerJobStage stage)
2270 static QString stageStringUnknown;
2273 stageStrings[SCHEDSTAGE_IDLE] =
i18n(
"Idle");
2274 stageStrings[SCHEDSTAGE_SLEWING] =
i18n(
"Slewing");
2275 stageStrings[SCHEDSTAGE_SLEW_COMPLETE] =
i18n(
"Slew complete");
2276 stageStrings[SCHEDSTAGE_FOCUSING] =
2277 stageStrings[SCHEDSTAGE_POSTALIGN_FOCUSING] =
i18n(
"Focusing");
2278 stageStrings[SCHEDSTAGE_FOCUS_COMPLETE] =
2279 stageStrings[SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE ] =
i18n(
"Focus complete");
2280 stageStrings[SCHEDSTAGE_ALIGNING] =
i18n(
"Aligning");
2281 stageStrings[SCHEDSTAGE_ALIGN_COMPLETE] =
i18n(
"Align complete");
2282 stageStrings[SCHEDSTAGE_RESLEWING] =
i18n(
"Repositioning");
2283 stageStrings[SCHEDSTAGE_RESLEWING_COMPLETE] =
i18n(
"Repositioning complete");
2285 stageStrings[SCHEDSTAGE_GUIDING] =
i18n(
"Guiding");
2286 stageStrings[SCHEDSTAGE_GUIDING_COMPLETE] =
i18n(
"Guiding complete");
2287 stageStrings[SCHEDSTAGE_CAPTURING] =
i18n(
"Capturing");
2288 stageStringUnknown =
i18n(
"Unknown");
2291 if (activeJob() ==
nullptr)
2292 jobStatus->setText(stageStrings[SCHEDSTAGE_IDLE]);
2294 jobStatus->setText(QString(
"%1: %2").arg(activeJob()->getName(),
2295 stageStrings.
value(stage, stageStringUnknown)));
2301 if (iface == process()->mountInterface())
2303 QVariant canMountPark = process()->mountInterface()->property(
"canPark");
2306 schedulerUnparkMount->setEnabled(canMountPark.
toBool());
2307 schedulerParkMount->setEnabled(canMountPark.
toBool());
2309 copyMountTargetB->setEnabled(
true);
2311 else if (iface == process()->capInterface())
2313 QVariant canCapPark = process()->capInterface()->property(
"canPark");
2316 schedulerCloseDustCover->setEnabled(canCapPark.
toBool());
2317 schedulerOpenDustCover->setEnabled(canCapPark.
toBool());
2321 schedulerCloseDustCover->setEnabled(
false);
2322 schedulerOpenDustCover->setEnabled(
false);
2325 else if (iface == process()->weatherInterface())
2327 QVariant status = process()->weatherInterface()->property(
"status");
2328 if (status.isValid())
2333 schedulerWeather->setEnabled(
true);
2336 schedulerWeather->setEnabled(
false);
2338 else if (iface == process()->domeInterface())
2340 QVariant canDomePark = process()->domeInterface()->property(
"canPark");
2343 schedulerUnparkDome->setEnabled(canDomePark.
toBool());
2344 schedulerParkDome->setEnabled(canDomePark.
toBool());
2347 else if (iface == process()->captureInterface())
2349 QVariant hasCoolerControl = process()->captureInterface()->property(
"coolerControl");
2350 if (hasCoolerControl.
isValid())
2352 schedulerWarmCCD->setEnabled(hasCoolerControl.
toBool());
2357void Scheduler::setWeatherStatus(ISD::Weather::Status status)
2359 TEST_PRINT(stderr,
"sch%d @@@setWeatherStatus(%d)\n", __LINE__,
static_cast<int>(status));
2360 ISD::Weather::Status newStatus = status;
2365 case ISD::Weather::WEATHER_OK:
2366 statusString =
i18n(
"Weather conditions are OK.");
2369 case ISD::Weather::WEATHER_WARNING:
2370 statusString =
i18n(
"Warning: weather conditions are in the WARNING zone.");
2373 case ISD::Weather::WEATHER_ALERT:
2374 statusString =
i18n(
"Caution: weather conditions are in the DANGER zone!");
2381 qCDebug(KSTARS_EKOS_SCHEDULER) << statusString;
2383 if (moduleState()->weatherStatus() == ISD::Weather::WEATHER_OK)
2384 weatherLabel->setPixmap(
2386 .pixmap(
QSize(32, 32)));
2387 else if (moduleState()->weatherStatus() == ISD::Weather::WEATHER_WARNING)
2389 weatherLabel->setPixmap(
2391 .pixmap(
QSize(32, 32)));
2392 KSNotification::event(
QLatin1String(
"WeatherWarning"),
i18n(
"Weather conditions in warning zone"),
2393 KSNotification::Scheduler, KSNotification::Warn);
2395 else if (moduleState()->weatherStatus() == ISD::Weather::WEATHER_ALERT)
2397 weatherLabel->setPixmap(
2399 .pixmap(QSize(32, 32)));
2400 KSNotification::event(QLatin1String(
"WeatherAlert"),
2401 i18n(
"Weather conditions are critical. Observatory shutdown is imminent"), KSNotification::Scheduler,
2402 KSNotification::Alert);
2406 .pixmap(QSize(32, 32)));
2408 weatherLabel->show();
2409 weatherLabel->setToolTip(statusString);
2411 process()->appendLogText(statusString);
2413 emit weatherChanged(moduleState()->weatherStatus());
2420 schedulerWeather->setEnabled(
false);
2421 weatherLabel->hide();
2424 changeSleepLabel(
i18n(
"Scheduler is in sleep mode"));
2431 case SCHEDULER_RUNNING:
2433 pi->startAnimation();
2436 startB->setToolTip(
i18n(
"Stop Scheduler"));
2437 pauseB->setEnabled(
true);
2438 pauseB->setChecked(
false);
2441 queueLoadB->setEnabled(
false);
2442 setJobManipulation(
true,
false, leadFollowerSelectionCB->currentIndex() == INDEX_LEAD);
2444 evaluateOnlyB->setEnabled(
false);
2445 startupB->setEnabled(
false);
2446 shutdownB->setEnabled(
false);
2453 emit newStatus(newState);
2458 pauseB->setCheckable(
true);
2459 pauseB->setChecked(
true);
2462void Scheduler::handleJobsUpdated(
QJsonArray jobsList)
2467 emit jobsUpdated(jobsList);
2473 return assistant->importMosaic(payload);
2476void Scheduler::startupStateChanged(StartupState state)
2478 jobStatus->setText(startupStateString(state));
2480 switch (moduleState()->startupState())
2485 case STARTUP_COMPLETE:
2487 process()->appendLogText(
i18n(
"Manual startup procedure completed successfully."));
2491 process()->appendLogText(
i18n(
"Manual startup procedure terminated due to errors."));
2499void Scheduler::shutdownStateChanged(ShutdownState state)
2501 if (state == SHUTDOWN_COMPLETE || state == SHUTDOWN_IDLE
2502 || state == SHUTDOWN_ERROR)
2505 pi->stopAnimation();
2510 if (state == SHUTDOWN_IDLE)
2511 jobStatus->setText(
i18n(
"Idle"));
2513 jobStatus->setText(shutdownStateString(state));
2515void Scheduler::ekosStateChanged(EkosState state)
2517 if (state == EKOS_IDLE)
2519 jobStatus->setText(
i18n(
"Idle"));
2520 pi->stopAnimation();
2523 jobStatus->setText(ekosStateString(state));
2525void Scheduler::indiStateChanged(INDIState state)
2527 if (state == INDI_IDLE)
2529 jobStatus->setText(
i18n(
"Idle"));
2530 pi->stopAnimation();
2533 jobStatus->setText(indiStateString(state));
2535 refreshOpticalTrain();
2538void Scheduler::indiCommunicationStatusChanged(CommunicationStatus status)
2540 if (status == Success)
2541 refreshOpticalTrain();
2543void Scheduler::parkWaitStateChanged(ParkWaitState state)
2545 jobStatus->setText(parkWaitStateString(state));
2548SchedulerJob *Scheduler::activeJob()
2550 return moduleState()->activeJob();
2553void Scheduler::loadGlobalSettings()
2558 QVariantMap settings;
2562 key = oneWidget->objectName();
2563 value = Options::self()->property(key.
toLatin1());
2564 if (value.
isValid() && oneWidget->count() > 0)
2566 oneWidget->setCurrentText(value.
toString());
2567 settings[key] = value;
2570 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2576 key = oneWidget->objectName();
2577 value = Options::self()->property(key.
toLatin1());
2580 oneWidget->setValue(value.
toDouble());
2581 settings[key] = value;
2584 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2590 key = oneWidget->objectName();
2591 value = Options::self()->property(key.
toLatin1());
2594 oneWidget->setValue(value.
toInt());
2595 settings[key] = value;
2598 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2604 key = oneWidget->objectName();
2605 value = Options::self()->property(key.
toLatin1());
2608 oneWidget->setChecked(value.
toBool());
2609 settings[key] = value;
2612 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2618 key = oneWidget->objectName();
2619 value = Options::self()->property(key.
toLatin1());
2622 oneWidget->setText(value.
toString());
2623 settings[key] = value;
2625 if (key ==
"sequenceEdit")
2627 else if (key ==
"schedulerStartupScript")
2629 else if (key ==
"schedulerShutdownScript")
2633 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2639 key = oneWidget->objectName();
2640 value = Options::self()->property(key.
toLatin1());
2643 oneWidget->setChecked(value.
toBool());
2644 settings[key] = value;
2651 key = oneWidget->objectName();
2652 value = Options::self()->property(key.
toLatin1());
2656 settings[key] = value;
2662 m_GlobalSettings = m_Settings = settings;
2665void Scheduler::syncSettings()
2667 QDoubleSpinBox *dsb =
nullptr;
2668 QSpinBox *sb =
nullptr;
2669 QCheckBox *cb =
nullptr;
2670 QRadioButton *rb =
nullptr;
2671 QComboBox *cbox =
nullptr;
2672 QLineEdit *lineedit =
nullptr;
2673 QDateTimeEdit *datetimeedit =
nullptr;
2677 bool removeKey =
false;
2682 value = dsb->
value();
2688 value = sb->
value();
2716 value = lineedit->
text();
2725 Options::self()->setProperty(key.
toLatin1(), value);
2728 m_Settings.remove(key);
2730 m_Settings[key] = value;
2731 m_GlobalSettings[key] = value;
2733 m_DebounceTimer.start();
2741 emit settingsUpdated(getAllSettings());
2742 Options::self()->save();
2748QVariantMap Scheduler::getAllSettings()
const
2750 QVariantMap settings;
2754 settings.insert(oneWidget->objectName(), oneWidget->currentText());
2758 settings.insert(oneWidget->objectName(), oneWidget->value());
2762 settings.insert(oneWidget->objectName(), oneWidget->value());
2766 settings.insert(oneWidget->objectName(), oneWidget->isChecked());
2772 if (!oneWidget->objectName().startsWith(
"qt_"))
2773 settings.insert(oneWidget->objectName(), oneWidget->text());
2778 settings.insert(oneWidget->objectName(), oneWidget->isChecked());
2783 settings.insert(oneWidget->objectName(), oneWidget->dateTime().toString(
Qt::ISODate));
2792void Scheduler::setAllSettings(
const QVariantMap &settings)
2796 disconnectSettings();
2798 for (
auto &name : settings.keys())
2804 syncControl(settings, name, comboBox);
2812 syncControl(settings, name, doubleSpinBox);
2820 syncControl(settings, name, spinBox);
2828 syncControl(settings, name, checkbox);
2836 syncControl(settings, name, lineedit);
2838 if (name ==
"sequenceEdit")
2840 else if (name ==
"fitsEdit")
2842 else if (name ==
"schedulerStartupScript")
2844 else if (name ==
"schedulerShutdownScript")
2854 syncControl(settings, name, radioButton);
2861 syncControl(settings, name, datetimeedit);
2866 m_Settings = settings;
2875void Scheduler::setTargetCoords(
const dms ra,
const dms dec,
bool isJ2000)
2879 targetCoords.setRA0(ra);
2880 targetCoords.setDec0(dec);
2881 targetCoords.apparentCoord(
static_cast<long double>(J2000), KStarsData::Instance()->updateNum()->julianDay());
2885 targetCoords.setRA(ra);
2886 targetCoords.setDec(dec);
2887 SkyPoint J2000Coord(targetCoords.ra(), targetCoords.dec());
2889 targetCoords.setRA0(J2000Coord.ra());
2890 targetCoords.setDec0(J2000Coord.dec());
2893 displayTargetCoords();
2895void Scheduler::displayTargetCoords()
2898 if (targetCoords.isValid() ==
false)
2901 if (epochCB->currentText() ==
"J2000")
2903 raBox->show(targetCoords.ra0());
2904 decBox->show(targetCoords.dec0());
2908 raBox->show(targetCoords.ra());
2909 decBox->show(targetCoords.dec());
2917bool Scheduler::syncControl(
const QVariantMap &settings,
const QString &key, QWidget * widget)
2919 QSpinBox *pSB =
nullptr;
2920 QDoubleSpinBox *pDSB =
nullptr;
2921 QCheckBox *pCB =
nullptr;
2922 QComboBox *pComboBox =
nullptr;
2923 QLineEdit *pLineEdit =
nullptr;
2924 QRadioButton *pRadioButton =
nullptr;
2925 QDateTimeEdit *pDateTimeEdit =
nullptr;
2930 const int value = settings[key].toInt(&ok);
2939 const double value = settings[key].toDouble(&ok);
2948 const bool value = settings[key].toBool();
2956 const QString value = settings[key].toString();
2962 const auto value = settings[key].toString();
2968 const bool value = settings[key].toBool();
2983void Scheduler::refreshOpticalTrain()
2985 opticalTrainCombo->blockSignals(
true);
2986 opticalTrainCombo->clear();
2987 opticalTrainCombo->addItem(
"--");
2988 opticalTrainCombo->addItems(OpticalTrainManager::Instance()->getTrainNames());
2989 opticalTrainCombo->blockSignals(
false);
2992void Scheduler::connectSettings()
3018 if (!oneWidget->objectName().startsWith(
"qt_"))
3027void Scheduler::disconnectSettings()
3058void Scheduler::handleAltitudeGraph(
int index)
3060 if (!m_altitudeGraph)
3061 m_altitudeGraph =
new SchedulerAltitudeGraph;
3063 if (index < 0 || index >= moduleState()->jobs().
size())
3065 auto job = moduleState()->jobs().at(index);
3067 QDateTime now = SchedulerModuleState::getLocalTime(), start,
end;
3068 QDateTime nextDawn, nextDusk;
3069 SchedulerModuleState::calculateDawnDusk(now, nextDawn, nextDusk);
3071 QVector<double> times, alts;
3072 QDateTime plotStart = (nextDusk < nextDawn) ? nextDusk : nextDusk.addDays(-1);
3081 plotStart = plotStart.
addSecs(-1 * 3600);
3083 auto plotEnd = nextDawn.
addSecs(1 * 3600);
3084 while (t.secsTo(plotEnd) > 0)
3086 double alt = SchedulerUtils::findAltitude(job->getTargetCoords(), t);
3088 double hour = midnight.
secsTo(t) / 3600.0;
3090 t = t.addSecs(60 * 10);
3093 KStarsDateTime ut = SchedulerModuleState::getGeo()->LTtoUT(KStarsDateTime(midnight));
3094 KSAlmanac ksal(ut, SchedulerModuleState::getGeo());
3095 m_altitudeGraph->setTitle(job->getName());
3096 m_altitudeGraph->plot(SchedulerModuleState::getGeo(), &ksal, times, alts);
3099 auto startTime = (job->getState() ==
SCHEDJOB_BUSY) ? job->getStateTime() : job->getStartupTime();
3100 if (startTime.isValid() && startTime < plotEnd && job->getStopTime().
isValid())
3102 auto stopTime = job->getStopTime();
3103 if (startTime < plotStart) startTime = plotStart;
3104 if (stopTime > plotEnd)
3107 QVector<double> runTimes, runAlts;
3109 while (t.secsTo(stopTime) > 0)
3111 double alt = SchedulerUtils::findAltitude(job->getTargetCoords(), t);
3113 double hour = midnight.
secsTo(t) / 3600.0;
3115 t = t.addSecs(60 * 10);
3118 m_altitudeGraph->plot(SchedulerModuleState::getGeo(), &ksal, runTimes, runAlts,
true);
3120 m_altitudeGraph->show();
The SchedulerProcess class holds the entire business logic for controlling the execution of the EKOS ...
Q_SCRIPTABLE Q_NOREPLY void runStartupProcedure()
runStartupProcedure Execute the startup of the scheduler itself to be prepared for running scheduler ...
Q_SCRIPTABLE Q_NOREPLY void startJobEvaluation()
startJobEvaluation Start job evaluation only without starting the scheduler process itself.
Q_SCRIPTABLE Q_NOREPLY void runShutdownProcedure()
runShutdownProcedure Shutdown the scheduler itself and EKOS (if configured to do so).
ErrorHandlingStrategy getErrorHandlingStrategy()
retrieve the error handling strategy from the UI
void moveJobUp()
moveJobUp Move the selected job up in the job list.
void watchJobChanges(bool enable)
Q_INVOKABLE void clearLog()
clearLog Clears log entry
void checkTwilightWarning(bool enabled)
checkWeather Check weather status and act accordingly depending on the current status of the schedule...
void saveJob(SchedulerJob *job=nullptr)
addToQueue Construct a SchedulerJob and add it to the queue or save job settings from current form va...
void setJobManipulation(bool can_reorder, bool can_delete, bool is_lead)
setJobManipulation Enable or disable job manipulation buttons.
void updateSchedulerURL(const QString &fileURL)
updateSchedulerURL Update scheduler URL after succesful loading a new file.
void settleSettings()
settleSettings Run this function after timeout from debounce timer to update database and emit settin...
Q_INVOKABLE void addJob(SchedulerJob *job=nullptr)
addJob Add a new job from form values
void selectSequence()
Selects sequence queue.
void insertJobTableRow(int row, bool above=true)
insertJobTableRow Insert a new row (empty) into the job table
Q_INVOKABLE bool load(bool clearQueue, const QString &filename=QString())
load Open a file dialog to select an ESL file, and load its contents.
void resumeCheckStatus()
resumeCheckStatus If the scheduler primary loop was suspended due to weather or sleep event,...
void handleSchedulerSleeping(bool shutdown, bool sleep)
handleSchedulerSleeping Update UI if scheduler is set to sleep
void prepareGUI()
prepareGUI Perform once only GUI prep processing
void moveJobDown()
moveJobDown Move the selected job down in the list.
bool importMosaic(const QJsonObject &payload)
importMosaic Import mosaic into planner and generate jobs for the scheduler.
void handleSetPaused()
handleSetPaused Update the UI when {
bool reorderJobs(QList< SchedulerJob * > reordered_sublist)
reorderJobs Change the order of jobs in the UI based on a subset of its jobs.
void syncGUIToGeneralSettings()
syncGUIToGeneralSettings set all UI fields that are not job specific
void updateNightTime(SchedulerJob const *job=nullptr)
updateNightTime update the Twilight restriction with the argument job properties.
bool loadFile(const QUrl &path)
loadFile Load scheduler jobs from disk
void handleSchedulerStateChanged(SchedulerState newState)
handleSchedulerStateChanged Update UI when the scheduler state changes
bool fillJobFromUI(SchedulerJob *job)
createJob Create a new job from form values.
Q_INVOKABLE void loadJob(QModelIndex i)
editJob Edit an observation job
void setSequence(const QString &sequenceFileURL)
Set the file URL pointing to the capture sequence file.
Q_INVOKABLE void updateJob(int index=-1)
addJob Add a new job from form values
void selectStartupScript()
Selects sequence queue.
void syncGUIToJob(SchedulerJob *job)
set all GUI fields to the values of the given scheduler job
void schedulerStopped()
schedulerStopped React when the process engine has stopped the scheduler
void selectObject()
select object from KStars's find dialog.
void updateCellStyle(SchedulerJob *job, QTableWidgetItem *cell)
Update the style of a cell, depending on the job's state.
Q_INVOKABLE void clearJobTable()
clearJobTable delete all rows in the job table
void setJobAddApply(bool add_mode)
setJobAddApply Set first button state to add new job or apply changes.
void handleConfigChanged()
handleConfigChanged Update UI after changes to the global configuration
bool saveFile(const QUrl &path)
saveFile Save scheduler jobs to disk
Q_SCRIPTABLE void sortJobsPerAltitude()
DBUS interface function.
void setErrorHandlingStrategy(ErrorHandlingStrategy strategy)
select the error handling strategy (no restart, restart after all terminated, restart immediately)
void clickQueueTable(QModelIndex index)
jobSelectionChanged Update UI state when the job list is clicked once.
void updateJobTable(SchedulerJob *job=nullptr)
updateJobTable Update the job's row in the job table.
void removeJob()
Remove a job from current table row.
void removeOneJob(int index)
Remove a job by selecting a table row.
void selectFITS()
Selects FITS file for solving.
Scheduler()
Constructor, the starndard scheduler constructor.
void interfaceReady(QDBusInterface *iface)
checkInterfaceReady Sometimes syncProperties() is not sufficient since the ready signal could have fi...
void queueTableSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
Update scheduler parameters to the currently selected scheduler job.
void selectShutdownScript()
Selects sequence queue.
Q_INVOKABLE QAction * action(const QString &name) const
KPageWidgetItem * addPage(QWidget *page, const QString &itemName, const QString &pixmapName=QString(), const QString &header=QString(), bool manage=true)
void setIcon(const QIcon &icon)
static KStars * Instance()
virtual KActionCollection * actionCollection() const
The QProgressIndicator class lets an application display a progress indicator to show that a long tas...
Provides all necessary information about an object in the sky: its coordinates, name(s),...
virtual QString name(void) const
The sky coordinates of a point in the sky.
const CachingDms & dec() const
const CachingDms & ra0() const
const CachingDms & ra() const
const CachingDms & dec0() const
bool isValid() const
isValid Check if the RA and DE fall within expected range
This is a subclass of SkyObject.
An angle, stored as degrees, but expressible in many ways.
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
StartupCondition
Conditions under which a SchedulerJob may start.
@ SCHEDJOB_ABORTED
Job encountered a transitory issue while processing, and will be rescheduled.
@ SCHEDJOB_INVALID
Job has an incorrect configuration, and cannot proceed.
@ SCHEDJOB_ERROR
Job encountered a fatal issue while processing, and must be reset manually.
@ SCHEDJOB_COMPLETE
Job finished all required captures.
@ SCHEDJOB_EVALUATION
Job is being evaluated.
@ SCHEDJOB_SCHEDULED
Job was evaluated, and has a schedule.
@ SCHEDJOB_BUSY
Job is being processed.
@ SCHEDJOB_IDLE
Job was just created, and is not evaluated yet.
ErrorHandlingStrategy
options what should happen if an error or abort occurs
CompletionCondition
Conditions under which a SchedulerJob may complete.
bool isValid(QStringView ifopt)
const QList< QKeySequence > & end()
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
void clicked(const QModelIndex &index)
void doubleClicked(const QModelIndex &index)
void rangeChanged(int min, int max)
void valueChanged(int value)
void triggered(bool checked)
void activated(int index)
void currentIndexChanged(int index)
void currentTextChanged(const QString &text)
QDate addDays(qint64 ndays) const const
QDateTime addSecs(qint64 s) const const
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
qint64 secsTo(const QDateTime &other) const const
QString toString(QStringView format, QCalendar cal) const const
void dateTimeChanged(const QDateTime &datetime)
void valueChanged(double d)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
void setItalic(bool enable)
Qt::KeyboardModifiers keyboardModifiers()
QIcon fromTheme(const QString &name)
QModelIndexList indexes() const const
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
void textChanged(const QString &text)
void append(QList< T > &&value)
bool contains(const AT &value) const const
qsizetype count() const const
void push_back(parameter_type value)
bool isEmpty() const const
T value(const Key &key, const T &defaultValue) const const
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T findChild(const QString &name, Qt::FindChildOptions options) const const
QList< T > findChildren(Qt::FindChildOptions options) const const
T qobject_cast(QObject *object)
QObject * sender() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
void setFont(const QFont &font)
void appendRow(QStandardItem *item)
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toLatin1() const const
QTextStream & center(QTextStream &stream)
QTextStream & dec(QTextStream &stream)
void resizeColumnToContents(int column)
void setText(const QString &text)
void setTextAlignment(Qt::Alignment alignment)
QString text() const const
bool setHMS(int h, int m, int s, int ms)
void setInterval(int msec)
void setSingleShot(bool singleShot)
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isValid() const const
void setPath(const QString &path, ParsingMode mode)
QString toLocalFile() const const
QString url(FormattingOptions options) const const
bool isValid() const const
bool toBool() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QString toString() const const