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 "skymapcomposite.h"
25#include "skycomponents/mosaiccomponent.h"
26#include "skyobjects/mosaictiles.h"
27#include "auxiliary/QProgressIndicator.h"
28#include "dialogs/finddialog.h"
29#include "ekos/manager.h"
30#include "ekos/capture/sequencejob.h"
31#include "ekos/capture/placeholderpath.h"
32#include "skyobjects/starobject.h"
33#include "greedyscheduler.h"
34#include "ekos/auxiliary/solverutils.h"
35#include "ekos/auxiliary/stellarsolverprofile.h"
37#include <KConfigDialog>
38#include <KActionCollection>
41#include <ekos_scheduler_debug.h>
43#include "ekos/capture/sequenceeditor.h"
48#define BAD_SCORE -1000
49#define RESTART_GUIDING_DELAY_MS 5000
51#define DEFAULT_MIN_ALTITUDE 15
52#define DEFAULT_MIN_MOON_SEPARATION 0
57#define TEST_PRINT if (false) fprintf
81 setupScheduler(ekosPathString, ekosInterfaceString);
88 schedulerPathString = path;
89 kstarsInterfaceString = interface;
90 setupScheduler(ekosPathStr, ekosInterfaceStr);
93void Scheduler::setupScheduler(
const QString &ekosPathStr,
const QString &ekosInterfaceStr)
97 qRegisterMetaType<Ekos::SchedulerState>(
"Ekos::SchedulerState");
98 qDBusRegisterMetaType<Ekos::SchedulerState>();
100 m_moduleState.
reset(
new SchedulerModuleState());
101 m_process.reset(
new SchedulerProcess(moduleState(), ekosPathStr, ekosInterfaceStr));
106 QDateTime currentDateTime = SchedulerModuleState::getLocalTime();
107 QTime currentTime = currentDateTime.
time();
109 currentDateTime.
setTime(currentTime);
112 startupTimeEdit->setDateTime(currentDateTime);
113 schedulerUntilValue->setDateTime(currentDateTime);
116 sleepLabel->setPixmap(
118 changeSleepLabel(
"",
false);
121 bottomLayout->addWidget(pi, 0);
123 geo = KStarsData::Instance()->
geo();
126 raBox->setUnits(dmsBox::HOURS);
130 queueTable->setToolTip(
131 i18n(
"Job scheduler list.\nClick to select a job in the list.\nDouble click to edit a job with the left-hand fields."));
132 QTableWidgetItem *statusHeader = queueTable->horizontalHeaderItem(SCHEDCOL_STATUS);
133 QTableWidgetItem *altitudeHeader = queueTable->horizontalHeaderItem(SCHEDCOL_ALTITUDE);
134 QTableWidgetItem *startupHeader = queueTable->horizontalHeaderItem(SCHEDCOL_STARTTIME);
135 QTableWidgetItem *completionHeader = queueTable->horizontalHeaderItem(SCHEDCOL_ENDTIME);
136 QTableWidgetItem *captureCountHeader = queueTable->horizontalHeaderItem(SCHEDCOL_CAPTURES);
138 if (statusHeader !=
nullptr)
139 statusHeader->
setToolTip(
i18n(
"Current status of the job, managed by the Scheduler.\n"
140 "If invalid, the Scheduler was not able to find a proper observation time for the target.\n"
141 "If aborted, the Scheduler missed the scheduled time or encountered transitory issues and will reschedule the job.\n"
142 "If complete, the Scheduler verified that all sequence captures requested were stored, including repeats."));
143 if (altitudeHeader !=
nullptr)
144 altitudeHeader->
setToolTip(
i18n(
"Current altitude of the target of the job.\n"
145 "A rising target is indicated with an arrow going up.\n"
146 "A setting target is indicated with an arrow going down."));
147 if (startupHeader !=
nullptr)
148 startupHeader->
setToolTip(
i18n(
"Startup time of the job, as estimated by the Scheduler.\n"
149 "The altitude at startup, if available, is displayed too.\n"
150 "Fixed time from user or culmination time is marked with a chronometer symbol."));
151 if (completionHeader !=
nullptr)
152 completionHeader->
setToolTip(
i18n(
"Completion time for the job, as estimated by the Scheduler.\n"
153 "You may specify a fixed time to limit duration of looping jobs. "
154 "A warning symbol indicates the altitude at completion may cause the job to abort before completion.\n"));
155 if (captureCountHeader !=
nullptr)
156 captureCountHeader->
setToolTip(
i18n(
"Count of captures stored for the job, based on its sequence job.\n"
157 "This is a summary, additional specific frame types may be required to complete the job."));
163 removeFromQueueB->setToolTip(
164 i18n(
"Remove selected job from the observation list.\nJob properties are copied in the edition fields before removal."));
168 queueUpB->setToolTip(
i18n(
"Move selected job one line up in the list.\n"));
171 queueDownB->setToolTip(
i18n(
"Move selected job one line down in the list.\n"));
175 evaluateOnlyB->setToolTip(
i18n(
"Reset state and force reevaluation of all observation jobs."));
178 sortJobsB->setToolTip(
179 i18n(
"Reset state and sort observation jobs per altitude and movement in sky, using the start time of the first job.\n"
180 "This action sorts setting targets before rising targets, and may help scheduling when starting your observation.\n"
181 "Note the algorithm first calculates all altitudes using the same time, then evaluates jobs."));
186 positionAngleSpin->setSpecialValueText(
"--");
201 selectShutdownScriptB->setIcon(
216 schedulerRepeatEverything->setEnabled(Options::rememberJobProgress() ==
false);
217 executionSequenceLimit->setEnabled(Options::rememberJobProgress() ==
false);
218 executionSequenceLimit->setValue(Options::schedulerExecutionSequencesLimit());
231 mosaicB->setDown(checked);
266 pauseB->setCheckable(
false);
285 connect(moduleState().data(), &SchedulerModuleState::ekosStateChanged,
this, &Scheduler::ekosStateChanged);
286 connect(moduleState().data(), &SchedulerModuleState::indiStateChanged,
this, &Scheduler::indiStateChanged);
288 connect(moduleState().data(), &SchedulerModuleState::startupStateChanged,
this, &Scheduler::startupStateChanged);
289 connect(moduleState().data(), &SchedulerModuleState::shutdownStateChanged,
this, &Scheduler::shutdownStateChanged);
290 connect(moduleState().data(), &SchedulerModuleState::parkWaitStateChanged,
this, &Scheduler::parkWaitStateChanged);
291 connect(moduleState().data(), &SchedulerModuleState::profilesChanged,
this, &Scheduler::updateProfiles);
293 connect(moduleState().data(), &SchedulerModuleState::jobStageChanged,
this, &Scheduler::updateJobStageUI);
295 connect(moduleState().data(), &SchedulerModuleState::currentProfileChanged,
this, [&]()
297 schedulerProfileCombo->setCurrentText(moduleState()->currentProfile());
302 connect(process().data(), &SchedulerProcess::shutdownStarted,
this, &Scheduler::handleShutdownStarted);
304 connect(process().data(), &SchedulerProcess::jobsUpdated,
this, &Scheduler::handleJobsUpdated);
305 connect(process().data(), &SchedulerProcess::targetDistance,
this, &Scheduler::targetDistance);
310 connect(process().data(), &SchedulerProcess::jobStarted,
this, &Scheduler::jobStarted);
311 connect(process().data(), &SchedulerProcess::jobEnded,
this, &Scheduler::jobEnded);
312 connect(process().data(), &SchedulerProcess::syncGreedyParams,
this, &Scheduler::syncGreedyParams);
314 connect(process().data(), &SchedulerProcess::changeSleepLabel,
this, &Scheduler::changeSleepLabel);
317 connect(process().data(), &SchedulerProcess::newWeatherStatus,
this, &Scheduler::setWeatherStatus);
330 Options::setErrorHandlingStrategy(strategy);
331 errorHandlingStrategyDelay->setEnabled(strategy != ERROR_DONT_RESTART);
335 Options::setErrorHandlingStrategyDelay(value);
339 if (Options::schedulerAlgorithm() != ALGORITHM_GREEDY)
341 process()->appendLogText(
342 i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
343 Options::setSchedulerAlgorithm(ALGORITHM_GREEDY);
347 setAlgorithm(Options::schedulerAlgorithm());
353 center.catalogueCoord(KStarsData::Instance()->updateNum()->julianDay());
354 raBox->show(
center.ra0());
355 decBox->show(
center.dec0());
362 if (!m_SequenceEditor)
363 m_SequenceEditor.
reset(
new SequenceEditor(
this));
365 m_SequenceEditor->show();
366 m_SequenceEditor->raise();
373 emit jobsUpdated(moduleState()->getJSONJobs());
376 moduleState()->calculateDawnDusk();
377 process()->loadProfiles();
381 loadGlobalSettings();
385QString Scheduler::getCurrentJobName()
387 return (activeJob() !=
nullptr ? activeJob()->getName() :
"");
393 if (enable == jobChangesAreWatched)
405 schedulerStartupScript,
406 schedulerShutdownScript
417 schedulerProfileCombo,
423 errorHandlingButtonGroup,
425 constraintButtonGroup,
426 completionButtonGroup,
427 startupProcedureButtonGroup,
428 shutdownProcedureGroup
433 errorHandlingRescheduleErrorsCB
438 schedulerExecutionSequencesLimit,
439 errorHandlingStrategyDelay
444 schedulerMoonSeparationValue,
445 schedulerAltitudeValue,
460 for (
auto *
const control : lineEdits)
465 for (
auto *
const control : dateEdits)
470 for (
auto *
const control : comboBoxes)
475 for (
auto *
const control : buttonGroups)
476#
if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
484 for (
auto *
const control : buttons)
489 for (
auto *
const control : spinBoxes)
494 for (
auto *
const control : dspinBoxes)
507 for (
auto *
const control : lineEdits)
509 for (
auto *
const control : dateEdits)
511 for (
auto *
const control : comboBoxes)
513 for (
auto *
const control : buttons)
515 for (
auto *
const control : buttonGroups)
516#
if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
521 for (
auto *
const control : spinBoxes)
523 for (
auto *
const control : dspinBoxes)
527 jobChangesAreWatched = enable;
532 schedulerRepeatEverything->setEnabled(Options::rememberJobProgress() ==
false);
533 executionSequenceLimit->setEnabled(Options::rememberJobProgress() ==
false);
538 if (FindDialog::Instance()->execWithParent(Ekos::Manager::Instance()) ==
QDialog::Accepted)
545void Scheduler::addObject(
SkyObject *
object)
547 if (
object !=
nullptr)
551 if (object->
name() ==
"star")
559 nameEdit->setText(finalObjectName);
560 raBox->show(object->
ra0());
561 decBox->show(object->
dec0());
570 "FITS (*.fits *.fit);;XISF (*.xisf)");
574 processFITSSelection(url);
577void Scheduler::processFITSSelection(
const QUrl &url)
587 const QString filename = fitsEdit->text();
589 double ra = 0, dec = 0;
591 char comment[128], error_status[512];
592 fitsfile *fptr =
nullptr;
594 if (fits_open_diskfile(&fptr, filename.
toLatin1(), READONLY, &status))
596 fits_report_error(stderr, status);
597 fits_get_errstatus(status, error_status);
603 if (fits_movabs_hdu(fptr, 1, IMAGE_HDU, &status))
605 fits_report_error(stderr, status);
606 fits_get_errstatus(status, error_status);
612 char objectra_str[32] = {0};
613 if (fits_read_key(fptr, TSTRING,
"OBJCTRA", objectra_str, comment, &status))
615 if (fits_read_key(fptr, TDOUBLE,
"RA", &ra, comment, &status))
617 fits_report_error(stderr, status);
618 fits_get_errstatus(status, error_status);
619 process()->appendLogText(
i18n(
"FITS header: cannot find OBJCTRA (%1).",
QString(error_status)));
631 char objectde_str[32] = {0};
632 if (fits_read_key(fptr, TSTRING,
"OBJCTDEC", objectde_str, comment, &status))
634 if (fits_read_key(fptr, TDOUBLE,
"DEC", &dec, comment, &status))
636 fits_report_error(stderr, status);
637 fits_get_errstatus(status, error_status);
638 process()->appendLogText(
i18n(
"FITS header: cannot find OBJCTDEC (%1).",
QString(error_status)));
652 char object_str[256] = {0};
653 if (fits_read_key(fptr, TSTRING,
"OBJECT", object_str, comment, &status))
656 nameEdit->setText(info.completeBaseName());
660 nameEdit->setText(object_str);
681 i18n(
"Ekos Sequence Queue (*.esq)"));
689 "Select Startup Script"),
691 i18n(
"Script (*)")));
692 if (moduleState()->startupScriptURL().isEmpty())
697 moduleState()->setDirty(
true);
698 schedulerStartupScript->setText(moduleState()->startupScriptURL().toLocalFile());
704 "Select Shutdown Script"),
706 i18n(
"Script (*)")));
707 if (moduleState()->shutdownScriptURL().isEmpty())
712 moduleState()->setDirty(
true);
713 schedulerShutdownScript->setText(moduleState()->shutdownScriptURL().toLocalFile());
718 if (0 <= jobUnderEdit)
721 job = moduleState()->jobs().at(jobUnderEdit);
731 int currentRow = moduleState()->currentPosition();
735 currentRow = queueTable->rowCount();
743 if (moduleState()->jobs().count() > currentRow)
744 moduleState()->setCurrentPosition(currentRow);
747 emit jobsUpdated(moduleState()->getJSONJobs());
752 if (nameEdit->text().isEmpty())
754 process()->appendLogText(
i18n(
"Warning: Target name is required."));
758 if (sequenceEdit->text().isEmpty())
760 process()->appendLogText(
i18n(
"Warning: Sequence file is required."));
765 if ((raBox->isEmpty() || decBox->isEmpty()) && fitsURL.
isEmpty())
767 process()->appendLogText(
i18n(
"Warning: Target coordinates are required."));
771 bool raOk =
false, decOk =
false;
772 dms ra(raBox->createDms(&raOk));
773 dms dec(decBox->createDms(&decOk));
777 process()->appendLogText(
i18n(
"Warning: RA value %1 is invalid.", raBox->text()));
783 process()->appendLogText(
i18n(
"Warning: DEC value %1 is invalid.", decBox->text()));
793 if (asapConditionR->isChecked())
794 startCondition = START_ASAP;
797 if (schedulerCompleteSequences->isChecked())
798 stopCondition = FINISH_SEQUENCE;
799 else if (schedulerRepeatSequences->isChecked())
800 stopCondition = FINISH_REPEAT;
801 else if (schedulerUntilTerminated->isChecked())
802 stopCondition = FINISH_LOOP;
804 double altConstraint = SchedulerJob::UNDEFINED_ALTITUDE;
805 if (schedulerAltitude->isChecked())
806 altConstraint = schedulerAltitudeValue->value();
808 double moonConstraint = -1;
809 if (schedulerMoonSeparation->isChecked())
810 moonConstraint = schedulerMoonSeparationValue->value();
814 SchedulerUtils::setupJob(*job, nameEdit->text(), groupEdit->text(), ra, dec,
815 KStarsData::Instance()->
ut().
djd(),
816 positionAngleSpin->value(), sequenceURL, fitsURL,
818 startCondition, startupTimeEdit->dateTime(),
819 stopCondition, schedulerUntilValue->dateTime(), schedulerExecutionSequencesLimit->value(),
823 schedulerWeather->isChecked(),
824 schedulerTwilight->isChecked(),
825 schedulerHorizon->isChecked(),
827 schedulerTrackStep->isChecked(),
828 schedulerFocusStep->isChecked(),
829 schedulerAlignStep->isChecked(),
830 schedulerGuideStep->isChecked());
842 int currentRow = moduleState()->currentPosition() + 1;
847 if (0 <= jobUnderEdit)
850 if (jobUnderEdit != currentRow - 1)
852 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"BUG: the observation job under edit does not match the selected row in the job table.";
856 job = moduleState()->jobs().at(jobUnderEdit);
869 job =
new SchedulerJob();
879 moduleState()->mutlableJobs().insert(currentRow, job);
887 foreach (SchedulerJob *a_job, moduleState()->jobs())
893 else if (a_job->getName() == job->getName())
895 int const a_job_row = moduleState()->jobs().indexOf(a_job);
898 process()->appendLogText(
i18n(
"Warning: job '%1' at row %2 has a duplicate target at row %3, "
899 "the scheduler may consider the same storage for captures.",
900 job->getName(), currentRow, a_job_row));
903 if (a_job->getSequenceFile() == job->getSequenceFile())
905 if (a_job->getRepeatsRequired() == job->getRepeatsRequired() && Options::rememberJobProgress())
906 process()->appendLogText(
i18n(
"Warning: jobs '%1' at row %2 and %3 probably require a different repeat count "
907 "as currently they will complete simultaneously after %4 batches (or disable option 'Remember job progress')",
908 job->getName(), currentRow, a_job_row, job->getRepeatsRequired()));
912 if (++numWarnings >= 1)
914 process()->appendLogText(
i18n(
"Skipped checking for duplicates."));
923 queueSaveAsB->setEnabled(
true);
924 queueSaveB->setEnabled(
true);
925 startB->setEnabled(
true);
926 evaluateOnlyB->setEnabled(
true);
928 checkJobInputComplete();
930 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' at row #%2 was saved.").
arg(job->getName()).
arg(currentRow + 1);
934 if (SCHEDULER_LOADING != moduleState()->schedulerState())
936 process()->evaluateJobs(
true);
942 nameEdit->setText(job->getName());
943 groupEdit->setText(job->getGroup());
945 raBox->show(job->getTargetCoords().
ra0());
946 decBox->show(job->getTargetCoords().
dec0());
949 fitsURL = job->getFITSFile().
isEmpty() ?
QUrl() : job->getFITSFile();
950 sequenceURL = job->getSequenceFile();
954 positionAngleSpin->setValue(job->getPositionAngle());
956 schedulerTrackStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_TRACK);
957 schedulerFocusStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_FOCUS);
958 schedulerAlignStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_ALIGN);
959 schedulerGuideStep->setChecked(job->getStepPipeline() & SchedulerJob::USE_GUIDE);
961 switch (job->getFileStartupCondition())
964 asapConditionR->setChecked(
true);
968 startupTimeConditionR->setChecked(
true);
969 startupTimeEdit->setDateTime(job->getStartupTime());
973 if (job->getMinAltitude())
975 schedulerAltitude->setChecked(
true);
976 schedulerAltitudeValue->setValue(job->getMinAltitude());
980 schedulerAltitude->setChecked(
false);
981 schedulerAltitudeValue->setValue(DEFAULT_MIN_ALTITUDE);
984 if (job->getMinMoonSeparation() >= 0)
986 schedulerMoonSeparation->setChecked(
true);
987 schedulerMoonSeparationValue->setValue(job->getMinMoonSeparation());
991 schedulerMoonSeparation->setChecked(
false);
992 schedulerMoonSeparationValue->setValue(DEFAULT_MIN_MOON_SEPARATION);
995 schedulerWeather->setChecked(job->getEnforceWeather());
997 schedulerTwilight->blockSignals(
true);
998 schedulerTwilight->setChecked(job->getEnforceTwilight());
999 schedulerTwilight->blockSignals(
false);
1001 schedulerHorizon->blockSignals(
true);
1002 schedulerHorizon->setChecked(job->getEnforceArtificialHorizon());
1003 schedulerHorizon->blockSignals(
false);
1005 switch (job->getCompletionCondition())
1007 case FINISH_SEQUENCE:
1008 schedulerCompleteSequences->setChecked(
true);
1012 schedulerRepeatSequences->setChecked(
true);
1013 schedulerExecutionSequencesLimit->setValue(job->getRepeatsRequired());
1017 schedulerUntilTerminated->setChecked(
true);
1021 schedulerUntil->setChecked(
true);
1022 schedulerUntilValue->setDateTime(job->getCompletionTime());
1033 schedulerParkDome->setChecked(Options::schedulerParkDome());
1034 schedulerParkMount->setChecked(Options::schedulerParkMount());
1035 schedulerCloseDustCover->setChecked(Options::schedulerCloseDustCover());
1036 schedulerWarmCCD->setChecked(Options::schedulerWarmCCD());
1037 schedulerUnparkDome->setChecked(Options::schedulerUnparkDome());
1038 schedulerUnparkMount->setChecked(Options::schedulerUnparkMount());
1039 schedulerOpenDustCover->setChecked(Options::schedulerOpenDustCover());
1041 errorHandlingStrategyDelay->setValue(Options::errorHandlingStrategyDelay());
1042 errorHandlingRescheduleErrorsCB->setChecked(Options::rescheduleErrors());
1044 schedulerShutdownScript->setText(moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile));
1046 if (process()->captureInterface() !=
nullptr)
1048 QVariant hasCoolerControl = process()->captureInterface()->property(
"coolerControl");
1049 if (hasCoolerControl.
isValid())
1051 schedulerWarmCCD->setEnabled(hasCoolerControl.
toBool());
1052 moduleState()->setCaptureReady(
true);
1060 if (job ==
nullptr && moduleState()->jobs().
size() > 0)
1062 int const currentRow = moduleState()->currentPosition();
1063 if (0 <= currentRow && currentRow < moduleState()->jobs().
size())
1064 job = moduleState()->jobs().at(currentRow);
1068 qCWarning(KSTARS_EKOS_SCHEDULER()) <<
"Cannot update night time, no matching job found at line" << currentRow;
1073 QDateTime const dawn = job ? job->getDawnAstronomicalTwilight() : moduleState()->Dawn();
1074 QDateTime const dusk = job ? job->getDuskAstronomicalTwilight() : moduleState()->Dusk();
1076 QChar const warning(dawn == dusk ? 0x26A0 :
'-');
1082 if (jobUnderEdit == i.
row())
1085 SchedulerJob *
const job = moduleState()->jobs().at(i.
row());
1100 startB->setEnabled(
false);
1101 evaluateOnlyB->setEnabled(
false);
1106 jobUnderEdit = i.
row();
1107 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' at row #%2 is currently edited.").
arg(job->getName()).
arg(
1117 queueSaveB->setToolTip(
"Save schedule to " + schedulerURL.
fileName());
1122 Q_UNUSED(deselected)
1125 if (jobChangesAreWatched ==
false || selected.
empty())
1131 if ((current.
row() + 1) > moduleState()->jobs().
size())
1133 qCWarning(KSTARS_EKOS_SCHEDULER()) <<
"Unexpected row number" << current.
row() <<
"- ignoring.";
1136 moduleState()->setCurrentPosition(current.
row());
1137 SchedulerJob *
const job = moduleState()->jobs().at(current.
row());
1141 if (jobUnderEdit < 0)
1143 else if (jobUnderEdit != current.
row())
1146 process()->appendLogText(
i18n(
"Stop editing of job #%1, resetting to original value.", jobUnderEdit + 1));
1151 else nightTime->setText(
"-");
1164 addToQueueB->setToolTip(
i18n(
"Use edition fields to create a new job in the observation list."));
1170 addToQueueB->setToolTip(
i18n(
"Apply job changes."));
1173 checkJobInputComplete();
1180 int const currentRow = moduleState()->currentPosition();
1181 queueUpB->setEnabled(0 < currentRow);
1182 queueDownB->setEnabled(currentRow < queueTable->rowCount() - 1);
1186 queueUpB->setEnabled(
false);
1187 queueDownB->setEnabled(
false);
1189 sortJobsB->setEnabled(can_reorder);
1190 removeFromQueueB->setEnabled(can_delete);
1196 foreach (SchedulerJob* job, moduleState()->jobs())
1197 if (!reordered_sublist.
contains(job))
1198 reordered_sublist.
append(job);
1200 if (moduleState()->jobs() != reordered_sublist)
1203 int const selectedRow = moduleState()->currentPosition();
1204 SchedulerJob *
const selectedJob = 0 <= selectedRow ? moduleState()->jobs().at(selectedRow) :
nullptr;
1207 moduleState()->setJobs(reordered_sublist);
1210 for (SchedulerJob *job : moduleState()->jobs())
1214 if (
nullptr != selectedJob)
1215 moduleState()->setCurrentPosition(moduleState()->jobs().indexOf(selectedJob));
1224 int const rowCount = queueTable->rowCount();
1225 int const currentRow = queueTable->currentRow();
1226 int const destinationRow = currentRow - 1;
1229 if (currentRow < 0 || rowCount <= 1 || destinationRow < 0)
1233#if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
1234 moduleState()->mutlableJobs().swapItemsAt(currentRow, destinationRow);
1236 moduleState()->jobs().swap(currentRow, destinationRow);
1244 moduleState()->setCurrentPosition(destinationRow);
1248 moduleState()->setDirty(
true);
1249 process()->evaluateJobs(
true);
1254 int const rowCount = queueTable->rowCount();
1255 int const currentRow = queueTable->currentRow();
1256 int const destinationRow = currentRow + 1;
1259 if (currentRow < 0 || rowCount <= 1 || destinationRow >= rowCount)
1263#if QT_VERSION >= QT_VERSION_CHECK(5,13,0)
1264 moduleState()->mutlableJobs().swapItemsAt(currentRow, destinationRow);
1266 moduleState()->mutlableJobs().swap(currentRow, destinationRow);
1274 moduleState()->setCurrentPosition(destinationRow);
1278 moduleState()->setDirty(
true);
1279 process()->evaluateJobs(
true);
1287 for (
auto onejob : moduleState()->jobs())
1293 const int row = moduleState()->jobs().indexOf(job);
1298 if (row >= queueTable->rowCount())
1301 QTableWidgetItem *nameCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_NAME));
1302 QTableWidgetItem *statusCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_STATUS));
1303 QTableWidgetItem *altitudeCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_ALTITUDE));
1304 QTableWidgetItem *startupCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_STARTTIME));
1305 QTableWidgetItem *completionCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_ENDTIME));
1306 QTableWidgetItem *captureCountCell = queueTable->item(row,
static_cast<int>(SCHEDCOL_CAPTURES));
1309 if (!nameCell)
return;
1311 if (
nullptr != nameCell)
1313 nameCell->
setText(job->getName());
1319 if (
nullptr != statusCell)
1322 static QString stateStringUnknown;
1333 stateStringUnknown =
i18n(
"Unknown");
1335 statusCell->
setText(stateStrings.
value(job->getState(), stateStringUnknown));
1342 if (
nullptr != startupCell)
1344 auto time = (job->getState() ==
SCHEDJOB_BUSY) ? job->getStateTime() : job->getStartupTime();
1349 .
arg(job->getAltitudeAtStartup() < job->getMinAltitude() ?
QString(
QChar(0x26A0)) :
"")
1350 .arg(
QChar(job->isSettingAtStartup() ? 0x2193 : 0x2191))
1351 .
arg(job->getAltitudeAtStartup(), 0,
'f', 1)
1352 .arg(time.toString(startupTimeEdit->displayFormat())));
1354 switch (job->getFileStartupCondition())
1383 if (
nullptr != altitudeCell)
1386 bool is_setting =
false;
1387 double const alt = SchedulerUtils::findAltitude(job->getTargetCoords(),
QDateTime(), &is_setting);
1390 .arg(
QChar(is_setting ? 0x2193 : 0x2191))
1391 .
arg(alt, 0,
'f', 1));
1398 if (
nullptr != completionCell)
1400 if (job->getGreedyCompletionTime().
isValid())
1403 .arg(job->getGreedyCompletionTime().
toString(
"hh:mm")));
1407 if (FINISH_LOOP != job->getCompletionCondition() && job->getCompletionTime().
isValid())
1410 .arg(job->getAltitudeAtCompletion() < job->getMinAltitude() ?
QString(
QChar(0x26A0)) :
"")
1411 .arg(
QChar(job->isSettingAtCompletion() ? 0x2193 : 0x2191))
1412 .
arg(job->getAltitudeAtCompletion(), 0,
'f', 1)
1413 .arg(job->getCompletionTime().
toString(startupTimeEdit->displayFormat())));
1415 switch (job->getCompletionCondition())
1421 case FINISH_SEQUENCE:
1440 if (
nullptr != captureCountCell)
1442 switch (job->getCompletionCondition())
1449 captureCountCell->
setText(
QString(
"%L1/-").arg(job->getCompletedCount()));
1452 case FINISH_SEQUENCE:
1456 captureCountCell->
setText(
QString(
"%L1/%L2").arg(job->getCompletedCount()).
arg(job->getSequenceCount()));
1460 QString tooltip = job->getProgressSummary();
1461 if (tooltip.
size() == 0)
1462 tooltip =
i18n(
"Count of captures stored for the job, based on its sequence job.\n"
1463 "This is a summary, additional specific frame types may be required to complete the job.");
1471 m_JobUpdateDebounce.
start();
1476 const int pos = above ? row : row + 1;
1479 if (row > queueTable->rowCount())
1482 queueTable->insertRow(
pos);
1485 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_NAME), nameCell);
1490 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_STATUS), statusCell);
1495 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_CAPTURES), captureCount);
1500 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_STARTTIME), startupCell);
1505 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_ALTITUDE), altitudeCell);
1510 queueTable->setItem(row,
static_cast<int>(SCHEDCOL_ENDTIME), completionCell);
1523void Scheduler::resetJobEdit()
1525 if (jobUnderEdit < 0)
1528 SchedulerJob *
const job = moduleState()->jobs().at(jobUnderEdit);
1529 Q_ASSERT_X(job !=
nullptr, __FUNCTION__,
"Edited job must be valid");
1531 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' at row #%2 is not longer edited.").
arg(job->getName()).
arg(
1545 evaluateOnlyB->setEnabled(
true);
1546 startB->setEnabled(
true);
1549 Q_ASSERT_X(jobUnderEdit == -1, __FUNCTION__,
"No more edited/selected job after exiting edit mode");
1554 int currentRow = moduleState()->currentPosition();
1557 if (moduleState()->
removeJob(currentRow) ==
false)
1562 queueTable->removeRow(currentRow);
1565 if (queueTable->rowCount() == 0)
1568 evaluateOnlyB->setEnabled(
false);
1569 queueSaveAsB->setEnabled(
false);
1570 queueSaveB->setEnabled(
false);
1571 startB->setEnabled(
false);
1572 pauseB->setEnabled(
false);
1579 queueTable->clearSelection();
1582 if (jobUnderEdit >= 0)
1586 process()->evaluateJobs(
true);
1594 moduleState()->setCurrentPosition(index);
1597void Scheduler::toggleScheduler()
1599 if (moduleState()->schedulerState() == SCHEDULER_RUNNING)
1601 moduleState()->disablePreemptiveShutdown();
1608void Scheduler::pause()
1610 moduleState()->setSchedulerState(SCHEDULER_PAUSED);
1611 process()->appendLogText(
i18n(
"Scheduler pause planned..."));
1612 pauseB->setEnabled(
false);
1615 startB->setToolTip(
i18n(
"Resume Scheduler"));
1618void Scheduler::syncGreedyParams()
1620 process()->getGreedyScheduler()->setParams(
1621 errorHandlingRestartImmediatelyButton->isChecked(),
1622 errorHandlingRestartQueueButton->isChecked(),
1623 errorHandlingRescheduleErrorsCB->isChecked(),
1624 errorHandlingStrategyDelay->value(),
1625 errorHandlingStrategyDelay->value());
1628void Scheduler::handleShutdownStarted()
1630 KSNotification::event(
QLatin1String(
"ObservatoryShutdown"),
i18n(
"Observatory is in the shutdown process"),
1631 KSNotification::Scheduler);
1632 weatherLabel->hide();
1635void Ekos::Scheduler::changeSleepLabel(
QString text,
bool show)
1637 sleepLabel->setToolTip(text);
1646 TEST_PRINT(stderr,
"%d Setting %s\n", __LINE__, timerStr(RUN_NOTHING).toLatin1().data());
1649 bool wasAborted =
false;
1650 for (
auto &oneJob : moduleState()->jobs())
1660 KSNotification::event(
QLatin1String(
"SchedulerAborted"),
i18n(
"Scheduler aborted."), KSNotification::Scheduler,
1661 KSNotification::Alert);
1663 startupB->setEnabled(
true);
1664 shutdownB->setEnabled(
true);
1667 if (moduleState()->preemptiveShutdown())
1669 changeSleepLabel(
i18n(
"Scheduler is in shutdown until next job is ready"));
1674 changeSleepLabel(
"",
false);
1677 startB->setToolTip(
i18n(
"Start Scheduler"));
1678 pauseB->setEnabled(
false);
1681 queueLoadB->setEnabled(
true);
1682 queueAppendB->setEnabled(
true);
1683 addToQueueB->setEnabled(
true);
1686 evaluateOnlyB->setEnabled(
true);
1692 return load(
true, path.toLocalFile());
1702 "Ekos Scheduler List (*.esl)");
1709 if (fileURL.
isValid() ==
false)
1712 KSNotification::sorry(message,
i18n(
"Invalid URL"));
1719 process()->removeAllJobs();
1721 const int row = moduleState()->jobs().count();
1726 const bool success = process()->appendEkosScheduleList(fileURL.
toLocalFile());
1733 if (moduleState()->jobs().count() > row)
1734 moduleState()->setCurrentPosition(row);
1737 process()->startJobEvaluation();
1747 if (jobUnderEdit >= 0)
1750 while (queueTable->rowCount() > 0)
1751 queueTable->removeRow(0);
1756 process()->clearLog();
1759void Scheduler::saveAs()
1761 schedulerURL.
clear();
1767 QUrl backupCurrent = schedulerURL;
1768 schedulerURL = path;
1774 schedulerURL = backupCurrent;
1779bool Scheduler::save()
1781 QUrl backupCurrent = schedulerURL;
1784 schedulerURL.
clear();
1787 if (moduleState()->dirty() ==
false && !schedulerURL.
isEmpty())
1794 "Ekos Scheduler List (*.esl)");
1798 schedulerURL = backupCurrent;
1810 if ((process()->saveScheduler(schedulerURL)) ==
false)
1812 KSNotification::error(
i18n(
"Failed to save scheduler list"),
i18n(
"Save"));
1817 queueSaveB->setToolTip(
"Save schedule to " + schedulerURL.
fileName());
1822 KSNotification::sorry(message,
i18n(
"Invalid URL"));
1829void Scheduler::checkJobInputComplete()
1832 bool const nameSelectionOK = !raBox->isEmpty() && !decBox->isEmpty() && !nameEdit->text().isEmpty();
1835 bool const fitsSelectionOK = !nameEdit->text().isEmpty() && !fitsURL.
isEmpty();
1838 bool const seqSelectionOK = !sequenceEdit->text().isEmpty();
1841 bool const addingOK = (nameSelectionOK || fitsSelectionOK) && seqSelectionOK;
1843 addToQueueB->setEnabled(addingOK);
1849 checkJobInputComplete();
1852 if (jobUnderEdit < 0)
1855 moduleState()->setDirty(
true);
1857 if (
sender() == startupProcedureButtonGroup ||
sender() == shutdownProcedureGroup)
1861 if (
sender() == schedulerStartupScript)
1863 else if (
sender() == schedulerShutdownScript)
1864 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(schedulerShutdownScript->text()));
1870 if (moduleState()->jobs().isEmpty())
1879 using namespace std::placeholders;
1881 std::stable_sort(sortedJobs.
begin() + 1, sortedJobs.
end(),
1882 std::bind(SchedulerJob::decreasingAltitudeOrder, _1, _2, moduleState()->jobs().first()->getStartupTime()));
1887 for (SchedulerJob * job : moduleState()->jobs())
1890 process()->evaluateJobs(
true);
1897 TEST_PRINT(stderr,
"%d Setting %s\n", __LINE__, timerStr(RUN_SCHEDULER).toLatin1().data());
1898 moduleState()->setupNextIteration(RUN_SCHEDULER);
1904 if (errorHandlingRestartQueueButton->isChecked())
1905 return ERROR_RESTART_AFTER_TERMINATION;
1906 else if (errorHandlingRestartImmediatelyButton->isChecked())
1907 return ERROR_RESTART_IMMEDIATELY;
1909 return ERROR_DONT_RESTART;
1914 errorHandlingStrategyDelay->setEnabled(strategy != ERROR_DONT_RESTART);
1918 case ERROR_RESTART_AFTER_TERMINATION:
1919 errorHandlingRestartQueueButton->setChecked(
true);
1921 case ERROR_RESTART_IMMEDIATELY:
1922 errorHandlingRestartImmediatelyButton->setChecked(
true);
1925 errorHandlingDontRestartButton->setChecked(
true);
1933void Scheduler::setAlgorithm(
int algIndex)
1935 if (algIndex != ALGORITHM_GREEDY)
1937 process()->appendLogText(
1938 i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
1939 algIndex = ALGORITHM_GREEDY;
1941 Options::setSchedulerAlgorithm(algIndex);
1943 groupLabel->setDisabled(
false);
1944 groupEdit->setDisabled(
false);
1945 queueTable->model()->setHeaderData(START_TIME_COLUMN,
Qt::Horizontal,
tr(
"Next Start"));
1946 queueTable->model()->setHeaderData(END_TIME_COLUMN,
Qt::Horizontal,
tr(
"Next End"));
1955 process()->appendLogText(
i18n(
"Turning off astronomical twilight check may cause the observatory to run during daylight. This can cause irreversible damage to your equipment!"));;
1958void Scheduler::updateProfiles()
1960 schedulerProfileCombo->blockSignals(
true);
1961 schedulerProfileCombo->clear();
1962 schedulerProfileCombo->addItems(moduleState()->profiles());
1963 schedulerProfileCombo->setCurrentText(moduleState()->currentProfile());
1964 schedulerProfileCombo->blockSignals(
false);
1967void Scheduler::updateJobStageUI(SchedulerJobStage stage)
1972 static QString stageStringUnknown;
1975 stageStrings[SCHEDSTAGE_IDLE] =
i18n(
"Idle");
1976 stageStrings[SCHEDSTAGE_SLEWING] =
i18n(
"Slewing");
1977 stageStrings[SCHEDSTAGE_SLEW_COMPLETE] =
i18n(
"Slew complete");
1978 stageStrings[SCHEDSTAGE_FOCUSING] =
1979 stageStrings[SCHEDSTAGE_POSTALIGN_FOCUSING] =
i18n(
"Focusing");
1980 stageStrings[SCHEDSTAGE_FOCUS_COMPLETE] =
1981 stageStrings[SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE ] =
i18n(
"Focus complete");
1982 stageStrings[SCHEDSTAGE_ALIGNING] =
i18n(
"Aligning");
1983 stageStrings[SCHEDSTAGE_ALIGN_COMPLETE] =
i18n(
"Align complete");
1984 stageStrings[SCHEDSTAGE_RESLEWING] =
i18n(
"Repositioning");
1985 stageStrings[SCHEDSTAGE_RESLEWING_COMPLETE] =
i18n(
"Repositioning complete");
1987 stageStrings[SCHEDSTAGE_GUIDING] =
i18n(
"Guiding");
1988 stageStrings[SCHEDSTAGE_GUIDING_COMPLETE] =
i18n(
"Guiding complete");
1989 stageStrings[SCHEDSTAGE_CAPTURING] =
i18n(
"Capturing");
1990 stageStringUnknown =
i18n(
"Unknown");
1993 if (activeJob() ==
nullptr)
1994 jobStatus->setText(stageStrings[SCHEDSTAGE_IDLE]);
1996 jobStatus->setText(
QString(
"%1: %2").arg(activeJob()->getName(),
1997 stageStrings.
value(stage, stageStringUnknown)));
2003 if (iface == process()->mountInterface())
2005 QVariant canMountPark = process()->mountInterface()->property(
"canPark");
2008 schedulerUnparkMount->setEnabled(canMountPark.
toBool());
2009 schedulerParkMount->setEnabled(canMountPark.
toBool());
2012 else if (iface == process()->capInterface())
2014 QVariant canCapPark = process()->capInterface()->property(
"canPark");
2017 schedulerCloseDustCover->setEnabled(canCapPark.
toBool());
2018 schedulerOpenDustCover->setEnabled(canCapPark.
toBool());
2022 schedulerCloseDustCover->setEnabled(
false);
2023 schedulerOpenDustCover->setEnabled(
false);
2026 else if (iface == process()->weatherInterface())
2028 QVariant status = process()->weatherInterface()->property(
"status");
2029 if (status.isValid())
2034 schedulerWeather->setEnabled(
true);
2037 schedulerWeather->setEnabled(
false);
2039 else if (iface == process()->domeInterface())
2041 QVariant canDomePark = process()->domeInterface()->property(
"canPark");
2044 schedulerUnparkDome->setEnabled(canDomePark.
toBool());
2045 schedulerParkDome->setEnabled(canDomePark.
toBool());
2048 else if (iface == process()->captureInterface())
2050 QVariant hasCoolerControl = process()->captureInterface()->property(
"coolerControl");
2051 if (hasCoolerControl.
isValid())
2053 schedulerWarmCCD->setEnabled(hasCoolerControl.
toBool());
2058void Scheduler::setWeatherStatus(ISD::Weather::Status status)
2060 TEST_PRINT(stderr,
"sch%d @@@setWeatherStatus(%d)\n", __LINE__,
static_cast<int>(status));
2061 ISD::Weather::Status newStatus = status;
2066 case ISD::Weather::WEATHER_OK:
2067 statusString =
i18n(
"Weather conditions are OK.");
2070 case ISD::Weather::WEATHER_WARNING:
2071 statusString =
i18n(
"Warning: weather conditions are in the WARNING zone.");
2074 case ISD::Weather::WEATHER_ALERT:
2075 statusString =
i18n(
"Caution: weather conditions are in the DANGER zone!");
2082 qCDebug(KSTARS_EKOS_SCHEDULER) << statusString;
2084 if (moduleState()->weatherStatus() == ISD::Weather::WEATHER_OK)
2085 weatherLabel->setPixmap(
2087 .pixmap(
QSize(32, 32)));
2088 else if (moduleState()->weatherStatus() == ISD::Weather::WEATHER_WARNING)
2090 weatherLabel->setPixmap(
2092 .pixmap(
QSize(32, 32)));
2093 KSNotification::event(
QLatin1String(
"WeatherWarning"),
i18n(
"Weather conditions in warning zone"),
2094 KSNotification::Scheduler, KSNotification::Warn);
2096 else if (moduleState()->weatherStatus() == ISD::Weather::WEATHER_ALERT)
2098 weatherLabel->setPixmap(
2100 .pixmap(
QSize(32, 32)));
2102 i18n(
"Weather conditions are critical. Observatory shutdown is imminent"), KSNotification::Scheduler,
2103 KSNotification::Alert);
2107 .pixmap(
QSize(32, 32)));
2109 weatherLabel->show();
2110 weatherLabel->setToolTip(statusString);
2112 process()->appendLogText(statusString);
2114 emit weatherChanged(moduleState()->weatherStatus());
2121 schedulerWeather->setEnabled(
false);
2122 weatherLabel->hide();
2125 changeSleepLabel(
i18n(
"Scheduler is in sleep mode"));
2132 case SCHEDULER_RUNNING:
2137 startB->setToolTip(
i18n(
"Stop Scheduler"));
2138 pauseB->setEnabled(
true);
2139 pauseB->setChecked(
false);
2142 queueLoadB->setEnabled(
false);
2145 evaluateOnlyB->setEnabled(
false);
2146 startupB->setEnabled(
false);
2147 shutdownB->setEnabled(
false);
2154 emit newStatus(newState);
2159 pauseB->setCheckable(
true);
2160 pauseB->setChecked(
true);
2163void Scheduler::handleJobsUpdated(
QJsonArray jobsList)
2168 emit jobsUpdated(jobsList);
2174 return assistant->importMosaic(payload);
2177void Scheduler::startupStateChanged(StartupState state)
2179 jobStatus->setText(startupStateString(state));
2181 switch (moduleState()->startupState())
2186 case STARTUP_COMPLETE:
2188 process()->appendLogText(
i18n(
"Manual startup procedure completed successfully."));
2192 process()->appendLogText(
i18n(
"Manual startup procedure terminated due to errors."));
2200void Scheduler::shutdownStateChanged(ShutdownState state)
2202 if (state == SHUTDOWN_COMPLETE || state == SHUTDOWN_IDLE
2203 || state == SHUTDOWN_ERROR)
2211 if (state == SHUTDOWN_IDLE)
2212 jobStatus->setText(
i18n(
"Idle"));
2214 jobStatus->setText(shutdownStateString(state));
2216void Scheduler::ekosStateChanged(EkosState state)
2218 if (state == EKOS_IDLE)
2220 jobStatus->setText(
i18n(
"Idle"));
2224 jobStatus->setText(ekosStateString(state));
2226void Scheduler::indiStateChanged(INDIState state)
2228 if (state == INDI_IDLE)
2230 jobStatus->setText(
i18n(
"Idle"));
2234 jobStatus->setText(indiStateString(state));
2236void Scheduler::parkWaitStateChanged(ParkWaitState state)
2238 jobStatus->setText(parkWaitStateString(state));
2241SchedulerJob *Scheduler::activeJob()
2243 return moduleState()->activeJob();
2246void Scheduler::loadGlobalSettings()
2251 QVariantMap settings;
2255 key = oneWidget->objectName();
2256 value = Options::self()->property(key.
toLatin1());
2257 if (value.
isValid() && oneWidget->count() > 0)
2259 oneWidget->setCurrentText(value.
toString());
2260 settings[key] = value;
2263 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2269 key = oneWidget->objectName();
2270 value = Options::self()->property(key.
toLatin1());
2274 settings[key] = value;
2277 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2283 key = oneWidget->objectName();
2284 value = Options::self()->property(key.
toLatin1());
2288 settings[key] = value;
2291 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2297 key = oneWidget->objectName();
2298 value = Options::self()->property(key.
toLatin1());
2301 oneWidget->setChecked(value.
toBool());
2302 settings[key] = value;
2305 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2311 key = oneWidget->objectName();
2312 value = Options::self()->property(key.
toLatin1());
2315 oneWidget->setText(value.
toString());
2316 settings[key] = value;
2318 if (key ==
"sequenceEdit")
2320 else if (key ==
"schedulerStartupScript")
2322 else if (key ==
"schedulerShutdownScript")
2326 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Option" << key <<
"not found!";
2332 key = oneWidget->objectName();
2333 value = Options::self()->property(key.
toLatin1());
2336 oneWidget->setChecked(value.
toBool());
2337 settings[key] = value;
2344 key = oneWidget->objectName();
2345 value = Options::self()->property(key.
toLatin1());
2349 settings[key] = value;
2355 m_GlobalSettings = m_Settings = settings;
2358void Scheduler::syncSettings()
2374 value = dsb->
value();
2380 value = sb->
value();
2392 m_Settings.remove(key);
2405 value = lineedit->
text();
2414 Options::self()->setProperty(key.
toLatin1(), value);
2416 m_Settings[key] = value;
2417 m_GlobalSettings[key] = value;
2419 emit settingsUpdated(getAllSettings());
2425QVariantMap Scheduler::getAllSettings()
const
2427 QVariantMap settings;
2449 if (!oneWidget->objectName().startsWith(
"qt_"))
2450 settings.insert(oneWidget->objectName(), oneWidget->text());
2460 settings.insert(oneWidget->objectName(), oneWidget->dateTime().toString(
Qt::ISODate));
2469void Scheduler::setAllSettings(
const QVariantMap &settings)
2473 disconnectSettings();
2475 for (
auto &name : settings.keys())
2481 syncControl(settings, name, comboBox);
2489 syncControl(settings, name, doubleSpinBox);
2497 syncControl(settings, name, spinBox);
2505 syncControl(settings, name, checkbox);
2513 syncControl(settings, name, lineedit);
2515 if (name ==
"sequenceEdit")
2517 else if (name ==
"fitsEdit")
2519 else if (name ==
"schedulerStartupScript")
2521 else if (name ==
"schedulerShutdownScript")
2531 syncControl(settings, name, radioButton);
2538 syncControl(settings, name, datetimeedit);
2543 m_Settings = settings;
2552bool Scheduler::syncControl(
const QVariantMap &settings,
const QString &key,
QWidget * widget)
2565 const int value = settings[key].toInt(&ok);
2574 const double value = settings[key].toDouble(&ok);
2583 const bool value = settings[key].toBool();
2591 const QString value = settings[key].toString();
2597 const auto value = settings[key].toString();
2603 const bool value = settings[key].toBool();
2605 pRadioButton->
click();
2618void Scheduler::connectSettings()
2644 if (!oneWidget->objectName().startsWith(
"qt_"))
2653void Scheduler::disconnectSettings()
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 updateSchedulerURL(const QString &fileURL)
updateSchedulerURL Update scheduler URL after succesful loading a new file.
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
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 setJobManipulation(bool can_reorder, bool can_delete)
setJobManipulation Enable or disable job manipulation buttons.
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.
void loadJob(QModelIndex i)
editJob Edit an observation job
void setSequence(const QString &sequenceFileURL)
Set the file URL pointing to the capture sequence file.
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.
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.
SkyObject * targetObject()
Q_INVOKABLE QAction * action(const QString &name) const
static KConfigDialog * exists(const QString &name)
void settingsChanged(const QString &dialogName)
const KStarsDateTime & ut() const
static KStars * Instance()
virtual KActionCollection * actionCollection() const
The QProgressIndicator class lets an application display a progress indicator to show that a long tas...
void stopAnimation()
Stops the spin animation.
void startAnimation()
Starts the spin animation.
The SchedulerState class holds all attributes defining the scheduler's state.
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 & ra0() const
const CachingDms & dec0() const
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.
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 currentIndexChanged(int index)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
bool isValid() const const
QString toString(QStringView format, QCalendar cal) const const
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)
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
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)
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)
void resizeColumnToContents(int column)
void setText(const QString &text)
void setTextAlignment(Qt::Alignment alignment)
bool setHMS(int h, int m, int s, int ms)
void setInterval(int msec)
void setSingleShot(bool singleShot)
QString fileName(ComponentFormattingOptions options) const const
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
void setValue(QVariant &&value)
bool toBool() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QString toString() const const