6#include "schedulerprocess.h"
7#include "schedulermodulestate.h"
8#include "scheduleradaptor.h"
9#include "greedyscheduler.h"
10#include "schedulerutils.h"
11#include "schedulerjob.h"
12#include "ekos/capture/sequencejob.h"
14#include "ksmessagebox.h"
15#include "ksnotification.h"
17#include "kstarsdata.h"
18#include "indi/indistd.h"
19#include "skymapcomposite.h"
20#include "mosaiccomponent.h"
21#include "mosaictiles.h"
22#include "ekos/auxiliary/opticaltrainmanager.h"
23#include "ekos/auxiliary/stellarsolverprofile.h"
24#include <ekos_scheduler_debug.h>
26#include <QtDBus/QDBusReply>
27#include <QtDBus/QDBusInterface>
29#define RESTART_GUIDING_DELAY_MS 5000
41 setObjectName(
"SchedulerProcess");
42 m_moduleState = state;
43 m_GreedyScheduler =
new GreedyScheduler();
51 connect(moduleState().data(), &SchedulerModuleState::schedulerStateChanged,
this, &SchedulerProcess::newStatus);
52 connect(moduleState().data(), &SchedulerModuleState::newLog,
this, &SchedulerProcess::appendLogText);
55 new SchedulerAdaptor(
this);
58 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"SchedulerProcess failed to register with dbus");
60 setEkosInterface(
new QDBusInterface(kstarsInterfaceString, ekosPathStr, ekosInterfaceStr,
62 setIndiInterface(
new QDBusInterface(kstarsInterfaceString, INDIPathString, INDIInterfaceString,
65 this, SLOT(setINDICommunicationStatus(Ekos::CommunicationStatus)));
67 this, SLOT(setEkosCommunicationStatus(Ekos::CommunicationStatus)));
69 SLOT(registerNewModule(
QString)));
71 SLOT(registerNewDevice(
QString,
int)));
76 return moduleState()->schedulerState();
79void SchedulerProcess::execute()
81 switch (moduleState()->schedulerState())
85 if (!moduleState()->startupScriptURL().isEmpty() && ! moduleState()->startupScriptURL().isValid())
87 appendLogText(
i18n(
"Warning: startup script URL %1 is not valid.",
93 if (!moduleState()->shutdownScriptURL().isEmpty() && !moduleState()->shutdownScriptURL().isValid())
95 appendLogText(
i18n(
"Warning: shutdown script URL %1 is not valid.",
101 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Scheduler is starting...";
103 moduleState()->setSchedulerState(SCHEDULER_RUNNING);
104 moduleState()->setupNextIteration(RUN_SCHEDULER);
106 appendLogText(
i18n(
"Scheduler started."));
107 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler started.";
110 case SCHEDULER_PAUSED:
111 moduleState()->setSchedulerState(SCHEDULER_RUNNING);
112 moduleState()->setupNextIteration(RUN_SCHEDULER);
114 appendLogText(
i18n(
"Scheduler resuming."));
115 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler resuming.";
128void SchedulerProcess::findNextJob()
130 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
141 __FUNCTION__,
"Finding next job requires current to be in error, aborted, idle or complete");
144 moduleState()->resetAlignFailureCount();
145 moduleState()->resetGuideFailureCount();
146 moduleState()->resetFocusFailureCount();
147 moduleState()->resetCaptureFailureCount();
151 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
152 moduleState()->resetCaptureBatch();
157 appendLogText(
i18n(
"Job '%1' is terminated due to errors.", activeJob()->getName()));
159 appendLogText(
i18n(
"Job '%1' is aborted.", activeJob()->getName()));
162 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
165 if (Options::errorHandlingStrategy() == ERROR_RESTART_IMMEDIATELY &&
167 (activeJob()->getState() ==
SCHEDJOB_ERROR && Options::rescheduleErrors())))
172 appendLogText(
i18n(
"Waiting %1 seconds to restart job '%2'.", Options::errorHandlingStrategyDelay(),
173 activeJob()->getName()));
176 moduleState()->setupNextIteration(RUN_WAKEUP, std::lround((Options::errorHandlingStrategyDelay() * 1000) /
177 KStarsData::Instance()->clock()->scale()));
178 emit changeSleepLabel(
i18n(
"Scheduler waits for a retry."));
183 moduleState()->setActiveJob(
nullptr);
184 moduleState()->setupNextIteration(RUN_SCHEDULER);
188 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
191 moduleState()->setActiveJob(
nullptr);
192 moduleState()->setupNextIteration(RUN_SCHEDULER);
196 else if (activeJob()->getCompletionCondition() == FINISH_SEQUENCE)
198 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
201 if (Options::rememberJobProgress())
203 foreach(SchedulerJob *a_job, moduleState()->jobs())
204 if (a_job == activeJob() || a_job->isDuplicateOf(activeJob()))
208 moduleState()->resetCaptureBatch();
212 appendLogText(
i18n(
"Job '%1' is complete.", activeJob()->getName()));
215 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
219 if (!canCountCaptures(*activeJob()))
222 moduleState()->setActiveJob(
nullptr);
223 moduleState()->setupNextIteration(RUN_SCHEDULER);
225 else if (activeJob()->getCompletionCondition() == FINISH_REPEAT &&
226 (activeJob()->getRepeatsRemaining() <= 1))
229 if (activeJob()->getRepeatsRemaining() > 0)
232 if (!Options::rememberJobProgress())
234 activeJob()->setRepeatsRemaining(activeJob()->getRepeatsRemaining() - 1);
235 activeJob()->setCompletedIterations(activeJob()->getCompletedIterations() + 1);
237 activeJob()->setStartupTime(
QDateTime());
241 foreach(SchedulerJob *a_job, moduleState()->jobs())
242 if (a_job == activeJob() || a_job->isDuplicateOf(activeJob()))
249 if (activeJob() ==
nullptr || activeJob()->getRepeatsRemaining() == 0)
251 stopCurrentJobAction();
253 if (activeJob() !=
nullptr)
255 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
256 appendLogText(
i18np(
"Job '%1' is complete after #%2 batch.",
257 "Job '%1' is complete after #%2 batches.",
258 activeJob()->getName(), activeJob()->getRepeatsRequired()));
259 if (!canCountCaptures(*activeJob()))
261 moduleState()->setActiveJob(
nullptr);
263 moduleState()->setupNextIteration(RUN_SCHEDULER);
269 if (executeJob(activeJob()) ==
false)
273 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN && Options::forceAlignmentBeforeJob())
276 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
280 else if ( (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE) )
282 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
286 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
288 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
292 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
294 moduleState()->updateJobStage(SCHEDSTAGE_SLEWING);
300 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
304 appendLogText(
i18np(
"Job '%1' is repeating, #%2 batch remaining.",
305 "Job '%1' is repeating, #%2 batches remaining.",
306 activeJob()->getName(), activeJob()->getRepeatsRemaining()));
308 moduleState()->setupNextIteration(RUN_JOBCHECK);
311 else if ((activeJob()->getCompletionCondition() == FINISH_LOOP) ||
312 (activeJob()->getCompletionCondition() == FINISH_REPEAT &&
313 activeJob()->getRepeatsRemaining() > 0))
316 if ((activeJob()->getCompletionCondition() == FINISH_REPEAT) &&
317 (activeJob()->getRepeatsRemaining() > 1))
320 if (!Options::rememberJobProgress())
322 activeJob()->setRepeatsRemaining(activeJob()->getRepeatsRemaining() - 1);
323 activeJob()->setCompletedIterations(activeJob()->getCompletedIterations() + 1);
325 activeJob()->setStartupTime(
QDateTime());
328 if (executeJob(activeJob()) ==
false)
331 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN && Options::forceAlignmentBeforeJob())
334 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
339 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
343 moduleState()->increaseCaptureBatch();
345 if (activeJob()->getCompletionCondition() == FINISH_REPEAT )
346 appendLogText(
i18np(
"Job '%1' is repeating, #%2 batch remaining.",
347 "Job '%1' is repeating, #%2 batches remaining.",
348 activeJob()->getName(), activeJob()->getRepeatsRemaining()));
350 appendLogText(
i18n(
"Job '%1' is repeating, looping indefinitely.", activeJob()->getName()));
353 moduleState()->setupNextIteration(RUN_JOBCHECK);
355 else if (activeJob()->getCompletionCondition() == FINISH_AT)
357 if (SchedulerModuleState::getLocalTime().secsTo(activeJob()->getFinishAtTime()) <= 0)
359 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
362 foreach(SchedulerJob *a_job, moduleState()->jobs())
363 if (a_job == activeJob() || a_job->isDuplicateOf(activeJob()))
365 stopCurrentJobAction();
367 moduleState()->resetCaptureBatch();
369 appendLogText(
i18np(
"Job '%1' stopping, reached completion time with #%2 batch done.",
370 "Job '%1' stopping, reached completion time with #%2 batches done.",
371 activeJob()->getName(), moduleState()->captureBatch() + 1));
374 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
376 moduleState()->setActiveJob(
nullptr);
377 moduleState()->setupNextIteration(RUN_SCHEDULER);
381 if (executeJob(activeJob()) ==
false)
384 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN && Options::forceAlignmentBeforeJob())
387 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
392 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
396 moduleState()->increaseCaptureBatch();
398 appendLogText(
i18np(
"Job '%1' completed #%2 batch before completion time, restarted.",
399 "Job '%1' completed #%2 batches before completion time, restarted.",
400 activeJob()->getName(), moduleState()->captureBatch()));
402 moduleState()->setupNextIteration(RUN_JOBCHECK);
408 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"BUGBUG! Job '" << activeJob()->getName() <<
409 "' timer elapsed, but no action to be taken.";
412 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
414 moduleState()->setActiveJob(
nullptr);
415 moduleState()->setupNextIteration(RUN_SCHEDULER);
419void Ekos::SchedulerProcess::stopCapturing(
QString train,
bool followersOnly)
421 if (train ==
"" && followersOnly)
423 for (
auto key : m_activeJobs.
keys())
426 SchedulerJob *job = m_activeJobs[key];
430 dbusargs.
append(job->getOpticalTrain());
443 for (
auto job : m_activeJobs.
values())
444 if (train ==
"" || job->getOpticalTrain() == train)
451 if (
nullptr != activeJob())
453 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Job '" << activeJob()->getName() <<
"' is stopping current action..." <<
454 activeJob()->getStage();
456 switch (activeJob()->getStage())
458 case SCHEDSTAGE_IDLE:
461 case SCHEDSTAGE_SLEWING:
465 case SCHEDSTAGE_FOCUSING:
469 case SCHEDSTAGE_ALIGNING:
475 case SCHEDSTAGE_CAPTURING:
484 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
493 if (moduleState()->preemptiveShutdown())
495 moduleState()->disablePreemptiveShutdown();
501 if (moduleState()->schedulerState() == SCHEDULER_RUNNING)
504 appendLogText(
i18n(
"Scheduler is awake. Jobs shall be started when scheduler is resumed."));
506 moduleState()->setupNextIteration(RUN_SCHEDULER);
513 foreach (
auto j, moduleState()->jobs())
516 emit updateJobTable(j);
518 moduleState()->init();
525 if (moduleState()->schedulerState() != SCHEDULER_RUNNING)
528 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Scheduler is stopping...";
532 if (!moduleState()->preemptiveShutdown())
534 for (
auto &oneJob : moduleState()->jobs())
536 if (oneJob == activeJob())
541 appendLogText(
i18n(
"Job '%1' has not been processed upon scheduler stop, marking aborted.", oneJob->getName()));
547 moduleState()->setupNextIteration(RUN_NOTHING);
548 moduleState()->cancelGuidingTimer();
550 moduleState()->setSchedulerState(SCHEDULER_IDLE);
551 moduleState()->setParkWaitState(PARKWAIT_IDLE);
552 moduleState()->setEkosState(EKOS_IDLE);
553 moduleState()->setIndiState(INDI_IDLE);
557 if (moduleState()->startupState() != STARTUP_COMPLETE || moduleState()->preemptiveShutdown())
559 if (moduleState()->startupState() == STARTUP_SCRIPT)
565 moduleState()->setStartupState(STARTUP_IDLE);
570 else if (moduleState()->startupState() == STARTUP_COMPLETE)
572 if (Options::schedulerUnparkDome())
573 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
574 else if (Options::schedulerUnparkMount())
575 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
576 else if (Options::schedulerOpenDustCover())
577 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
580 moduleState()->setShutdownState(SHUTDOWN_IDLE);
582 moduleState()->setActiveJob(
nullptr);
583 moduleState()->resetFailureCounters();
584 moduleState()->setAutofocusCompleted(
false);
587 if (moduleState()->preemptiveShutdown())
589 QDateTime const now = SchedulerModuleState::getLocalTime();
590 int const nextObservationTime = now.
secsTo(moduleState()->preemptiveShutdownWakeupTime());
591 moduleState()->setupNextIteration(RUN_WAKEUP,
592 std::lround(((nextObservationTime + 1) * 1000)
593 / KStarsData::Instance()->clock()->scale()));
595 emit schedulerStopped();
600 if (captureInterface().isNull() ==
false)
601 captureInterface()->setProperty(
"targetName",
QString());
607 emit schedulerStopped();
612 emit clearJobTable();
614 qDeleteAll(moduleState()->jobs());
615 moduleState()->mutlableJobs().clear();
616 moduleState()->setCurrentPosition(-1);
628 emit changeCurrentSequence(sequenceFileURL);
633 if (moduleState()->schedulerState() == SCHEDULER_RUNNING)
637 foreach (SchedulerJob *job, moduleState()->jobs())
638 job->setCompletedCount(0);
646 Q_ASSERT_X(
nullptr != job, __FUNCTION__,
647 "There must be a valid current job for Scheduler to test sleep requirement");
649 if (job->getLightFramesRequired() ==
false)
652 QDateTime const now = SchedulerModuleState::getLocalTime();
653 int const nextObservationTime = now.
secsTo(job->getStartupTime());
657 if (getGreedyScheduler()->getScheduledJob() != job)
662 if (moduleState()->startupState() == STARTUP_COMPLETE &&
663 Options::preemptiveShutdown() &&
664 nextObservationTime > (Options::preemptiveShutdownTime() * 3600))
667 "Job '%1' scheduled for execution at %2. "
668 "Observatory scheduled for shutdown until next job is ready.",
669 job->getName(), job->getStartupTime().
toString()));
670 moduleState()->enablePreemptiveShutdown(job->getStartupTime());
672 emit schedulerSleeping(
true,
false);
681 else if (nextObservationTime > Options::leadTime() * 60 &&
682 moduleState()->startupState() == STARTUP_COMPLETE &&
683 moduleState()->parkWaitState() == PARKWAIT_IDLE &&
684 (job->getStepPipeline() & SchedulerJob::USE_TRACK) &&
686 Options::schedulerParkMount())
689 "Job '%1' scheduled for execution at %2. "
690 "Parking the mount until the job is ready.",
691 job->getName(), job->getStartupTime().
toString()));
693 moduleState()->setParkWaitState(PARKWAIT_PARK);
697 else if (nextObservationTime > Options::leadTime() * 60)
699 appendLogText(
i18n(
"Sleeping until observation job %1 is ready at %2...", job->getName(),
703 if (nextObservationTime > Options::leadTime() * 60 * 12 && !Options::preemptiveShutdown())
705 dms delay(
static_cast<double>(nextObservationTime * 15.0 / 3600.0));
707 "Warning: Job '%1' is %2 away from now, you may want to enable Preemptive Shutdown.",
708 job->getName(), delay.toHMSString()));
716 moduleState()->setupNextIteration(RUN_WAKEUP,
717 std::lround(((nextObservationTime + 1) * 1000) / KStarsData::Instance()->clock()->scale()));
719 emit schedulerSleeping(
false,
true);
728 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting slewing must be valid");
733 moduleState()->setParkWaitState(PARKWAIT_UNPARK);
737 if (Options::resetMountModelBeforeJob())
742 SkyPoint target = activeJob()->getTargetCoords();
744 telescopeSlew.
append(target.ra().Hours());
745 telescopeSlew.
append(target.dec().Degrees());
752 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' slew request received DBUS error: %2").
arg(
759 moduleState()->updateJobStage(SCHEDSTAGE_SLEWING);
766 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting focusing must be valid");
770 if (activeJob()->getStage() == SCHEDSTAGE_RESLEWING_COMPLETE ||
771 activeJob()->getStage() == SCHEDSTAGE_POSTALIGN_FOCUSING)
777 moduleState()->updateJobStage(SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE);
788 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' canAutoFocus request received DBUS error: %2").
arg(
798 if (focusModeReply.value() ==
false)
800 appendLogText(
i18n(
"Warning: job '%1' is unable to proceed with autofocus, not supported.", activeJob()->getName()));
801 activeJob()->setStepPipeline(
802 static_cast<SchedulerJob::StepPipeline
>(activeJob()->getStepPipeline() & ~SchedulerJob::USE_FOCUS));
803 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
816 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' resetFrame request received DBUS error: %2").
arg(
828 if (!activeJob()->getInitialFilter().isEmpty())
830 focusInterface()->setProperty(
"filter", activeJob()->getInitialFilter());
834 if (Options::focusUseFullField() ==
false)
838 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setAutoStarEnabled", autoStar)).type() ==
841 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' setAutoFocusStar request received DBUS error: %1").
arg(
855 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' startFocus request received DBUS error: %2").
arg(
865 moduleState()->updateJobStage(SCHEDSTAGE_FOCUSING);
867 moduleState()->startCurrentOperationTimer();
872 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting aligning must be valid");
883 moduleState()->setIndexToUse(-1);
884 moduleState()->setHealpixToUse(-1);
887 if (activeJob()->getFITSFile().isEmpty() ==
false)
893 appendLogText(
i18n(
"Warning: job '%1' target FITS file does not exist.", activeJob()->getName()));
902 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"loadAndSlew", solveArgs)).type() ==
905 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request received DBUS error: %2",
914 else if (reply.
arguments().first().toBool() ==
false)
916 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request failed.", activeJob()->getName()));
922 appendLogText(
i18n(
"Job '%1' is plate solving %2.", activeJob()->getName(), activeJob()->getFITSFile().fileName()));
928 const SkyPoint targetCoords = activeJob()->getTargetCoords();
930 targetArgs << targetCoords.ra0().Hours() << targetCoords.dec0().Degrees();
931 rotationArgs << activeJob()->getPositionAngle();
933 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords",
936 appendLogText(
i18n(
"Warning: job '%1' setTargetCoords request received DBUS error: %2",
947 if (activeJob()->getPositionAngle() >= -180)
949 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetPositionAngle",
952 appendLogText(
i18n(
"Warning: job '%1' setTargetPositionAngle request received DBUS error: %2").arg(
965 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request received DBUS error: %2").arg(
974 else if (reply.
arguments().first().toBool() ==
false)
976 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request failed.", activeJob()->getName()));
982 appendLogText(
i18n(
"Job '%1' is capturing and plate solving.", activeJob()->getName()));
986 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
987 moduleState()->startCurrentOperationTimer();
992 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting guiding must be valid");
997 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
998 appendLogText(
i18n(
"Guiding already running for %1, starting next scheduler action...", activeJob()->getName()));
1000 moduleState()->startCurrentOperationTimer();
1013 if (resetCalibration && Options::resetGuideCalibration())
1020 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1022 appendLogText(
i18n(
"Starting guiding procedure for %1 ...", activeJob()->getName()));
1024 moduleState()->startCurrentOperationTimer();
1029 if (!guideInterface())
1033 if (
nullptr != activeJob() && (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE))
1035 qCInfo(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' is stopping guiding...").
arg(activeJob()->getName());
1037 moduleState()->resetGuideFailureCount();
1039 stopCapturing(
"",
true);
1043 if (moduleState()->isGuidingTimerActive())
1044 moduleState()->cancelGuidingTimer();
1049 if ((moduleState()->restartGuidingInterval() > 0) &&
1050 (moduleState()->restartGuidingTime().msecsTo(KStarsData::Instance()->ut()) > moduleState()->restartGuidingInterval()))
1052 moduleState()->cancelGuidingTimer();
1059 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting capturing must be valid");
1062 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE &&
getGuidingStatus() != GUIDE_GUIDING)
1065 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1070 startSingleCapture(activeJob(), restart);
1071 for (
auto follower : activeJob()->followerJobs())
1074 if (follower->getState() ==
SCHEDJOB_SCHEDULED || (follower->getStage() == SCHEDSTAGE_CAPTURING && follower->isStopped()))
1077 follower->setStage(SCHEDSTAGE_CAPTURING);
1078 startSingleCapture(follower, restart);
1082 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
1084 KSNotification::event(
QLatin1String(
"EkosScheduledImagingStart"),
1085 i18n(
"Ekos job (%1) - Capture started", activeJob()->getName()), KSNotification::Scheduler);
1087 if (moduleState()->captureBatch() > 0)
1088 appendLogText(
i18n(
"Job '%1' capture is in progress (batch #%2)...", activeJob()->getName(),
1089 moduleState()->captureBatch() + 1));
1091 appendLogText(
i18n(
"Job '%1' capture is in progress...", activeJob()->getName()));
1093 moduleState()->startCurrentOperationTimer();
1096void SchedulerProcess::startSingleCapture(SchedulerJob *job,
bool restart)
1098 captureInterface()->setProperty(
"targetName", job->getName());
1101 QVariant train(job->getOpticalTrain());
1103 if (restart ==
false)
1108 QVariant targetName(job->getName());
1112 dbusargs.
append(targetName);
1114 "loadSequenceQueue",
1118 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1119 QString(
"Warning: job '%1' loadSequenceQueue request received DBUS error: %1").
arg(job->getName()).
arg(
1126 else if (captureReply.value() ==
false)
1128 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1129 QString(
"Warning: job '%1' loadSequenceQueue request failed").
arg(job->getName());
1138 for (
auto &e : fMap.keys())
1143 dbusargs.
append(fMap.value(e));
1146 if ((reply = captureInterface()->callWithArgumentList(
QDBus::Block,
"setCapturedFramesMap",
1147 dbusargs)).
type() ==
1150 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1151 QString(
"Warning: job '%1' setCapturedFramesCount request received DBUS error: %1").
arg(job->getName()).
arg(
1168 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1169 QString(
"Warning: job '%1' start request received DBUS error: %1").
arg(job->getName()).
arg(
1176 QString trainName = startReply.value();
1177 m_activeJobs[trainName] = job;
1183 QVariant gotoMode(
static_cast<int>(mode));
1189 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Loading profiles";
1193 moduleState()->updateProfiles(profiles);
1196void SchedulerProcess::executeScript(
const QString &filename)
1205 checkProcessExit(exitCode);
1209 scriptProcess().
start(filename, arguments);
1214 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1217 switch (moduleState()->ekosState())
1221 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1223 moduleState()->setEkosState(EKOS_READY);
1229 moduleState()->setEkosState(EKOS_STARTING);
1230 moduleState()->startCurrentOperationTimer();
1232 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos communication status is" << moduleState()->ekosCommunicationStatus() <<
1241 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1244 moduleState()->resetEkosConnectFailureCount();
1245 moduleState()->setEkosState(EKOS_READY);
1248 else if (moduleState()->ekosCommunicationStatus() == Ekos::Error)
1250 if (moduleState()->increaseEkosConnectFailureCount())
1261 else if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1264 else if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1266 if (moduleState()->increaseEkosConnectFailureCount())
1273 moduleState()->startCurrentOperationTimer();
1287 if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1290 moduleState()->setEkosState(EKOS_IDLE);
1304 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1307 switch (moduleState()->indiState())
1311 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1313 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1314 moduleState()->resetIndiConnectFailureCount();
1315 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI Properties...";
1319 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Connecting INDI devices...";
1321 moduleState()->setIndiState(INDI_CONNECTING);
1323 moduleState()->startCurrentOperationTimer();
1328 case INDI_CONNECTING:
1330 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1333 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1335 else if (moduleState()->indiCommunicationStatus() == Ekos::Error)
1337 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1344 appendLogText(
i18n(
"One or more INDI devices failed to connect. Check INDI control panel for details."));
1349 else if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1351 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1355 moduleState()->startCurrentOperationTimer();
1359 appendLogText(
i18n(
"One or more INDI devices timed out. Check INDI control panel for details."));
1366 case INDI_DISCONNECTING:
1368 if (moduleState()->indiCommunicationStatus() == Ekos::Idle)
1371 moduleState()->setIndiState(INDI_IDLE);
1377 case INDI_PROPERTY_CHECK:
1379 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI properties.";
1381 if (Options::schedulerUnparkDome() && moduleState()->domeReady() ==
false)
1383 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1385 moduleState()->startCurrentOperationTimer();
1386 appendLogText(
i18n(
"Warning: dome device not ready after timeout, attempting to recover..."));
1396 if (Options::schedulerUnparkMount() && moduleState()->mountReady() ==
false)
1398 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1400 moduleState()->startCurrentOperationTimer();
1401 appendLogText(
i18n(
"Warning: mount device not ready after timeout, attempting to recover..."));
1406 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount unpark required but mount is not yet ready.";
1411 if (Options::schedulerOpenDustCover() && moduleState()->capReady() ==
false)
1413 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1415 moduleState()->startCurrentOperationTimer();
1416 appendLogText(
i18n(
"Warning: cap device not ready after timeout, attempting to recover..."));
1421 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap unpark required but cap is not yet ready.";
1426 if (captureInterface().isNull())
1429 if (moduleState()->captureReady() ==
false)
1431 QVariant hasCoolerControl = captureInterface()->property(
"coolerControl");
1432 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cooler control" << (!hasCoolerControl.
isValid() ?
"invalid" :
1433 (hasCoolerControl.
toBool() ?
"True" :
"Faklse"));
1434 if (hasCoolerControl.
isValid())
1435 moduleState()->setCaptureReady(
true);
1437 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"Capture module is not ready yet...";
1440 moduleState()->setIndiState(INDI_READY);
1441 moduleState()->resetIndiConnectFailureCount();
1455 if (moduleState()->indiState() == INDI_DISCONNECTING
1460 if (moduleState()->indiState() != INDI_IDLE && Options::stopEkosAfterShutdown())
1467 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
1471 if (moduleState()->ekosState() != EKOS_IDLE && Options::stopEkosAfterShutdown())
1477 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
1490 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Disconnecting INDI...";
1491 moduleState()->setIndiState(INDI_DISCONNECTING);
1495void SchedulerProcess::stopEkos()
1497 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Stopping Ekos...";
1498 moduleState()->setEkosState(EKOS_STOPPING);
1499 moduleState()->resetEkosConnectFailureCount();
1501 moduleState()->setMountReady(
false);
1502 moduleState()->setCaptureReady(
false);
1503 moduleState()->setDomeReady(
false);
1504 moduleState()->setCapReady(
false);
1509 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
1513 switch (moduleState()->ekosState())
1524 switch (moduleState()->indiState())
1527 case INDI_DISCONNECTING:
1536 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1538 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Ekos is currently connected, checking INDI before mitigating connection loss.");
1541 if (moduleState()->isINDIConnected())
1544 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"INDI is currently connected, no connection loss mitigation needed.");
1563 if (capInterface().isNull())
1566 QVariant parkingStatus = capInterface()->property(
"parkStatus");
1567 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1569 if (parkingStatus.
isValid() ==
false)
1571 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
1572 capInterface()->lastError().type());
1574 parkingStatus = ISD::PARK_ERROR;
1577 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1581 case ISD::PARK_PARKED:
1582 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1585 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
1587 moduleState()->resetParkingCapFailureCount();
1590 case ISD::PARK_UNPARKED:
1591 if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1593 moduleState()->setStartupState(STARTUP_COMPLETE);
1596 moduleState()->resetParkingCapFailureCount();
1599 case ISD::PARK_PARKING:
1600 case ISD::PARK_UNPARKING:
1602 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1604 if (moduleState()->increaseParkingCapFailureCount())
1607 if (status == ISD::PARK_PARKING)
1616 case ISD::PARK_ERROR:
1617 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1620 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1622 else if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1625 moduleState()->setStartupState(STARTUP_ERROR);
1627 moduleState()->resetParkingCapFailureCount();
1635void SchedulerProcess::checkMountParkingStatus()
1637 if (mountInterface().isNull())
1640 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
1641 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1643 if (parkingStatus.
isValid() ==
false)
1645 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
1646 mountInterface()->lastError().type());
1648 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1651 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1656 case ISD::PARK_PARKED:
1659 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1660 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1663 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1664 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1667 moduleState()->resetParkingMountFailureCount();
1671 case ISD::PARK_UNPARKED:
1674 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1675 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1678 if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1679 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1682 moduleState()->resetParkingMountFailureCount();
1688 case ISD::PARK_UNPARKING:
1689 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1691 if (moduleState()->increaseParkingMountFailureCount())
1693 appendLogText(
i18n(
"Warning: mount unpark operation timed out on attempt %1/%2. Restarting operation...",
1694 moduleState()->parkingMountFailureCount(), moduleState()->maxFailureAttempts()));
1699 appendLogText(
i18n(
"Warning: mount unpark operation timed out on last attempt."));
1700 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1703 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
1708 case ISD::PARK_PARKING:
1709 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1711 if (moduleState()->increaseParkingMountFailureCount())
1713 appendLogText(
i18n(
"Warning: mount park operation timed out on attempt %1/%2. Restarting operation...",
1714 moduleState()->parkingMountFailureCount(),
1715 moduleState()->maxFailureAttempts()));
1720 appendLogText(
i18n(
"Warning: mount park operation timed out on last attempt."));
1721 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1724 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Parking mount in progress...";
1729 case ISD::PARK_ERROR:
1730 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1733 moduleState()->setStartupState(STARTUP_ERROR);
1734 moduleState()->resetParkingMountFailureCount();
1736 else if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1738 if (moduleState()->increaseParkingMountFailureCount())
1740 appendLogText(
i18n(
"Warning: mount park operation failed on attempt %1/%2. Restarting operation...",
1741 moduleState()->parkingMountFailureCount(),
1742 moduleState()->maxFailureAttempts()));
1748 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1749 moduleState()->resetParkingMountFailureCount();
1753 else if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1756 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1757 moduleState()->resetParkingMountFailureCount();
1759 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1762 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1763 moduleState()->resetParkingMountFailureCount();
1769 case ISD::PARK_UNKNOWN:
1771 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1772 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1775 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1776 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1779 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1780 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1781 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1782 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1784 moduleState()->resetParkingMountFailureCount();
1789void SchedulerProcess::checkDomeParkingStatus()
1791 if (domeInterface().isNull())
1794 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
1795 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1797 if (parkingStatus.
isValid() ==
false)
1799 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
1800 mountInterface()->lastError().
type());
1802 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1805 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1809 case ISD::PARK_PARKED:
1810 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1814 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
1816 moduleState()->resetParkingDomeFailureCount();
1819 case ISD::PARK_UNPARKED:
1820 if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1822 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1825 moduleState()->resetParkingDomeFailureCount();
1828 case ISD::PARK_PARKING:
1829 case ISD::PARK_UNPARKING:
1831 if (moduleState()->getCurrentOperationMsec() > (120 * 1000))
1833 if (moduleState()->increaseParkingDomeFailureCount())
1836 if (status == ISD::PARK_PARKING)
1845 case ISD::PARK_ERROR:
1846 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1848 if (moduleState()->increaseParkingDomeFailureCount())
1856 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1857 moduleState()->resetParkingDomeFailureCount();
1860 else if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1862 if (moduleState()->increaseParkingDomeFailureCount())
1870 moduleState()->setStartupState(STARTUP_ERROR);
1871 moduleState()->resetParkingDomeFailureCount();
1883 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1886 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Checking Startup State (%1)...").
arg(moduleState()->startupState());
1888 switch (moduleState()->startupState())
1892 KSNotification::event(
QLatin1String(
"ObservatoryStartup"),
i18n(
"Observatory is in the startup process"),
1893 KSNotification::Scheduler);
1895 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Startup Idle. Starting startup process...";
1902 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1904 if (moduleState()->startupScriptURL().isEmpty() ==
false)
1907 if (!activeJob() || activeJob()->getLightFramesRequired())
1908 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
1910 moduleState()->setStartupState(STARTUP_COMPLETE);
1914 if (moduleState()->currentProfile() !=
i18n(
"Default"))
1917 profile.
append(moduleState()->currentProfile());
1918 ekosInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setProfile", profile);
1921 if (moduleState()->startupScriptURL().isEmpty() ==
false)
1923 moduleState()->setStartupState(STARTUP_SCRIPT);
1928 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
1932 case STARTUP_SCRIPT:
1935 case STARTUP_UNPARK_DOME:
1939 if (activeJob() ==
nullptr || activeJob()->getLightFramesRequired())
1941 if (Options::schedulerUnparkDome())
1944 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1948 moduleState()->setStartupState(STARTUP_COMPLETE);
1954 case STARTUP_UNPARKING_DOME:
1955 checkDomeParkingStatus();
1958 case STARTUP_UNPARK_MOUNT:
1959 if (Options::schedulerUnparkMount())
1962 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1965 case STARTUP_UNPARKING_MOUNT:
1966 checkMountParkingStatus();
1969 case STARTUP_UNPARK_CAP:
1970 if (Options::schedulerOpenDustCover())
1973 moduleState()->setStartupState(STARTUP_COMPLETE);
1976 case STARTUP_UNPARKING_CAP:
1980 case STARTUP_COMPLETE:
1993 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking shutdown state...";
1995 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1998 switch (moduleState()->shutdownState())
2002 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Starting shutdown process...";
2004 moduleState()->setActiveJob(
nullptr);
2005 moduleState()->setupNextIteration(RUN_SHUTDOWN);
2006 emit shutdownStarted();
2008 if (Options::schedulerWarmCCD())
2015 if (captureInterface())
2017 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Setting coolerControl=false";
2018 captureInterface()->setProperty(
"coolerControl",
false);
2023 if (moduleState()->isINDIConnected())
2025 if (Options::schedulerCloseDustCover())
2027 moduleState()->setShutdownState(SHUTDOWN_PARK_CAP);
2031 if (Options::schedulerParkMount())
2033 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2037 if (Options::schedulerParkDome())
2039 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2043 else appendLogText(
i18n(
"Warning: Bypassing parking procedures, no INDI connection."));
2045 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2047 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2051 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2054 case SHUTDOWN_PARK_CAP:
2055 if (!moduleState()->isINDIConnected())
2057 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2058 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2060 else if (Options::schedulerCloseDustCover())
2063 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2066 case SHUTDOWN_PARKING_CAP:
2070 case SHUTDOWN_PARK_MOUNT:
2071 if (!moduleState()->isINDIConnected())
2073 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2074 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2076 else if (Options::schedulerParkMount())
2079 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2082 case SHUTDOWN_PARKING_MOUNT:
2083 checkMountParkingStatus();
2086 case SHUTDOWN_PARK_DOME:
2087 if (!moduleState()->isINDIConnected())
2089 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2090 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2092 else if (Options::schedulerParkDome())
2095 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2098 case SHUTDOWN_PARKING_DOME:
2099 checkDomeParkingStatus();
2102 case SHUTDOWN_SCRIPT:
2103 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2106 if (moduleState()->ekosState() != EKOS_IDLE && Options::shutdownScriptTerminatesINDI())
2112 moduleState()->setShutdownState(SHUTDOWN_SCRIPT_RUNNING);
2116 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2119 case SHUTDOWN_SCRIPT_RUNNING:
2122 case SHUTDOWN_COMPLETE:
2125 case SHUTDOWN_ERROR:
2135 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2138 if (moduleState()->parkWaitState() == PARKWAIT_IDLE)
2143 switch (moduleState()->parkWaitState())
2149 case PARKWAIT_PARKING:
2150 checkMountParkingStatus();
2153 case PARKWAIT_UNPARK:
2157 case PARKWAIT_UNPARKING:
2158 checkMountParkingStatus();
2162 case PARKWAIT_PARKED:
2163 case PARKWAIT_UNPARKED:
2166 case PARKWAIT_ERROR:
2178 if (moduleState()->startupState() == STARTUP_IDLE
2179 || moduleState()->startupState() == STARTUP_ERROR
2180 || moduleState()->startupState() == STARTUP_COMPLETE)
2187 moduleState()->setStartupState(STARTUP_IDLE);
2193 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the startup procedure manually?"));
2197 switch (moduleState()->startupState())
2202 case STARTUP_SCRIPT:
2206 case STARTUP_UNPARK_DOME:
2209 case STARTUP_UNPARKING_DOME:
2210 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking dome...";
2214 case STARTUP_UNPARK_MOUNT:
2217 case STARTUP_UNPARKING_MOUNT:
2218 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking mount...";
2222 case STARTUP_UNPARK_CAP:
2225 case STARTUP_UNPARKING_CAP:
2228 case STARTUP_COMPLETE:
2235 moduleState()->setStartupState(STARTUP_IDLE);
2244 if (moduleState()->shutdownState() == SHUTDOWN_IDLE
2245 || moduleState()->shutdownState() == SHUTDOWN_ERROR
2246 || moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
2252 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2257 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the shutdown procedure manually?"));
2261 switch (moduleState()->shutdownState())
2266 case SHUTDOWN_SCRIPT:
2269 case SHUTDOWN_SCRIPT_RUNNING:
2273 case SHUTDOWN_PARK_DOME:
2276 case SHUTDOWN_PARKING_DOME:
2277 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking dome...";
2281 case SHUTDOWN_PARK_MOUNT:
2284 case SHUTDOWN_PARKING_MOUNT:
2285 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking mount...";
2289 case SHUTDOWN_PARK_CAP:
2290 case SHUTDOWN_PARKING_CAP:
2293 case SHUTDOWN_COMPLETE:
2296 case SHUTDOWN_ERROR:
2300 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2308 moduleState()->setupNextIteration(RUN_NOTHING);
2310 emit schedulerPaused();
2316 for (SchedulerJob * job : moduleState()->jobs())
2319 job->setCompletedCount(0);
2328 auto finished_or_aborted = [](SchedulerJob
const *
const job)
2335 auto neither_scheduled_nor_aborted = [](SchedulerJob
const *
const job)
2343 if (jobs.
isEmpty() || std::all_of(jobs.
begin(), jobs.
end(), neither_scheduled_nor_aborted))
2346 moduleState()->setActiveJob(
nullptr);
2350 else if (std::all_of(jobs.
begin(), jobs.
end(), finished_or_aborted) &&
2351 strategy != ERROR_DONT_RESTART)
2353 appendLogText(
i18n(
"Only aborted jobs left in the scheduler queue after evaluating, rescheduling those."));
2354 std::for_each(jobs.
begin(), jobs.
end(), [](SchedulerJob * job)
2356 if (SCHEDJOB_ABORTED == job->getState())
2357 job->setState(SCHEDJOB_EVALUATION);
2364 SchedulerJob *scheduledJob = getGreedyScheduler()->getScheduledJob();
2368 moduleState()->setActiveJob(
nullptr);
2371 if (activeJob() !=
nullptr && scheduledJob != activeJob())
2374 for (
auto job : m_activeJobs.
values())
2376 stopCapturing(job->getOpticalTrain(),
false);
2379 m_activeJobs.
clear();
2381 moduleState()->setActiveJob(scheduledJob);
2389 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2393 moduleState()->resetSequenceExecutionCounter();
2401 for (
auto job : moduleState()->jobs())
2405 if (moduleState()->jobs().isEmpty())
2408 if (Options::rememberJobProgress())
2411 moduleState()->calculateDawnDusk();
2413 getGreedyScheduler()->scheduleJobs(moduleState()->jobs(), SchedulerModuleState::getLocalTime(),
2414 moduleState()->capturedFramesCount(),
this);
2418 if (!evaluateOnly && moduleState()->schedulerState() == SCHEDULER_RUNNING)
2423 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos finished evaluating jobs, no job selection required.";
2425 emit jobsUpdated(moduleState()->getJSONJobs());
2430 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2432 if (activeJob() ==
nullptr)
2437 switch (activeJob()->getState())
2453 if (activeJob() ==
nullptr)
2456 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE
2457 || moduleState()->shutdownState() == SHUTDOWN_ERROR)
2463 if (moduleState()->shutdownState() > SHUTDOWN_IDLE)
2466 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
2481 if (
nullptr == activeJob() && moduleState()->checkRepeatSequence())
2490 moduleState()->increaseSequenceExecutionCounter();
2491 appendLogText(
i18n(
"Starting job sequence iteration #%1", moduleState()->sequenceExecutionCounter()));
2497 if (
nullptr == activeJob())
2507 if (moduleState()->startupState() == STARTUP_ERROR)
2515 if ((moduleState()->startupState() == STARTUP_IDLE
2517 || moduleState()->startupState() == STARTUP_SCRIPT)
2533 if (moduleState()->startupState() > STARTUP_SCRIPT
2534 && moduleState()->startupState() < STARTUP_ERROR
2546 emit updateJobTable();
2554 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Get next action...";
2556 switch (activeJob()->getStage())
2558 case SCHEDSTAGE_IDLE:
2559 if (activeJob()->getLightFramesRequired())
2561 if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
2563 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2565 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3485";
2568 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2570 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2583 if (activeJob()->getStepPipeline())
2585 i18n(
"Job '%1' is proceeding directly to capture stage because only calibration frames are pending.",
2586 activeJob()->getName()));
2592 case SCHEDSTAGE_SLEW_COMPLETE:
2593 if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2595 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3514";
2598 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2600 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2606 case SCHEDSTAGE_FOCUS_COMPLETE:
2607 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2609 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2615 case SCHEDSTAGE_ALIGN_COMPLETE:
2616 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING);
2619 case SCHEDSTAGE_RESLEWING_COMPLETE:
2622 if ((activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS) && activeJob()->getInSequenceFocus())
2625 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3544";
2628 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2634 case SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE:
2635 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2641 case SCHEDSTAGE_GUIDING_COMPLETE:
2657 moduleState()->iterationTimer().setSingleShot(
true);
2658 moduleState()->iterationTimer().start(msSleep);
2665 if (moduleState()->startMSecs() == 0)
2666 moduleState()->setStartMSecs(now);
2679 moduleState()->setIterationSetup(
false);
2680 switch (keepTimerState)
2683 changeSleepLabel(
"",
false);
2696 moduleState()->setTimerInterval(-1);
2699 if (!moduleState()->iterationSetup())
2705 moduleState()->setTimerInterval(moduleState()->updatePeriodMs());
2708 return moduleState()->timerInterval();
2713 Q_ASSERT_X(activeJob(), __FUNCTION__,
"Actual current job is required to check job stage");
2717 if (checkJobStageCounter == 0)
2719 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking job stage for" << activeJob()->getName() <<
"startup" <<
2720 activeJob()->getStartupCondition() << activeJob()->getStartupTime().
toString() <<
"state" << activeJob()->getState();
2721 if (checkJobStageCounter++ == 30)
2722 checkJobStageCounter = 0;
2725 emit syncGreedyParams();
2726 if (!getGreedyScheduler()->checkJob(moduleState()->leadJobs(), SchedulerModuleState::getLocalTime(), activeJob()))
2733 checkJobStageEpilogue();
2736void SchedulerProcess::checkJobStageEpilogue()
2751 if (!activeJob())
return;
2752 switch (activeJob()->getStage())
2754 case SCHEDSTAGE_IDLE:
2756 emit jobStarted(activeJob()->getName());
2760 case SCHEDSTAGE_ALIGNING:
2762 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(ALIGN_INACTIVITY_TIMEOUT))
2764 QVariant const status = alignInterface()->property(
"status");
2769 if (moduleState()->increaseAlignFailureCount())
2771 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align module timed out. Restarting request...";
2776 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
2782 moduleState()->startCurrentOperationTimer();
2786 case SCHEDSTAGE_CAPTURING:
2788 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(CAPTURE_INACTIVITY_TIMEOUT))
2795 if (moduleState()->increaseCaptureFailureCount())
2797 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"capture module timed out. Restarting request...";
2802 appendLogText(
i18n(
"Warning: job '%1' capture procedure failed, marking aborted.", activeJob()->getName()));
2807 else moduleState()->startCurrentOperationTimer();
2811 case SCHEDSTAGE_FOCUSING:
2813 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(FOCUS_INACTIVITY_TIMEOUT))
2816 Ekos::FocusState focusStatus =
static_cast<Ekos::FocusState
>(
status.toInt());
2818 if (focusStatus == Ekos::FOCUS_IDLE || focusStatus == Ekos::FOCUS_WAITING)
2820 if (moduleState()->increaseFocusFailureCount())
2822 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus module timed out. Restarting request...";
2827 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
2832 else moduleState()->startCurrentOperationTimer();
2836 case SCHEDSTAGE_GUIDING:
2838 if (moduleState()->getCurrentOperationMsec() > GUIDE_INACTIVITY_TIMEOUT)
2842 if (guideStatus == Ekos::GUIDE_IDLE || guideStatus == Ekos::GUIDE_CONNECTED || guideStatus == Ekos::GUIDE_DISCONNECTED)
2844 if (moduleState()->increaseGuideFailureCount())
2846 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"guide module timed out. Restarting request...";
2851 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
2856 else moduleState()->startCurrentOperationTimer();
2860 case SCHEDSTAGE_SLEWING:
2861 case SCHEDSTAGE_RESLEWING:
2864 QVariant const slewStatus = mountInterface()->property(
"status");
2870 ISD::Mount::Status
const status =
static_cast<ISD::Mount::Status
>(slewStatus.
toInt());
2871 setMountStatus(status);
2875 appendLogText(
i18n(
"Warning: job '%1' lost connection to the mount, attempting to reconnect.", activeJob()->getName()));
2883 case SCHEDSTAGE_SLEW_COMPLETE:
2884 case SCHEDSTAGE_RESLEWING_COMPLETE:
2886 if (moduleState()->domeReady())
2888 QVariant const isDomeMoving = domeInterface()->property(
"isMoving");
2892 appendLogText(
i18n(
"Warning: job '%1' lost connection to the dome, attempting to reconnect.", activeJob()->getName()));
2898 if (!isDomeMoving.
value<
bool>())
2911 moduleState()->calculateDawnDusk();
2913 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2925 if (activeJob() == job &&
SCHEDJOB_BUSY == activeJob()->getState())
2928 moduleState()->setActiveJob(job);
2940 else if (0 < SchedulerModuleState::getLocalTime().secsTo(activeJob()->getStartupTime()))
2945 if (job->getCompletionCondition() == FINISH_SEQUENCE && Options::rememberJobProgress())
2946 captureInterface()->setProperty(
"targetName", job->getName());
2948 moduleState()->calculateDawnDusk();
2952 moduleState()->setAutofocusCompleted(
false);
2954 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Executing Job " << activeJob()->getName();
2957 emit jobsUpdated(moduleState()->getJSONJobs());
2959 KSNotification::event(
QLatin1String(
"EkosSchedulerJobStart"),
2960 i18n(
"Ekos job started (%1)", activeJob()->getName()), KSNotification::Scheduler);
2963 moduleState()->setupNextIteration(RUN_JOBCHECK);
2975 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
2984 outstream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<
Qt::endl;
2985 outstream <<
"<SchedulerList version='2.0'>" <<
Qt::endl;
2987 outstream <<
"<Profile>" <<
QString(entityXML(strdup(moduleState()->currentProfile().toStdString().c_str()))) <<
2990 auto tiles = KStarsData::Instance()->
skyComposite()->mosaicComponent()->tiles();
2991 bool useMosaicInfo = !tiles->sequenceFile().isEmpty();
2995 outstream <<
"<Mosaic>" <<
Qt::endl;
2996 outstream <<
"<Target>" << tiles->targetName() <<
"</Target>" <<
Qt::endl;
2997 outstream <<
"<Group>" << tiles->group() <<
"</Group>" <<
Qt::endl;
2999 QString ccArg, ccValue = tiles->completionCondition(&ccArg);
3000 if (ccValue ==
"FinishSequence")
3001 outstream <<
"<FinishSequence/>" <<
Qt::endl;
3002 else if (ccValue ==
"FinishLoop")
3003 outstream <<
"<FinishLoop/>" <<
Qt::endl;
3004 else if (ccValue ==
"FinishRepeat")
3005 outstream <<
"<FinishRepeat>" << ccArg <<
"</FinishRepeat>" <<
Qt::endl;
3007 outstream <<
"<Sequence>" << tiles->sequenceFile() <<
"</Sequence>" <<
Qt::endl;
3008 outstream <<
"<Directory>" << tiles->outputDirectory() <<
"</Directory>" <<
Qt::endl;
3010 outstream <<
"<FocusEveryN>" << tiles->focusEveryN() <<
"</FocusEveryN>" <<
Qt::endl;
3011 outstream <<
"<AlignEveryN>" << tiles->alignEveryN() <<
"</AlignEveryN>" <<
Qt::endl;
3012 if (tiles->isTrackChecked())
3013 outstream <<
"<TrackChecked/>" <<
Qt::endl;
3014 if (tiles->isFocusChecked())
3015 outstream <<
"<FocusChecked/>" <<
Qt::endl;
3016 if (tiles->isAlignChecked())
3017 outstream <<
"<AlignChecked/>" <<
Qt::endl;
3018 if (tiles->isGuideChecked())
3019 outstream <<
"<GuideChecked/>" <<
Qt::endl;
3020 outstream <<
"<Overlap>" << cLocale.
toString(tiles->overlap()) <<
"</Overlap>" <<
Qt::endl;
3021 outstream <<
"<CenterRA>" << cLocale.
toString(tiles->ra0().Hours()) <<
"</CenterRA>" <<
Qt::endl;
3022 outstream <<
"<CenterDE>" << cLocale.
toString(tiles->dec0().Degrees()) <<
"</CenterDE>" <<
Qt::endl;
3023 outstream <<
"<GridW>" << tiles->gridSize().width() <<
"</GridW>" <<
Qt::endl;
3024 outstream <<
"<GridH>" << tiles->gridSize().height() <<
"</GridH>" <<
Qt::endl;
3025 outstream <<
"<FOVW>" << cLocale.
toString(tiles->mosaicFOV().width()) <<
"</FOVW>" <<
Qt::endl;
3026 outstream <<
"<FOVH>" << cLocale.
toString(tiles->mosaicFOV().height()) <<
"</FOVH>" <<
Qt::endl;
3027 outstream <<
"<CameraFOVW>" << cLocale.
toString(tiles->cameraFOV().width()) <<
"</CameraFOVW>" <<
Qt::endl;
3028 outstream <<
"<CameraFOVH>" << cLocale.
toString(tiles->cameraFOV().height()) <<
"</CameraFOVH>" <<
Qt::endl;
3029 outstream <<
"</Mosaic>" <<
Qt::endl;
3033 for (
auto &job : moduleState()->jobs())
3038 outstream <<
"<JobType lead='" << (job->isLead() ?
"true" :
"false") <<
"'/>" <<
Qt::endl;
3043 outstream <<
"<Coordinates>" <<
Qt::endl;
3044 outstream <<
"<J2000RA>" << cLocale.
toString(job->getTargetCoords().ra0().Hours()) <<
"</J2000RA>" <<
Qt::endl;
3045 outstream <<
"<J2000DE>" << cLocale.
toString(job->getTargetCoords().dec0().Degrees()) <<
"</J2000DE>" <<
Qt::endl;
3046 outstream <<
"</Coordinates>" <<
Qt::endl;
3049 if (! job->getOpticalTrain().
isEmpty())
3050 outstream <<
"<OpticalTrain>" <<
QString(entityXML(strdup(job->getOpticalTrain().
toStdString().c_str()))) <<
3053 if (job->isLead() && job->getFITSFile().
isValid() && job->getFITSFile().
isEmpty() ==
false)
3056 outstream <<
"<PositionAngle>" << job->getPositionAngle() <<
"</PositionAngle>" <<
Qt::endl;
3058 outstream <<
"<Sequence>" << job->getSequenceFile().
toLocalFile() <<
"</Sequence>" <<
Qt::endl;
3060 if (useMosaicInfo && index < tiles->tiles().size())
3062 auto oneTile = tiles->tiles().at(index++);
3063 outstream <<
"<TileCenter>" <<
Qt::endl;
3064 outstream <<
"<X>" << cLocale.
toString(oneTile->center.x()) <<
"</X>" <<
Qt::endl;
3065 outstream <<
"<Y>" << cLocale.
toString(oneTile->center.y()) <<
"</Y>" <<
Qt::endl;
3066 outstream <<
"<Rotation>" << cLocale.
toString(oneTile->rotation) <<
"</Rotation>" <<
Qt::endl;
3067 outstream <<
"</TileCenter>" <<
Qt::endl;
3072 outstream <<
"<StartupCondition>" <<
Qt::endl;
3073 if (job->getFileStartupCondition() == START_ASAP)
3074 outstream <<
"<Condition>ASAP</Condition>" <<
Qt::endl;
3075 else if (job->getFileStartupCondition() == START_AT)
3076 outstream <<
"<Condition value='" << job->getStartAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3078 outstream <<
"</StartupCondition>" <<
Qt::endl;
3080 outstream <<
"<Constraints>" <<
Qt::endl;
3081 if (job->hasMinAltitude())
3082 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinAltitude()) <<
"'>MinimumAltitude</Constraint>" <<
3084 if (job->getMinMoonSeparation() > 0)
3085 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinMoonSeparation()) <<
"'>MoonSeparation</Constraint>"
3087 if (job->getEnforceWeather())
3088 outstream <<
"<Constraint>EnforceWeather</Constraint>" <<
Qt::endl;
3089 if (job->getEnforceTwilight())
3090 outstream <<
"<Constraint>EnforceTwilight</Constraint>" <<
Qt::endl;
3091 if (job->getEnforceArtificialHorizon())
3092 outstream <<
"<Constraint>EnforceArtificialHorizon</Constraint>" <<
Qt::endl;
3093 outstream <<
"</Constraints>" <<
Qt::endl;
3096 outstream <<
"<CompletionCondition>" <<
Qt::endl;
3097 if (job->getCompletionCondition() == FINISH_SEQUENCE)
3098 outstream <<
"<Condition>Sequence</Condition>" <<
Qt::endl;
3099 else if (job->getCompletionCondition() == FINISH_REPEAT)
3100 outstream <<
"<Condition value='" << cLocale.
toString(job->getRepeatsRequired()) <<
"'>Repeat</Condition>" <<
Qt::endl;
3101 else if (job->getCompletionCondition() == FINISH_LOOP)
3102 outstream <<
"<Condition>Loop</Condition>" <<
Qt::endl;
3103 else if (job->getCompletionCondition() == FINISH_AT)
3104 outstream <<
"<Condition value='" << job->getFinishAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3106 outstream <<
"</CompletionCondition>" <<
Qt::endl;
3110 outstream <<
"<Steps>" <<
Qt::endl;
3111 if (job->getStepPipeline() & SchedulerJob::USE_TRACK)
3112 outstream <<
"<Step>Track</Step>" <<
Qt::endl;
3113 if (job->getStepPipeline() & SchedulerJob::USE_FOCUS)
3114 outstream <<
"<Step>Focus</Step>" <<
Qt::endl;
3115 if (job->getStepPipeline() & SchedulerJob::USE_ALIGN)
3116 outstream <<
"<Step>Align</Step>" <<
Qt::endl;
3117 if (job->getStepPipeline() & SchedulerJob::USE_GUIDE)
3118 outstream <<
"<Step>Guide</Step>" <<
Qt::endl;
3119 outstream <<
"</Steps>" <<
Qt::endl;
3124 outstream <<
"<SchedulerAlgorithm value='" << ALGORITHM_GREEDY <<
"'/>" <<
Qt::endl;
3125 outstream <<
"<ErrorHandlingStrategy value='" << Options::errorHandlingStrategy() <<
"'>" <<
Qt::endl;
3126 if (Options::rescheduleErrors())
3127 outstream <<
"<RescheduleErrors />" <<
Qt::endl;
3128 outstream <<
"<delay>" << Options::errorHandlingStrategyDelay() <<
"</delay>" <<
Qt::endl;
3129 outstream <<
"</ErrorHandlingStrategy>" <<
Qt::endl;
3131 outstream <<
"<StartupProcedure>" <<
Qt::endl;
3132 if (moduleState()->startupScriptURL().isEmpty() ==
false)
3133 outstream <<
"<Procedure value='" << moduleState()->startupScriptURL().toString(
QUrl::PreferLocalFile) <<
3134 "'>StartupScript</Procedure>" <<
Qt::endl;
3135 if (Options::schedulerUnparkDome())
3136 outstream <<
"<Procedure>UnparkDome</Procedure>" <<
Qt::endl;
3137 if (Options::schedulerUnparkMount())
3138 outstream <<
"<Procedure>UnparkMount</Procedure>" <<
Qt::endl;
3139 if (Options::schedulerOpenDustCover())
3140 outstream <<
"<Procedure>UnparkCap</Procedure>" <<
Qt::endl;
3141 outstream <<
"</StartupProcedure>" <<
Qt::endl;
3143 outstream <<
"<ShutdownProcedure>" <<
Qt::endl;
3144 if (Options::schedulerWarmCCD())
3145 outstream <<
"<Procedure>WarmCCD</Procedure>" <<
Qt::endl;
3146 if (Options::schedulerCloseDustCover())
3147 outstream <<
"<Procedure>ParkCap</Procedure>" <<
Qt::endl;
3148 if (Options::schedulerParkMount())
3149 outstream <<
"<Procedure>ParkMount</Procedure>" <<
Qt::endl;
3150 if (Options::schedulerParkDome())
3151 outstream <<
"<Procedure>ParkDome</Procedure>" <<
Qt::endl;
3152 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
3153 outstream <<
"<Procedure value='" << moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile) <<
3154 "'>schedulerStartupScript</Procedure>" <<
3156 outstream <<
"</ShutdownProcedure>" <<
Qt::endl;
3158 outstream <<
"</SchedulerList>" <<
Qt::endl;
3162 moduleState()->setDirty(
false);
3166void SchedulerProcess::checkAlignment(
const QVariantMap &metadata,
const QString &trainname)
3169 if (activeJob() ==
nullptr || (activeJob()->getOpticalTrain() !=
"" && activeJob()->getOpticalTrain() != trainname))
3171 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Ignoring metadata from train =" << trainname <<
"for alignment check.";
3175 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN &&
3176 metadata[
"type"].toInt() == FRAME_LIGHT &&
3177 Options::alignCheckFrequency() > 0 &&
3178 moduleState()->increaseSolverIteration() >= Options::alignCheckFrequency())
3180 moduleState()->resetSolverIteration();
3182 auto filename = metadata[
"filename"].toString();
3183 auto exposure = metadata[
"exposure"].toDouble();
3185 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking alignment on train =" << trainname <<
"for" << filename;
3187 constexpr double minSolverSeconds = 5.0;
3188 double solverTimeout = std::max(exposure - 2, minSolverSeconds);
3189 if (solverTimeout >= minSolverSeconds)
3191 auto profiles = getDefaultAlignOptionsProfiles();
3193 SSolver::Parameters parameters;
3198 parameters = profiles.at(Options::solveOptionsProfile());
3200 catch (std::out_of_range
const &)
3202 parameters = profiles[0];
3206 parameters.search_radius = parameters.search_radius * 2;
3211 auto width = metadata[
"width"].toUInt() / (metadata[
"binx"].isValid() ? metadata[
"binx"].toUInt() : 1);
3212 auto height = metadata[
"height"].toUInt() / (metadata[
"biny"].isValid() ? metadata[
"biny"].toUInt() : 1);
3214 auto lowScale = Options::astrometryImageScaleLow();
3215 auto highScale = Options::astrometryImageScaleHigh();
3218 if (Options::astrometryImageScaleUnits() == SSolver::DEG_WIDTH)
3220 lowScale = (lowScale * 3600) / std::max(width, height);
3221 highScale = (highScale * 3600) / std::min(width, height);
3223 else if (Options::astrometryImageScaleUnits() == SSolver::ARCMIN_WIDTH)
3225 lowScale = (lowScale * 60) / std::max(width, height);
3226 highScale = (highScale * 60) / std::min(width, height);
3229 m_Solver->useScale(Options::astrometryUseImageScale(), lowScale, highScale);
3230 m_Solver->usePosition(Options::astrometryUsePosition(), activeJob()->getTargetCoords().ra().Degrees(),
3231 activeJob()->getTargetCoords().
dec().Degrees());
3232 m_Solver->setHealpix(moduleState()->indexToUse(), moduleState()->healpixToUse());
3233 m_Solver->runSolver(filename);
3238void SchedulerProcess::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
3240 disconnect(m_Solver.
get(), &SolverUtils::done,
this, &Ekos::SchedulerProcess::solverDone);
3246 if (moduleState()->indexToUse() != -1 || moduleState()->healpixToUse() != -1)
3247 healpixString =
QString(
"Healpix %1 Index %2").
arg(moduleState()->healpixToUse()).
arg(moduleState()->indexToUse());
3249 if (timedOut || !success)
3252 moduleState()->setIndexToUse(-1);
3253 moduleState()->setHealpixToUse(-1);
3259 m_Solver->getSolutionHealpix(&index, &healpix);
3260 moduleState()->setIndexToUse(index);
3261 moduleState()->setHealpixToUse(healpix);
3270 const double ra = solution.ra;
3271 const double dec = solution.dec;
3273 const auto target = activeJob()->getTargetCoords();
3276 alignCoord.
setRA0(ra / 15.0);
3280 const double diffRa = (alignCoord.
ra().deltaAngle(target.ra())).Degrees() * 3600;
3281 const double diffDec = (alignCoord.
dec().deltaAngle(target.dec())).Degrees() * 3600;
3284 const double diffTotal = hypot(diffRa, diffDec);
3288 qCDebug(KSTARS_EKOS_SCHEDULER) <<
3289 QString(
"Target Distance: %1\" Target (RA: %2 DE: %3) Current (RA: %4 DE: %5) %6 solved in %7s")
3290 .
arg(
QString(
"%L1").arg(diffTotal, 0,
'f', 0),
3291 target.ra().toDMSString(),
3292 target.dec().toDMSString(),
3293 alignCoord.
ra().toDMSString(),
3294 alignCoord.
dec().toDMSString(),
3296 QString(
"%L1").arg(elapsedSeconds, 0,
'f', 2));
3297 emit targetDistance(diffTotal);
3300 if (diffTotal / 60 > Options::alignCheckThreshold())
3312 SchedulerState const old_state = moduleState()->schedulerState();
3313 moduleState()->setSchedulerState(SCHEDULER_LOADING);
3320 QString message =
i18n(
"Unable to open file %1", fileURL);
3321 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3322 moduleState()->setSchedulerState(old_state);
3326 LilXML *xmlParser = newLilXML();
3327 char errmsg[MAXRBUF];
3328 XMLEle *root =
nullptr;
3329 XMLEle *ep =
nullptr;
3330 XMLEle *subEP =
nullptr;
3337 SchedulerJob *lastLead =
nullptr;
3340 const QStringList allTrainNames = OpticalTrainManager::Instance()->getTrainNames();
3345 root = readXMLEle(xmlParser, c, errmsg);
3349 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
3351 const char *tag = tagXMLEle(ep);
3352 if (!strcmp(tag,
"Job"))
3354 SchedulerJob *newJob = SchedulerUtils::createJob(ep, lastLead);
3356 if (newJob->isLead())
3360 remainingTrainNames = allTrainNames;
3363 const QString trainname = newJob->getOpticalTrain();
3364 bool allowedName = (newJob->isLead() && trainname.
isEmpty()) || allTrainNames.
contains(trainname);
3365 bool availableName = (newJob->isLead() && trainname.
isEmpty()) || !remainingTrainNames.
isEmpty();
3367 if (!allowedName && availableName)
3370 i18n(
"Warning: train name is empty, selecting \"%1\".", remainingTrainNames.
first()) :
3371 i18n(
"Warning: train name %2 does not exist, selecting \"%1\".", remainingTrainNames.
first(), trainname);
3377 newJob->setOpticalTrain(remainingTrainNames.
first());
3380 else if (!availableName)
3382 const QString message =
i18n(
"Warning: no available train name for scheduler job, select the optical train name manually.");
3390 emit addJob(newJob);
3392 else if (!strcmp(tag,
"Mosaic"))
3395 auto tiles = KStarsData::Instance()->
skyComposite()->mosaicComponent()->tiles();
3396 tiles->fromXML(fileURL);
3398 else if (!strcmp(tag,
"Profile"))
3400 moduleState()->setCurrentProfile(pcdataXMLEle(ep));
3403 else if (!strcmp(tag,
"SchedulerAlgorithm"))
3405 int algIndex = cLocale.
toInt(findXMLAttValu(ep,
"value"));
3406 if (algIndex != ALGORITHM_GREEDY)
3407 appendLogText(
i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
3409 else if (!strcmp(tag,
"ErrorHandlingStrategy"))
3414 subEP = findXMLEle(ep,
"delay");
3417 Options::setErrorHandlingStrategyDelay(cLocale.
toInt(pcdataXMLEle(subEP)));
3419 subEP = findXMLEle(ep,
"RescheduleErrors");
3420 Options::setRescheduleErrors(subEP !=
nullptr);
3422 else if (!strcmp(tag,
"StartupProcedure"))
3425 Options::setSchedulerUnparkDome(
false);
3426 Options::setSchedulerUnparkMount(
false);
3427 Options::setSchedulerOpenDustCover(
false);
3429 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3431 const char *proc = pcdataXMLEle(procedure);
3433 if (!strcmp(proc,
"StartupScript"))
3435 moduleState()->setStartupScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3437 else if (!strcmp(proc,
"UnparkDome"))
3438 Options::setSchedulerUnparkDome(
true);
3439 else if (!strcmp(proc,
"UnparkMount"))
3440 Options::setSchedulerUnparkMount(
true);
3441 else if (!strcmp(proc,
"UnparkCap"))
3442 Options::setSchedulerOpenDustCover(
true);
3445 else if (!strcmp(tag,
"ShutdownProcedure"))
3448 Options::setSchedulerWarmCCD(
false);
3449 Options::setSchedulerParkDome(
false);
3450 Options::setSchedulerParkMount(
false);
3451 Options::setSchedulerCloseDustCover(
false);
3453 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3455 const char *proc = pcdataXMLEle(procedure);
3457 if (!strcmp(proc,
"ShutdownScript"))
3459 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3461 else if (!strcmp(proc,
"WarmCCD"))
3462 Options::setSchedulerWarmCCD(
true);
3463 else if (!strcmp(proc,
"ParkDome"))
3464 Options::setSchedulerParkDome(
true);
3465 else if (!strcmp(proc,
"ParkMount"))
3466 Options::setSchedulerParkMount(
true);
3467 else if (!strcmp(proc,
"ParkCap"))
3468 Options::setSchedulerCloseDustCover(
true);
3473 emit syncGUIToGeneralSettings();
3478 delLilXML(xmlParser);
3479 moduleState()->setSchedulerState(old_state);
3484 moduleState()->setDirty(
false);
3485 delLilXML(xmlParser);
3486 emit updateSchedulerURL(fileURL);
3488 moduleState()->setSchedulerState(old_state);
3495 int const max_log_count = 2000;
3496 if (moduleState()->logText().size() > max_log_count)
3497 moduleState()->logText().removeLast();
3499 moduleState()->logText().prepend(
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
3500 SchedulerModuleState::getLocalTime().toString(
"yyyy-MM-ddThh:mm:ss"), logentry));
3502 qCInfo(KSTARS_EKOS_SCHEDULER) << logentry;
3504 emit newLog(logentry);
3509 moduleState()->logText().clear();
3513void SchedulerProcess::setAlignStatus(
AlignState status)
3515 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3518 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align State" << Ekos::getAlignStatusString(status);
3523 QDateTime const now = SchedulerModuleState::getLocalTime();
3524 if (now < activeJob()->getStartupTime())
3528 if (activeJob()->getStage() == SCHEDSTAGE_ALIGNING)
3534 moduleState()->resetAlignFailureCount();
3536 moduleState()->updateJobStage(SCHEDSTAGE_ALIGN_COMPLETE);
3539 if (activeJob()->getFITSFile().isEmpty() ==
false)
3545 activeJob()->setTargetCoords(
dms(values[0] * 15.0),
dms(values[1]), KStarsData::Instance()->ut().djd());
3552 appendLogText(
i18n(
"Warning: job '%1' alignment failed.", activeJob()->getName()));
3554 if (moduleState()->increaseAlignFailureCount())
3556 if (Options::resetMountModelOnAlignFail() && moduleState()->maxFailureAttempts() - 1 < moduleState()->alignFailureCount())
3558 appendLogText(
i18n(
"Warning: job '%1' forcing mount model reset after failing alignment #%2.", activeJob()->getName(),
3559 moduleState()->alignFailureCount()));
3562 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3567 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
3576void SchedulerProcess::setGuideStatus(GuideState status)
3578 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3581 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Guide State" << Ekos::getGuideStatusString(status);
3586 QDateTime const now = SchedulerModuleState::getLocalTime();
3587 if (now < activeJob()->getStartupTime())
3591 if (activeJob()->getStage() == SCHEDSTAGE_GUIDING)
3593 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Calibration & Guide stage...";
3596 if (status == Ekos::GUIDE_GUIDING)
3598 appendLogText(
i18n(
"Job '%1' guiding is in progress.", activeJob()->getName()));
3599 moduleState()->resetGuideFailureCount();
3601 moduleState()->cancelGuidingTimer();
3603 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
3606 else if (status == Ekos::GUIDE_CALIBRATION_ERROR ||
3607 status == Ekos::GUIDE_ABORTED)
3609 if (status == Ekos::GUIDE_ABORTED)
3610 appendLogText(
i18n(
"Warning: job '%1' guiding failed.", activeJob()->getName()));
3612 appendLogText(
i18n(
"Warning: job '%1' calibration failed.", activeJob()->getName()));
3618 if (moduleState()->isGuidingTimerActive())
3621 if (moduleState()->increaseGuideFailureCount())
3623 if (status == Ekos::GUIDE_CALIBRATION_ERROR &&
3624 Options::realignAfterCalibrationFailure())
3626 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3631 appendLogText(
i18n(
"Job '%1' is guiding, guiding procedure will be restarted in %2 seconds.", activeJob()->getName(),
3632 (RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount()) / 1000));
3633 moduleState()->startGuidingTimer(RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount());
3638 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3647void SchedulerProcess::setCaptureStatus(CaptureState status,
const QString &trainname)
3649 if (activeJob() ==
nullptr || !m_activeJobs.
contains(trainname))
3652 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Capture State" << Ekos::getCaptureStatusString(status) <<
"train =" << trainname;
3654 SchedulerJob *job = m_activeJobs[trainname];
3659 QDateTime const now = SchedulerModuleState::getLocalTime();
3660 if (now < job->getStartupTime())
3664 if (job->getStage() == SCHEDSTAGE_CAPTURING)
3676 const SkyPoint targetCoords = activeJob()->getTargetCoords();
3678 targetArgs << targetCoords.
ra0().Hours() << targetCoords.
dec0().Degrees();
3679 alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords", targetArgs);
3684 appendLogText(
i18n(
"[%2] Warning: job '%1' failed to capture target.", job->getName(), trainname));
3689 if (moduleState()->increaseCaptureFailureCount())
3694 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
3698 if (gStatus == Ekos::GUIDE_ABORTED ||
3699 gStatus == Ekos::GUIDE_CALIBRATION_ERROR ||
3700 gStatus == GUIDE_DITHERING_ERROR)
3702 appendLogText(
i18n(
"[%2] Job '%1' is capturing, is restarting its guiding procedure (attempt #%3 of %4).",
3703 activeJob()->getName(), trainname,
3704 moduleState()->captureFailureCount(), moduleState()->maxFailureAttempts()));
3711 appendLogText(
i18n(
"Warning: job '%1' failed its capture procedure, restarting capture.", activeJob()->getName()));
3717 appendLogText(
i18n(
"[%2] Warning: job '%1' failed its capture procedure, marking aborted.", job->getName(), trainname));
3720 stopCapturing(
"",
true);
3727 if (job->leadJob()->getStage() == SCHEDSTAGE_CAPTURING)
3730 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted, is restarting.", job->getName(), trainname));
3732 startSingleCapture(job,
true);
3736 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted.", job->getName(), trainname));
3743 KSNotification::event(
QLatin1String(
"EkosScheduledImagingFinished"),
3744 i18n(
"[%2] Job (%1) - Capture finished", job->getName(), trainname), KSNotification::Scheduler);
3756 if (job->getCompletionCondition() == FINISH_LOOP ||
3757 (job->getCompletionCondition() == FINISH_REPEAT && job->getRepeatsRemaining() > 0))
3760 startSingleCapture(job,
false);
3766 job->setStage(SCHEDSTAGE_COMPLETE);
3774 if (Options::rememberJobProgress())
3778 for (
const auto &job : moduleState()->jobs())
3779 SchedulerUtils::estimateJobTime(job, moduleState()->capturedFramesCount(),
this);
3783 activeJob()->setCompletedCount(job->getCompletedCount() + 1);
3787 moduleState()->resetCaptureFailureCount();
3792void SchedulerProcess::setFocusStatus(FocusState status,
const QString &trainname)
3796 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3799 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus State" << Ekos::getFocusStatusString(status);
3804 QDateTime const now = SchedulerModuleState::getLocalTime();
3805 if (now < activeJob()->getStartupTime())
3809 if (activeJob()->getStage() == SCHEDSTAGE_FOCUSING)
3812 if (status == Ekos::FOCUS_COMPLETE)
3816 moduleState()->setAutofocusCompleted(
true);
3818 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
3822 else if (status == Ekos::FOCUS_FAILED || status == Ekos::FOCUS_ABORTED)
3824 appendLogText(
i18n(
"Warning: job '%1' focusing failed.", activeJob()->getName()));
3826 if (moduleState()->increaseFocusFailureCount())
3828 appendLogText(
i18n(
"Job '%1' is restarting its focusing procedure.", activeJob()->getName()));
3832 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 6883";
3837 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
3846void SchedulerProcess::setMountStatus(ISD::Mount::Status status)
3848 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3851 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount State changed to" <<
status;
3855 if (
static_cast<QDateTime const
>(SchedulerModuleState::getLocalTime()) < activeJob()->getStartupTime())
3858 switch (activeJob()->getStage())
3860 case SCHEDSTAGE_SLEWING:
3862 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Slewing stage...";
3864 if (status == ISD::Mount::MOUNT_TRACKING)
3867 moduleState()->updateJobStage(SCHEDSTAGE_SLEW_COMPLETE);
3870 else if (status == ISD::Mount::MOUNT_ERROR)
3872 appendLogText(
i18n(
"Warning: job '%1' slew failed, marking terminated due to errors.", activeJob()->getName()));
3876 else if (status == ISD::Mount::MOUNT_IDLE)
3878 appendLogText(
i18n(
"Warning: job '%1' found not slewing, restarting.", activeJob()->getName()));
3879 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
3885 case SCHEDSTAGE_RESLEWING:
3887 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Re-slewing stage...";
3889 if (status == ISD::Mount::MOUNT_TRACKING)
3891 appendLogText(
i18n(
"Job '%1' repositioning is complete.", activeJob()->getName()));
3892 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING_COMPLETE);
3895 else if (status == ISD::Mount::MOUNT_ERROR)
3897 appendLogText(
i18n(
"Warning: job '%1' repositioning failed, marking terminated due to errors.", activeJob()->getName()));
3901 else if (status == ISD::Mount::MOUNT_IDLE)
3903 appendLogText(
i18n(
"Warning: job '%1' found not repositioning, restarting.", activeJob()->getName()));
3904 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
3912 case SCHEDSTAGE_FOCUSING:
3913 case SCHEDSTAGE_ALIGNING:
3914 case SCHEDSTAGE_GUIDING:
3915 if (status == ISD::Mount::MOUNT_PARKED)
3917 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
3924 case SCHEDSTAGE_CAPTURING:
3925 if (status == ISD::Mount::MOUNT_PARKED && activeJob() && activeJob()->getLightFramesRequired()
3926 && activeJob()->getCalibrationMountPark() ==
false)
3928 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
3938void SchedulerProcess::setWeatherStatus(ISD::Weather::Status status)
3940 ISD::Weather::Status newStatus =
status;
3942 if (newStatus == moduleState()->weatherStatus())
3945 moduleState()->setWeatherStatus(newStatus);
3949 if (activeJob() && activeJob()->getEnforceWeather() && moduleState()->weatherStatus() == ISD::Weather::WEATHER_ALERT
3950 && moduleState()->schedulerState() != Ekos::SCHEDULER_IDLE && moduleState()->schedulerState() != Ekos::SCHEDULER_SHUTDOWN)
3961 emit newWeatherStatus(status);
3964void SchedulerProcess::checkStartupProcedure()
3970void SchedulerProcess::checkShutdownProcedure()
3975 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
3979 if (Options::stopEkosAfterShutdown())
3982 else if (moduleState()->shutdownState() == SHUTDOWN_ERROR)
3985 moduleState()->setShutdownState(SHUTDOWN_IDLE);
3994void SchedulerProcess::parkCap()
3996 if (capInterface().isNull())
3999 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4003 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4004 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4006 if (parkingStatus.
isValid() ==
false)
4008 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
4009 mountInterface()->lastError().
type());
4011 parkingStatus = ISD::PARK_ERROR;
4014 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4016 if (status != ISD::PARK_PARKED)
4018 moduleState()->setShutdownState(SHUTDOWN_PARKING_CAP);
4019 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking dust cap...";
4023 moduleState()->startCurrentOperationTimer();
4028 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
4032void SchedulerProcess::unParkCap()
4034 if (capInterface().isNull())
4036 appendLogText(
i18n(
"Dust cover unpark requested but no dust covers detected."));
4037 moduleState()->setStartupState(STARTUP_ERROR);
4041 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4042 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4044 if (parkingStatus.
isValid() ==
false)
4046 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
4047 mountInterface()->lastError().
type());
4049 parkingStatus = ISD::PARK_ERROR;
4052 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4054 if (status != ISD::PARK_UNPARKED)
4056 moduleState()->setStartupState(STARTUP_UNPARKING_CAP);
4060 moduleState()->startCurrentOperationTimer();
4065 moduleState()->setStartupState(STARTUP_COMPLETE);
4069void SchedulerProcess::parkMount()
4071 if (mountInterface().isNull())
4074 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4078 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4079 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4081 if (parkingStatus.
isValid() ==
false)
4083 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
4084 mountInterface()->lastError().
type());
4086 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4089 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4093 case ISD::PARK_PARKED:
4094 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4095 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
4097 moduleState()->setParkWaitState(PARKWAIT_PARKED);
4101 case ISD::PARK_UNPARKING:
4107 case ISD::PARK_ERROR:
4108 case ISD::PARK_UNKNOWN:
4109 case ISD::PARK_UNPARKED:
4111 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking mount...";
4116 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount park request received DBUS error: %1").
arg(
4119 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4121 else moduleState()->startCurrentOperationTimer();
4125 case ISD::PARK_PARKING:
4127 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4128 moduleState()->setShutdownState(SHUTDOWN_PARKING_MOUNT);
4130 moduleState()->setParkWaitState(PARKWAIT_PARKING);
4141void SchedulerProcess::unParkMount()
4143 if (mountInterface().isNull())
4146 moduleState()->setStartupState(STARTUP_ERROR);
4150 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4151 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4153 if (parkingStatus.
isValid() ==
false)
4155 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
4156 mountInterface()->lastError().
type());
4158 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4161 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4166 case ISD::PARK_UNPARKED:
4167 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4168 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
4170 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
4175 case ISD::PARK_PARKING:
4181 case ISD::PARK_ERROR:
4182 case ISD::PARK_UNKNOWN:
4183 case ISD::PARK_PARKED:
4189 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount unpark request received DBUS error: %1").
arg(
4192 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4194 else moduleState()->startCurrentOperationTimer();
4199 case ISD::PARK_UNPARKING:
4200 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4201 moduleState()->setStartupState(STARTUP_UNPARKING_MOUNT);
4203 moduleState()->setParkWaitState(PARKWAIT_UNPARKING);
4204 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
4215 if (mountInterface().isNull())
4219 QVariant canPark = mountInterface()->property(
"canPark");
4220 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount can park:" << (!canPark.
isValid() ?
"invalid" : (canPark.
toBool() ?
"T" :
"F"));
4222 if (canPark.
isValid() ==
false)
4224 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount canPark request received DBUS error: %1").
arg(
4225 mountInterface()->lastError().type());
4229 else if (canPark.
toBool() ==
true)
4233 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4234 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4236 if (parkingStatus.
isValid() ==
false)
4238 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parking status property is invalid %1.").
arg(
4239 mountInterface()->lastError().type());
4245 switch (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()))
4249 case ISD::PARK_PARKED:
4264void SchedulerProcess::parkDome()
4267 if (domeInterface().isNull())
4270 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4276 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4277 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4279 if (parkingStatus.
isValid() ==
false)
4281 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4282 mountInterface()->lastError().type());
4284 parkingStatus = ISD::PARK_ERROR;
4287 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4288 if (status != ISD::PARK_PARKED)
4290 moduleState()->setShutdownState(SHUTDOWN_PARKING_DOME);
4294 moduleState()->startCurrentOperationTimer();
4299 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
4303void SchedulerProcess::unParkDome()
4306 if (domeInterface().isNull())
4309 moduleState()->setStartupState(STARTUP_ERROR);
4313 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4314 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4316 if (parkingStatus.
isValid() ==
false)
4318 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4319 mountInterface()->lastError().
type());
4321 parkingStatus = ISD::PARK_ERROR;
4324 if (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()) != ISD::PARK_UNPARKED)
4326 moduleState()->setStartupState(STARTUP_UNPARKING_DOME);
4330 moduleState()->startCurrentOperationTimer();
4335 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
4341 QVariant guideStatus = guideInterface()->property(
"status");
4342 Ekos::GuideState gStatus =
static_cast<Ekos::GuideState
>(guideStatus.
toInt());
4347const QString &SchedulerProcess::profile()
const
4349 return moduleState()->currentProfile();
4352void SchedulerProcess::setProfile(
const QString &newProfile)
4354 moduleState()->setCurrentProfile(newProfile);
4357QString SchedulerProcess::currentJobName()
4359 auto job = moduleState()->activeJob();
4360 return ( job !=
nullptr ? job->getName() :
QString() );
4363QString SchedulerProcess::currentJobJson()
4365 auto job = moduleState()->activeJob();
4366 if( job !=
nullptr )
4376QString SchedulerProcess::jsonJobs()
4383 return moduleState()->logText();
4388 if (domeInterface().isNull())
4391 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4392 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4394 if (parkingStatus.
isValid() ==
false)
4396 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4397 mountInterface()->lastError().type());
4399 parkingStatus = ISD::PARK_ERROR;
4402 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4404 return status == ISD::PARK_PARKED;
4407void SchedulerProcess::simClockScaleChanged(
float newScale)
4409 if (moduleState()->currentlySleeping())
4412 (moduleState()->iterationTimer().remainingTime())
4413 * KStarsData::Instance()->clock()->scale()
4415 appendLogText(
i18n(
"Sleeping for %1 on simulation clock update until next observation job is ready...",
4416 remainingTimeMs.
toString(
"hh:mm:ss")));
4417 moduleState()->iterationTimer().stop();
4422void SchedulerProcess::simClockTimeChanged()
4424 moduleState()->calculateDawnDusk();
4427 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
4431void SchedulerProcess::setINDICommunicationStatus(CommunicationStatus status)
4433 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler INDI status is" <<
status;
4435 moduleState()->setIndiCommunicationStatus(status);
4438void SchedulerProcess::setEkosCommunicationStatus(CommunicationStatus status)
4440 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler Ekos status is" <<
status;
4442 moduleState()->setEkosCommunicationStatus(status);
4447void SchedulerProcess::checkInterfaceReady(
QDBusInterface * iface)
4449 if (iface == mountInterface())
4452 moduleState()->setMountReady(
true);
4454 else if (iface == capInterface())
4457 moduleState()->setCapReady(
true);
4459 else if (iface == observatoryInterface())
4463 setWeatherStatus(
static_cast<ISD::Weather::Status
>(
status.toInt()));
4465 else if (iface == weatherInterface())
4469 setWeatherStatus(
static_cast<ISD::Weather::Status
>(
status.toInt()));
4471 else if (iface == domeInterface())
4474 moduleState()->setDomeReady(
true);
4476 else if (iface == captureInterface())
4479 moduleState()->setCaptureReady(
true);
4482 emit interfaceReady(iface);
4485void SchedulerProcess::registerNewModule(
const QString &name)
4487 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Registering new Module (" <<
name <<
")";
4489 if (name ==
"Focus")
4491 delete focusInterface();
4492 setFocusInterface(
new QDBusInterface(kstarsInterfaceString, focusPathString, focusInterfaceString,
4494 connect(focusInterface(), SIGNAL(newStatus(Ekos::FocusState,
const QString)),
this,
4497 else if (name ==
"Capture")
4499 delete captureInterface();
4500 setCaptureInterface(
new QDBusInterface(kstarsInterfaceString, capturePathString, captureInterfaceString,
4503 connect(captureInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4506 connect(captureInterface(), SIGNAL(captureComplete(QVariantMap,
const QString)),
this, SLOT(checkAlignment(QVariantMap,
4509 checkInterfaceReady(captureInterface());
4511 else if (name ==
"Mount")
4513 delete mountInterface();
4514 setMountInterface(
new QDBusInterface(kstarsInterfaceString, mountPathString, mountInterfaceString,
4517 connect(mountInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4518 connect(mountInterface(), SIGNAL(newStatus(ISD::Mount::Status)),
this, SLOT(setMountStatus(ISD::Mount::Status)),
4521 checkInterfaceReady(mountInterface());
4523 else if (name ==
"Align")
4525 delete alignInterface();
4526 setAlignInterface(
new QDBusInterface(kstarsInterfaceString, alignPathString, alignInterfaceString,
4531 else if (name ==
"Guide")
4533 delete guideInterface();
4534 setGuideInterface(
new QDBusInterface(kstarsInterfaceString, guidePathString, guideInterfaceString,
4536 connect(guideInterface(), SIGNAL(newStatus(Ekos::GuideState)),
this,
4539 else if (name ==
"Observatory")
4541 delete observatoryInterface();
4542 setObservatoryInterface(
new QDBusInterface(kstarsInterfaceString, observatoryPathString, observatoryInterfaceString,
4544 connect(observatoryInterface(), SIGNAL(newStatus(ISD::Weather::Status)),
this,
4546 checkInterfaceReady(observatoryInterface());
4550void SchedulerProcess::registerNewDevice(
const QString &name,
int interface)
4554 if (interface & INDI::BaseDevice::DOME_INTERFACE)
4557 dbusargs.
append(INDI::BaseDevice::DOME_INTERFACE);
4563 setDomePathString(paths.value().last());
4564 delete domeInterface();
4565 setDomeInterface(
new QDBusInterface(kstarsInterfaceString, domePathString,
4566 domeInterfaceString,
4568 connect(domeInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4569 checkInterfaceReady(domeInterface());
4594 if (interface & INDI::BaseDevice::DUSTCAP_INTERFACE)
4597 dbusargs.
append(INDI::BaseDevice::DUSTCAP_INTERFACE);
4603 setDustCapPathString(paths.value().last());
4604 delete capInterface();
4605 setCapInterface(
new QDBusInterface(kstarsInterfaceString, dustCapPathString,
4606 dustCapInterfaceString,
4608 connect(capInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4609 checkInterfaceReady(capInterface());
4616 XMLEle *ep =
nullptr;
4617 XMLEle *subEP =
nullptr;
4619 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
4621 if (!strcmp(tagXMLEle(ep),
"Job"))
4623 for (subEP = nextXMLEle(ep, 1); subEP !=
nullptr; subEP = nextXMLEle(ep, 0))
4625 if (!strcmp(tagXMLEle(subEP),
"Prefix"))
4627 XMLEle *rawPrefix = findXMLEle(subEP,
"RawPrefix");
4633 else if (!strcmp(tagXMLEle(subEP),
"FITSDirectory"))
4646 if (outputFile ==
nullptr)
4648 QString message =
i18n(
"Unable to write to file %1", filename);
4649 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
4653 fprintf(outputFile,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4654 prXMLEle(outputFile, root, 0);
4668 KSNotification::sorry(
i18n(
"Unable to open file %1", sFile.
fileName()),
4669 i18n(
"Could Not Open File"));
4673 LilXML *xmlParser = newLilXML();
4674 char errmsg[MAXRBUF];
4675 XMLEle *root =
nullptr;
4680 root = readXMLEle(xmlParser, c, errmsg);
4686 delLilXML(xmlParser);
4691void SchedulerProcess::checkProcessExit(
int exitCode)
4697 if (moduleState()->startupState() == STARTUP_SCRIPT)
4698 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
4699 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4700 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
4705 if (moduleState()->startupState() == STARTUP_SCRIPT)
4708 moduleState()->setStartupState(STARTUP_ERROR);
4710 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4713 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4718void SchedulerProcess::readProcessOutput()
4720 appendLogText(scriptProcess().readAllStandardOutput().simplified());
4723bool SchedulerProcess::canCountCaptures(
const SchedulerJob &job)
4726 bool hasAutoFocus =
false;
4727 SchedulerJob tempJob = job;
4728 if (SchedulerUtils::loadSequenceQueue(tempJob.getSequenceFile().toLocalFile(), &tempJob, seqjobs, hasAutoFocus,
4734 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_LOCAL)
4748 forced |= std::any_of(moduleState()->jobs().begin(),
4749 moduleState()->jobs().end(), [](SchedulerJob * oneJob) ->
bool
4756 moduleState()->capturedFramesCount().clear();
4759 for (SchedulerJob *oneJob : moduleState()->jobs())
4766 bool hasAutoFocus =
false;
4770 if (SchedulerUtils::loadSequenceQueue(oneJob->getSequenceFile().
toLocalFile(), oneJob, seqjobs, hasAutoFocus,
4773 appendLogText(
i18n(
"Warning: job '%1' has inaccessible sequence '%2', marking invalid.", oneJob->getName(),
4779 oneJob->clearProgress();
4781 for (SequenceJob *oneSeqJob : seqjobs)
4785 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_LOCAL)
4789 QString const signature = oneSeqJob->getSignature();
4797 const int count = newFramesCount.
constFind(signature).value();
4798 newJobFramesCount[signature] = count;
4799 oneJob->addProgress(count, oneSeqJob);
4805 CapturedFramesMap::const_iterator
const earlierRunIterator =
4806 moduleState()->capturedFramesCount().constFind(signature);
4808 if (moduleState()->capturedFramesCount().constEnd() != earlierRunIterator)
4810 count = earlierRunIterator.value();
4813 count = PlaceholderPath::getCompletedFiles(signature);
4815 newFramesCount[signature] = count;
4816 newJobFramesCount[signature] = count;
4817 oneJob->addProgress(count, oneSeqJob);
4821 SchedulerUtils::updateLightFramesRequired(oneJob, seqjobs, newFramesCount);
4824 moduleState()->setCapturedFramesCount(newFramesCount);
4827 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Frame map summary:";
4828 CapturedFramesMap::const_iterator it = moduleState()->capturedFramesCount().constBegin();
4829 for (; it != moduleState()->capturedFramesCount().constEnd(); it++)
4830 qCDebug(KSTARS_EKOS_SCHEDULER) <<
" " << it.key() <<
':' << it.value();
4834SchedulerJob *SchedulerProcess::activeJob()
4836 return moduleState()->activeJob();
4839void SchedulerProcess::printStates(
const QString &label)
4841 qCDebug(KSTARS_EKOS_SCHEDULER) <<
4842 QString(
"%1 %2 %3%4 %5 %6 %7 %8 %9\n")
4844 .
arg(timerStr(moduleState()->timerState()))
4845 .
arg(getSchedulerStatusString(moduleState()->schedulerState()))
4846 .
arg((moduleState()->timerState() == RUN_JOBCHECK && activeJob() !=
nullptr) ?
4847 QString(
"(%1 %2)").arg(SchedulerJob::jobStatusString(activeJob()->getState()))
4848 .arg(SchedulerJob::jobStageString(activeJob()->getStage())) :
"")
4849 .
arg(ekosStateString(moduleState()->ekosState()))
4850 .
arg(indiStateString(moduleState()->indiState()))
4851 .
arg(startupStateString(moduleState()->startupState()))
4852 .
arg(shutdownStateString(moduleState()->shutdownState()))
4853 .
arg(parkWaitStateString(moduleState()->parkWaitState())).
toLatin1().
data();
4854 foreach (
auto j, moduleState()->jobs())
4855 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"job %1 %2\n").
arg(j->getName()).
arg(SchedulerJob::jobStatusString(
Q_SCRIPTABLE Q_NOREPLY void startAstrometry()
startAstrometry initiation of the capture and solve operation.
bool shouldSchedulerSleep(SchedulerJob *job)
shouldSchedulerSleep Check if the scheduler needs to sleep until the job is ready
Q_SCRIPTABLE Q_NOREPLY void startCapture(bool restart=false)
startCapture The current job file name is solved to an url which is fed to ekos.
void loadProfiles()
loadProfiles Load the existing EKOS profiles
Q_SCRIPTABLE Q_NOREPLY void runStartupProcedure()
runStartupProcedure Execute the startup of the scheduler itself to be prepared for running scheduler ...
void checkCapParkingStatus()
checkDomeParkingStatus check dome parking status and updating corresponding states accordingly.
void getNextAction()
getNextAction Checking for the next appropriate action regarding the current state of the scheduler a...
Q_SCRIPTABLE bool isMountParked()
Q_SCRIPTABLE Q_NOREPLY void startJobEvaluation()
startJobEvaluation Start job evaluation only without starting the scheduler process itself.
Q_SCRIPTABLE Q_NOREPLY void resetJobs()
resetJobs Reset all jobs counters
void selectActiveJob(const QList< SchedulerJob * > &jobs)
selectActiveJob Select the job that should be executed
Q_SCRIPTABLE void wakeUpScheduler()
wakeUpScheduler Wake up scheduler from sleep state
Q_SCRIPTABLE Q_NOREPLY void setPaused()
setPaused pausing the scheduler
Q_SCRIPTABLE bool checkParkWaitState()
checkParkWaitState Check park wait state.
bool executeJob(SchedulerJob *job)
executeJob After the best job is selected, we call this in order to start the process that will execu...
bool createJobSequence(XMLEle *root, const QString &prefix, const QString &outputDir)
createJobSequence Creates a job sequence for the mosaic tool given the prefix and output dir.
void iterate()
Repeatedly runs a scheduler iteration and then sleeps timerInterval millisconds and run the next iter...
Q_SCRIPTABLE Q_NOREPLY void startGuiding(bool resetCalibration=false)
startGuiding After ekos is fed the calibration options, we start the guiding process
void findNextJob()
findNextJob Check if the job met the completion criteria, and if it did, then it search for next job ...
Q_SCRIPTABLE bool appendEkosScheduleList(const QString &fileURL)
appendEkosScheduleList Append the contents of an ESL file to the queue.
Q_SCRIPTABLE void execute()
execute Execute the schedule, start if idle or paused.
Q_SCRIPTABLE bool isDomeParked()
Q_SCRIPTABLE bool saveScheduler(const QUrl &fileURL)
saveScheduler Save scheduler jobs to a file
Q_SCRIPTABLE Q_NOREPLY void appendLogText(const QString &logentry) override
appendLogText Append a new line to the logging.
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS interface function.
Q_SCRIPTABLE Q_NOREPLY void removeAllJobs()
DBUS interface function.
Q_SCRIPTABLE void stopCurrentJobAction()
stopCurrentJobAction Stop whatever action taking place in the current job (eg.
Q_SCRIPTABLE Q_NOREPLY void stopGuiding()
stopGuiding After guiding is done we need to stop the process
bool checkShutdownState()
checkShutdownState Check shutdown procedure stages and make sure all stages are complete.
Q_SCRIPTABLE Q_NOREPLY void startSlew()
startSlew DBus call for initiating slew
bool checkEkosState()
checkEkosState Check ekos startup stages and take whatever action necessary to get Ekos up and runnin...
bool checkStatus()
checkJobStatus Check the overall state of the scheduler, Ekos, and INDI.
bool checkINDIState()
checkINDIState Check INDI startup stages and take whatever action necessary to get INDI devices conne...
XMLEle * getSequenceJobRoot(const QString &filename) const
getSequenceJobRoot Read XML data from capture sequence job
Q_SCRIPTABLE Q_NOREPLY void runShutdownProcedure()
runShutdownProcedure Shutdown the scheduler itself and EKOS (if configured to do so).
Q_SCRIPTABLE Q_NOREPLY void setSequence(const QString &sequenceFileURL)
DBUS interface function.
Q_SCRIPTABLE bool loadScheduler(const QString &fileURL)
DBUS interface function.
Q_SCRIPTABLE Q_NOREPLY void startFocusing()
startFocusing DBus call for feeding ekos the specified settings and initiating focus operation
void checkJobStage()
checkJobStage Check the progress of the job states and make DBUS calls to start the next stage until ...
bool checkStartupState()
checkStartupState Check startup procedure stages and make sure all stages are complete.
void processGuidingTimer()
processGuidingTimer Check the guiding timer, and possibly restart guiding.
GuideState getGuidingStatus()
getGuidingStatus Retrieve the guiding status.
Q_SCRIPTABLE Q_NOREPLY void resetAllJobs()
DBUS interface function.
void applyConfig()
applyConfig Apply configuration changes from the global configuration dialog.
Q_SCRIPTABLE void clearLog()
clearLog Clear log entry
Q_SCRIPTABLE Q_NOREPLY void disconnectINDI()
disconnectINDI disconnect all INDI devices from server.
Q_SCRIPTABLE Q_NOREPLY void stop()
DBUS interface function.
bool manageConnectionLoss()
manageConnectionLoss Mitigate loss of connection with the INDI server.
void updateCompletedJobsCount(bool forced=false)
updateCompletedJobsCount For each scheduler job, examine sequence job storage and count captures.
Q_SCRIPTABLE Q_NOREPLY void evaluateJobs(bool evaluateOnly)
evaluateJobs evaluates the current state of each objects and gives each one a score based on the cons...
int runSchedulerIteration()
Run a single scheduler iteration.
void setSolverAction(Align::GotoMode mode)
setSolverAction set the GOTO mode for the solver
Q_SCRIPTABLE bool completeShutdown()
completeShutdown Try to complete the scheduler shutdown
static KConfigDialog * exists(const QString &name)
void settingsChanged(const QString &dialogName)
SkyMapComposite * skyComposite()
This is the main window for KStars.
static KStars * Instance()
The SchedulerState class holds all attributes defining the scheduler's state.
Sequence Job is a container for the details required to capture a series of images.
void timeChanged()
The time has changed (emitted by setUTC() )
void scaleChanged(float)
The timestep has changed.
The sky coordinates of a point in the sky.
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
const CachingDms & dec() const
const CachingDms & ra0() const
const CachingDms & ra() const
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
const CachingDms & dec0() const
void setDec0(dms d)
Sets Dec0, the catalog Declination.
An angle, stored as degrees, but expressible in many ways.
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
Ekos is an advanced Astrophotography tool for Linux.
SchedulerJobStatus
States of a SchedulerJob.
@ 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.
QMap< QString, uint16_t > CapturedFramesMap
mapping signature --> frames count
ErrorHandlingStrategy
options what should happen if an error or abort occurs
@ ALIGN_FAILED
Alignment failed.
@ ALIGN_ABORTED
Alignment aborted by user or agent.
@ ALIGN_IDLE
No ongoing operations.
@ ALIGN_COMPLETE
Alignment successfully completed.
CaptureState
Capture states.
SchedulerTimerState
IterationTypes, the different types of scheduler iterations that are run.
GeoCoordinates geo(const QVariant &location)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
bool isValid(QStringView ifopt)
QString name(StandardAction id)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
const char * constData() const const
QDateTime addSecs(qint64 s) const const
qint64 currentMSecsSinceEpoch()
qint64 secsTo(const QDateTime &other) const const
QString toString(QStringView format, QCalendar cal) const const
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QDBusConnection sessionBus()
void unregisterObject(const QString &path, UnregisterMode mode)
QString errorString(ErrorType error)
QString message() const const
ErrorType type() const const
QList< QVariant > arguments() const const
QString errorMessage() const const
const QDBusError & error()
bool isValid() const const
bool mkpath(const QString &dirPath) const const
bool exists() const const
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QByteArray toJson(JsonFormat format) const const
void append(QList< T > &&value)
bool isEmpty() const const
int toInt(QStringView s, bool *ok) const const
QString toString(QDate date, FormatType format) const const
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
bool contains(const Key &key) const const
QList< Key > keys() const const
QList< T > values() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QVariant property(const char *name) const const
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void readyReadStandardOutput()
void start(OpenMode mode)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QByteArray toLatin1() const const
std::string toStdString() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QTextStream & dec(QTextStream &stream)
QTextStream & endl(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QTime fromMSecsSinceStartOfDay(int msecs)
int msecsSinceStartOfDay() const const
QString toString(QStringView format) const const
bool isEmpty() const const
bool isValid() const const
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const