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
38SchedulerProcess::SchedulerProcess(QSharedPointer<SchedulerModuleState> state,
const QString &ekosPathStr,
39 const QString &ekosInterfaceStr) : QObject(KStars::Instance())
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();
81 switch (moduleState()->schedulerState())
85 if (!moduleState()->startupScriptURL().isEmpty() && ! moduleState()->startupScriptURL().isValid())
93 if (!moduleState()->shutdownScriptURL().isEmpty() && !moduleState()->shutdownScriptURL().isValid())
101 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Scheduler is starting...";
103 moduleState()->setSchedulerState(SCHEDULER_RUNNING);
104 moduleState()->setupNextIteration(RUN_SCHEDULER);
107 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler started.";
110 case SCHEDULER_PAUSED:
111 moduleState()->setSchedulerState(SCHEDULER_RUNNING);
112 moduleState()->setupNextIteration(RUN_SCHEDULER);
115 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler resuming.";
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()));
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();
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)
253 if (activeJob() !=
nullptr)
255 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
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);
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);
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());
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 )
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()))
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);
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());
438 QList<QVariant> dbusargs;
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)
561 scriptProcess().disconnect();
562 scriptProcess().terminate();
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()->resetAutofocusCompleted();
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());
604 scriptProcess().terminate();
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)
661 if (Options::schedulerWeather())
663 ISD::Weather::Status weatherStatus = moduleState()->weatherStatus();
664 if (weatherStatus == ISD::Weather::WEATHER_WARNING || weatherStatus == ISD::Weather::WEATHER_ALERT)
667 if (moduleState()->weatherGracePeriodActive())
669 appendLogText(
i18n(
"Job '%1' cannot start because weather status is %2 and grace period is over.",
670 job->getName(), (weatherStatus == ISD::Weather::WEATHER_WARNING) ?
i18n(
"Warning") :
i18n(
"Alert")));
672 moduleState()->setWeatherGracePeriodActive(
false);
677 QDateTime wakeupTime = SchedulerModuleState::getLocalTime().
addSecs(Options::schedulerWeatherGracePeriod() * 60);
679 appendLogText(
i18n(
"Job '%1' cannot start because weather status is %2. Waiting until weather improves or until %3",
680 job->getName(), (weatherStatus == ISD::Weather::WEATHER_WARNING) ?
i18n(
"Warning") :
i18n(
"Alert"),
684 moduleState()->setWeatherGracePeriodActive(
true);
685 moduleState()->enablePreemptiveShutdown(wakeupTime);
687 emit schedulerSleeping(
true,
true);
692 moduleState()->setWeatherGracePeriodActive(
false);
696 if (moduleState()->startupState() == STARTUP_COMPLETE &&
697 Options::preemptiveShutdown() &&
698 nextObservationTime > (Options::preemptiveShutdownTime() * 3600))
701 "Job '%1' scheduled for execution at %2. "
702 "Observatory scheduled for shutdown until next job is ready.",
703 job->getName(), job->getStartupTime().
toString()));
704 moduleState()->enablePreemptiveShutdown(job->getStartupTime());
706 emit schedulerSleeping(
true,
false);
715 else if (nextObservationTime > Options::leadTime() * 60 &&
716 moduleState()->startupState() == STARTUP_COMPLETE &&
717 moduleState()->parkWaitState() == PARKWAIT_IDLE &&
718 (job->getStepPipeline() & SchedulerJob::USE_TRACK) &&
720 Options::schedulerParkMount())
723 "Job '%1' scheduled for execution at %2. "
724 "Parking the mount until the job is ready.",
725 job->getName(), job->getStartupTime().
toString()));
727 moduleState()->setParkWaitState(PARKWAIT_PARK);
731 else if (nextObservationTime > Options::leadTime() * 60)
733 auto log =
i18n(
"Sleeping until observation job %1 is ready at %2", job->getName(),
736 KSNotification::event(
QLatin1String(
"SchedulerSleeping"), log, KSNotification::Scheduler,
737 KSNotification::Info);
740 if (nextObservationTime > Options::leadTime() * 60 * 12 && !Options::preemptiveShutdown())
742 dms delay(
static_cast<double>(nextObservationTime * 15.0 / 3600.0));
744 "Warning: Job '%1' is %2 away from now, you may want to enable Preemptive Shutdown.",
753 moduleState()->setupNextIteration(RUN_WAKEUP,
754 std::lround(((nextObservationTime + 1) * 1000) / KStarsData::Instance()->clock()->scale()));
756 emit schedulerSleeping(
false,
true);
765 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting slewing must be valid");
770 moduleState()->setParkWaitState(PARKWAIT_UNPARK);
774 if (Options::resetMountModelBeforeJob())
779 SkyPoint target = activeJob()->getTargetCoords();
789 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' slew request received DBUS error: %2").
arg(
796 moduleState()->updateJobStage(SCHEDSTAGE_SLEWING);
803 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting focusing must be valid");
806 if (activeJob()->getStage() == SCHEDSTAGE_RESLEWING_COMPLETE ||
807 activeJob()->getStage() == SCHEDSTAGE_POSTALIGN_FOCUSING)
813 moduleState()->updateJobStage(SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE);
818 if (activeJob()->getOpticalTrain() !=
"")
819 m_activeJobs.insert(activeJob()->getOpticalTrain(), activeJob());
822 QVariant opticalTrain = captureInterface()->property(
"opticalTrain");
824 if (opticalTrain.
isValid() ==
false)
826 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' opticalTrain request failed.").
arg(activeJob()->getName());
835 m_activeJobs.insert(opticalTrain.
toString(), activeJob());
836 activeJob()->setOpticalTrain(opticalTrain.
toString());
842 foreach (
auto follower, activeJob()->followerJobs())
844 m_activeJobs.insert(follower->getOpticalTrain(), follower);
856 dBusArgs.
append(job->getOpticalTrain());
857 boolReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"canAutoFocus", dBusArgs);
861 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' canAutoFocus request received DBUS error: %2").
arg(
871 if (boolReply.value() ==
false)
873 appendLogText(
i18n(
"Warning: job '%1' is unable to proceed with autofocus, not supported.", job->getName()));
874 job->setStepPipeline(
875 static_cast<SchedulerJob::StepPipeline
>(job->getStepPipeline() & ~SchedulerJob::USE_FOCUS));
876 moduleState()->setAutofocusCompleted(job->getOpticalTrain(),
true);
877 if (moduleState()->autofocusCompleted())
879 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
888 if ((reply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"clearAutoFocusHFR",
891 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' clearAutoFocusHFR request received DBUS error: %2").arg(
902 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"resetFrame",
905 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' resetFrame request received DBUS error: %2").arg(
917 if (!job->getInitialFilter().
isEmpty())
920 dBusArgs.
append(job->getInitialFilter());
921 dBusArgs.
append(job->getOpticalTrain());
922 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setFilter",
925 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' setFilter request received DBUS error: %1").arg(
937 dBusArgs.
append(job->getOpticalTrain());
938 boolReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"useFullField", dBusArgs);
942 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' useFullField request received DBUS error: %2").arg(
952 if (boolReply.value() ==
false)
957 dBusArgs.
append(job->getOpticalTrain());
958 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setAutoStarEnabled", dBusArgs)).
type() ==
961 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' setAutoFocusStar request received DBUS error: %1").arg(
974 dBusArgs.
append(job->getOpticalTrain());
978 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' startFocus request received DBUS error: %2").arg(
988 moduleState()->updateJobStage(SCHEDSTAGE_FOCUSING);
990 moduleState()->startCurrentOperationTimer();
995 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting aligning must be valid");
1006 moduleState()->setIndexToUse(-1);
1007 moduleState()->setHealpixToUse(-1);
1010 if (activeJob()->getFITSFile().isEmpty() ==
false)
1016 appendLogText(
i18n(
"Warning: job '%1' target FITS file does not exist.", activeJob()->getName()));
1025 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"loadAndSlew", solveArgs)).type() ==
1028 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request received DBUS error: %2",
1037 else if (reply.
arguments().first().toBool() ==
false)
1039 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request failed.", activeJob()->getName()));
1045 appendLogText(
i18n(
"Job '%1' is plate solving %2.", activeJob()->getName(), activeJob()->getFITSFile().fileName()));
1051 const SkyPoint targetCoords = activeJob()->getTargetCoords();
1054 rotationArgs << activeJob()->getPositionAngle();
1056 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords",
1059 appendLogText(
i18n(
"Warning: job '%1' setTargetCoords request received DBUS error: %2",
1070 if (activeJob()->getPositionAngle() >= -180)
1072 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetPositionAngle",
1075 appendLogText(
i18n(
"Warning: job '%1' setTargetPositionAngle request received DBUS error: %2").arg(
1088 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request received DBUS error: %2").arg(
1097 else if (reply.
arguments().first().toBool() ==
false)
1099 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request failed.", activeJob()->getName()));
1105 appendLogText(
i18n(
"Job '%1' is capturing and plate solving.", activeJob()->getName()));
1109 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
1110 moduleState()->startCurrentOperationTimer();
1115 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting guiding must be valid");
1120 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
1121 appendLogText(
i18n(
"Guiding already running for %1, starting next scheduler action...", activeJob()->getName()));
1123 moduleState()->startCurrentOperationTimer();
1136 if (resetCalibration && Options::resetGuideCalibration())
1143 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1145 appendLogText(
i18n(
"Starting guiding procedure for %1 ...", activeJob()->getName()));
1147 moduleState()->startCurrentOperationTimer();
1152 if (!guideInterface())
1156 if (
nullptr != activeJob() && (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE))
1158 qCInfo(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' is stopping guiding...").
arg(activeJob()->getName());
1160 moduleState()->resetGuideFailureCount();
1162 stopCapturing(
"",
true);
1166 if (moduleState()->isGuidingTimerActive())
1167 moduleState()->cancelGuidingTimer();
1172 if ((moduleState()->restartGuidingInterval() > 0) &&
1173 (moduleState()->restartGuidingTime().msecsTo(KStarsData::Instance()->ut()) > moduleState()->restartGuidingInterval()))
1175 moduleState()->cancelGuidingTimer();
1182 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting capturing must be valid");
1185 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE &&
getGuidingStatus() != GUIDE_GUIDING)
1188 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1193 startSingleCapture(activeJob(), restart);
1194 for (
auto follower : activeJob()->followerJobs())
1197 if (follower->getState() ==
SCHEDJOB_SCHEDULED || (follower->getStage() == SCHEDSTAGE_CAPTURING && follower->isStopped()))
1200 follower->setStage(SCHEDSTAGE_CAPTURING);
1201 startSingleCapture(follower, restart);
1205 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
1207 KSNotification::event(
QLatin1String(
"EkosScheduledImagingStart"),
1208 i18n(
"Ekos job (%1) - Capture started", activeJob()->getName()), KSNotification::Scheduler);
1210 if (moduleState()->captureBatch() > 0)
1211 appendLogText(
i18n(
"Job '%1' capture is in progress (batch #%2)...", activeJob()->getName(),
1212 moduleState()->captureBatch() + 1));
1214 appendLogText(
i18n(
"Job '%1' capture is in progress...", activeJob()->getName()));
1216 moduleState()->startCurrentOperationTimer();
1219void SchedulerProcess::startSingleCapture(SchedulerJob *job,
bool restart)
1221 captureInterface()->setProperty(
"targetName", job->getName());
1224 QVariant train(job->getOpticalTrain());
1226 if (restart ==
false)
1231 QVariant targetName(job->getName());
1235 dbusargs.
append(targetName);
1237 "loadSequenceQueue",
1241 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1242 QString(
"Warning: job '%1' loadSequenceQueue request received DBUS error: %1").
arg(job->getName()).
arg(
1249 else if (captureReply.value() ==
false)
1251 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1252 QString(
"Warning: job '%1' loadSequenceQueue request failed").
arg(job->getName());
1261 for (
auto &e : fMap.keys())
1263 QList<QVariant> dbusargs;
1266 dbusargs.
append(fMap.value(e));
1269 if ((reply = captureInterface()->callWithArgumentList(
QDBus::Block,
"setCapturedFramesMap",
1270 dbusargs)).
type() ==
1273 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1274 QString(
"Warning: job '%1' setCapturedFramesCount request received DBUS error: %1").arg(job->getName()).arg(
1283 QList<QVariant> dbusargs;
1286 QDBusReply<QString>
const startReply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"start",
1291 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1292 QString(
"Warning: job '%1' start request received DBUS error: %1").arg(job->getName()).arg(
1299 QString trainName = startReply.value();
1300 m_activeJobs[trainName] = job;
1306 QVariant gotoMode(
static_cast<int>(mode));
1312 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Loading profiles";
1316 moduleState()->updateProfiles(profiles);
1319void SchedulerProcess::executeScript(
const QString &filename)
1328 checkProcessExit(exitCode);
1332 scriptProcess().
start(filename, arguments);
1337 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1340 switch (moduleState()->ekosState())
1344 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1346 moduleState()->setEkosState(EKOS_READY);
1352 moduleState()->setEkosState(EKOS_STARTING);
1353 moduleState()->startCurrentOperationTimer();
1355 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos communication status is" << moduleState()->ekosCommunicationStatus() <<
1364 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1367 moduleState()->resetEkosConnectFailureCount();
1368 moduleState()->setEkosState(EKOS_READY);
1371 else if (moduleState()->ekosCommunicationStatus() == Ekos::Error)
1373 if (moduleState()->increaseEkosConnectFailureCount())
1384 else if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1387 else if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1389 if (moduleState()->increaseEkosConnectFailureCount())
1396 moduleState()->startCurrentOperationTimer();
1410 if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1413 moduleState()->setEkosState(EKOS_IDLE);
1427 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1430 switch (moduleState()->indiState())
1434 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1436 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1437 moduleState()->resetIndiConnectFailureCount();
1438 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI Properties...";
1442 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Connecting INDI devices...";
1444 moduleState()->setIndiState(INDI_CONNECTING);
1446 moduleState()->startCurrentOperationTimer();
1451 case INDI_CONNECTING:
1453 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1456 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1458 else if (moduleState()->indiCommunicationStatus() == Ekos::Error)
1460 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1467 appendLogText(
i18n(
"One or more INDI devices failed to connect. Check INDI control panel for details."));
1472 else if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1474 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1478 moduleState()->startCurrentOperationTimer();
1482 appendLogText(
i18n(
"One or more INDI devices timed out. Check INDI control panel for details."));
1489 case INDI_DISCONNECTING:
1491 if (moduleState()->indiCommunicationStatus() == Ekos::Idle)
1494 moduleState()->setIndiState(INDI_IDLE);
1500 case INDI_PROPERTY_CHECK:
1502 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI properties.";
1504 if (Options::schedulerUnparkDome() && moduleState()->domeReady() ==
false)
1506 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1508 moduleState()->startCurrentOperationTimer();
1509 appendLogText(
i18n(
"Warning: dome device not ready after timeout, attempting to recover..."));
1519 if (Options::schedulerUnparkMount() && moduleState()->mountReady() ==
false)
1521 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1523 moduleState()->startCurrentOperationTimer();
1524 appendLogText(
i18n(
"Warning: mount device not ready after timeout, attempting to recover..."));
1529 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount unpark required but mount is not yet ready.";
1534 if (Options::schedulerOpenDustCover() && moduleState()->capReady() ==
false)
1536 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1538 moduleState()->startCurrentOperationTimer();
1539 appendLogText(
i18n(
"Warning: cap device not ready after timeout, attempting to recover..."));
1544 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap unpark required but cap is not yet ready.";
1549 if (captureInterface().isNull())
1552 if (moduleState()->captureReady() ==
false)
1554 QVariant hasCoolerControl = captureInterface()->property(
"coolerControl");
1555 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cooler control" << (!hasCoolerControl.
isValid() ?
"invalid" :
1556 (hasCoolerControl.
toBool() ?
"True" :
"Faklse"));
1557 if (hasCoolerControl.
isValid())
1558 moduleState()->setCaptureReady(
true);
1560 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"Capture module is not ready yet...";
1563 moduleState()->setIndiState(INDI_READY);
1564 moduleState()->resetIndiConnectFailureCount();
1578 if (moduleState()->indiState() == INDI_DISCONNECTING
1583 if (moduleState()->weatherGracePeriodActive() ==
false)
1586 if (moduleState()->indiState() != INDI_IDLE && Options::stopEkosAfterShutdown())
1593 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
1597 if (moduleState()->ekosState() != EKOS_IDLE && Options::stopEkosAfterShutdown())
1604 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
1617 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Disconnecting INDI...";
1618 moduleState()->setIndiState(INDI_DISCONNECTING);
1622void SchedulerProcess::stopEkos()
1624 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Stopping Ekos...";
1625 moduleState()->setEkosState(EKOS_STOPPING);
1626 moduleState()->resetEkosConnectFailureCount();
1628 moduleState()->setMountReady(
false);
1629 moduleState()->setCaptureReady(
false);
1630 moduleState()->setDomeReady(
false);
1631 moduleState()->setCapReady(
false);
1636 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
1640 switch (moduleState()->ekosState())
1651 switch (moduleState()->indiState())
1654 case INDI_DISCONNECTING:
1663 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1665 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Ekos is currently connected, checking INDI before mitigating connection loss.");
1668 if (moduleState()->isINDIConnected())
1671 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"INDI is currently connected, no connection loss mitigation needed.");
1690 if (capInterface().isNull())
1693 QVariant parkingStatus = capInterface()->property(
"parkStatus");
1694 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1696 if (parkingStatus.
isValid() ==
false)
1698 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
1699 capInterface()->lastError().type());
1701 parkingStatus = ISD::PARK_ERROR;
1704 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1708 case ISD::PARK_PARKED:
1709 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1712 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
1714 moduleState()->resetParkingCapFailureCount();
1717 case ISD::PARK_UNPARKED:
1718 if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1720 moduleState()->setStartupState(STARTUP_COMPLETE);
1723 moduleState()->resetParkingCapFailureCount();
1726 case ISD::PARK_PARKING:
1727 case ISD::PARK_UNPARKING:
1729 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1731 if (moduleState()->increaseParkingCapFailureCount())
1734 if (status == ISD::PARK_PARKING)
1743 case ISD::PARK_ERROR:
1744 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1747 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1749 else if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1752 moduleState()->setStartupState(STARTUP_ERROR);
1754 moduleState()->resetParkingCapFailureCount();
1762void SchedulerProcess::checkMountParkingStatus()
1764 if (mountInterface().isNull())
1767 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
1768 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1770 if (parkingStatus.
isValid() ==
false)
1772 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
1773 mountInterface()->lastError().type());
1775 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1778 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1783 case ISD::PARK_PARKED:
1786 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1787 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1790 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1791 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1794 moduleState()->resetParkingMountFailureCount();
1798 case ISD::PARK_UNPARKED:
1801 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1802 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1805 if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1806 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1809 moduleState()->resetParkingMountFailureCount();
1815 case ISD::PARK_UNPARKING:
1816 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1818 if (moduleState()->increaseParkingMountFailureCount())
1820 appendLogText(
i18n(
"Warning: mount unpark operation timed out on attempt %1/%2. Restarting operation...",
1821 moduleState()->parkingMountFailureCount(), moduleState()->maxFailureAttempts()));
1826 appendLogText(
i18n(
"Warning: mount unpark operation timed out on last attempt."));
1827 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1830 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
1835 case ISD::PARK_PARKING:
1836 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1838 if (moduleState()->increaseParkingMountFailureCount())
1840 appendLogText(
i18n(
"Warning: mount park operation timed out on attempt %1/%2. Restarting operation...",
1841 moduleState()->parkingMountFailureCount(),
1842 moduleState()->maxFailureAttempts()));
1847 appendLogText(
i18n(
"Warning: mount park operation timed out on last attempt."));
1848 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1851 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Parking mount in progress...";
1856 case ISD::PARK_ERROR:
1857 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1860 moduleState()->setStartupState(STARTUP_ERROR);
1861 moduleState()->resetParkingMountFailureCount();
1863 else if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1865 if (moduleState()->increaseParkingMountFailureCount())
1867 appendLogText(
i18n(
"Warning: mount park operation failed on attempt %1/%2. Restarting operation...",
1868 moduleState()->parkingMountFailureCount(),
1869 moduleState()->maxFailureAttempts()));
1875 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1876 moduleState()->resetParkingMountFailureCount();
1880 else if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1883 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1884 moduleState()->resetParkingMountFailureCount();
1886 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1889 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1890 moduleState()->resetParkingMountFailureCount();
1896 case ISD::PARK_UNKNOWN:
1898 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1899 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1902 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1903 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1906 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1907 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1908 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1909 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1911 moduleState()->resetParkingMountFailureCount();
1916void SchedulerProcess::checkDomeParkingStatus()
1918 if (domeInterface().isNull())
1921 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
1922 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1924 if (parkingStatus.
isValid() ==
false)
1926 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
1927 mountInterface()->lastError().
type());
1929 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1932 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1936 case ISD::PARK_PARKED:
1937 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1941 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
1943 moduleState()->resetParkingDomeFailureCount();
1946 case ISD::PARK_UNPARKED:
1947 if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1949 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1952 moduleState()->resetParkingDomeFailureCount();
1955 case ISD::PARK_PARKING:
1956 case ISD::PARK_UNPARKING:
1958 if (moduleState()->getCurrentOperationMsec() > (120 * 1000))
1960 if (moduleState()->increaseParkingDomeFailureCount())
1963 if (status == ISD::PARK_PARKING)
1972 case ISD::PARK_ERROR:
1973 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1975 if (moduleState()->increaseParkingDomeFailureCount())
1983 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1984 moduleState()->resetParkingDomeFailureCount();
1987 else if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1989 if (moduleState()->increaseParkingDomeFailureCount())
1997 moduleState()->setStartupState(STARTUP_ERROR);
1998 moduleState()->resetParkingDomeFailureCount();
2010 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2013 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Checking Startup State (%1)...").
arg(moduleState()->startupState());
2015 switch (moduleState()->startupState())
2019 KSNotification::event(
QLatin1String(
"ObservatoryStartup"),
i18n(
"Observatory is in the startup process"),
2020 KSNotification::Scheduler);
2022 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Startup Idle. Starting startup process...";
2029 if (Options::alwaysExecuteStartupScript() ==
false && moduleState()->ekosCommunicationStatus() == Ekos::Success)
2031 if (moduleState()->startupScriptURL().isEmpty() ==
false)
2034 if (!activeJob() || activeJob()->getLightFramesRequired())
2035 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
2037 moduleState()->setStartupState(STARTUP_COMPLETE);
2041 if (moduleState()->currentProfile() !=
i18n(
"Default"))
2044 profile.
append(moduleState()->currentProfile());
2045 ekosInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setProfile", profile);
2048 if (moduleState()->startupScriptURL().isEmpty() ==
false)
2050 moduleState()->setStartupState(STARTUP_SCRIPT);
2055 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
2059 case STARTUP_SCRIPT:
2062 case STARTUP_UNPARK_DOME:
2066 if (activeJob() ==
nullptr || activeJob()->getLightFramesRequired())
2068 if (Options::schedulerUnparkDome())
2071 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
2075 moduleState()->setStartupState(STARTUP_COMPLETE);
2081 case STARTUP_UNPARKING_DOME:
2082 checkDomeParkingStatus();
2085 case STARTUP_UNPARK_MOUNT:
2086 if (Options::schedulerUnparkMount())
2089 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
2092 case STARTUP_UNPARKING_MOUNT:
2093 checkMountParkingStatus();
2096 case STARTUP_UNPARK_CAP:
2097 if (Options::schedulerOpenDustCover())
2100 moduleState()->setStartupState(STARTUP_COMPLETE);
2103 case STARTUP_UNPARKING_CAP:
2107 case STARTUP_COMPLETE:
2120 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking shutdown state...";
2122 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2125 switch (moduleState()->shutdownState())
2129 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Starting shutdown process...";
2131 moduleState()->setActiveJob(
nullptr);
2132 moduleState()->setupNextIteration(RUN_SHUTDOWN);
2133 emit shutdownStarted();
2135 if (Options::schedulerWarmCCD())
2142 if (captureInterface())
2144 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Setting coolerControl=false";
2145 captureInterface()->setProperty(
"coolerControl",
false);
2150 if (moduleState()->isINDIConnected())
2152 if (Options::schedulerCloseDustCover())
2154 moduleState()->setShutdownState(SHUTDOWN_PARK_CAP);
2158 if (Options::schedulerParkMount())
2160 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2164 if (Options::schedulerParkDome())
2166 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2170 else appendLogText(
i18n(
"Warning: Bypassing parking procedures, no INDI connection."));
2172 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2174 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2178 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2181 case SHUTDOWN_PARK_CAP:
2182 if (!moduleState()->isINDIConnected())
2184 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2185 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2187 else if (Options::schedulerCloseDustCover())
2190 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2193 case SHUTDOWN_PARKING_CAP:
2197 case SHUTDOWN_PARK_MOUNT:
2198 if (!moduleState()->isINDIConnected())
2200 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2201 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2203 else if (Options::schedulerParkMount())
2206 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2209 case SHUTDOWN_PARKING_MOUNT:
2210 checkMountParkingStatus();
2213 case SHUTDOWN_PARK_DOME:
2214 if (!moduleState()->isINDIConnected())
2216 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2217 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2219 else if (Options::schedulerParkDome())
2222 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2225 case SHUTDOWN_PARKING_DOME:
2226 checkDomeParkingStatus();
2229 case SHUTDOWN_SCRIPT:
2230 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2233 if (moduleState()->ekosState() != EKOS_IDLE && Options::shutdownScriptTerminatesINDI())
2239 moduleState()->setShutdownState(SHUTDOWN_SCRIPT_RUNNING);
2243 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2246 case SHUTDOWN_SCRIPT_RUNNING:
2249 case SHUTDOWN_COMPLETE:
2252 case SHUTDOWN_ERROR:
2262 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2265 if (moduleState()->parkWaitState() == PARKWAIT_IDLE)
2270 switch (moduleState()->parkWaitState())
2276 case PARKWAIT_PARKING:
2277 checkMountParkingStatus();
2280 case PARKWAIT_UNPARK:
2284 case PARKWAIT_UNPARKING:
2285 checkMountParkingStatus();
2289 case PARKWAIT_PARKED:
2290 case PARKWAIT_UNPARKED:
2293 case PARKWAIT_ERROR:
2305 if (moduleState()->startupState() == STARTUP_IDLE
2306 || moduleState()->startupState() == STARTUP_ERROR
2307 || moduleState()->startupState() == STARTUP_COMPLETE)
2311 KSMessageBox::Instance()->disconnect(
this);
2314 moduleState()->setStartupState(STARTUP_IDLE);
2320 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the startup procedure manually?"));
2324 switch (moduleState()->startupState())
2329 case STARTUP_SCRIPT:
2330 scriptProcess().terminate();
2333 case STARTUP_UNPARK_DOME:
2336 case STARTUP_UNPARKING_DOME:
2337 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking dome...";
2341 case STARTUP_UNPARK_MOUNT:
2344 case STARTUP_UNPARKING_MOUNT:
2345 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking mount...";
2349 case STARTUP_UNPARK_CAP:
2352 case STARTUP_UNPARKING_CAP:
2355 case STARTUP_COMPLETE:
2362 moduleState()->setStartupState(STARTUP_IDLE);
2371 if (moduleState()->shutdownState() == SHUTDOWN_IDLE
2372 || moduleState()->shutdownState() == SHUTDOWN_ERROR
2373 || moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
2377 KSMessageBox::Instance()->disconnect(
this);
2379 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2384 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the shutdown procedure manually?"));
2388 switch (moduleState()->shutdownState())
2393 case SHUTDOWN_SCRIPT:
2396 case SHUTDOWN_SCRIPT_RUNNING:
2397 scriptProcess().terminate();
2400 case SHUTDOWN_PARK_DOME:
2403 case SHUTDOWN_PARKING_DOME:
2404 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking dome...";
2408 case SHUTDOWN_PARK_MOUNT:
2411 case SHUTDOWN_PARKING_MOUNT:
2412 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking mount...";
2416 case SHUTDOWN_PARK_CAP:
2417 case SHUTDOWN_PARKING_CAP:
2420 case SHUTDOWN_COMPLETE:
2423 case SHUTDOWN_ERROR:
2427 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2435 moduleState()->setupNextIteration(RUN_NOTHING);
2437 emit schedulerPaused();
2443 for (SchedulerJob * job : moduleState()->jobs())
2446 job->setCompletedCount(0);
2455 auto finished_or_aborted = [](SchedulerJob
const *
const job)
2462 auto neither_scheduled_nor_aborted = [](SchedulerJob
const *
const job)
2470 if (jobs.
isEmpty() || std::all_of(jobs.
begin(), jobs.
end(), neither_scheduled_nor_aborted))
2473 moduleState()->setActiveJob(
nullptr);
2477 else if (std::all_of(jobs.
begin(), jobs.
end(), finished_or_aborted) &&
2478 strategy != ERROR_DONT_RESTART)
2480 appendLogText(
i18n(
"Only aborted jobs left in the scheduler queue after evaluating, rescheduling those."));
2481 std::for_each(jobs.
begin(), jobs.
end(), [](SchedulerJob * job)
2483 if (SCHEDJOB_ABORTED == job->getState())
2484 job->setState(SCHEDJOB_EVALUATION);
2491 SchedulerJob *scheduledJob = getGreedyScheduler()->getScheduledJob();
2495 moduleState()->setActiveJob(
nullptr);
2498 if (activeJob() !=
nullptr && scheduledJob != activeJob())
2501 for (
auto job : m_activeJobs.values())
2503 stopCapturing(job->getOpticalTrain(),
false);
2506 m_activeJobs.clear();
2508 moduleState()->setActiveJob(scheduledJob);
2516 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2520 moduleState()->resetSequenceExecutionCounter();
2528 for (
auto job : moduleState()->jobs())
2532 if (moduleState()->jobs().isEmpty())
2535 if (Options::rememberJobProgress())
2538 moduleState()->calculateDawnDusk();
2540 getGreedyScheduler()->scheduleJobs(moduleState()->jobs(), SchedulerModuleState::getLocalTime(),
2541 moduleState()->capturedFramesCount(),
this);
2545 if (!evaluateOnly && moduleState()->schedulerState() == SCHEDULER_RUNNING)
2550 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos finished evaluating jobs, no job selection required.";
2552 emit jobsUpdated(moduleState()->getJSONJobs());
2557 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2559 if (activeJob() ==
nullptr)
2564 switch (activeJob()->getState())
2580 if (activeJob() ==
nullptr)
2583 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE
2584 || moduleState()->shutdownState() == SHUTDOWN_ERROR)
2590 if (moduleState()->shutdownState() > SHUTDOWN_IDLE)
2593 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
2608 if (
nullptr == activeJob() && moduleState()->checkRepeatSequence())
2617 moduleState()->increaseSequenceExecutionCounter();
2618 appendLogText(
i18n(
"Starting job sequence iteration #%1", moduleState()->sequenceExecutionCounter()));
2624 if (
nullptr == activeJob())
2634 if (moduleState()->startupState() == STARTUP_ERROR)
2642 if ((moduleState()->startupState() == STARTUP_IDLE
2644 || moduleState()->startupState() == STARTUP_SCRIPT)
2660 if (moduleState()->startupState() > STARTUP_SCRIPT
2661 && moduleState()->startupState() < STARTUP_ERROR
2673 emit updateJobTable();
2681 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Get next action...";
2683 switch (activeJob()->getStage())
2685 case SCHEDSTAGE_IDLE:
2686 if (activeJob()->getLightFramesRequired())
2688 if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
2690 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2692 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3485";
2695 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2697 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2710 if (activeJob()->getStepPipeline())
2712 i18n(
"Job '%1' is proceeding directly to capture stage because only calibration frames are pending.",
2713 activeJob()->getName()));
2719 case SCHEDSTAGE_SLEW_COMPLETE:
2720 if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2722 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3514";
2725 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2727 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2733 case SCHEDSTAGE_FOCUS_COMPLETE:
2734 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2736 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2742 case SCHEDSTAGE_ALIGN_COMPLETE:
2743 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING);
2746 case SCHEDSTAGE_RESLEWING_COMPLETE:
2749 if ((activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS) && activeJob()->getInSequenceFocus())
2752 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3544";
2755 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2761 case SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE:
2762 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2768 case SCHEDSTAGE_GUIDING_COMPLETE:
2784 moduleState()->iterationTimer().setSingleShot(
true);
2785 moduleState()->iterationTimer().start(msSleep);
2792 if (moduleState()->startMSecs() == 0)
2793 moduleState()->setStartMSecs(now);
2806 moduleState()->setIterationSetup(
false);
2807 switch (keepTimerState)
2810 changeSleepLabel(
"",
false);
2823 moduleState()->setTimerInterval(-1);
2826 if (!moduleState()->iterationSetup())
2832 moduleState()->setTimerInterval(moduleState()->updatePeriodMs());
2835 return moduleState()->timerInterval();
2840 Q_ASSERT_X(activeJob(), __FUNCTION__,
"Actual current job is required to check job stage");
2844 if (checkJobStageCounter == 0)
2846 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking job stage for" << activeJob()->getName() <<
"startup" <<
2847 activeJob()->getStartupCondition() << activeJob()->getStartupTime().toString() <<
"state" << activeJob()->getState();
2848 if (checkJobStageCounter++ == 30)
2849 checkJobStageCounter = 0;
2852 emit syncGreedyParams();
2853 if (!getGreedyScheduler()->checkJob(moduleState()->leadJobs(), SchedulerModuleState::getLocalTime(), activeJob()))
2860 checkJobStageEpilogue();
2863void SchedulerProcess::checkJobStageEpilogue()
2878 if (!activeJob())
return;
2879 switch (activeJob()->getStage())
2881 case SCHEDSTAGE_IDLE:
2883 emit jobStarted(activeJob()->getName());
2887 case SCHEDSTAGE_ALIGNING:
2889 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(ALIGN_INACTIVITY_TIMEOUT))
2891 QVariant const status = alignInterface()->property(
"status");
2896 if (moduleState()->increaseAlignFailureCount())
2898 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align module timed out. Restarting request...";
2903 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
2909 moduleState()->startCurrentOperationTimer();
2913 case SCHEDSTAGE_CAPTURING:
2915 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(CAPTURE_INACTIVITY_TIMEOUT))
2917 QVariant
const status = captureInterface()->property(
"status");
2922 if (moduleState()->increaseCaptureFailureCount())
2924 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"capture module timed out. Restarting request...";
2929 appendLogText(
i18n(
"Warning: job '%1' capture procedure failed, marking aborted.", activeJob()->getName()));
2934 else moduleState()->startCurrentOperationTimer();
2938 case SCHEDSTAGE_FOCUSING:
2940 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(FOCUS_INACTIVITY_TIMEOUT))
2942 bool success =
true;
2943 foreach (
const QString trainname, m_activeJobs.keys())
2945 QList<QVariant> dbusargs;
2946 dbusargs.
append(trainname);
2947 QDBusReply<Ekos::FocusState> statusReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"status", dbusargs);
2950 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' status request received DBUS error: %2").arg(
2960 Ekos::FocusState focusStatus = statusReply.value();
2961 if (focusStatus == Ekos::FOCUS_IDLE || focusStatus == Ekos::FOCUS_WAITING)
2963 if (moduleState()->increaseFocusFailureCount(trainname))
2965 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus module timed out. Restarting request...";
2973 if (success ==
false)
2975 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
2980 else moduleState()->startCurrentOperationTimer();
2983 case SCHEDSTAGE_GUIDING:
2985 if (moduleState()->getCurrentOperationMsec() > GUIDE_INACTIVITY_TIMEOUT)
2989 if (guideStatus == Ekos::GUIDE_IDLE || guideStatus == Ekos::GUIDE_CONNECTED || guideStatus == Ekos::GUIDE_DISCONNECTED)
2991 if (moduleState()->increaseGuideFailureCount())
2993 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"guide module timed out. Restarting request...";
2998 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3003 else moduleState()->startCurrentOperationTimer();
3007 case SCHEDSTAGE_SLEWING:
3008 case SCHEDSTAGE_RESLEWING:
3011 QVariant
const slewStatus = mountInterface()->property(
"status");
3017 ISD::Mount::Status
const status =
static_cast<ISD::Mount::Status
>(slewStatus.
toInt());
3018 setMountStatus(status);
3022 appendLogText(
i18n(
"Warning: job '%1' lost connection to the mount, attempting to reconnect.", activeJob()->getName()));
3030 case SCHEDSTAGE_SLEW_COMPLETE:
3031 case SCHEDSTAGE_RESLEWING_COMPLETE:
3033 if (moduleState()->domeReady())
3035 QVariant
const isDomeMoving = domeInterface()->property(
"isMoving");
3039 appendLogText(
i18n(
"Warning: job '%1' lost connection to the dome, attempting to reconnect.", activeJob()->getName()));
3045 if (!isDomeMoving.
value<
bool>())
3058 moduleState()->calculateDawnDusk();
3060 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
3072 if (activeJob() == job &&
SCHEDJOB_BUSY == activeJob()->getState())
3075 moduleState()->setActiveJob(job);
3087 else if (0 < SchedulerModuleState::getLocalTime().secsTo(activeJob()->getStartupTime()))
3092 if (job->getCompletionCondition() == FINISH_SEQUENCE && Options::rememberJobProgress())
3093 captureInterface()->setProperty(
"targetName", job->getName());
3095 moduleState()->calculateDawnDusk();
3099 moduleState()->setAutofocusCompleted(job->getOpticalTrain(),
false);
3101 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Executing Job " << activeJob()->getName();
3104 emit jobsUpdated(moduleState()->getJSONJobs());
3106 KSNotification::event(
QLatin1String(
"EkosSchedulerJobStart"),
3107 i18n(
"Ekos job started (%1)", activeJob()->getName()), KSNotification::Scheduler);
3110 moduleState()->setupNextIteration(RUN_JOBCHECK);
3122 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3131 outstream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<
Qt::endl;
3132 outstream <<
"<SchedulerList version='2.1'>" <<
Qt::endl;
3134 outstream <<
"<Profile>" <<
QString(entityXML(strdup(moduleState()->currentProfile().toStdString().c_str()))) <<
3137 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
3138 bool useMosaicInfo = !tiles->sequenceFile().isEmpty();
3142 outstream <<
"<Mosaic>" <<
Qt::endl;
3143 outstream <<
"<Target>" << tiles->targetName() <<
"</Target>" <<
Qt::endl;
3144 outstream <<
"<Group>" << tiles->group() <<
"</Group>" <<
Qt::endl;
3146 QString ccArg, ccValue = tiles->completionCondition(&ccArg);
3147 if (ccValue ==
"FinishSequence")
3148 outstream <<
"<FinishSequence/>" <<
Qt::endl;
3149 else if (ccValue ==
"FinishLoop")
3150 outstream <<
"<FinishLoop/>" <<
Qt::endl;
3151 else if (ccValue ==
"FinishRepeat")
3152 outstream <<
"<FinishRepeat>" << ccArg <<
"</FinishRepeat>" <<
Qt::endl;
3154 outstream <<
"<Sequence>" << tiles->sequenceFile() <<
"</Sequence>" <<
Qt::endl;
3155 outstream <<
"<Directory>" << tiles->outputDirectory() <<
"</Directory>" <<
Qt::endl;
3157 outstream <<
"<FocusEveryN>" << tiles->focusEveryN() <<
"</FocusEveryN>" <<
Qt::endl;
3158 outstream <<
"<AlignEveryN>" << tiles->alignEveryN() <<
"</AlignEveryN>" <<
Qt::endl;
3159 if (tiles->isTrackChecked())
3160 outstream <<
"<TrackChecked/>" <<
Qt::endl;
3161 if (tiles->isFocusChecked())
3162 outstream <<
"<FocusChecked/>" <<
Qt::endl;
3163 if (tiles->isAlignChecked())
3164 outstream <<
"<AlignChecked/>" <<
Qt::endl;
3165 if (tiles->isGuideChecked())
3166 outstream <<
"<GuideChecked/>" <<
Qt::endl;
3167 outstream <<
"<Overlap>" << cLocale.
toString(tiles->overlap()) <<
"</Overlap>" <<
Qt::endl;
3168 outstream <<
"<CenterRA>" << cLocale.
toString(tiles->ra0().Hours()) <<
"</CenterRA>" <<
Qt::endl;
3169 outstream <<
"<CenterDE>" << cLocale.
toString(tiles->dec0().Degrees()) <<
"</CenterDE>" <<
Qt::endl;
3170 outstream <<
"<GridW>" << tiles->gridSize().width() <<
"</GridW>" <<
Qt::endl;
3171 outstream <<
"<GridH>" << tiles->gridSize().height() <<
"</GridH>" <<
Qt::endl;
3172 outstream <<
"<FOVW>" << cLocale.
toString(tiles->mosaicFOV().width()) <<
"</FOVW>" <<
Qt::endl;
3173 outstream <<
"<FOVH>" << cLocale.
toString(tiles->mosaicFOV().height()) <<
"</FOVH>" <<
Qt::endl;
3174 outstream <<
"<CameraFOVW>" << cLocale.
toString(tiles->cameraFOV().width()) <<
"</CameraFOVW>" <<
Qt::endl;
3175 outstream <<
"<CameraFOVH>" << cLocale.
toString(tiles->cameraFOV().height()) <<
"</CameraFOVH>" <<
Qt::endl;
3176 outstream <<
"</Mosaic>" <<
Qt::endl;
3180 for (
auto &job : moduleState()->jobs())
3185 outstream <<
"<JobType lead='" << (job->isLead() ?
"true" :
"false") <<
"'/>" <<
Qt::endl;
3190 outstream <<
"<Coordinates>" <<
Qt::endl;
3193 outstream <<
"</Coordinates>" <<
Qt::endl;
3196 if (! job->getOpticalTrain().
isEmpty())
3197 outstream <<
"<OpticalTrain>" <<
QString(entityXML(strdup(job->getOpticalTrain().
toStdString().c_str()))) <<
3200 if (job->isLead() && job->getFITSFile().
isValid() && job->getFITSFile().
isEmpty() ==
false)
3203 outstream <<
"<PositionAngle>" << job->getPositionAngle() <<
"</PositionAngle>" <<
Qt::endl;
3205 outstream <<
"<Sequence>" << job->getSequenceFile().
toLocalFile() <<
"</Sequence>" <<
Qt::endl;
3207 if (useMosaicInfo && index < tiles->tiles().size())
3209 auto oneTile = tiles->tiles().at(index++);
3210 outstream <<
"<TileCenter>" <<
Qt::endl;
3211 outstream <<
"<X>" << cLocale.
toString(oneTile->center.x()) <<
"</X>" <<
Qt::endl;
3212 outstream <<
"<Y>" << cLocale.
toString(oneTile->center.y()) <<
"</Y>" <<
Qt::endl;
3213 outstream <<
"<Rotation>" << cLocale.
toString(oneTile->rotation) <<
"</Rotation>" <<
Qt::endl;
3214 outstream <<
"</TileCenter>" <<
Qt::endl;
3219 outstream <<
"<StartupCondition>" <<
Qt::endl;
3220 if (job->getFileStartupCondition() == START_ASAP)
3221 outstream <<
"<Condition>ASAP</Condition>" <<
Qt::endl;
3222 else if (job->getFileStartupCondition() == START_AT)
3223 outstream <<
"<Condition value='" << job->getStartAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3225 outstream <<
"</StartupCondition>" <<
Qt::endl;
3227 outstream <<
"<Constraints>" <<
Qt::endl;
3228 if (job->hasMinAltitude())
3229 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinAltitude()) <<
"'>MinimumAltitude</Constraint>" <<
3231 if (job->getMinMoonSeparation() > 0)
3232 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinMoonSeparation()) <<
"'>MoonSeparation</Constraint>"
3234 if (job->getMaxMoonAltitude() < 90)
3235 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMaxMoonAltitude()) <<
"'>MoonMaxAltitude</Constraint>"
3237 if (job->getEnforceTwilight())
3238 outstream <<
"<Constraint>EnforceTwilight</Constraint>" <<
Qt::endl;
3239 if (job->getEnforceArtificialHorizon())
3240 outstream <<
"<Constraint>EnforceArtificialHorizon</Constraint>" <<
Qt::endl;
3241 outstream <<
"</Constraints>" <<
Qt::endl;
3244 outstream <<
"<CompletionCondition>" <<
Qt::endl;
3245 if (job->getCompletionCondition() == FINISH_SEQUENCE)
3246 outstream <<
"<Condition>Sequence</Condition>" <<
Qt::endl;
3247 else if (job->getCompletionCondition() == FINISH_REPEAT)
3248 outstream <<
"<Condition value='" << cLocale.
toString(job->getRepeatsRequired()) <<
"'>Repeat</Condition>" <<
Qt::endl;
3249 else if (job->getCompletionCondition() == FINISH_LOOP)
3250 outstream <<
"<Condition>Loop</Condition>" <<
Qt::endl;
3251 else if (job->getCompletionCondition() == FINISH_AT)
3252 outstream <<
"<Condition value='" << job->getFinishAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3254 outstream <<
"</CompletionCondition>" <<
Qt::endl;
3258 outstream <<
"<Steps>" <<
Qt::endl;
3259 if (job->getStepPipeline() & SchedulerJob::USE_TRACK)
3260 outstream <<
"<Step>Track</Step>" <<
Qt::endl;
3261 if (job->getStepPipeline() & SchedulerJob::USE_FOCUS)
3262 outstream <<
"<Step>Focus</Step>" <<
Qt::endl;
3263 if (job->getStepPipeline() & SchedulerJob::USE_ALIGN)
3264 outstream <<
"<Step>Align</Step>" <<
Qt::endl;
3265 if (job->getStepPipeline() & SchedulerJob::USE_GUIDE)
3266 outstream <<
"<Step>Guide</Step>" <<
Qt::endl;
3267 outstream <<
"</Steps>" <<
Qt::endl;
3272 outstream <<
"<SchedulerAlgorithm value='" << ALGORITHM_GREEDY <<
"'/>" <<
Qt::endl;
3273 outstream <<
"<ErrorHandlingStrategy value='" << Options::errorHandlingStrategy() <<
"'>" <<
Qt::endl;
3274 if (Options::rescheduleErrors())
3275 outstream <<
"<RescheduleErrors />" <<
Qt::endl;
3276 outstream <<
"<delay>" << Options::errorHandlingStrategyDelay() <<
"</delay>" <<
Qt::endl;
3277 outstream <<
"</ErrorHandlingStrategy>" <<
Qt::endl;
3279 outstream <<
"<StartupProcedure>" <<
Qt::endl;
3280 if (moduleState()->startupScriptURL().isEmpty() ==
false)
3281 outstream <<
"<Procedure value='" << moduleState()->startupScriptURL().toString(
QUrl::PreferLocalFile) <<
3282 "'>StartupScript</Procedure>" <<
Qt::endl;
3283 if (Options::schedulerUnparkDome())
3284 outstream <<
"<Procedure>UnparkDome</Procedure>" <<
Qt::endl;
3285 if (Options::schedulerUnparkMount())
3286 outstream <<
"<Procedure>UnparkMount</Procedure>" <<
Qt::endl;
3287 if (Options::schedulerOpenDustCover())
3288 outstream <<
"<Procedure>UnparkCap</Procedure>" <<
Qt::endl;
3289 outstream <<
"</StartupProcedure>" <<
Qt::endl;
3291 outstream <<
"<ShutdownProcedure>" <<
Qt::endl;
3292 if (Options::schedulerWarmCCD())
3293 outstream <<
"<Procedure>WarmCCD</Procedure>" <<
Qt::endl;
3294 if (Options::schedulerCloseDustCover())
3295 outstream <<
"<Procedure>ParkCap</Procedure>" <<
Qt::endl;
3296 if (Options::schedulerParkMount())
3297 outstream <<
"<Procedure>ParkMount</Procedure>" <<
Qt::endl;
3298 if (Options::schedulerParkDome())
3299 outstream <<
"<Procedure>ParkDome</Procedure>" <<
Qt::endl;
3300 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
3301 outstream <<
"<Procedure value='" << moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile) <<
3302 "'>schedulerStartupScript</Procedure>" <<
3304 outstream <<
"</ShutdownProcedure>" <<
Qt::endl;
3306 outstream <<
"</SchedulerList>" <<
Qt::endl;
3310 moduleState()->setDirty(
false);
3314void SchedulerProcess::checkAlignment(
const QVariantMap &metadata,
const QString &trainname)
3317 if (activeJob() ==
nullptr || (activeJob()->getOpticalTrain() !=
"" && activeJob()->getOpticalTrain() != trainname))
3319 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Ignoring metadata from train =" << trainname <<
"for alignment check.";
3323 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN &&
3324 metadata[
"type"].toInt() == FRAME_LIGHT &&
3325 Options::alignCheckFrequency() > 0 &&
3326 moduleState()->increaseSolverIteration() >= Options::alignCheckFrequency())
3328 moduleState()->resetSolverIteration();
3330 auto filename = metadata[
"filename"].toString();
3331 auto exposure = metadata[
"exposure"].toDouble();
3333 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking alignment on train =" << trainname <<
"for" << filename;
3335 constexpr double minSolverSeconds = 5.0;
3336 double solverTimeout = std::max(exposure - 2, minSolverSeconds);
3337 if (solverTimeout >= minSolverSeconds)
3339 auto profiles = getDefaultAlignOptionsProfiles();
3341 SSolver::Parameters parameters;
3346 parameters = profiles.at(Options::solveOptionsProfile());
3348 catch (std::out_of_range
const &)
3350 parameters = profiles[0];
3354 parameters.search_radius = parameters.search_radius * 2;
3359 auto width = metadata[
"width"].toUInt() / (metadata[
"binx"].isValid() ? metadata[
"binx"].toUInt() : 1);
3360 auto height = metadata[
"height"].toUInt() / (metadata[
"biny"].isValid() ? metadata[
"biny"].toUInt() : 1);
3362 auto lowScale = Options::astrometryImageScaleLow();
3363 auto highScale = Options::astrometryImageScaleHigh();
3366 if (Options::astrometryImageScaleUnits() == SSolver::DEG_WIDTH)
3368 lowScale = (lowScale * 3600) / std::max(width, height);
3369 highScale = (highScale * 3600) / std::min(width, height);
3371 else if (Options::astrometryImageScaleUnits() == SSolver::ARCMIN_WIDTH)
3373 lowScale = (lowScale * 60) / std::max(width, height);
3374 highScale = (highScale * 60) / std::min(width, height);
3377 m_Solver->useScale(Options::astrometryUseImageScale(), lowScale, highScale);
3378 m_Solver->usePosition(Options::astrometryUsePosition(), activeJob()->getTargetCoords().ra().Degrees(),
3379 activeJob()->getTargetCoords().
dec().Degrees());
3380 m_Solver->setHealpix(moduleState()->indexToUse(), moduleState()->healpixToUse());
3381 m_Solver->runSolver(filename);
3386void SchedulerProcess::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
3388 disconnect(m_Solver.get(), &SolverUtils::done,
this, &Ekos::SchedulerProcess::solverDone);
3393 QString healpixString =
"";
3394 if (moduleState()->indexToUse() != -1 || moduleState()->healpixToUse() != -1)
3395 healpixString = QString(
"Healpix %1 Index %2").
arg(moduleState()->healpixToUse()).
arg(moduleState()->indexToUse());
3397 if (timedOut || !success)
3400 moduleState()->setIndexToUse(-1);
3401 moduleState()->setHealpixToUse(-1);
3407 m_Solver->getSolutionHealpix(&index, &healpix);
3408 moduleState()->setIndexToUse(index);
3409 moduleState()->setHealpixToUse(healpix);
3413 appendLogText(
i18n(
"Solver timed out: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3415 appendLogText(
i18n(
"Solver failed: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3418 const double ra = solution.ra;
3419 const double dec = solution.dec;
3421 const auto target = activeJob()->getTargetCoords();
3423 SkyPoint alignCoord;
3424 alignCoord.
setRA0(ra / 15.0);
3428 const double diffRa = (alignCoord.
ra().deltaAngle(target.ra())).Degrees() * 3600;
3429 const double diffDec = (alignCoord.
dec().deltaAngle(target.dec())).Degrees() * 3600;
3432 const double diffTotal = hypot(diffRa, diffDec);
3436 qCDebug(KSTARS_EKOS_SCHEDULER) <<
3437 QString(
"Target Distance: %1\" Target (RA: %2 DE: %3) Current (RA: %4 DE: %5) %6 solved in %7s")
3438 .arg(QString(
"%L1").arg(diffTotal, 0,
'f', 0),
3439 target.ra().toDMSString(),
3440 target.dec().toDMSString(),
3444 QString(
"%L1").arg(elapsedSeconds, 0,
'f', 2));
3445 emit targetDistance(diffTotal);
3448 if (diffTotal / 60 > Options::alignCheckThreshold())
3460 SchedulerState
const old_state = moduleState()->schedulerState();
3461 moduleState()->setSchedulerState(SCHEDULER_LOADING);
3468 QString message =
i18n(
"Unable to open file %1", fileURL);
3469 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3470 moduleState()->setSchedulerState(old_state);
3474 LilXML *xmlParser = newLilXML();
3475 char errmsg[MAXRBUF];
3476 XMLEle *root =
nullptr;
3477 XMLEle *ep =
nullptr;
3478 XMLEle *subEP =
nullptr;
3485 SchedulerJob *lastLead =
nullptr;
3488 const QStringList allTrainNames = OpticalTrainManager::Instance()->getTrainNames();
3493 root = readXMLEle(xmlParser, c, errmsg);
3497 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
3499 const char *tag = tagXMLEle(ep);
3500 if (!strcmp(tag,
"Job"))
3502 SchedulerJob *newJob = SchedulerUtils::createJob(ep, lastLead);
3504 if (newJob->isLead())
3508 remainingTrainNames = allTrainNames;
3511 const QString trainname = newJob->getOpticalTrain();
3512 bool allowedName = (newJob->isLead() && trainname.
isEmpty()) || allTrainNames.
contains(trainname);
3513 bool availableName = (newJob->isLead() && trainname.
isEmpty()) || !remainingTrainNames.
isEmpty();
3515 if (!allowedName && availableName)
3518 i18n(
"Warning: train name is empty, selecting \"%1\".", remainingTrainNames.
first()) :
3519 i18n(
"Warning: train name %2 does not exist, selecting \"%1\".", remainingTrainNames.
first(), trainname);
3525 newJob->setOpticalTrain(remainingTrainNames.
first());
3528 else if (!availableName)
3530 const QString message =
i18n(
"Warning: no available train name for scheduler job, select the optical train name manually.");
3538 emit addJob(newJob);
3540 else if (!strcmp(tag,
"Mosaic"))
3543 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
3544 tiles->fromXML(fileURL);
3546 else if (!strcmp(tag,
"Profile"))
3548 moduleState()->setCurrentProfile(pcdataXMLEle(ep));
3551 else if (!strcmp(tag,
"SchedulerAlgorithm"))
3553 int algIndex = cLocale.
toInt(findXMLAttValu(ep,
"value"));
3554 if (algIndex != ALGORITHM_GREEDY)
3555 appendLogText(
i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
3557 else if (!strcmp(tag,
"ErrorHandlingStrategy"))
3562 subEP = findXMLEle(ep,
"delay");
3565 Options::setErrorHandlingStrategyDelay(cLocale.
toInt(pcdataXMLEle(subEP)));
3567 subEP = findXMLEle(ep,
"RescheduleErrors");
3568 Options::setRescheduleErrors(subEP !=
nullptr);
3570 else if (!strcmp(tag,
"StartupProcedure"))
3573 Options::setSchedulerUnparkDome(
false);
3574 Options::setSchedulerUnparkMount(
false);
3575 Options::setSchedulerOpenDustCover(
false);
3577 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3579 const char *proc = pcdataXMLEle(procedure);
3581 if (!strcmp(proc,
"StartupScript"))
3583 moduleState()->setStartupScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3585 else if (!strcmp(proc,
"UnparkDome"))
3586 Options::setSchedulerUnparkDome(
true);
3587 else if (!strcmp(proc,
"UnparkMount"))
3588 Options::setSchedulerUnparkMount(
true);
3589 else if (!strcmp(proc,
"UnparkCap"))
3590 Options::setSchedulerOpenDustCover(
true);
3593 else if (!strcmp(tag,
"ShutdownProcedure"))
3596 Options::setSchedulerWarmCCD(
false);
3597 Options::setSchedulerParkDome(
false);
3598 Options::setSchedulerParkMount(
false);
3599 Options::setSchedulerCloseDustCover(
false);
3601 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3603 const char *proc = pcdataXMLEle(procedure);
3605 if (!strcmp(proc,
"ShutdownScript"))
3607 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3609 else if (!strcmp(proc,
"WarmCCD"))
3610 Options::setSchedulerWarmCCD(
true);
3611 else if (!strcmp(proc,
"ParkDome"))
3612 Options::setSchedulerParkDome(
true);
3613 else if (!strcmp(proc,
"ParkMount"))
3614 Options::setSchedulerParkMount(
true);
3615 else if (!strcmp(proc,
"ParkCap"))
3616 Options::setSchedulerCloseDustCover(
true);
3621 emit syncGUIToGeneralSettings();
3626 delLilXML(xmlParser);
3627 moduleState()->setSchedulerState(old_state);
3632 moduleState()->setDirty(
false);
3633 delLilXML(xmlParser);
3634 emit updateSchedulerURL(fileURL);
3636 moduleState()->setSchedulerState(old_state);
3646 int const max_log_count = 2000;
3647 if (moduleState()->logText().size() > max_log_count)
3648 moduleState()->logText().removeLast();
3650 moduleState()->logText().prepend(
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
3651 SchedulerModuleState::getLocalTime().toString(
"yyyy-MM-ddThh:mm:ss"), logentry));
3653 qCInfo(KSTARS_EKOS_SCHEDULER) << logentry;
3655 emit newLog(logentry);
3660 moduleState()->logText().clear();
3664void SchedulerProcess::setAlignStatus(
AlignState status)
3666 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3669 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align State" << Ekos::getAlignStatusString(status);
3674 QDateTime const now = SchedulerModuleState::getLocalTime();
3675 if (now < activeJob()->getStartupTime())
3679 if (activeJob()->getStage() == SCHEDSTAGE_ALIGNING)
3685 moduleState()->resetAlignFailureCount();
3687 moduleState()->updateJobStage(SCHEDSTAGE_ALIGN_COMPLETE);
3690 if (activeJob()->getFITSFile().isEmpty() ==
false)
3696 activeJob()->setTargetCoords(
dms(values[0] * 15.0),
dms(values[1]), KStarsData::Instance()->ut().djd());
3703 appendLogText(
i18n(
"Warning: job '%1' alignment failed.", activeJob()->getName()));
3705 if (moduleState()->increaseAlignFailureCount())
3707 if (Options::resetMountModelOnAlignFail() && moduleState()->maxFailureAttempts() - 1 < moduleState()->alignFailureCount())
3709 appendLogText(
i18n(
"Warning: job '%1' forcing mount model reset after failing alignment #%2.", activeJob()->getName(),
3710 moduleState()->alignFailureCount()));
3713 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3718 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
3727void SchedulerProcess::setGuideStatus(GuideState status)
3729 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3732 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Guide State" << Ekos::getGuideStatusString(status);
3737 QDateTime
const now = SchedulerModuleState::getLocalTime();
3738 if (now < activeJob()->getStartupTime())
3742 if (activeJob()->getStage() == SCHEDSTAGE_GUIDING)
3744 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Calibration & Guide stage...";
3747 if (status == Ekos::GUIDE_GUIDING)
3749 appendLogText(
i18n(
"Job '%1' guiding is in progress.", activeJob()->getName()));
3750 moduleState()->resetGuideFailureCount();
3752 moduleState()->cancelGuidingTimer();
3754 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
3757 else if (status == Ekos::GUIDE_CALIBRATION_ERROR ||
3758 status == Ekos::GUIDE_ABORTED)
3760 if (status == Ekos::GUIDE_ABORTED)
3761 appendLogText(
i18n(
"Warning: job '%1' guiding failed.", activeJob()->getName()));
3763 appendLogText(
i18n(
"Warning: job '%1' calibration failed.", activeJob()->getName()));
3769 if (moduleState()->isGuidingTimerActive())
3772 if (moduleState()->increaseGuideFailureCount())
3774 if (status == Ekos::GUIDE_CALIBRATION_ERROR &&
3775 Options::realignAfterCalibrationFailure())
3777 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3782 appendLogText(
i18n(
"Job '%1' is guiding, guiding procedure will be restarted in %2 seconds.", activeJob()->getName(),
3783 (RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount()) / 1000));
3784 moduleState()->startGuidingTimer(RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount());
3789 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3798void SchedulerProcess::setCaptureStatus(
CaptureState status,
const QString &trainname)
3800 if (activeJob() ==
nullptr || !m_activeJobs.contains(trainname))
3803 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Capture State" << Ekos::getCaptureStatusString(status) <<
"train =" << trainname;
3805 SchedulerJob *job = m_activeJobs[trainname];
3810 QDateTime
const now = SchedulerModuleState::getLocalTime();
3811 if (now < job->getStartupTime())
3815 if (job->getStage() == SCHEDSTAGE_CAPTURING)
3827 const SkyPoint targetCoords = activeJob()->getTargetCoords();
3828 QList<QVariant> targetArgs;
3830 alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords", targetArgs);
3835 appendLogText(
i18n(
"[%2] Warning: job '%1' failed to capture target.", job->getName(), trainname));
3840 if (moduleState()->increaseCaptureFailureCount())
3845 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
3849 if (gStatus == Ekos::GUIDE_ABORTED ||
3850 gStatus == Ekos::GUIDE_CALIBRATION_ERROR ||
3851 gStatus == GUIDE_DITHERING_ERROR)
3853 appendLogText(
i18n(
"[%2] Job '%1' is capturing, is restarting its guiding procedure (attempt #%3 of %4).",
3854 activeJob()->getName(), trainname,
3855 moduleState()->captureFailureCount(), moduleState()->maxFailureAttempts()));
3862 appendLogText(
i18n(
"Warning: job '%1' failed its capture procedure, restarting capture.", activeJob()->getName()));
3868 appendLogText(
i18n(
"[%2] Warning: job '%1' failed its capture procedure, marking aborted.", job->getName(), trainname));
3871 stopCapturing(
"",
true);
3878 if (job->leadJob()->getStage() == SCHEDSTAGE_CAPTURING)
3881 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted, is restarting.", job->getName(), trainname));
3883 startSingleCapture(job,
true);
3887 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted.", job->getName(), trainname));
3894 KSNotification::event(QLatin1String(
"EkosScheduledImagingFinished"),
3895 i18n(
"[%2] Job (%1) - Capture finished", job->getName(), trainname), KSNotification::Scheduler);
3907 if (job->getCompletionCondition() == FINISH_LOOP ||
3908 (job->getCompletionCondition() == FINISH_REPEAT && job->getRepeatsRemaining() > 0))
3911 startSingleCapture(job,
false);
3917 job->setStage(SCHEDSTAGE_COMPLETE);
3925 if (Options::rememberJobProgress())
3929 for (
const auto &job : moduleState()->jobs())
3930 SchedulerUtils::estimateJobTime(job, moduleState()->capturedFramesCount(),
this);
3934 activeJob()->setCompletedCount(job->getCompletedCount() + 1);
3938 moduleState()->resetCaptureFailureCount();
3943void SchedulerProcess::setFocusStatus(FocusState status,
const QString &trainname)
3945 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3948 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Train " << trainname <<
"focus state" << Ekos::getFocusStatusString(status);
3951 if (m_activeJobs.contains(trainname) ==
false)
3954 SchedulerJob *currentJob = m_activeJobs[trainname];
3959 QDateTime
const now = SchedulerModuleState::getLocalTime();
3960 if (now < activeJob()->getStartupTime())
3964 if (activeJob()->getStage() == SCHEDSTAGE_FOCUSING)
3967 if (status == Ekos::FOCUS_COMPLETE)
3969 appendLogText(
i18n(
"Job '%1' focusing train '%2' is complete.", currentJob->getName(), trainname));
3971 moduleState()->setAutofocusCompleted(trainname,
true);
3973 if (moduleState()->autofocusCompleted())
3975 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
3979 else if (status == Ekos::FOCUS_FAILED || status == Ekos::FOCUS_ABORTED)
3981 appendLogText(
i18n(
"Warning: job '%1' focusing failed.", currentJob->getName()));
3983 if (moduleState()->increaseFocusFailureCount(trainname))
3985 appendLogText(
i18n(
"Job '%1' for train '%2' is restarting its focusing procedure.", currentJob->getName(), trainname));
3990 appendLogText(
i18n(
"Warning: job '%1' on train '%2' focusing procedure failed, marking aborted.", activeJob()->getName(),
4000void SchedulerProcess::setMountStatus(ISD::Mount::Status status)
4002 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
4005 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount State changed to" << status;
4009 if (
static_cast<QDateTime const
>(SchedulerModuleState::getLocalTime()) < activeJob()->getStartupTime())
4012 switch (activeJob()->getStage())
4014 case SCHEDSTAGE_SLEWING:
4016 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Slewing stage...";
4018 if (status == ISD::Mount::MOUNT_TRACKING)
4021 moduleState()->updateJobStage(SCHEDSTAGE_SLEW_COMPLETE);
4024 else if (status == ISD::Mount::MOUNT_ERROR)
4026 appendLogText(
i18n(
"Warning: job '%1' slew failed, marking terminated due to errors.", activeJob()->getName()));
4030 else if (status == ISD::Mount::MOUNT_IDLE)
4032 appendLogText(
i18n(
"Warning: job '%1' found not slewing, restarting.", activeJob()->getName()));
4033 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
4039 case SCHEDSTAGE_RESLEWING:
4041 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Re-slewing stage...";
4043 if (status == ISD::Mount::MOUNT_TRACKING)
4045 appendLogText(
i18n(
"Job '%1' repositioning is complete.", activeJob()->getName()));
4046 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING_COMPLETE);
4049 else if (status == ISD::Mount::MOUNT_ERROR)
4051 appendLogText(
i18n(
"Warning: job '%1' repositioning failed, marking terminated due to errors.", activeJob()->getName()));
4055 else if (status == ISD::Mount::MOUNT_IDLE)
4057 appendLogText(
i18n(
"Warning: job '%1' found not repositioning, restarting.", activeJob()->getName()));
4058 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
4066 case SCHEDSTAGE_FOCUSING:
4067 case SCHEDSTAGE_ALIGNING:
4068 case SCHEDSTAGE_GUIDING:
4069 if (status == ISD::Mount::MOUNT_PARKED)
4071 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
4078 case SCHEDSTAGE_CAPTURING:
4079 if (status == ISD::Mount::MOUNT_PARKED && activeJob() && activeJob()->getLightFramesRequired()
4080 && activeJob()->getCalibrationMountPark() ==
false)
4082 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
4092void SchedulerProcess::setWeatherStatus(ISD::Weather::Status status)
4094 ISD::Weather::Status newStatus = status;
4096 if (newStatus == moduleState()->weatherStatus())
4099 ISD::Weather::Status oldStatus = moduleState()->weatherStatus();
4100 moduleState()->setWeatherStatus(newStatus);
4103 if (moduleState()->preemptiveShutdown() &&
4104 oldStatus != ISD::Weather::WEATHER_OK &&
4105 newStatus == ISD::Weather::WEATHER_OK)
4108 moduleState()->setWeatherGracePeriodActive(
false);
4112 else if (activeJob() && Options::schedulerWeather() && (newStatus == ISD::Weather::WEATHER_ALERT &&
4113 moduleState()->schedulerState() != Ekos::SCHEDULER_IDLE &&
4114 moduleState()->schedulerState() != Ekos::SCHEDULER_SHUTDOWN))
4116 appendLogText(
i18n(
"Weather alert detected. Starting soft shutdown procedure."));
4127 QDateTime wakeupTime = SchedulerModuleState::getLocalTime().addSecs(Options::schedulerWeatherGracePeriod() * 60);
4128 moduleState()->setWeatherGracePeriodActive(
true);
4129 moduleState()->enablePreemptiveShutdown(wakeupTime);
4131 appendLogText(
i18n(
"Observatory scheduled for soft shutdown until weather improves or until %1.",
4135 emit schedulerSleeping(
true,
true);
4140 emit newWeatherStatus(status);
4143void SchedulerProcess::checkStartupProcedure()
4149void SchedulerProcess::checkShutdownProcedure()
4154 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
4158 if (Options::stopEkosAfterShutdown())
4161 else if (moduleState()->shutdownState() == SHUTDOWN_ERROR)
4164 moduleState()->setShutdownState(SHUTDOWN_IDLE);
4173void SchedulerProcess::parkCap()
4175 if (capInterface().isNull())
4178 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4182 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4183 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4185 if (parkingStatus.
isValid() ==
false)
4187 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4188 mountInterface()->lastError().
type());
4190 parkingStatus = ISD::PARK_ERROR;
4193 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4195 if (status != ISD::PARK_PARKED)
4197 moduleState()->setShutdownState(SHUTDOWN_PARKING_CAP);
4198 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking dust cap...";
4202 moduleState()->startCurrentOperationTimer();
4207 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
4211void SchedulerProcess::unParkCap()
4213 if (capInterface().isNull())
4215 appendLogText(
i18n(
"Dust cover unpark requested but no dust covers detected."));
4216 moduleState()->setStartupState(STARTUP_ERROR);
4220 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4221 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4223 if (parkingStatus.
isValid() ==
false)
4225 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4226 mountInterface()->lastError().
type());
4228 parkingStatus = ISD::PARK_ERROR;
4231 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4233 if (status != ISD::PARK_UNPARKED)
4235 moduleState()->setStartupState(STARTUP_UNPARKING_CAP);
4239 moduleState()->startCurrentOperationTimer();
4244 moduleState()->setStartupState(STARTUP_COMPLETE);
4248void SchedulerProcess::parkMount()
4250 if (mountInterface().isNull())
4253 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4257 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4258 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4260 if (parkingStatus.
isValid() ==
false)
4262 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4263 mountInterface()->lastError().
type());
4265 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4268 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4272 case ISD::PARK_PARKED:
4273 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4274 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
4276 moduleState()->setParkWaitState(PARKWAIT_PARKED);
4280 case ISD::PARK_UNPARKING:
4286 case ISD::PARK_ERROR:
4287 case ISD::PARK_UNKNOWN:
4288 case ISD::PARK_UNPARKED:
4290 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking mount...";
4291 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"park");
4295 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount park request received DBUS error: %1").arg(
4298 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4300 else moduleState()->startCurrentOperationTimer();
4304 case ISD::PARK_PARKING:
4306 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4307 moduleState()->setShutdownState(SHUTDOWN_PARKING_MOUNT);
4309 moduleState()->setParkWaitState(PARKWAIT_PARKING);
4320void SchedulerProcess::unParkMount()
4322 if (mountInterface().isNull())
4325 moduleState()->setStartupState(STARTUP_ERROR);
4329 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4330 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4332 if (parkingStatus.
isValid() ==
false)
4334 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4335 mountInterface()->lastError().
type());
4337 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4340 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4345 case ISD::PARK_UNPARKED:
4346 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4347 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
4349 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
4354 case ISD::PARK_PARKING:
4360 case ISD::PARK_ERROR:
4361 case ISD::PARK_UNKNOWN:
4362 case ISD::PARK_PARKED:
4364 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"unpark");
4368 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount unpark request received DBUS error: %1").arg(
4371 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4373 else moduleState()->startCurrentOperationTimer();
4378 case ISD::PARK_UNPARKING:
4379 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4380 moduleState()->setStartupState(STARTUP_UNPARKING_MOUNT);
4382 moduleState()->setParkWaitState(PARKWAIT_UNPARKING);
4383 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
4394 QVariant var = mountInterface()->property(
"equatorialCoords");
4397 if (var.isValid() ==
false || var.canConvert<
QList<double>>() ==
false)
4399 qCCritical(KSTARS_EKOS_SCHEDULER) <<
"Warning: reading equatorial coordinates received an unexpected value:" << var;
4404 if (coords.
size() != 2)
4406 qCCritical(KSTARS_EKOS_SCHEDULER) <<
"Warning: reading equatorial coordinates received" << coords.
size() <<
4407 "instead of 2 values: " << coords;
4411 return SkyPoint(coords[0], coords[1]);
4416 if (mountInterface().isNull())
4420 QVariant canPark = mountInterface()->property(
"canPark");
4421 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount can park:" << (!canPark.
isValid() ?
"invalid" : (canPark.
toBool() ?
"T" :
"F"));
4423 if (canPark.
isValid() ==
false)
4425 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount canPark request received DBUS error: %1").
arg(
4426 mountInterface()->lastError().type());
4430 else if (canPark.
toBool() ==
true)
4434 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4435 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4437 if (parkingStatus.
isValid() ==
false)
4439 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parking status property is invalid %1.").
arg(
4440 mountInterface()->lastError().type());
4446 switch (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()))
4450 case ISD::PARK_PARKED:
4465void SchedulerProcess::parkDome()
4468 if (domeInterface().isNull())
4471 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4477 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4478 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4480 if (parkingStatus.
isValid() ==
false)
4482 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4483 mountInterface()->lastError().type());
4485 parkingStatus = ISD::PARK_ERROR;
4488 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4489 if (status != ISD::PARK_PARKED)
4491 moduleState()->setShutdownState(SHUTDOWN_PARKING_DOME);
4495 moduleState()->startCurrentOperationTimer();
4500 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
4504void SchedulerProcess::unParkDome()
4507 if (domeInterface().isNull())
4510 moduleState()->setStartupState(STARTUP_ERROR);
4514 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4515 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4517 if (parkingStatus.
isValid() ==
false)
4519 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
4520 mountInterface()->lastError().
type());
4522 parkingStatus = ISD::PARK_ERROR;
4525 if (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()) != ISD::PARK_UNPARKED)
4527 moduleState()->setStartupState(STARTUP_UNPARKING_DOME);
4531 moduleState()->startCurrentOperationTimer();
4536 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
4542 QVariant guideStatus = guideInterface()->property(
"status");
4543 Ekos::GuideState gStatus =
static_cast<Ekos::GuideState
>(guideStatus.
toInt());
4548const QString &SchedulerProcess::profile()
const
4550 return moduleState()->currentProfile();
4553void SchedulerProcess::setProfile(
const QString &newProfile)
4555 moduleState()->setCurrentProfile(newProfile);
4558QString SchedulerProcess::currentJobName()
4560 auto job = moduleState()->activeJob();
4561 return ( job !=
nullptr ? job->getName() : QString() );
4564QString SchedulerProcess::currentJobJson()
4566 auto job = moduleState()->activeJob();
4567 if( job !=
nullptr )
4569 return QString( QJsonDocument( job->toJson() ).toJson() );
4577QString SchedulerProcess::jsonJobs()
4579 return QString( QJsonDocument( moduleState()->getJSONJobs() ).toJson() );
4582QStringList SchedulerProcess::logText()
4584 return moduleState()->logText();
4589 if (domeInterface().isNull())
4592 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4593 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4595 if (parkingStatus.
isValid() ==
false)
4597 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4598 mountInterface()->lastError().type());
4600 parkingStatus = ISD::PARK_ERROR;
4603 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4605 return status == ISD::PARK_PARKED;
4608void SchedulerProcess::simClockScaleChanged(
float newScale)
4610 if (moduleState()->currentlySleeping())
4613 (moduleState()->iterationTimer().remainingTime())
4614 * KStarsData::Instance()->clock()->scale()
4616 appendLogText(
i18n(
"Sleeping for %1 on simulation clock update until next observation job is ready...",
4617 remainingTimeMs.
toString(
"hh:mm:ss")));
4618 moduleState()->iterationTimer().stop();
4623void SchedulerProcess::simClockTimeChanged()
4625 moduleState()->calculateDawnDusk();
4628 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
4632void SchedulerProcess::setINDICommunicationStatus(CommunicationStatus status)
4634 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler INDI status is" << status;
4636 moduleState()->setIndiCommunicationStatus(status);
4639void SchedulerProcess::setEkosCommunicationStatus(CommunicationStatus status)
4641 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler Ekos status is" << status;
4643 moduleState()->setEkosCommunicationStatus(status);
4648void SchedulerProcess::checkInterfaceReady(QDBusInterface * iface)
4650 if (iface == mountInterface())
4653 moduleState()->setMountReady(
true);
4655 else if (iface == capInterface())
4658 moduleState()->setCapReady(
true);
4660 else if (iface == observatoryInterface())
4662 QVariant status = observatoryInterface()->property(
"status");
4663 if (status.isValid())
4664 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4666 else if (iface == weatherInterface())
4668 QVariant status = weatherInterface()->property(
"status");
4669 if (status.isValid())
4670 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4672 else if (iface == domeInterface())
4675 moduleState()->setDomeReady(
true);
4677 else if (iface == captureInterface())
4680 moduleState()->setCaptureReady(
true);
4683 emit interfaceReady(iface);
4686void SchedulerProcess::registerNewModule(
const QString &name)
4688 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Registering new Module (" <<
name <<
")";
4690 if (name ==
"Focus")
4692 delete focusInterface();
4693 setFocusInterface(
new QDBusInterface(kstarsInterfaceString, focusPathString, focusInterfaceString,
4695 connect(focusInterface(), SIGNAL(newStatus(Ekos::FocusState,
const QString)),
this,
4698 else if (name ==
"Capture")
4700 delete captureInterface();
4701 setCaptureInterface(
new QDBusInterface(kstarsInterfaceString, capturePathString, captureInterfaceString,
4704 connect(captureInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4707 connect(captureInterface(), SIGNAL(captureComplete(QVariantMap,
const QString)),
this, SLOT(checkAlignment(QVariantMap,
4710 checkInterfaceReady(captureInterface());
4712 else if (name ==
"Mount")
4714 delete mountInterface();
4715 setMountInterface(
new QDBusInterface(kstarsInterfaceString, mountPathString, mountInterfaceString,
4718 connect(mountInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4719 connect(mountInterface(), SIGNAL(newStatus(ISD::Mount::Status)),
this, SLOT(setMountStatus(ISD::Mount::Status)),
4722 checkInterfaceReady(mountInterface());
4724 else if (name ==
"Align")
4726 delete alignInterface();
4727 setAlignInterface(
new QDBusInterface(kstarsInterfaceString, alignPathString, alignInterfaceString,
4732 else if (name ==
"Guide")
4734 delete guideInterface();
4735 setGuideInterface(
new QDBusInterface(kstarsInterfaceString, guidePathString, guideInterfaceString,
4737 connect(guideInterface(), SIGNAL(newStatus(Ekos::GuideState)),
this,
4740 else if (name ==
"Observatory")
4742 delete observatoryInterface();
4743 setObservatoryInterface(
new QDBusInterface(kstarsInterfaceString, observatoryPathString, observatoryInterfaceString,
4745 connect(observatoryInterface(), SIGNAL(newStatus(ISD::Weather::Status)),
this,
4747 checkInterfaceReady(observatoryInterface());
4751void SchedulerProcess::registerNewDevice(
const QString &name,
int interface)
4755 if (interface & INDI::BaseDevice::DOME_INTERFACE)
4757 QList<QVariant> dbusargs;
4758 dbusargs.
append(INDI::BaseDevice::DOME_INTERFACE);
4759 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4764 setDomePathString(paths.value().last());
4765 delete domeInterface();
4766 setDomeInterface(
new QDBusInterface(kstarsInterfaceString, domePathString,
4767 domeInterfaceString,
4769 connect(domeInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4770 checkInterfaceReady(domeInterface());
4795 if (interface & INDI::BaseDevice::DUSTCAP_INTERFACE)
4797 QList<QVariant> dbusargs;
4798 dbusargs.
append(INDI::BaseDevice::DUSTCAP_INTERFACE);
4799 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4804 setDustCapPathString(paths.value().last());
4805 delete capInterface();
4806 setCapInterface(
new QDBusInterface(kstarsInterfaceString, dustCapPathString,
4807 dustCapInterfaceString,
4809 connect(capInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4810 checkInterfaceReady(capInterface());
4817 XMLEle *ep =
nullptr;
4818 XMLEle *subEP =
nullptr;
4820 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
4822 if (!strcmp(tagXMLEle(ep),
"Job"))
4824 for (subEP = nextXMLEle(ep, 1); subEP !=
nullptr; subEP = nextXMLEle(ep, 0))
4826 if (!strcmp(tagXMLEle(subEP),
"TargetName"))
4831 else if (!strcmp(tagXMLEle(subEP),
"FITSDirectory"))
4844 if (outputFile ==
nullptr)
4846 QString message =
i18n(
"Unable to write to file %1", filename);
4847 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
4851 fprintf(outputFile,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4852 prXMLEle(outputFile, root, 0);
4866 KSNotification::sorry(
i18n(
"Unable to open file %1", sFile.
fileName()),
4867 i18n(
"Could Not Open File"));
4871 LilXML *xmlParser = newLilXML();
4872 char errmsg[MAXRBUF];
4873 XMLEle *root =
nullptr;
4878 root = readXMLEle(xmlParser, c, errmsg);
4884 delLilXML(xmlParser);
4889void SchedulerProcess::checkProcessExit(
int exitCode)
4895 if (moduleState()->startupState() == STARTUP_SCRIPT)
4896 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
4897 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4898 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
4903 if (moduleState()->startupState() == STARTUP_SCRIPT)
4906 moduleState()->setStartupState(STARTUP_ERROR);
4908 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4911 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4916void SchedulerProcess::readProcessOutput()
4918 appendLogText(scriptProcess().readAllStandardOutput().simplified());
4921bool SchedulerProcess::canCountCaptures(
const SchedulerJob &job)
4923 QList<QSharedPointer<SequenceJob>> seqjobs;
4924 bool hasAutoFocus =
false;
4925 SchedulerJob tempJob = job;
4926 if (SchedulerUtils::loadSequenceQueue(tempJob.getSequenceFile().toLocalFile(), &tempJob, seqjobs, hasAutoFocus,
4930 for (
auto oneSeqJob : seqjobs)
4932 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
4946 forced |= std::any_of(moduleState()->jobs().begin(),
4947 moduleState()->jobs().end(), [](SchedulerJob * oneJob) ->
bool
4954 moduleState()->capturedFramesCount().clear();
4957 for (SchedulerJob *oneJob : moduleState()->jobs())
4964 bool hasAutoFocus =
false;
4968 if (SchedulerUtils::loadSequenceQueue(oneJob->getSequenceFile().
toLocalFile(), oneJob, seqjobs, hasAutoFocus,
4971 appendLogText(
i18n(
"Warning: job '%1' has inaccessible sequence '%2', marking invalid.", oneJob->getName(),
4977 oneJob->clearProgress();
4979 for (
auto oneSeqJob : seqjobs)
4983 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
4987 QString const signature = oneSeqJob->getSignature();
4995 const int count = newFramesCount.
constFind(signature).value();
4996 newJobFramesCount[signature] = count;
4997 oneJob->addProgress(count, oneSeqJob);
5003 CapturedFramesMap::const_iterator
const earlierRunIterator =
5004 moduleState()->capturedFramesCount().constFind(signature);
5006 if (moduleState()->capturedFramesCount().constEnd() != earlierRunIterator)
5008 count = earlierRunIterator.value();
5011 count = PlaceholderPath::getCompletedFiles(signature);
5013 newFramesCount[signature] = count;
5014 newJobFramesCount[signature] = count;
5015 oneJob->addProgress(count, oneSeqJob);
5019 SchedulerUtils::updateLightFramesRequired(oneJob, seqjobs, newFramesCount);
5022 moduleState()->setCapturedFramesCount(newFramesCount);
5025 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Frame map summary:";
5026 CapturedFramesMap::const_iterator it = moduleState()->capturedFramesCount().constBegin();
5027 for (; it != moduleState()->capturedFramesCount().constEnd(); it++)
5028 qCDebug(KSTARS_EKOS_SCHEDULER) <<
" " << it.key() <<
':' << it.value();
5032SchedulerJob *SchedulerProcess::activeJob()
5034 return moduleState()->activeJob();
5037void SchedulerProcess::printStates(
const QString &label)
5039 qCDebug(KSTARS_EKOS_SCHEDULER) <<
5040 QString(
"%1 %2 %3%4 %5 %6 %7 %8 %9\n")
5042 .
arg(timerStr(moduleState()->timerState()))
5043 .
arg(getSchedulerStatusString(moduleState()->schedulerState()))
5044 .
arg((moduleState()->timerState() == RUN_JOBCHECK && activeJob() !=
nullptr) ?
5045 QString(
"(%1 %2)").arg(SchedulerJob::jobStatusString(activeJob()->getState()))
5046 .arg(SchedulerJob::jobStageString(activeJob()->getStage())) :
"")
5047 .
arg(ekosStateString(moduleState()->ekosState()))
5048 .
arg(indiStateString(moduleState()->indiState()))
5049 .
arg(startupStateString(moduleState()->startupState()))
5050 .
arg(shutdownStateString(moduleState()->shutdownState()))
5051 .
arg(parkWaitStateString(moduleState()->parkWaitState())).
toLatin1().
data();
5052 foreach (
auto j, moduleState()->jobs())
5053 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.
SkyPoint mountCoords()
mountCoords read the equatorial coordinates from the mount
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)
static KStars * Instance()
The SchedulerState class holds all attributes defining the scheduler's state.
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.
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
const double & Degrees() const
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)
QString name(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)
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 QString &fileName)
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
void append(QList< T > &&value)
bool isEmpty() const const
qsizetype size() const const
T value(qsizetype i) 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
QList< Key > keys() 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
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const
QString toString() const const