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)
662 if (moduleState()->startupState() == STARTUP_COMPLETE &&
663 Options::preemptiveShutdown() &&
664 nextObservationTime > (Options::preemptiveShutdownTime() * 3600))
667 "Job '%1' scheduled for execution at %2. "
668 "Observatory scheduled for shutdown until next job is ready.",
669 job->getName(), job->getStartupTime().
toString()));
670 moduleState()->enablePreemptiveShutdown(job->getStartupTime());
672 emit schedulerSleeping(
true,
false);
681 else if (nextObservationTime > Options::leadTime() * 60 &&
682 moduleState()->startupState() == STARTUP_COMPLETE &&
683 moduleState()->parkWaitState() == PARKWAIT_IDLE &&
684 (job->getStepPipeline() & SchedulerJob::USE_TRACK) &&
686 Options::schedulerParkMount())
689 "Job '%1' scheduled for execution at %2. "
690 "Parking the mount until the job is ready.",
691 job->getName(), job->getStartupTime().
toString()));
693 moduleState()->setParkWaitState(PARKWAIT_PARK);
697 else if (nextObservationTime > Options::leadTime() * 60)
699 auto log =
i18n(
"Sleeping until observation job %1 is ready at %2", job->getName(),
702 KSNotification::event(
QLatin1String(
"SchedulerSleeping"), log, KSNotification::Scheduler,
703 KSNotification::Info);
706 if (nextObservationTime > Options::leadTime() * 60 * 12 && !Options::preemptiveShutdown())
708 dms delay(
static_cast<double>(nextObservationTime * 15.0 / 3600.0));
710 "Warning: Job '%1' is %2 away from now, you may want to enable Preemptive Shutdown.",
719 moduleState()->setupNextIteration(RUN_WAKEUP,
720 std::lround(((nextObservationTime + 1) * 1000) / KStarsData::Instance()->clock()->scale()));
722 emit schedulerSleeping(
false,
true);
731 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting slewing must be valid");
736 moduleState()->setParkWaitState(PARKWAIT_UNPARK);
740 if (Options::resetMountModelBeforeJob())
745 SkyPoint target = activeJob()->getTargetCoords();
755 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' slew request received DBUS error: %2").
arg(
762 moduleState()->updateJobStage(SCHEDSTAGE_SLEWING);
769 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting focusing must be valid");
772 if (activeJob()->getStage() == SCHEDSTAGE_RESLEWING_COMPLETE ||
773 activeJob()->getStage() == SCHEDSTAGE_POSTALIGN_FOCUSING)
779 moduleState()->updateJobStage(SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE);
784 if (activeJob()->getOpticalTrain() !=
"")
785 m_activeJobs.insert(activeJob()->getOpticalTrain(), activeJob());
788 QVariant opticalTrain = captureInterface()->property(
"opticalTrain");
790 if (opticalTrain.
isValid() ==
false)
792 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' opticalTrain request failed.").
arg(activeJob()->getName());
801 m_activeJobs.insert(opticalTrain.
toString(), activeJob());
802 activeJob()->setOpticalTrain(opticalTrain.
toString());
808 foreach (
auto follower, activeJob()->followerJobs())
810 m_activeJobs.insert(follower->getOpticalTrain(), follower);
822 dBusArgs.
append(job->getOpticalTrain());
823 boolReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"canAutoFocus", dBusArgs);
827 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' canAutoFocus request received DBUS error: %2").
arg(
837 if (boolReply.value() ==
false)
839 appendLogText(
i18n(
"Warning: job '%1' is unable to proceed with autofocus, not supported.", job->getName()));
840 job->setStepPipeline(
841 static_cast<SchedulerJob::StepPipeline
>(job->getStepPipeline() & ~SchedulerJob::USE_FOCUS));
842 moduleState()->setAutofocusCompleted(job->getOpticalTrain(),
true);
843 if (moduleState()->autofocusCompleted())
845 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
854 if ((reply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"clearAutoFocusHFR",
857 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' clearAutoFocusHFR request received DBUS error: %2").arg(
868 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"resetFrame",
871 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' resetFrame request received DBUS error: %2").arg(
883 if (!job->getInitialFilter().
isEmpty())
886 dBusArgs.
append(job->getInitialFilter());
887 dBusArgs.
append(job->getOpticalTrain());
888 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setFilter",
891 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' setFilter request received DBUS error: %1").arg(
903 dBusArgs.
append(job->getOpticalTrain());
904 boolReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"useFullField", dBusArgs);
908 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' useFullField request received DBUS error: %2").arg(
918 if (boolReply.value() ==
false)
923 dBusArgs.
append(job->getOpticalTrain());
924 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setAutoStarEnabled", dBusArgs)).
type() ==
927 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' setAutoFocusStar request received DBUS error: %1").arg(
940 dBusArgs.
append(job->getOpticalTrain());
944 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' startFocus request received DBUS error: %2").arg(
954 moduleState()->updateJobStage(SCHEDSTAGE_FOCUSING);
956 moduleState()->startCurrentOperationTimer();
961 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting aligning must be valid");
972 moduleState()->setIndexToUse(-1);
973 moduleState()->setHealpixToUse(-1);
976 if (activeJob()->getFITSFile().isEmpty() ==
false)
982 appendLogText(
i18n(
"Warning: job '%1' target FITS file does not exist.", activeJob()->getName()));
991 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"loadAndSlew", solveArgs)).type() ==
994 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request received DBUS error: %2",
1003 else if (reply.
arguments().first().toBool() ==
false)
1005 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request failed.", activeJob()->getName()));
1011 appendLogText(
i18n(
"Job '%1' is plate solving %2.", activeJob()->getName(), activeJob()->getFITSFile().fileName()));
1017 const SkyPoint targetCoords = activeJob()->getTargetCoords();
1020 rotationArgs << activeJob()->getPositionAngle();
1022 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords",
1025 appendLogText(
i18n(
"Warning: job '%1' setTargetCoords request received DBUS error: %2",
1036 if (activeJob()->getPositionAngle() >= -180)
1038 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetPositionAngle",
1041 appendLogText(
i18n(
"Warning: job '%1' setTargetPositionAngle request received DBUS error: %2").arg(
1054 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request received DBUS error: %2").arg(
1063 else if (reply.
arguments().first().toBool() ==
false)
1065 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request failed.", activeJob()->getName()));
1071 appendLogText(
i18n(
"Job '%1' is capturing and plate solving.", activeJob()->getName()));
1075 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
1076 moduleState()->startCurrentOperationTimer();
1081 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting guiding must be valid");
1086 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
1087 appendLogText(
i18n(
"Guiding already running for %1, starting next scheduler action...", activeJob()->getName()));
1089 moduleState()->startCurrentOperationTimer();
1102 if (resetCalibration && Options::resetGuideCalibration())
1109 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1111 appendLogText(
i18n(
"Starting guiding procedure for %1 ...", activeJob()->getName()));
1113 moduleState()->startCurrentOperationTimer();
1118 if (!guideInterface())
1122 if (
nullptr != activeJob() && (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE))
1124 qCInfo(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' is stopping guiding...").
arg(activeJob()->getName());
1126 moduleState()->resetGuideFailureCount();
1128 stopCapturing(
"",
true);
1132 if (moduleState()->isGuidingTimerActive())
1133 moduleState()->cancelGuidingTimer();
1138 if ((moduleState()->restartGuidingInterval() > 0) &&
1139 (moduleState()->restartGuidingTime().msecsTo(KStarsData::Instance()->ut()) > moduleState()->restartGuidingInterval()))
1141 moduleState()->cancelGuidingTimer();
1148 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting capturing must be valid");
1151 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE &&
getGuidingStatus() != GUIDE_GUIDING)
1154 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1159 startSingleCapture(activeJob(), restart);
1160 for (
auto follower : activeJob()->followerJobs())
1163 if (follower->getState() ==
SCHEDJOB_SCHEDULED || (follower->getStage() == SCHEDSTAGE_CAPTURING && follower->isStopped()))
1166 follower->setStage(SCHEDSTAGE_CAPTURING);
1167 startSingleCapture(follower, restart);
1171 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
1173 KSNotification::event(
QLatin1String(
"EkosScheduledImagingStart"),
1174 i18n(
"Ekos job (%1) - Capture started", activeJob()->getName()), KSNotification::Scheduler);
1176 if (moduleState()->captureBatch() > 0)
1177 appendLogText(
i18n(
"Job '%1' capture is in progress (batch #%2)...", activeJob()->getName(),
1178 moduleState()->captureBatch() + 1));
1180 appendLogText(
i18n(
"Job '%1' capture is in progress...", activeJob()->getName()));
1182 moduleState()->startCurrentOperationTimer();
1185void SchedulerProcess::startSingleCapture(SchedulerJob *job,
bool restart)
1187 captureInterface()->setProperty(
"targetName", job->getName());
1190 QVariant train(job->getOpticalTrain());
1192 if (restart ==
false)
1197 QVariant targetName(job->getName());
1201 dbusargs.
append(targetName);
1203 "loadSequenceQueue",
1207 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1208 QString(
"Warning: job '%1' loadSequenceQueue request received DBUS error: %1").
arg(job->getName()).
arg(
1215 else if (captureReply.value() ==
false)
1217 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1218 QString(
"Warning: job '%1' loadSequenceQueue request failed").
arg(job->getName());
1227 for (
auto &e : fMap.keys())
1229 QList<QVariant> dbusargs;
1232 dbusargs.
append(fMap.value(e));
1235 if ((reply = captureInterface()->callWithArgumentList(
QDBus::Block,
"setCapturedFramesMap",
1236 dbusargs)).
type() ==
1239 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1240 QString(
"Warning: job '%1' setCapturedFramesCount request received DBUS error: %1").arg(job->getName()).arg(
1249 QList<QVariant> dbusargs;
1252 QDBusReply<QString>
const startReply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"start",
1257 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1258 QString(
"Warning: job '%1' start request received DBUS error: %1").arg(job->getName()).arg(
1265 QString trainName = startReply.value();
1266 m_activeJobs[trainName] = job;
1272 QVariant gotoMode(
static_cast<int>(mode));
1278 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Loading profiles";
1282 moduleState()->updateProfiles(profiles);
1285void SchedulerProcess::executeScript(
const QString &filename)
1294 checkProcessExit(exitCode);
1298 scriptProcess().
start(filename, arguments);
1303 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1306 switch (moduleState()->ekosState())
1310 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1312 moduleState()->setEkosState(EKOS_READY);
1318 moduleState()->setEkosState(EKOS_STARTING);
1319 moduleState()->startCurrentOperationTimer();
1321 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos communication status is" << moduleState()->ekosCommunicationStatus() <<
1330 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1333 moduleState()->resetEkosConnectFailureCount();
1334 moduleState()->setEkosState(EKOS_READY);
1337 else if (moduleState()->ekosCommunicationStatus() == Ekos::Error)
1339 if (moduleState()->increaseEkosConnectFailureCount())
1350 else if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1353 else if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1355 if (moduleState()->increaseEkosConnectFailureCount())
1362 moduleState()->startCurrentOperationTimer();
1376 if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1379 moduleState()->setEkosState(EKOS_IDLE);
1393 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1396 switch (moduleState()->indiState())
1400 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1402 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1403 moduleState()->resetIndiConnectFailureCount();
1404 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI Properties...";
1408 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Connecting INDI devices...";
1410 moduleState()->setIndiState(INDI_CONNECTING);
1412 moduleState()->startCurrentOperationTimer();
1417 case INDI_CONNECTING:
1419 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1422 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1424 else if (moduleState()->indiCommunicationStatus() == Ekos::Error)
1426 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1433 appendLogText(
i18n(
"One or more INDI devices failed to connect. Check INDI control panel for details."));
1438 else if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1440 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1444 moduleState()->startCurrentOperationTimer();
1448 appendLogText(
i18n(
"One or more INDI devices timed out. Check INDI control panel for details."));
1455 case INDI_DISCONNECTING:
1457 if (moduleState()->indiCommunicationStatus() == Ekos::Idle)
1460 moduleState()->setIndiState(INDI_IDLE);
1466 case INDI_PROPERTY_CHECK:
1468 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI properties.";
1470 if (Options::schedulerUnparkDome() && moduleState()->domeReady() ==
false)
1472 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1474 moduleState()->startCurrentOperationTimer();
1475 appendLogText(
i18n(
"Warning: dome device not ready after timeout, attempting to recover..."));
1485 if (Options::schedulerUnparkMount() && moduleState()->mountReady() ==
false)
1487 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1489 moduleState()->startCurrentOperationTimer();
1490 appendLogText(
i18n(
"Warning: mount device not ready after timeout, attempting to recover..."));
1495 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount unpark required but mount is not yet ready.";
1500 if (Options::schedulerOpenDustCover() && moduleState()->capReady() ==
false)
1502 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1504 moduleState()->startCurrentOperationTimer();
1505 appendLogText(
i18n(
"Warning: cap device not ready after timeout, attempting to recover..."));
1510 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap unpark required but cap is not yet ready.";
1515 if (captureInterface().isNull())
1518 if (moduleState()->captureReady() ==
false)
1520 QVariant hasCoolerControl = captureInterface()->property(
"coolerControl");
1521 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cooler control" << (!hasCoolerControl.
isValid() ?
"invalid" :
1522 (hasCoolerControl.
toBool() ?
"True" :
"Faklse"));
1523 if (hasCoolerControl.
isValid())
1524 moduleState()->setCaptureReady(
true);
1526 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"Capture module is not ready yet...";
1529 moduleState()->setIndiState(INDI_READY);
1530 moduleState()->resetIndiConnectFailureCount();
1544 if (moduleState()->indiState() == INDI_DISCONNECTING
1549 if (moduleState()->indiState() != INDI_IDLE && Options::stopEkosAfterShutdown())
1556 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
1560 if (moduleState()->ekosState() != EKOS_IDLE && Options::stopEkosAfterShutdown())
1566 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
1579 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Disconnecting INDI...";
1580 moduleState()->setIndiState(INDI_DISCONNECTING);
1584void SchedulerProcess::stopEkos()
1586 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Stopping Ekos...";
1587 moduleState()->setEkosState(EKOS_STOPPING);
1588 moduleState()->resetEkosConnectFailureCount();
1590 moduleState()->setMountReady(
false);
1591 moduleState()->setCaptureReady(
false);
1592 moduleState()->setDomeReady(
false);
1593 moduleState()->setCapReady(
false);
1598 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
1602 switch (moduleState()->ekosState())
1613 switch (moduleState()->indiState())
1616 case INDI_DISCONNECTING:
1625 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1627 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Ekos is currently connected, checking INDI before mitigating connection loss.");
1630 if (moduleState()->isINDIConnected())
1633 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"INDI is currently connected, no connection loss mitigation needed.");
1652 if (capInterface().isNull())
1655 QVariant parkingStatus = capInterface()->property(
"parkStatus");
1656 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1658 if (parkingStatus.
isValid() ==
false)
1660 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
1661 capInterface()->lastError().type());
1663 parkingStatus = ISD::PARK_ERROR;
1666 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1670 case ISD::PARK_PARKED:
1671 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1674 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
1676 moduleState()->resetParkingCapFailureCount();
1679 case ISD::PARK_UNPARKED:
1680 if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1682 moduleState()->setStartupState(STARTUP_COMPLETE);
1685 moduleState()->resetParkingCapFailureCount();
1688 case ISD::PARK_PARKING:
1689 case ISD::PARK_UNPARKING:
1691 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1693 if (moduleState()->increaseParkingCapFailureCount())
1696 if (status == ISD::PARK_PARKING)
1705 case ISD::PARK_ERROR:
1706 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1709 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1711 else if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1714 moduleState()->setStartupState(STARTUP_ERROR);
1716 moduleState()->resetParkingCapFailureCount();
1724void SchedulerProcess::checkMountParkingStatus()
1726 if (mountInterface().isNull())
1729 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
1730 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1732 if (parkingStatus.
isValid() ==
false)
1734 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
1735 mountInterface()->lastError().type());
1737 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1740 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1745 case ISD::PARK_PARKED:
1748 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1749 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1752 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1753 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1756 moduleState()->resetParkingMountFailureCount();
1760 case ISD::PARK_UNPARKED:
1763 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1764 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1767 if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1768 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1771 moduleState()->resetParkingMountFailureCount();
1777 case ISD::PARK_UNPARKING:
1778 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1780 if (moduleState()->increaseParkingMountFailureCount())
1782 appendLogText(
i18n(
"Warning: mount unpark operation timed out on attempt %1/%2. Restarting operation...",
1783 moduleState()->parkingMountFailureCount(), moduleState()->maxFailureAttempts()));
1788 appendLogText(
i18n(
"Warning: mount unpark operation timed out on last attempt."));
1789 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1792 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
1797 case ISD::PARK_PARKING:
1798 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1800 if (moduleState()->increaseParkingMountFailureCount())
1802 appendLogText(
i18n(
"Warning: mount park operation timed out on attempt %1/%2. Restarting operation...",
1803 moduleState()->parkingMountFailureCount(),
1804 moduleState()->maxFailureAttempts()));
1809 appendLogText(
i18n(
"Warning: mount park operation timed out on last attempt."));
1810 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1813 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Parking mount in progress...";
1818 case ISD::PARK_ERROR:
1819 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1822 moduleState()->setStartupState(STARTUP_ERROR);
1823 moduleState()->resetParkingMountFailureCount();
1825 else if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1827 if (moduleState()->increaseParkingMountFailureCount())
1829 appendLogText(
i18n(
"Warning: mount park operation failed on attempt %1/%2. Restarting operation...",
1830 moduleState()->parkingMountFailureCount(),
1831 moduleState()->maxFailureAttempts()));
1837 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1838 moduleState()->resetParkingMountFailureCount();
1842 else if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1845 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1846 moduleState()->resetParkingMountFailureCount();
1848 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1851 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1852 moduleState()->resetParkingMountFailureCount();
1858 case ISD::PARK_UNKNOWN:
1860 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1861 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1864 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1865 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1868 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1869 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1870 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1871 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1873 moduleState()->resetParkingMountFailureCount();
1878void SchedulerProcess::checkDomeParkingStatus()
1880 if (domeInterface().isNull())
1883 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
1884 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1886 if (parkingStatus.
isValid() ==
false)
1888 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
1889 mountInterface()->lastError().
type());
1891 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1894 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1898 case ISD::PARK_PARKED:
1899 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1903 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
1905 moduleState()->resetParkingDomeFailureCount();
1908 case ISD::PARK_UNPARKED:
1909 if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1911 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1914 moduleState()->resetParkingDomeFailureCount();
1917 case ISD::PARK_PARKING:
1918 case ISD::PARK_UNPARKING:
1920 if (moduleState()->getCurrentOperationMsec() > (120 * 1000))
1922 if (moduleState()->increaseParkingDomeFailureCount())
1925 if (status == ISD::PARK_PARKING)
1934 case ISD::PARK_ERROR:
1935 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1937 if (moduleState()->increaseParkingDomeFailureCount())
1945 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1946 moduleState()->resetParkingDomeFailureCount();
1949 else if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1951 if (moduleState()->increaseParkingDomeFailureCount())
1959 moduleState()->setStartupState(STARTUP_ERROR);
1960 moduleState()->resetParkingDomeFailureCount();
1972 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1975 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Checking Startup State (%1)...").
arg(moduleState()->startupState());
1977 switch (moduleState()->startupState())
1981 KSNotification::event(
QLatin1String(
"ObservatoryStartup"),
i18n(
"Observatory is in the startup process"),
1982 KSNotification::Scheduler);
1984 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Startup Idle. Starting startup process...";
1991 if (Options::alwaysExecuteStartupScript() ==
false && moduleState()->ekosCommunicationStatus() == Ekos::Success)
1993 if (moduleState()->startupScriptURL().isEmpty() ==
false)
1996 if (!activeJob() || activeJob()->getLightFramesRequired())
1997 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
1999 moduleState()->setStartupState(STARTUP_COMPLETE);
2003 if (moduleState()->currentProfile() !=
i18n(
"Default"))
2006 profile.
append(moduleState()->currentProfile());
2007 ekosInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setProfile", profile);
2010 if (moduleState()->startupScriptURL().isEmpty() ==
false)
2012 moduleState()->setStartupState(STARTUP_SCRIPT);
2017 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
2021 case STARTUP_SCRIPT:
2024 case STARTUP_UNPARK_DOME:
2028 if (activeJob() ==
nullptr || activeJob()->getLightFramesRequired())
2030 if (Options::schedulerUnparkDome())
2033 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
2037 moduleState()->setStartupState(STARTUP_COMPLETE);
2043 case STARTUP_UNPARKING_DOME:
2044 checkDomeParkingStatus();
2047 case STARTUP_UNPARK_MOUNT:
2048 if (Options::schedulerUnparkMount())
2051 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
2054 case STARTUP_UNPARKING_MOUNT:
2055 checkMountParkingStatus();
2058 case STARTUP_UNPARK_CAP:
2059 if (Options::schedulerOpenDustCover())
2062 moduleState()->setStartupState(STARTUP_COMPLETE);
2065 case STARTUP_UNPARKING_CAP:
2069 case STARTUP_COMPLETE:
2082 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking shutdown state...";
2084 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2087 switch (moduleState()->shutdownState())
2091 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Starting shutdown process...";
2093 moduleState()->setActiveJob(
nullptr);
2094 moduleState()->setupNextIteration(RUN_SHUTDOWN);
2095 emit shutdownStarted();
2097 if (Options::schedulerWarmCCD())
2104 if (captureInterface())
2106 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Setting coolerControl=false";
2107 captureInterface()->setProperty(
"coolerControl",
false);
2112 if (moduleState()->isINDIConnected())
2114 if (Options::schedulerCloseDustCover())
2116 moduleState()->setShutdownState(SHUTDOWN_PARK_CAP);
2120 if (Options::schedulerParkMount())
2122 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2126 if (Options::schedulerParkDome())
2128 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2132 else appendLogText(
i18n(
"Warning: Bypassing parking procedures, no INDI connection."));
2134 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2136 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2140 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2143 case SHUTDOWN_PARK_CAP:
2144 if (!moduleState()->isINDIConnected())
2146 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2147 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2149 else if (Options::schedulerCloseDustCover())
2152 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2155 case SHUTDOWN_PARKING_CAP:
2159 case SHUTDOWN_PARK_MOUNT:
2160 if (!moduleState()->isINDIConnected())
2162 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2163 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2165 else if (Options::schedulerParkMount())
2168 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2171 case SHUTDOWN_PARKING_MOUNT:
2172 checkMountParkingStatus();
2175 case SHUTDOWN_PARK_DOME:
2176 if (!moduleState()->isINDIConnected())
2178 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2179 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2181 else if (Options::schedulerParkDome())
2184 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2187 case SHUTDOWN_PARKING_DOME:
2188 checkDomeParkingStatus();
2191 case SHUTDOWN_SCRIPT:
2192 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2195 if (moduleState()->ekosState() != EKOS_IDLE && Options::shutdownScriptTerminatesINDI())
2201 moduleState()->setShutdownState(SHUTDOWN_SCRIPT_RUNNING);
2205 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2208 case SHUTDOWN_SCRIPT_RUNNING:
2211 case SHUTDOWN_COMPLETE:
2214 case SHUTDOWN_ERROR:
2224 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2227 if (moduleState()->parkWaitState() == PARKWAIT_IDLE)
2232 switch (moduleState()->parkWaitState())
2238 case PARKWAIT_PARKING:
2239 checkMountParkingStatus();
2242 case PARKWAIT_UNPARK:
2246 case PARKWAIT_UNPARKING:
2247 checkMountParkingStatus();
2251 case PARKWAIT_PARKED:
2252 case PARKWAIT_UNPARKED:
2255 case PARKWAIT_ERROR:
2267 if (moduleState()->startupState() == STARTUP_IDLE
2268 || moduleState()->startupState() == STARTUP_ERROR
2269 || moduleState()->startupState() == STARTUP_COMPLETE)
2273 KSMessageBox::Instance()->disconnect(
this);
2276 moduleState()->setStartupState(STARTUP_IDLE);
2282 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the startup procedure manually?"));
2286 switch (moduleState()->startupState())
2291 case STARTUP_SCRIPT:
2292 scriptProcess().terminate();
2295 case STARTUP_UNPARK_DOME:
2298 case STARTUP_UNPARKING_DOME:
2299 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking dome...";
2303 case STARTUP_UNPARK_MOUNT:
2306 case STARTUP_UNPARKING_MOUNT:
2307 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking mount...";
2311 case STARTUP_UNPARK_CAP:
2314 case STARTUP_UNPARKING_CAP:
2317 case STARTUP_COMPLETE:
2324 moduleState()->setStartupState(STARTUP_IDLE);
2333 if (moduleState()->shutdownState() == SHUTDOWN_IDLE
2334 || moduleState()->shutdownState() == SHUTDOWN_ERROR
2335 || moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
2339 KSMessageBox::Instance()->disconnect(
this);
2341 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2346 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the shutdown procedure manually?"));
2350 switch (moduleState()->shutdownState())
2355 case SHUTDOWN_SCRIPT:
2358 case SHUTDOWN_SCRIPT_RUNNING:
2359 scriptProcess().terminate();
2362 case SHUTDOWN_PARK_DOME:
2365 case SHUTDOWN_PARKING_DOME:
2366 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking dome...";
2370 case SHUTDOWN_PARK_MOUNT:
2373 case SHUTDOWN_PARKING_MOUNT:
2374 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking mount...";
2378 case SHUTDOWN_PARK_CAP:
2379 case SHUTDOWN_PARKING_CAP:
2382 case SHUTDOWN_COMPLETE:
2385 case SHUTDOWN_ERROR:
2389 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2397 moduleState()->setupNextIteration(RUN_NOTHING);
2399 emit schedulerPaused();
2405 for (SchedulerJob * job : moduleState()->jobs())
2408 job->setCompletedCount(0);
2417 auto finished_or_aborted = [](SchedulerJob
const *
const job)
2424 auto neither_scheduled_nor_aborted = [](SchedulerJob
const *
const job)
2432 if (jobs.
isEmpty() || std::all_of(jobs.
begin(), jobs.
end(), neither_scheduled_nor_aborted))
2435 moduleState()->setActiveJob(
nullptr);
2439 else if (std::all_of(jobs.
begin(), jobs.
end(), finished_or_aborted) &&
2440 strategy != ERROR_DONT_RESTART)
2442 appendLogText(
i18n(
"Only aborted jobs left in the scheduler queue after evaluating, rescheduling those."));
2443 std::for_each(jobs.
begin(), jobs.
end(), [](SchedulerJob * job)
2445 if (SCHEDJOB_ABORTED == job->getState())
2446 job->setState(SCHEDJOB_EVALUATION);
2453 SchedulerJob *scheduledJob = getGreedyScheduler()->getScheduledJob();
2457 moduleState()->setActiveJob(
nullptr);
2460 if (activeJob() !=
nullptr && scheduledJob != activeJob())
2463 for (
auto job : m_activeJobs.values())
2465 stopCapturing(job->getOpticalTrain(),
false);
2468 m_activeJobs.clear();
2470 moduleState()->setActiveJob(scheduledJob);
2478 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2482 moduleState()->resetSequenceExecutionCounter();
2490 for (
auto job : moduleState()->jobs())
2494 if (moduleState()->jobs().isEmpty())
2497 if (Options::rememberJobProgress())
2500 moduleState()->calculateDawnDusk();
2502 getGreedyScheduler()->scheduleJobs(moduleState()->jobs(), SchedulerModuleState::getLocalTime(),
2503 moduleState()->capturedFramesCount(),
this);
2507 if (!evaluateOnly && moduleState()->schedulerState() == SCHEDULER_RUNNING)
2512 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos finished evaluating jobs, no job selection required.";
2514 emit jobsUpdated(moduleState()->getJSONJobs());
2519 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2521 if (activeJob() ==
nullptr)
2526 switch (activeJob()->getState())
2542 if (activeJob() ==
nullptr)
2545 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE
2546 || moduleState()->shutdownState() == SHUTDOWN_ERROR)
2552 if (moduleState()->shutdownState() > SHUTDOWN_IDLE)
2555 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
2570 if (
nullptr == activeJob() && moduleState()->checkRepeatSequence())
2579 moduleState()->increaseSequenceExecutionCounter();
2580 appendLogText(
i18n(
"Starting job sequence iteration #%1", moduleState()->sequenceExecutionCounter()));
2586 if (
nullptr == activeJob())
2596 if (moduleState()->startupState() == STARTUP_ERROR)
2604 if ((moduleState()->startupState() == STARTUP_IDLE
2606 || moduleState()->startupState() == STARTUP_SCRIPT)
2622 if (moduleState()->startupState() > STARTUP_SCRIPT
2623 && moduleState()->startupState() < STARTUP_ERROR
2635 emit updateJobTable();
2643 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Get next action...";
2645 switch (activeJob()->getStage())
2647 case SCHEDSTAGE_IDLE:
2648 if (activeJob()->getLightFramesRequired())
2650 if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
2652 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2654 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3485";
2657 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2659 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2672 if (activeJob()->getStepPipeline())
2674 i18n(
"Job '%1' is proceeding directly to capture stage because only calibration frames are pending.",
2675 activeJob()->getName()));
2681 case SCHEDSTAGE_SLEW_COMPLETE:
2682 if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2684 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3514";
2687 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2689 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2695 case SCHEDSTAGE_FOCUS_COMPLETE:
2696 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2698 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2704 case SCHEDSTAGE_ALIGN_COMPLETE:
2705 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING);
2708 case SCHEDSTAGE_RESLEWING_COMPLETE:
2711 if ((activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS) && activeJob()->getInSequenceFocus())
2714 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3544";
2717 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2723 case SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE:
2724 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2730 case SCHEDSTAGE_GUIDING_COMPLETE:
2746 moduleState()->iterationTimer().setSingleShot(
true);
2747 moduleState()->iterationTimer().start(msSleep);
2754 if (moduleState()->startMSecs() == 0)
2755 moduleState()->setStartMSecs(now);
2768 moduleState()->setIterationSetup(
false);
2769 switch (keepTimerState)
2772 changeSleepLabel(
"",
false);
2785 moduleState()->setTimerInterval(-1);
2788 if (!moduleState()->iterationSetup())
2794 moduleState()->setTimerInterval(moduleState()->updatePeriodMs());
2797 return moduleState()->timerInterval();
2802 Q_ASSERT_X(activeJob(), __FUNCTION__,
"Actual current job is required to check job stage");
2806 if (checkJobStageCounter == 0)
2808 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking job stage for" << activeJob()->getName() <<
"startup" <<
2809 activeJob()->getStartupCondition() << activeJob()->getStartupTime().toString() <<
"state" << activeJob()->getState();
2810 if (checkJobStageCounter++ == 30)
2811 checkJobStageCounter = 0;
2814 emit syncGreedyParams();
2815 if (!getGreedyScheduler()->checkJob(moduleState()->leadJobs(), SchedulerModuleState::getLocalTime(), activeJob()))
2822 checkJobStageEpilogue();
2825void SchedulerProcess::checkJobStageEpilogue()
2840 if (!activeJob())
return;
2841 switch (activeJob()->getStage())
2843 case SCHEDSTAGE_IDLE:
2845 emit jobStarted(activeJob()->getName());
2849 case SCHEDSTAGE_ALIGNING:
2851 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(ALIGN_INACTIVITY_TIMEOUT))
2853 QVariant const status = alignInterface()->property(
"status");
2858 if (moduleState()->increaseAlignFailureCount())
2860 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align module timed out. Restarting request...";
2865 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
2871 moduleState()->startCurrentOperationTimer();
2875 case SCHEDSTAGE_CAPTURING:
2877 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(CAPTURE_INACTIVITY_TIMEOUT))
2879 QVariant
const status = captureInterface()->property(
"status");
2884 if (moduleState()->increaseCaptureFailureCount())
2886 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"capture module timed out. Restarting request...";
2891 appendLogText(
i18n(
"Warning: job '%1' capture procedure failed, marking aborted.", activeJob()->getName()));
2896 else moduleState()->startCurrentOperationTimer();
2900 case SCHEDSTAGE_FOCUSING:
2902 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(FOCUS_INACTIVITY_TIMEOUT))
2904 bool success =
true;
2905 foreach (
const QString trainname, m_activeJobs.keys())
2907 QList<QVariant> dbusargs;
2908 dbusargs.
append(trainname);
2909 QDBusReply<Ekos::FocusState> statusReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"status", dbusargs);
2912 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' status request received DBUS error: %2").arg(
2922 Ekos::FocusState focusStatus = statusReply.value();
2923 if (focusStatus == Ekos::FOCUS_IDLE || focusStatus == Ekos::FOCUS_WAITING)
2925 if (moduleState()->increaseFocusFailureCount(trainname))
2927 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus module timed out. Restarting request...";
2935 if (success ==
false)
2937 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
2942 else moduleState()->startCurrentOperationTimer();
2945 case SCHEDSTAGE_GUIDING:
2947 if (moduleState()->getCurrentOperationMsec() > GUIDE_INACTIVITY_TIMEOUT)
2951 if (guideStatus == Ekos::GUIDE_IDLE || guideStatus == Ekos::GUIDE_CONNECTED || guideStatus == Ekos::GUIDE_DISCONNECTED)
2953 if (moduleState()->increaseGuideFailureCount())
2955 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"guide module timed out. Restarting request...";
2960 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
2965 else moduleState()->startCurrentOperationTimer();
2969 case SCHEDSTAGE_SLEWING:
2970 case SCHEDSTAGE_RESLEWING:
2973 QVariant
const slewStatus = mountInterface()->property(
"status");
2979 ISD::Mount::Status
const status =
static_cast<ISD::Mount::Status
>(slewStatus.
toInt());
2980 setMountStatus(status);
2984 appendLogText(
i18n(
"Warning: job '%1' lost connection to the mount, attempting to reconnect.", activeJob()->getName()));
2992 case SCHEDSTAGE_SLEW_COMPLETE:
2993 case SCHEDSTAGE_RESLEWING_COMPLETE:
2995 if (moduleState()->domeReady())
2997 QVariant
const isDomeMoving = domeInterface()->property(
"isMoving");
3001 appendLogText(
i18n(
"Warning: job '%1' lost connection to the dome, attempting to reconnect.", activeJob()->getName()));
3007 if (!isDomeMoving.
value<
bool>())
3020 moduleState()->calculateDawnDusk();
3022 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
3034 if (activeJob() == job &&
SCHEDJOB_BUSY == activeJob()->getState())
3037 moduleState()->setActiveJob(job);
3049 else if (0 < SchedulerModuleState::getLocalTime().secsTo(activeJob()->getStartupTime()))
3054 if (job->getCompletionCondition() == FINISH_SEQUENCE && Options::rememberJobProgress())
3055 captureInterface()->setProperty(
"targetName", job->getName());
3057 moduleState()->calculateDawnDusk();
3061 moduleState()->setAutofocusCompleted(job->getOpticalTrain(),
false);
3063 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Executing Job " << activeJob()->getName();
3066 emit jobsUpdated(moduleState()->getJSONJobs());
3068 KSNotification::event(
QLatin1String(
"EkosSchedulerJobStart"),
3069 i18n(
"Ekos job started (%1)", activeJob()->getName()), KSNotification::Scheduler);
3072 moduleState()->setupNextIteration(RUN_JOBCHECK);
3084 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3093 outstream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<
Qt::endl;
3094 outstream <<
"<SchedulerList version='2.1'>" <<
Qt::endl;
3096 outstream <<
"<Profile>" <<
QString(entityXML(strdup(moduleState()->currentProfile().toStdString().c_str()))) <<
3099 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
3100 bool useMosaicInfo = !tiles->sequenceFile().isEmpty();
3104 outstream <<
"<Mosaic>" <<
Qt::endl;
3105 outstream <<
"<Target>" << tiles->targetName() <<
"</Target>" <<
Qt::endl;
3106 outstream <<
"<Group>" << tiles->group() <<
"</Group>" <<
Qt::endl;
3108 QString ccArg, ccValue = tiles->completionCondition(&ccArg);
3109 if (ccValue ==
"FinishSequence")
3110 outstream <<
"<FinishSequence/>" <<
Qt::endl;
3111 else if (ccValue ==
"FinishLoop")
3112 outstream <<
"<FinishLoop/>" <<
Qt::endl;
3113 else if (ccValue ==
"FinishRepeat")
3114 outstream <<
"<FinishRepeat>" << ccArg <<
"</FinishRepeat>" <<
Qt::endl;
3116 outstream <<
"<Sequence>" << tiles->sequenceFile() <<
"</Sequence>" <<
Qt::endl;
3117 outstream <<
"<Directory>" << tiles->outputDirectory() <<
"</Directory>" <<
Qt::endl;
3119 outstream <<
"<FocusEveryN>" << tiles->focusEveryN() <<
"</FocusEveryN>" <<
Qt::endl;
3120 outstream <<
"<AlignEveryN>" << tiles->alignEveryN() <<
"</AlignEveryN>" <<
Qt::endl;
3121 if (tiles->isTrackChecked())
3122 outstream <<
"<TrackChecked/>" <<
Qt::endl;
3123 if (tiles->isFocusChecked())
3124 outstream <<
"<FocusChecked/>" <<
Qt::endl;
3125 if (tiles->isAlignChecked())
3126 outstream <<
"<AlignChecked/>" <<
Qt::endl;
3127 if (tiles->isGuideChecked())
3128 outstream <<
"<GuideChecked/>" <<
Qt::endl;
3129 outstream <<
"<Overlap>" << cLocale.
toString(tiles->overlap()) <<
"</Overlap>" <<
Qt::endl;
3130 outstream <<
"<CenterRA>" << cLocale.
toString(tiles->ra0().Hours()) <<
"</CenterRA>" <<
Qt::endl;
3131 outstream <<
"<CenterDE>" << cLocale.
toString(tiles->dec0().Degrees()) <<
"</CenterDE>" <<
Qt::endl;
3132 outstream <<
"<GridW>" << tiles->gridSize().width() <<
"</GridW>" <<
Qt::endl;
3133 outstream <<
"<GridH>" << tiles->gridSize().height() <<
"</GridH>" <<
Qt::endl;
3134 outstream <<
"<FOVW>" << cLocale.
toString(tiles->mosaicFOV().width()) <<
"</FOVW>" <<
Qt::endl;
3135 outstream <<
"<FOVH>" << cLocale.
toString(tiles->mosaicFOV().height()) <<
"</FOVH>" <<
Qt::endl;
3136 outstream <<
"<CameraFOVW>" << cLocale.
toString(tiles->cameraFOV().width()) <<
"</CameraFOVW>" <<
Qt::endl;
3137 outstream <<
"<CameraFOVH>" << cLocale.
toString(tiles->cameraFOV().height()) <<
"</CameraFOVH>" <<
Qt::endl;
3138 outstream <<
"</Mosaic>" <<
Qt::endl;
3142 for (
auto &job : moduleState()->jobs())
3147 outstream <<
"<JobType lead='" << (job->isLead() ?
"true" :
"false") <<
"'/>" <<
Qt::endl;
3152 outstream <<
"<Coordinates>" <<
Qt::endl;
3155 outstream <<
"</Coordinates>" <<
Qt::endl;
3158 if (! job->getOpticalTrain().
isEmpty())
3159 outstream <<
"<OpticalTrain>" <<
QString(entityXML(strdup(job->getOpticalTrain().
toStdString().c_str()))) <<
3162 if (job->isLead() && job->getFITSFile().
isValid() && job->getFITSFile().
isEmpty() ==
false)
3165 outstream <<
"<PositionAngle>" << job->getPositionAngle() <<
"</PositionAngle>" <<
Qt::endl;
3167 outstream <<
"<Sequence>" << job->getSequenceFile().
toLocalFile() <<
"</Sequence>" <<
Qt::endl;
3169 if (useMosaicInfo && index < tiles->tiles().size())
3171 auto oneTile = tiles->tiles().at(index++);
3172 outstream <<
"<TileCenter>" <<
Qt::endl;
3173 outstream <<
"<X>" << cLocale.
toString(oneTile->center.x()) <<
"</X>" <<
Qt::endl;
3174 outstream <<
"<Y>" << cLocale.
toString(oneTile->center.y()) <<
"</Y>" <<
Qt::endl;
3175 outstream <<
"<Rotation>" << cLocale.
toString(oneTile->rotation) <<
"</Rotation>" <<
Qt::endl;
3176 outstream <<
"</TileCenter>" <<
Qt::endl;
3181 outstream <<
"<StartupCondition>" <<
Qt::endl;
3182 if (job->getFileStartupCondition() == START_ASAP)
3183 outstream <<
"<Condition>ASAP</Condition>" <<
Qt::endl;
3184 else if (job->getFileStartupCondition() == START_AT)
3185 outstream <<
"<Condition value='" << job->getStartAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3187 outstream <<
"</StartupCondition>" <<
Qt::endl;
3189 outstream <<
"<Constraints>" <<
Qt::endl;
3190 if (job->hasMinAltitude())
3191 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinAltitude()) <<
"'>MinimumAltitude</Constraint>" <<
3193 if (job->getMinMoonSeparation() > 0)
3194 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinMoonSeparation()) <<
"'>MoonSeparation</Constraint>"
3196 if (job->getMaxMoonAltitude() < 90)
3197 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMaxMoonAltitude()) <<
"'>MoonMaxAltitude</Constraint>"
3199 if (job->getEnforceWeather())
3200 outstream <<
"<Constraint>EnforceWeather</Constraint>" <<
Qt::endl;
3201 if (job->getEnforceTwilight())
3202 outstream <<
"<Constraint>EnforceTwilight</Constraint>" <<
Qt::endl;
3203 if (job->getEnforceArtificialHorizon())
3204 outstream <<
"<Constraint>EnforceArtificialHorizon</Constraint>" <<
Qt::endl;
3205 outstream <<
"</Constraints>" <<
Qt::endl;
3208 outstream <<
"<CompletionCondition>" <<
Qt::endl;
3209 if (job->getCompletionCondition() == FINISH_SEQUENCE)
3210 outstream <<
"<Condition>Sequence</Condition>" <<
Qt::endl;
3211 else if (job->getCompletionCondition() == FINISH_REPEAT)
3212 outstream <<
"<Condition value='" << cLocale.
toString(job->getRepeatsRequired()) <<
"'>Repeat</Condition>" <<
Qt::endl;
3213 else if (job->getCompletionCondition() == FINISH_LOOP)
3214 outstream <<
"<Condition>Loop</Condition>" <<
Qt::endl;
3215 else if (job->getCompletionCondition() == FINISH_AT)
3216 outstream <<
"<Condition value='" << job->getFinishAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3218 outstream <<
"</CompletionCondition>" <<
Qt::endl;
3222 outstream <<
"<Steps>" <<
Qt::endl;
3223 if (job->getStepPipeline() & SchedulerJob::USE_TRACK)
3224 outstream <<
"<Step>Track</Step>" <<
Qt::endl;
3225 if (job->getStepPipeline() & SchedulerJob::USE_FOCUS)
3226 outstream <<
"<Step>Focus</Step>" <<
Qt::endl;
3227 if (job->getStepPipeline() & SchedulerJob::USE_ALIGN)
3228 outstream <<
"<Step>Align</Step>" <<
Qt::endl;
3229 if (job->getStepPipeline() & SchedulerJob::USE_GUIDE)
3230 outstream <<
"<Step>Guide</Step>" <<
Qt::endl;
3231 outstream <<
"</Steps>" <<
Qt::endl;
3236 outstream <<
"<SchedulerAlgorithm value='" << ALGORITHM_GREEDY <<
"'/>" <<
Qt::endl;
3237 outstream <<
"<ErrorHandlingStrategy value='" << Options::errorHandlingStrategy() <<
"'>" <<
Qt::endl;
3238 if (Options::rescheduleErrors())
3239 outstream <<
"<RescheduleErrors />" <<
Qt::endl;
3240 outstream <<
"<delay>" << Options::errorHandlingStrategyDelay() <<
"</delay>" <<
Qt::endl;
3241 outstream <<
"</ErrorHandlingStrategy>" <<
Qt::endl;
3243 outstream <<
"<StartupProcedure>" <<
Qt::endl;
3244 if (moduleState()->startupScriptURL().isEmpty() ==
false)
3245 outstream <<
"<Procedure value='" << moduleState()->startupScriptURL().toString(
QUrl::PreferLocalFile) <<
3246 "'>StartupScript</Procedure>" <<
Qt::endl;
3247 if (Options::schedulerUnparkDome())
3248 outstream <<
"<Procedure>UnparkDome</Procedure>" <<
Qt::endl;
3249 if (Options::schedulerUnparkMount())
3250 outstream <<
"<Procedure>UnparkMount</Procedure>" <<
Qt::endl;
3251 if (Options::schedulerOpenDustCover())
3252 outstream <<
"<Procedure>UnparkCap</Procedure>" <<
Qt::endl;
3253 outstream <<
"</StartupProcedure>" <<
Qt::endl;
3255 outstream <<
"<ShutdownProcedure>" <<
Qt::endl;
3256 if (Options::schedulerWarmCCD())
3257 outstream <<
"<Procedure>WarmCCD</Procedure>" <<
Qt::endl;
3258 if (Options::schedulerCloseDustCover())
3259 outstream <<
"<Procedure>ParkCap</Procedure>" <<
Qt::endl;
3260 if (Options::schedulerParkMount())
3261 outstream <<
"<Procedure>ParkMount</Procedure>" <<
Qt::endl;
3262 if (Options::schedulerParkDome())
3263 outstream <<
"<Procedure>ParkDome</Procedure>" <<
Qt::endl;
3264 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
3265 outstream <<
"<Procedure value='" << moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile) <<
3266 "'>schedulerStartupScript</Procedure>" <<
3268 outstream <<
"</ShutdownProcedure>" <<
Qt::endl;
3270 outstream <<
"</SchedulerList>" <<
Qt::endl;
3274 moduleState()->setDirty(
false);
3278void SchedulerProcess::checkAlignment(
const QVariantMap &metadata,
const QString &trainname)
3281 if (activeJob() ==
nullptr || (activeJob()->getOpticalTrain() !=
"" && activeJob()->getOpticalTrain() != trainname))
3283 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Ignoring metadata from train =" << trainname <<
"for alignment check.";
3287 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN &&
3288 metadata[
"type"].toInt() == FRAME_LIGHT &&
3289 Options::alignCheckFrequency() > 0 &&
3290 moduleState()->increaseSolverIteration() >= Options::alignCheckFrequency())
3292 moduleState()->resetSolverIteration();
3294 auto filename = metadata[
"filename"].toString();
3295 auto exposure = metadata[
"exposure"].toDouble();
3297 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking alignment on train =" << trainname <<
"for" << filename;
3299 constexpr double minSolverSeconds = 5.0;
3300 double solverTimeout = std::max(exposure - 2, minSolverSeconds);
3301 if (solverTimeout >= minSolverSeconds)
3303 auto profiles = getDefaultAlignOptionsProfiles();
3305 SSolver::Parameters parameters;
3310 parameters = profiles.at(Options::solveOptionsProfile());
3312 catch (std::out_of_range
const &)
3314 parameters = profiles[0];
3318 parameters.search_radius = parameters.search_radius * 2;
3323 auto width = metadata[
"width"].toUInt() / (metadata[
"binx"].isValid() ? metadata[
"binx"].toUInt() : 1);
3324 auto height = metadata[
"height"].toUInt() / (metadata[
"biny"].isValid() ? metadata[
"biny"].toUInt() : 1);
3326 auto lowScale = Options::astrometryImageScaleLow();
3327 auto highScale = Options::astrometryImageScaleHigh();
3330 if (Options::astrometryImageScaleUnits() == SSolver::DEG_WIDTH)
3332 lowScale = (lowScale * 3600) / std::max(width, height);
3333 highScale = (highScale * 3600) / std::min(width, height);
3335 else if (Options::astrometryImageScaleUnits() == SSolver::ARCMIN_WIDTH)
3337 lowScale = (lowScale * 60) / std::max(width, height);
3338 highScale = (highScale * 60) / std::min(width, height);
3341 m_Solver->useScale(Options::astrometryUseImageScale(), lowScale, highScale);
3342 m_Solver->usePosition(Options::astrometryUsePosition(), activeJob()->getTargetCoords().ra().Degrees(),
3343 activeJob()->getTargetCoords().
dec().Degrees());
3344 m_Solver->setHealpix(moduleState()->indexToUse(), moduleState()->healpixToUse());
3345 m_Solver->runSolver(filename);
3350void SchedulerProcess::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
3352 disconnect(m_Solver.get(), &SolverUtils::done,
this, &Ekos::SchedulerProcess::solverDone);
3357 QString healpixString =
"";
3358 if (moduleState()->indexToUse() != -1 || moduleState()->healpixToUse() != -1)
3359 healpixString = QString(
"Healpix %1 Index %2").
arg(moduleState()->healpixToUse()).
arg(moduleState()->indexToUse());
3361 if (timedOut || !success)
3364 moduleState()->setIndexToUse(-1);
3365 moduleState()->setHealpixToUse(-1);
3371 m_Solver->getSolutionHealpix(&index, &healpix);
3372 moduleState()->setIndexToUse(index);
3373 moduleState()->setHealpixToUse(healpix);
3377 appendLogText(
i18n(
"Solver timed out: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3379 appendLogText(
i18n(
"Solver failed: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3382 const double ra = solution.ra;
3383 const double dec = solution.dec;
3385 const auto target = activeJob()->getTargetCoords();
3387 SkyPoint alignCoord;
3388 alignCoord.
setRA0(ra / 15.0);
3392 const double diffRa = (alignCoord.
ra().deltaAngle(target.ra())).Degrees() * 3600;
3393 const double diffDec = (alignCoord.
dec().deltaAngle(target.dec())).Degrees() * 3600;
3396 const double diffTotal = hypot(diffRa, diffDec);
3400 qCDebug(KSTARS_EKOS_SCHEDULER) <<
3401 QString(
"Target Distance: %1\" Target (RA: %2 DE: %3) Current (RA: %4 DE: %5) %6 solved in %7s")
3402 .arg(QString(
"%L1").arg(diffTotal, 0,
'f', 0),
3403 target.ra().toDMSString(),
3404 target.dec().toDMSString(),
3408 QString(
"%L1").arg(elapsedSeconds, 0,
'f', 2));
3409 emit targetDistance(diffTotal);
3412 if (diffTotal / 60 > Options::alignCheckThreshold())
3424 SchedulerState
const old_state = moduleState()->schedulerState();
3425 moduleState()->setSchedulerState(SCHEDULER_LOADING);
3432 QString message =
i18n(
"Unable to open file %1", fileURL);
3433 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3434 moduleState()->setSchedulerState(old_state);
3438 LilXML *xmlParser = newLilXML();
3439 char errmsg[MAXRBUF];
3440 XMLEle *root =
nullptr;
3441 XMLEle *ep =
nullptr;
3442 XMLEle *subEP =
nullptr;
3449 SchedulerJob *lastLead =
nullptr;
3452 const QStringList allTrainNames = OpticalTrainManager::Instance()->getTrainNames();
3457 root = readXMLEle(xmlParser, c, errmsg);
3461 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
3463 const char *tag = tagXMLEle(ep);
3464 if (!strcmp(tag,
"Job"))
3466 SchedulerJob *newJob = SchedulerUtils::createJob(ep, lastLead);
3468 if (newJob->isLead())
3472 remainingTrainNames = allTrainNames;
3475 const QString trainname = newJob->getOpticalTrain();
3476 bool allowedName = (newJob->isLead() && trainname.
isEmpty()) || allTrainNames.
contains(trainname);
3477 bool availableName = (newJob->isLead() && trainname.
isEmpty()) || !remainingTrainNames.
isEmpty();
3479 if (!allowedName && availableName)
3482 i18n(
"Warning: train name is empty, selecting \"%1\".", remainingTrainNames.
first()) :
3483 i18n(
"Warning: train name %2 does not exist, selecting \"%1\".", remainingTrainNames.
first(), trainname);
3489 newJob->setOpticalTrain(remainingTrainNames.
first());
3492 else if (!availableName)
3494 const QString message =
i18n(
"Warning: no available train name for scheduler job, select the optical train name manually.");
3502 emit addJob(newJob);
3504 else if (!strcmp(tag,
"Mosaic"))
3507 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
3508 tiles->fromXML(fileURL);
3510 else if (!strcmp(tag,
"Profile"))
3512 moduleState()->setCurrentProfile(pcdataXMLEle(ep));
3515 else if (!strcmp(tag,
"SchedulerAlgorithm"))
3517 int algIndex = cLocale.
toInt(findXMLAttValu(ep,
"value"));
3518 if (algIndex != ALGORITHM_GREEDY)
3519 appendLogText(
i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
3521 else if (!strcmp(tag,
"ErrorHandlingStrategy"))
3526 subEP = findXMLEle(ep,
"delay");
3529 Options::setErrorHandlingStrategyDelay(cLocale.
toInt(pcdataXMLEle(subEP)));
3531 subEP = findXMLEle(ep,
"RescheduleErrors");
3532 Options::setRescheduleErrors(subEP !=
nullptr);
3534 else if (!strcmp(tag,
"StartupProcedure"))
3537 Options::setSchedulerUnparkDome(
false);
3538 Options::setSchedulerUnparkMount(
false);
3539 Options::setSchedulerOpenDustCover(
false);
3541 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3543 const char *proc = pcdataXMLEle(procedure);
3545 if (!strcmp(proc,
"StartupScript"))
3547 moduleState()->setStartupScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3549 else if (!strcmp(proc,
"UnparkDome"))
3550 Options::setSchedulerUnparkDome(
true);
3551 else if (!strcmp(proc,
"UnparkMount"))
3552 Options::setSchedulerUnparkMount(
true);
3553 else if (!strcmp(proc,
"UnparkCap"))
3554 Options::setSchedulerOpenDustCover(
true);
3557 else if (!strcmp(tag,
"ShutdownProcedure"))
3560 Options::setSchedulerWarmCCD(
false);
3561 Options::setSchedulerParkDome(
false);
3562 Options::setSchedulerParkMount(
false);
3563 Options::setSchedulerCloseDustCover(
false);
3565 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3567 const char *proc = pcdataXMLEle(procedure);
3569 if (!strcmp(proc,
"ShutdownScript"))
3571 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3573 else if (!strcmp(proc,
"WarmCCD"))
3574 Options::setSchedulerWarmCCD(
true);
3575 else if (!strcmp(proc,
"ParkDome"))
3576 Options::setSchedulerParkDome(
true);
3577 else if (!strcmp(proc,
"ParkMount"))
3578 Options::setSchedulerParkMount(
true);
3579 else if (!strcmp(proc,
"ParkCap"))
3580 Options::setSchedulerCloseDustCover(
true);
3585 emit syncGUIToGeneralSettings();
3590 delLilXML(xmlParser);
3591 moduleState()->setSchedulerState(old_state);
3596 moduleState()->setDirty(
false);
3597 delLilXML(xmlParser);
3598 emit updateSchedulerURL(fileURL);
3600 moduleState()->setSchedulerState(old_state);
3607 int const max_log_count = 2000;
3608 if (moduleState()->logText().size() > max_log_count)
3609 moduleState()->logText().removeLast();
3611 moduleState()->logText().prepend(
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
3612 SchedulerModuleState::getLocalTime().toString(
"yyyy-MM-ddThh:mm:ss"), logentry));
3614 qCInfo(KSTARS_EKOS_SCHEDULER) << logentry;
3616 emit newLog(logentry);
3621 moduleState()->logText().clear();
3625void SchedulerProcess::setAlignStatus(
AlignState status)
3627 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3630 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align State" << Ekos::getAlignStatusString(status);
3635 QDateTime const now = SchedulerModuleState::getLocalTime();
3636 if (now < activeJob()->getStartupTime())
3640 if (activeJob()->getStage() == SCHEDSTAGE_ALIGNING)
3646 moduleState()->resetAlignFailureCount();
3648 moduleState()->updateJobStage(SCHEDSTAGE_ALIGN_COMPLETE);
3651 if (activeJob()->getFITSFile().isEmpty() ==
false)
3657 activeJob()->setTargetCoords(
dms(values[0] * 15.0),
dms(values[1]), KStarsData::Instance()->ut().djd());
3664 appendLogText(
i18n(
"Warning: job '%1' alignment failed.", activeJob()->getName()));
3666 if (moduleState()->increaseAlignFailureCount())
3668 if (Options::resetMountModelOnAlignFail() && moduleState()->maxFailureAttempts() - 1 < moduleState()->alignFailureCount())
3670 appendLogText(
i18n(
"Warning: job '%1' forcing mount model reset after failing alignment #%2.", activeJob()->getName(),
3671 moduleState()->alignFailureCount()));
3674 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3679 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
3688void SchedulerProcess::setGuideStatus(GuideState status)
3690 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3693 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Guide State" << Ekos::getGuideStatusString(status);
3698 QDateTime
const now = SchedulerModuleState::getLocalTime();
3699 if (now < activeJob()->getStartupTime())
3703 if (activeJob()->getStage() == SCHEDSTAGE_GUIDING)
3705 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Calibration & Guide stage...";
3708 if (status == Ekos::GUIDE_GUIDING)
3710 appendLogText(
i18n(
"Job '%1' guiding is in progress.", activeJob()->getName()));
3711 moduleState()->resetGuideFailureCount();
3713 moduleState()->cancelGuidingTimer();
3715 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
3718 else if (status == Ekos::GUIDE_CALIBRATION_ERROR ||
3719 status == Ekos::GUIDE_ABORTED)
3721 if (status == Ekos::GUIDE_ABORTED)
3722 appendLogText(
i18n(
"Warning: job '%1' guiding failed.", activeJob()->getName()));
3724 appendLogText(
i18n(
"Warning: job '%1' calibration failed.", activeJob()->getName()));
3730 if (moduleState()->isGuidingTimerActive())
3733 if (moduleState()->increaseGuideFailureCount())
3735 if (status == Ekos::GUIDE_CALIBRATION_ERROR &&
3736 Options::realignAfterCalibrationFailure())
3738 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3743 appendLogText(
i18n(
"Job '%1' is guiding, guiding procedure will be restarted in %2 seconds.", activeJob()->getName(),
3744 (RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount()) / 1000));
3745 moduleState()->startGuidingTimer(RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount());
3750 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3759void SchedulerProcess::setCaptureStatus(
CaptureState status,
const QString &trainname)
3761 if (activeJob() ==
nullptr || !m_activeJobs.contains(trainname))
3764 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Capture State" << Ekos::getCaptureStatusString(status) <<
"train =" << trainname;
3766 SchedulerJob *job = m_activeJobs[trainname];
3771 QDateTime
const now = SchedulerModuleState::getLocalTime();
3772 if (now < job->getStartupTime())
3776 if (job->getStage() == SCHEDSTAGE_CAPTURING)
3788 const SkyPoint targetCoords = activeJob()->getTargetCoords();
3789 QList<QVariant> targetArgs;
3791 alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords", targetArgs);
3796 appendLogText(
i18n(
"[%2] Warning: job '%1' failed to capture target.", job->getName(), trainname));
3801 if (moduleState()->increaseCaptureFailureCount())
3806 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
3810 if (gStatus == Ekos::GUIDE_ABORTED ||
3811 gStatus == Ekos::GUIDE_CALIBRATION_ERROR ||
3812 gStatus == GUIDE_DITHERING_ERROR)
3814 appendLogText(
i18n(
"[%2] Job '%1' is capturing, is restarting its guiding procedure (attempt #%3 of %4).",
3815 activeJob()->getName(), trainname,
3816 moduleState()->captureFailureCount(), moduleState()->maxFailureAttempts()));
3823 appendLogText(
i18n(
"Warning: job '%1' failed its capture procedure, restarting capture.", activeJob()->getName()));
3829 appendLogText(
i18n(
"[%2] Warning: job '%1' failed its capture procedure, marking aborted.", job->getName(), trainname));
3832 stopCapturing(
"",
true);
3839 if (job->leadJob()->getStage() == SCHEDSTAGE_CAPTURING)
3842 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted, is restarting.", job->getName(), trainname));
3844 startSingleCapture(job,
true);
3848 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted.", job->getName(), trainname));
3855 KSNotification::event(QLatin1String(
"EkosScheduledImagingFinished"),
3856 i18n(
"[%2] Job (%1) - Capture finished", job->getName(), trainname), KSNotification::Scheduler);
3868 if (job->getCompletionCondition() == FINISH_LOOP ||
3869 (job->getCompletionCondition() == FINISH_REPEAT && job->getRepeatsRemaining() > 0))
3872 startSingleCapture(job,
false);
3878 job->setStage(SCHEDSTAGE_COMPLETE);
3886 if (Options::rememberJobProgress())
3890 for (
const auto &job : moduleState()->jobs())
3891 SchedulerUtils::estimateJobTime(job, moduleState()->capturedFramesCount(),
this);
3895 activeJob()->setCompletedCount(job->getCompletedCount() + 1);
3899 moduleState()->resetCaptureFailureCount();
3904void SchedulerProcess::setFocusStatus(FocusState status,
const QString &trainname)
3906 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3909 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Train " << trainname <<
"focus state" << Ekos::getFocusStatusString(status);
3912 if (m_activeJobs.contains(trainname) ==
false)
3915 SchedulerJob *currentJob = m_activeJobs[trainname];
3920 QDateTime
const now = SchedulerModuleState::getLocalTime();
3921 if (now < activeJob()->getStartupTime())
3925 if (activeJob()->getStage() == SCHEDSTAGE_FOCUSING)
3928 if (status == Ekos::FOCUS_COMPLETE)
3930 appendLogText(
i18n(
"Job '%1' focusing train '%2' is complete.", currentJob->getName(), trainname));
3932 moduleState()->setAutofocusCompleted(trainname,
true);
3934 if (moduleState()->autofocusCompleted())
3936 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
3940 else if (status == Ekos::FOCUS_FAILED || status == Ekos::FOCUS_ABORTED)
3942 appendLogText(
i18n(
"Warning: job '%1' focusing failed.", currentJob->getName()));
3944 if (moduleState()->increaseFocusFailureCount(trainname))
3946 appendLogText(
i18n(
"Job '%1' for train '%2' is restarting its focusing procedure.", currentJob->getName(), trainname));
3951 appendLogText(
i18n(
"Warning: job '%1' on train '%2' focusing procedure failed, marking aborted.", activeJob()->getName(),
3961void SchedulerProcess::setMountStatus(ISD::Mount::Status status)
3963 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3966 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount State changed to" << status;
3970 if (
static_cast<QDateTime const
>(SchedulerModuleState::getLocalTime()) < activeJob()->getStartupTime())
3973 switch (activeJob()->getStage())
3975 case SCHEDSTAGE_SLEWING:
3977 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Slewing stage...";
3979 if (status == ISD::Mount::MOUNT_TRACKING)
3982 moduleState()->updateJobStage(SCHEDSTAGE_SLEW_COMPLETE);
3985 else if (status == ISD::Mount::MOUNT_ERROR)
3987 appendLogText(
i18n(
"Warning: job '%1' slew failed, marking terminated due to errors.", activeJob()->getName()));
3991 else if (status == ISD::Mount::MOUNT_IDLE)
3993 appendLogText(
i18n(
"Warning: job '%1' found not slewing, restarting.", activeJob()->getName()));
3994 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
4000 case SCHEDSTAGE_RESLEWING:
4002 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Re-slewing stage...";
4004 if (status == ISD::Mount::MOUNT_TRACKING)
4006 appendLogText(
i18n(
"Job '%1' repositioning is complete.", activeJob()->getName()));
4007 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING_COMPLETE);
4010 else if (status == ISD::Mount::MOUNT_ERROR)
4012 appendLogText(
i18n(
"Warning: job '%1' repositioning failed, marking terminated due to errors.", activeJob()->getName()));
4016 else if (status == ISD::Mount::MOUNT_IDLE)
4018 appendLogText(
i18n(
"Warning: job '%1' found not repositioning, restarting.", activeJob()->getName()));
4019 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
4027 case SCHEDSTAGE_FOCUSING:
4028 case SCHEDSTAGE_ALIGNING:
4029 case SCHEDSTAGE_GUIDING:
4030 if (status == ISD::Mount::MOUNT_PARKED)
4032 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
4039 case SCHEDSTAGE_CAPTURING:
4040 if (status == ISD::Mount::MOUNT_PARKED && activeJob() && activeJob()->getLightFramesRequired()
4041 && activeJob()->getCalibrationMountPark() ==
false)
4043 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
4053void SchedulerProcess::setWeatherStatus(ISD::Weather::Status status)
4055 ISD::Weather::Status newStatus = status;
4057 if (newStatus == moduleState()->weatherStatus())
4060 moduleState()->setWeatherStatus(newStatus);
4064 if (activeJob() && activeJob()->getEnforceWeather() && moduleState()->weatherStatus() == ISD::Weather::WEATHER_ALERT
4065 && moduleState()->schedulerState() != Ekos::SCHEDULER_IDLE && moduleState()->schedulerState() != Ekos::SCHEDULER_SHUTDOWN)
4076 emit newWeatherStatus(status);
4079void SchedulerProcess::checkStartupProcedure()
4085void SchedulerProcess::checkShutdownProcedure()
4090 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
4094 if (Options::stopEkosAfterShutdown())
4097 else if (moduleState()->shutdownState() == SHUTDOWN_ERROR)
4100 moduleState()->setShutdownState(SHUTDOWN_IDLE);
4109void SchedulerProcess::parkCap()
4111 if (capInterface().isNull())
4114 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4118 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4119 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4121 if (parkingStatus.
isValid() ==
false)
4123 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4124 mountInterface()->lastError().
type());
4126 parkingStatus = ISD::PARK_ERROR;
4129 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4131 if (status != ISD::PARK_PARKED)
4133 moduleState()->setShutdownState(SHUTDOWN_PARKING_CAP);
4134 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking dust cap...";
4138 moduleState()->startCurrentOperationTimer();
4143 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
4147void SchedulerProcess::unParkCap()
4149 if (capInterface().isNull())
4151 appendLogText(
i18n(
"Dust cover unpark requested but no dust covers detected."));
4152 moduleState()->setStartupState(STARTUP_ERROR);
4156 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4157 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4159 if (parkingStatus.
isValid() ==
false)
4161 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4162 mountInterface()->lastError().
type());
4164 parkingStatus = ISD::PARK_ERROR;
4167 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4169 if (status != ISD::PARK_UNPARKED)
4171 moduleState()->setStartupState(STARTUP_UNPARKING_CAP);
4175 moduleState()->startCurrentOperationTimer();
4180 moduleState()->setStartupState(STARTUP_COMPLETE);
4184void SchedulerProcess::parkMount()
4186 if (mountInterface().isNull())
4189 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4193 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4194 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4196 if (parkingStatus.
isValid() ==
false)
4198 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4199 mountInterface()->lastError().
type());
4201 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4204 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4208 case ISD::PARK_PARKED:
4209 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4210 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
4212 moduleState()->setParkWaitState(PARKWAIT_PARKED);
4216 case ISD::PARK_UNPARKING:
4222 case ISD::PARK_ERROR:
4223 case ISD::PARK_UNKNOWN:
4224 case ISD::PARK_UNPARKED:
4226 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking mount...";
4227 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"park");
4231 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount park request received DBUS error: %1").arg(
4234 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4236 else moduleState()->startCurrentOperationTimer();
4240 case ISD::PARK_PARKING:
4242 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4243 moduleState()->setShutdownState(SHUTDOWN_PARKING_MOUNT);
4245 moduleState()->setParkWaitState(PARKWAIT_PARKING);
4256void SchedulerProcess::unParkMount()
4258 if (mountInterface().isNull())
4261 moduleState()->setStartupState(STARTUP_ERROR);
4265 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4266 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4268 if (parkingStatus.
isValid() ==
false)
4270 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4271 mountInterface()->lastError().
type());
4273 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4276 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4281 case ISD::PARK_UNPARKED:
4282 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4283 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
4285 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
4290 case ISD::PARK_PARKING:
4296 case ISD::PARK_ERROR:
4297 case ISD::PARK_UNKNOWN:
4298 case ISD::PARK_PARKED:
4300 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"unpark");
4304 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount unpark request received DBUS error: %1").arg(
4307 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4309 else moduleState()->startCurrentOperationTimer();
4314 case ISD::PARK_UNPARKING:
4315 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4316 moduleState()->setStartupState(STARTUP_UNPARKING_MOUNT);
4318 moduleState()->setParkWaitState(PARKWAIT_UNPARKING);
4319 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
4330 QVariant var = mountInterface()->property(
"equatorialCoords");
4333 if (var.isValid() ==
false || var.canConvert<
QList<double>>() ==
false)
4335 qCCritical(KSTARS_EKOS_SCHEDULER) <<
"Warning: reading equatorial coordinates received an unexpected value:" << var;
4340 if (coords.
size() != 2)
4342 qCCritical(KSTARS_EKOS_SCHEDULER) <<
"Warning: reading equatorial coordinates received" << coords.
size() <<
4343 "instead of 2 values: " << coords;
4347 return SkyPoint(coords[0], coords[1]);
4352 if (mountInterface().isNull())
4356 QVariant canPark = mountInterface()->property(
"canPark");
4357 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount can park:" << (!canPark.
isValid() ?
"invalid" : (canPark.
toBool() ?
"T" :
"F"));
4359 if (canPark.
isValid() ==
false)
4361 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount canPark request received DBUS error: %1").
arg(
4362 mountInterface()->lastError().type());
4366 else if (canPark.
toBool() ==
true)
4370 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4371 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4373 if (parkingStatus.
isValid() ==
false)
4375 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parking status property is invalid %1.").
arg(
4376 mountInterface()->lastError().type());
4382 switch (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()))
4386 case ISD::PARK_PARKED:
4401void SchedulerProcess::parkDome()
4404 if (domeInterface().isNull())
4407 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4413 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4414 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4416 if (parkingStatus.
isValid() ==
false)
4418 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4419 mountInterface()->lastError().type());
4421 parkingStatus = ISD::PARK_ERROR;
4424 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4425 if (status != ISD::PARK_PARKED)
4427 moduleState()->setShutdownState(SHUTDOWN_PARKING_DOME);
4431 moduleState()->startCurrentOperationTimer();
4436 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
4440void SchedulerProcess::unParkDome()
4443 if (domeInterface().isNull())
4446 moduleState()->setStartupState(STARTUP_ERROR);
4450 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4451 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4453 if (parkingStatus.
isValid() ==
false)
4455 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
4456 mountInterface()->lastError().
type());
4458 parkingStatus = ISD::PARK_ERROR;
4461 if (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()) != ISD::PARK_UNPARKED)
4463 moduleState()->setStartupState(STARTUP_UNPARKING_DOME);
4467 moduleState()->startCurrentOperationTimer();
4472 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
4478 QVariant guideStatus = guideInterface()->property(
"status");
4479 Ekos::GuideState gStatus =
static_cast<Ekos::GuideState
>(guideStatus.
toInt());
4484const QString &SchedulerProcess::profile()
const
4486 return moduleState()->currentProfile();
4489void SchedulerProcess::setProfile(
const QString &newProfile)
4491 moduleState()->setCurrentProfile(newProfile);
4494QString SchedulerProcess::currentJobName()
4496 auto job = moduleState()->activeJob();
4497 return ( job !=
nullptr ? job->getName() : QString() );
4500QString SchedulerProcess::currentJobJson()
4502 auto job = moduleState()->activeJob();
4503 if( job !=
nullptr )
4505 return QString( QJsonDocument( job->toJson() ).toJson() );
4513QString SchedulerProcess::jsonJobs()
4515 return QString( QJsonDocument( moduleState()->getJSONJobs() ).toJson() );
4518QStringList SchedulerProcess::logText()
4520 return moduleState()->logText();
4525 if (domeInterface().isNull())
4528 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4529 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4531 if (parkingStatus.
isValid() ==
false)
4533 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4534 mountInterface()->lastError().type());
4536 parkingStatus = ISD::PARK_ERROR;
4539 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4541 return status == ISD::PARK_PARKED;
4544void SchedulerProcess::simClockScaleChanged(
float newScale)
4546 if (moduleState()->currentlySleeping())
4549 (moduleState()->iterationTimer().remainingTime())
4550 * KStarsData::Instance()->clock()->scale()
4552 appendLogText(
i18n(
"Sleeping for %1 on simulation clock update until next observation job is ready...",
4553 remainingTimeMs.
toString(
"hh:mm:ss")));
4554 moduleState()->iterationTimer().stop();
4559void SchedulerProcess::simClockTimeChanged()
4561 moduleState()->calculateDawnDusk();
4564 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
4568void SchedulerProcess::setINDICommunicationStatus(CommunicationStatus status)
4570 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler INDI status is" << status;
4572 moduleState()->setIndiCommunicationStatus(status);
4575void SchedulerProcess::setEkosCommunicationStatus(CommunicationStatus status)
4577 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler Ekos status is" << status;
4579 moduleState()->setEkosCommunicationStatus(status);
4584void SchedulerProcess::checkInterfaceReady(QDBusInterface * iface)
4586 if (iface == mountInterface())
4589 moduleState()->setMountReady(
true);
4591 else if (iface == capInterface())
4594 moduleState()->setCapReady(
true);
4596 else if (iface == observatoryInterface())
4598 QVariant status = observatoryInterface()->property(
"status");
4599 if (status.isValid())
4600 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4602 else if (iface == weatherInterface())
4604 QVariant status = weatherInterface()->property(
"status");
4605 if (status.isValid())
4606 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4608 else if (iface == domeInterface())
4611 moduleState()->setDomeReady(
true);
4613 else if (iface == captureInterface())
4616 moduleState()->setCaptureReady(
true);
4619 emit interfaceReady(iface);
4622void SchedulerProcess::registerNewModule(
const QString &name)
4624 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Registering new Module (" <<
name <<
")";
4626 if (name ==
"Focus")
4628 delete focusInterface();
4629 setFocusInterface(
new QDBusInterface(kstarsInterfaceString, focusPathString, focusInterfaceString,
4631 connect(focusInterface(), SIGNAL(newStatus(Ekos::FocusState,
const QString)),
this,
4634 else if (name ==
"Capture")
4636 delete captureInterface();
4637 setCaptureInterface(
new QDBusInterface(kstarsInterfaceString, capturePathString, captureInterfaceString,
4640 connect(captureInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4643 connect(captureInterface(), SIGNAL(captureComplete(QVariantMap,
const QString)),
this, SLOT(checkAlignment(QVariantMap,
4646 checkInterfaceReady(captureInterface());
4648 else if (name ==
"Mount")
4650 delete mountInterface();
4651 setMountInterface(
new QDBusInterface(kstarsInterfaceString, mountPathString, mountInterfaceString,
4654 connect(mountInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4655 connect(mountInterface(), SIGNAL(newStatus(ISD::Mount::Status)),
this, SLOT(setMountStatus(ISD::Mount::Status)),
4658 checkInterfaceReady(mountInterface());
4660 else if (name ==
"Align")
4662 delete alignInterface();
4663 setAlignInterface(
new QDBusInterface(kstarsInterfaceString, alignPathString, alignInterfaceString,
4668 else if (name ==
"Guide")
4670 delete guideInterface();
4671 setGuideInterface(
new QDBusInterface(kstarsInterfaceString, guidePathString, guideInterfaceString,
4673 connect(guideInterface(), SIGNAL(newStatus(Ekos::GuideState)),
this,
4676 else if (name ==
"Observatory")
4678 delete observatoryInterface();
4679 setObservatoryInterface(
new QDBusInterface(kstarsInterfaceString, observatoryPathString, observatoryInterfaceString,
4681 connect(observatoryInterface(), SIGNAL(newStatus(ISD::Weather::Status)),
this,
4683 checkInterfaceReady(observatoryInterface());
4687void SchedulerProcess::registerNewDevice(
const QString &name,
int interface)
4691 if (interface & INDI::BaseDevice::DOME_INTERFACE)
4693 QList<QVariant> dbusargs;
4694 dbusargs.
append(INDI::BaseDevice::DOME_INTERFACE);
4695 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4700 setDomePathString(paths.value().last());
4701 delete domeInterface();
4702 setDomeInterface(
new QDBusInterface(kstarsInterfaceString, domePathString,
4703 domeInterfaceString,
4705 connect(domeInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4706 checkInterfaceReady(domeInterface());
4731 if (interface & INDI::BaseDevice::DUSTCAP_INTERFACE)
4733 QList<QVariant> dbusargs;
4734 dbusargs.
append(INDI::BaseDevice::DUSTCAP_INTERFACE);
4735 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4740 setDustCapPathString(paths.value().last());
4741 delete capInterface();
4742 setCapInterface(
new QDBusInterface(kstarsInterfaceString, dustCapPathString,
4743 dustCapInterfaceString,
4745 connect(capInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4746 checkInterfaceReady(capInterface());
4753 XMLEle *ep =
nullptr;
4754 XMLEle *subEP =
nullptr;
4756 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
4758 if (!strcmp(tagXMLEle(ep),
"Job"))
4760 for (subEP = nextXMLEle(ep, 1); subEP !=
nullptr; subEP = nextXMLEle(ep, 0))
4762 if (!strcmp(tagXMLEle(subEP),
"TargetName"))
4767 else if (!strcmp(tagXMLEle(subEP),
"FITSDirectory"))
4780 if (outputFile ==
nullptr)
4782 QString message =
i18n(
"Unable to write to file %1", filename);
4783 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
4787 fprintf(outputFile,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4788 prXMLEle(outputFile, root, 0);
4802 KSNotification::sorry(
i18n(
"Unable to open file %1", sFile.
fileName()),
4803 i18n(
"Could Not Open File"));
4807 LilXML *xmlParser = newLilXML();
4808 char errmsg[MAXRBUF];
4809 XMLEle *root =
nullptr;
4814 root = readXMLEle(xmlParser, c, errmsg);
4820 delLilXML(xmlParser);
4825void SchedulerProcess::checkProcessExit(
int exitCode)
4831 if (moduleState()->startupState() == STARTUP_SCRIPT)
4832 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
4833 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4834 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
4839 if (moduleState()->startupState() == STARTUP_SCRIPT)
4842 moduleState()->setStartupState(STARTUP_ERROR);
4844 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4847 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4852void SchedulerProcess::readProcessOutput()
4854 appendLogText(scriptProcess().readAllStandardOutput().simplified());
4857bool SchedulerProcess::canCountCaptures(
const SchedulerJob &job)
4859 QList<QSharedPointer<SequenceJob>> seqjobs;
4860 bool hasAutoFocus =
false;
4861 SchedulerJob tempJob = job;
4862 if (SchedulerUtils::loadSequenceQueue(tempJob.getSequenceFile().toLocalFile(), &tempJob, seqjobs, hasAutoFocus,
4866 for (
auto oneSeqJob : seqjobs)
4868 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
4882 forced |= std::any_of(moduleState()->jobs().begin(),
4883 moduleState()->jobs().end(), [](SchedulerJob * oneJob) ->
bool
4890 moduleState()->capturedFramesCount().clear();
4893 for (SchedulerJob *oneJob : moduleState()->jobs())
4900 bool hasAutoFocus =
false;
4904 if (SchedulerUtils::loadSequenceQueue(oneJob->getSequenceFile().
toLocalFile(), oneJob, seqjobs, hasAutoFocus,
4907 appendLogText(
i18n(
"Warning: job '%1' has inaccessible sequence '%2', marking invalid.", oneJob->getName(),
4913 oneJob->clearProgress();
4915 for (
auto oneSeqJob : seqjobs)
4919 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
4923 QString const signature = oneSeqJob->getSignature();
4931 const int count = newFramesCount.
constFind(signature).value();
4932 newJobFramesCount[signature] = count;
4933 oneJob->addProgress(count, oneSeqJob);
4939 CapturedFramesMap::const_iterator
const earlierRunIterator =
4940 moduleState()->capturedFramesCount().constFind(signature);
4942 if (moduleState()->capturedFramesCount().constEnd() != earlierRunIterator)
4944 count = earlierRunIterator.value();
4947 count = PlaceholderPath::getCompletedFiles(signature);
4949 newFramesCount[signature] = count;
4950 newJobFramesCount[signature] = count;
4951 oneJob->addProgress(count, oneSeqJob);
4955 SchedulerUtils::updateLightFramesRequired(oneJob, seqjobs, newFramesCount);
4958 moduleState()->setCapturedFramesCount(newFramesCount);
4961 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Frame map summary:";
4962 CapturedFramesMap::const_iterator it = moduleState()->capturedFramesCount().constBegin();
4963 for (; it != moduleState()->capturedFramesCount().constEnd(); it++)
4964 qCDebug(KSTARS_EKOS_SCHEDULER) <<
" " << it.key() <<
':' << it.value();
4968SchedulerJob *SchedulerProcess::activeJob()
4970 return moduleState()->activeJob();
4973void SchedulerProcess::printStates(
const QString &label)
4975 qCDebug(KSTARS_EKOS_SCHEDULER) <<
4976 QString(
"%1 %2 %3%4 %5 %6 %7 %8 %9\n")
4978 .
arg(timerStr(moduleState()->timerState()))
4979 .
arg(getSchedulerStatusString(moduleState()->schedulerState()))
4980 .
arg((moduleState()->timerState() == RUN_JOBCHECK && activeJob() !=
nullptr) ?
4981 QString(
"(%1 %2)").arg(SchedulerJob::jobStatusString(activeJob()->getState()))
4982 .arg(SchedulerJob::jobStageString(activeJob()->getStage())) :
"")
4983 .
arg(ekosStateString(moduleState()->ekosState()))
4984 .
arg(indiStateString(moduleState()->indiState()))
4985 .
arg(startupStateString(moduleState()->startupState()))
4986 .
arg(shutdownStateString(moduleState()->shutdownState()))
4987 .
arg(parkWaitStateString(moduleState()->parkWaitState())).
toLatin1().
data();
4988 foreach (
auto j, moduleState()->jobs())
4989 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...)
Ekos is an advanced Astrophotography tool for Linux.
SchedulerJobStatus
States of a SchedulerJob.
@ SCHEDJOB_ABORTED
Job encountered a transitory issue while processing, and will be rescheduled.
@ SCHEDJOB_INVALID
Job has an incorrect configuration, and cannot proceed.
@ SCHEDJOB_ERROR
Job encountered a fatal issue while processing, and must be reset manually.
@ SCHEDJOB_COMPLETE
Job finished all required captures.
@ SCHEDJOB_EVALUATION
Job is being evaluated.
@ SCHEDJOB_SCHEDULED
Job was evaluated, and has a schedule.
@ SCHEDJOB_BUSY
Job is being processed.
@ SCHEDJOB_IDLE
Job was just created, and is not evaluated yet.
QMap< QString, uint16_t > CapturedFramesMap
mapping signature --> frames count
ErrorHandlingStrategy
options what should happen if an error or abort occurs
@ ALIGN_FAILED
Alignment failed.
@ ALIGN_ABORTED
Alignment aborted by user or agent.
@ ALIGN_IDLE
No ongoing operations.
@ ALIGN_COMPLETE
Alignment successfully completed.
CaptureState
Capture states.
SchedulerTimerState
IterationTypes, the different types of scheduler iterations that are run.
GeoCoordinates geo(const QVariant &location)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
bool isValid(QStringView ifopt)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QString name(StandardAction id)
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