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()->setAutofocusCompleted(
false);
587 if (moduleState()->preemptiveShutdown())
589 QDateTime const now = SchedulerModuleState::getLocalTime();
590 int const nextObservationTime = now.
secsTo(moduleState()->preemptiveShutdownWakeupTime());
591 moduleState()->setupNextIteration(RUN_WAKEUP,
592 std::lround(((nextObservationTime + 1) * 1000)
593 / KStarsData::Instance()->clock()->scale()));
595 emit schedulerStopped();
600 if (captureInterface().isNull() ==
false)
601 captureInterface()->setProperty(
"targetName",
QString());
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");
773 if (activeJob()->getStage() == SCHEDSTAGE_RESLEWING_COMPLETE ||
774 activeJob()->getStage() == SCHEDSTAGE_POSTALIGN_FOCUSING)
780 moduleState()->updateJobStage(SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE);
791 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' canAutoFocus request received DBUS error: %2").
arg(
801 if (focusModeReply.value() ==
false)
803 appendLogText(
i18n(
"Warning: job '%1' is unable to proceed with autofocus, not supported.", activeJob()->getName()));
804 activeJob()->setStepPipeline(
805 static_cast<SchedulerJob::StepPipeline
>(activeJob()->getStepPipeline() & ~SchedulerJob::USE_FOCUS));
806 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
819 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' resetFrame request received DBUS error: %2").
arg(
831 if (!activeJob()->getInitialFilter().isEmpty())
833 focusInterface()->setProperty(
"filter", activeJob()->getInitialFilter());
837 if (Options::focusUseFullField() ==
false)
841 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setAutoStarEnabled", autoStar)).type() ==
844 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' setAutoFocusStar request received DBUS error: %1").
arg(
858 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' startFocus request received DBUS error: %2").
arg(
868 moduleState()->updateJobStage(SCHEDSTAGE_FOCUSING);
870 moduleState()->startCurrentOperationTimer();
875 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting aligning must be valid");
886 moduleState()->setIndexToUse(-1);
887 moduleState()->setHealpixToUse(-1);
890 if (activeJob()->getFITSFile().isEmpty() ==
false)
896 appendLogText(
i18n(
"Warning: job '%1' target FITS file does not exist.", activeJob()->getName()));
905 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"loadAndSlew", solveArgs)).type() ==
908 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request received DBUS error: %2",
917 else if (reply.
arguments().first().toBool() ==
false)
919 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request failed.", activeJob()->getName()));
925 appendLogText(
i18n(
"Job '%1' is plate solving %2.", activeJob()->getName(), activeJob()->getFITSFile().fileName()));
931 const SkyPoint targetCoords = activeJob()->getTargetCoords();
934 rotationArgs << activeJob()->getPositionAngle();
936 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords",
939 appendLogText(
i18n(
"Warning: job '%1' setTargetCoords request received DBUS error: %2",
950 if (activeJob()->getPositionAngle() >= -180)
952 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetPositionAngle",
955 appendLogText(
i18n(
"Warning: job '%1' setTargetPositionAngle request received DBUS error: %2").arg(
968 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request received DBUS error: %2").arg(
977 else if (reply.
arguments().first().toBool() ==
false)
979 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request failed.", activeJob()->getName()));
985 appendLogText(
i18n(
"Job '%1' is capturing and plate solving.", activeJob()->getName()));
989 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
990 moduleState()->startCurrentOperationTimer();
995 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting guiding must be valid");
1000 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
1001 appendLogText(
i18n(
"Guiding already running for %1, starting next scheduler action...", activeJob()->getName()));
1003 moduleState()->startCurrentOperationTimer();
1016 if (resetCalibration && Options::resetGuideCalibration())
1023 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1025 appendLogText(
i18n(
"Starting guiding procedure for %1 ...", activeJob()->getName()));
1027 moduleState()->startCurrentOperationTimer();
1032 if (!guideInterface())
1036 if (
nullptr != activeJob() && (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE))
1038 qCInfo(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' is stopping guiding...").
arg(activeJob()->getName());
1040 moduleState()->resetGuideFailureCount();
1042 stopCapturing(
"",
true);
1046 if (moduleState()->isGuidingTimerActive())
1047 moduleState()->cancelGuidingTimer();
1052 if ((moduleState()->restartGuidingInterval() > 0) &&
1053 (moduleState()->restartGuidingTime().msecsTo(KStarsData::Instance()->ut()) > moduleState()->restartGuidingInterval()))
1055 moduleState()->cancelGuidingTimer();
1062 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting capturing must be valid");
1065 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE &&
getGuidingStatus() != GUIDE_GUIDING)
1068 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1073 startSingleCapture(activeJob(), restart);
1074 for (
auto follower : activeJob()->followerJobs())
1077 if (follower->getState() ==
SCHEDJOB_SCHEDULED || (follower->getStage() == SCHEDSTAGE_CAPTURING && follower->isStopped()))
1080 follower->setStage(SCHEDSTAGE_CAPTURING);
1081 startSingleCapture(follower, restart);
1085 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
1087 KSNotification::event(
QLatin1String(
"EkosScheduledImagingStart"),
1088 i18n(
"Ekos job (%1) - Capture started", activeJob()->getName()), KSNotification::Scheduler);
1090 if (moduleState()->captureBatch() > 0)
1091 appendLogText(
i18n(
"Job '%1' capture is in progress (batch #%2)...", activeJob()->getName(),
1092 moduleState()->captureBatch() + 1));
1094 appendLogText(
i18n(
"Job '%1' capture is in progress...", activeJob()->getName()));
1096 moduleState()->startCurrentOperationTimer();
1099void SchedulerProcess::startSingleCapture(SchedulerJob *job,
bool restart)
1101 captureInterface()->setProperty(
"targetName", job->getName());
1104 QVariant train(job->getOpticalTrain());
1106 if (restart ==
false)
1111 QVariant targetName(job->getName());
1115 dbusargs.
append(targetName);
1117 "loadSequenceQueue",
1121 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1122 QString(
"Warning: job '%1' loadSequenceQueue request received DBUS error: %1").
arg(job->getName()).
arg(
1129 else if (captureReply.value() ==
false)
1131 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1132 QString(
"Warning: job '%1' loadSequenceQueue request failed").
arg(job->getName());
1141 for (
auto &e : fMap.keys())
1143 QList<QVariant> dbusargs;
1146 dbusargs.
append(fMap.value(e));
1149 if ((reply = captureInterface()->callWithArgumentList(
QDBus::Block,
"setCapturedFramesMap",
1150 dbusargs)).
type() ==
1153 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1154 QString(
"Warning: job '%1' setCapturedFramesCount request received DBUS error: %1").arg(job->getName()).arg(
1163 QList<QVariant> dbusargs;
1166 QDBusReply<QString>
const startReply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"start",
1171 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1172 QString(
"Warning: job '%1' start request received DBUS error: %1").arg(job->getName()).arg(
1179 QString trainName = startReply.value();
1180 m_activeJobs[trainName] = job;
1186 QVariant gotoMode(
static_cast<int>(mode));
1192 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Loading profiles";
1196 moduleState()->updateProfiles(profiles);
1199void SchedulerProcess::executeScript(
const QString &filename)
1208 checkProcessExit(exitCode);
1212 scriptProcess().
start(filename, arguments);
1217 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1220 switch (moduleState()->ekosState())
1224 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1226 moduleState()->setEkosState(EKOS_READY);
1232 moduleState()->setEkosState(EKOS_STARTING);
1233 moduleState()->startCurrentOperationTimer();
1235 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos communication status is" << moduleState()->ekosCommunicationStatus() <<
1244 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1247 moduleState()->resetEkosConnectFailureCount();
1248 moduleState()->setEkosState(EKOS_READY);
1251 else if (moduleState()->ekosCommunicationStatus() == Ekos::Error)
1253 if (moduleState()->increaseEkosConnectFailureCount())
1264 else if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1267 else if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1269 if (moduleState()->increaseEkosConnectFailureCount())
1276 moduleState()->startCurrentOperationTimer();
1290 if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1293 moduleState()->setEkosState(EKOS_IDLE);
1307 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1310 switch (moduleState()->indiState())
1314 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1316 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1317 moduleState()->resetIndiConnectFailureCount();
1318 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI Properties...";
1322 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Connecting INDI devices...";
1324 moduleState()->setIndiState(INDI_CONNECTING);
1326 moduleState()->startCurrentOperationTimer();
1331 case INDI_CONNECTING:
1333 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1336 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1338 else if (moduleState()->indiCommunicationStatus() == Ekos::Error)
1340 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1347 appendLogText(
i18n(
"One or more INDI devices failed to connect. Check INDI control panel for details."));
1352 else if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1354 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1358 moduleState()->startCurrentOperationTimer();
1362 appendLogText(
i18n(
"One or more INDI devices timed out. Check INDI control panel for details."));
1369 case INDI_DISCONNECTING:
1371 if (moduleState()->indiCommunicationStatus() == Ekos::Idle)
1374 moduleState()->setIndiState(INDI_IDLE);
1380 case INDI_PROPERTY_CHECK:
1382 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI properties.";
1384 if (Options::schedulerUnparkDome() && moduleState()->domeReady() ==
false)
1386 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1388 moduleState()->startCurrentOperationTimer();
1389 appendLogText(
i18n(
"Warning: dome device not ready after timeout, attempting to recover..."));
1399 if (Options::schedulerUnparkMount() && moduleState()->mountReady() ==
false)
1401 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1403 moduleState()->startCurrentOperationTimer();
1404 appendLogText(
i18n(
"Warning: mount device not ready after timeout, attempting to recover..."));
1409 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount unpark required but mount is not yet ready.";
1414 if (Options::schedulerOpenDustCover() && moduleState()->capReady() ==
false)
1416 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1418 moduleState()->startCurrentOperationTimer();
1419 appendLogText(
i18n(
"Warning: cap device not ready after timeout, attempting to recover..."));
1424 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap unpark required but cap is not yet ready.";
1429 if (captureInterface().isNull())
1432 if (moduleState()->captureReady() ==
false)
1434 QVariant hasCoolerControl = captureInterface()->property(
"coolerControl");
1435 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cooler control" << (!hasCoolerControl.
isValid() ?
"invalid" :
1436 (hasCoolerControl.
toBool() ?
"True" :
"Faklse"));
1437 if (hasCoolerControl.
isValid())
1438 moduleState()->setCaptureReady(
true);
1440 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"Capture module is not ready yet...";
1443 moduleState()->setIndiState(INDI_READY);
1444 moduleState()->resetIndiConnectFailureCount();
1458 if (moduleState()->indiState() == INDI_DISCONNECTING
1463 if (moduleState()->indiState() != INDI_IDLE && Options::stopEkosAfterShutdown())
1470 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
1474 if (moduleState()->ekosState() != EKOS_IDLE && Options::stopEkosAfterShutdown())
1480 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
1493 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Disconnecting INDI...";
1494 moduleState()->setIndiState(INDI_DISCONNECTING);
1498void SchedulerProcess::stopEkos()
1500 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Stopping Ekos...";
1501 moduleState()->setEkosState(EKOS_STOPPING);
1502 moduleState()->resetEkosConnectFailureCount();
1504 moduleState()->setMountReady(
false);
1505 moduleState()->setCaptureReady(
false);
1506 moduleState()->setDomeReady(
false);
1507 moduleState()->setCapReady(
false);
1512 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
1516 switch (moduleState()->ekosState())
1527 switch (moduleState()->indiState())
1530 case INDI_DISCONNECTING:
1539 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1541 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Ekos is currently connected, checking INDI before mitigating connection loss.");
1544 if (moduleState()->isINDIConnected())
1547 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"INDI is currently connected, no connection loss mitigation needed.");
1566 if (capInterface().isNull())
1569 QVariant parkingStatus = capInterface()->property(
"parkStatus");
1570 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1572 if (parkingStatus.
isValid() ==
false)
1574 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
1575 capInterface()->lastError().type());
1577 parkingStatus = ISD::PARK_ERROR;
1580 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1584 case ISD::PARK_PARKED:
1585 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1588 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
1590 moduleState()->resetParkingCapFailureCount();
1593 case ISD::PARK_UNPARKED:
1594 if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1596 moduleState()->setStartupState(STARTUP_COMPLETE);
1599 moduleState()->resetParkingCapFailureCount();
1602 case ISD::PARK_PARKING:
1603 case ISD::PARK_UNPARKING:
1605 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1607 if (moduleState()->increaseParkingCapFailureCount())
1610 if (status == ISD::PARK_PARKING)
1619 case ISD::PARK_ERROR:
1620 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1623 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1625 else if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1628 moduleState()->setStartupState(STARTUP_ERROR);
1630 moduleState()->resetParkingCapFailureCount();
1638void SchedulerProcess::checkMountParkingStatus()
1640 if (mountInterface().isNull())
1643 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
1644 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1646 if (parkingStatus.
isValid() ==
false)
1648 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
1649 mountInterface()->lastError().type());
1651 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1654 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1659 case ISD::PARK_PARKED:
1662 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1663 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1666 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1667 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1670 moduleState()->resetParkingMountFailureCount();
1674 case ISD::PARK_UNPARKED:
1677 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1678 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1681 if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1682 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1685 moduleState()->resetParkingMountFailureCount();
1691 case ISD::PARK_UNPARKING:
1692 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1694 if (moduleState()->increaseParkingMountFailureCount())
1696 appendLogText(
i18n(
"Warning: mount unpark operation timed out on attempt %1/%2. Restarting operation...",
1697 moduleState()->parkingMountFailureCount(), moduleState()->maxFailureAttempts()));
1702 appendLogText(
i18n(
"Warning: mount unpark operation timed out on last attempt."));
1703 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1706 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
1711 case ISD::PARK_PARKING:
1712 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1714 if (moduleState()->increaseParkingMountFailureCount())
1716 appendLogText(
i18n(
"Warning: mount park operation timed out on attempt %1/%2. Restarting operation...",
1717 moduleState()->parkingMountFailureCount(),
1718 moduleState()->maxFailureAttempts()));
1723 appendLogText(
i18n(
"Warning: mount park operation timed out on last attempt."));
1724 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1727 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Parking mount in progress...";
1732 case ISD::PARK_ERROR:
1733 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1736 moduleState()->setStartupState(STARTUP_ERROR);
1737 moduleState()->resetParkingMountFailureCount();
1739 else if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1741 if (moduleState()->increaseParkingMountFailureCount())
1743 appendLogText(
i18n(
"Warning: mount park operation failed on attempt %1/%2. Restarting operation...",
1744 moduleState()->parkingMountFailureCount(),
1745 moduleState()->maxFailureAttempts()));
1751 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1752 moduleState()->resetParkingMountFailureCount();
1756 else if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1759 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1760 moduleState()->resetParkingMountFailureCount();
1762 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1765 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1766 moduleState()->resetParkingMountFailureCount();
1772 case ISD::PARK_UNKNOWN:
1774 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1775 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1778 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1779 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1782 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1783 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1784 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1785 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1787 moduleState()->resetParkingMountFailureCount();
1792void SchedulerProcess::checkDomeParkingStatus()
1794 if (domeInterface().isNull())
1797 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
1798 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1800 if (parkingStatus.
isValid() ==
false)
1802 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
1803 mountInterface()->lastError().
type());
1805 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1808 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1812 case ISD::PARK_PARKED:
1813 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1817 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
1819 moduleState()->resetParkingDomeFailureCount();
1822 case ISD::PARK_UNPARKED:
1823 if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1825 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1828 moduleState()->resetParkingDomeFailureCount();
1831 case ISD::PARK_PARKING:
1832 case ISD::PARK_UNPARKING:
1834 if (moduleState()->getCurrentOperationMsec() > (120 * 1000))
1836 if (moduleState()->increaseParkingDomeFailureCount())
1839 if (status == ISD::PARK_PARKING)
1848 case ISD::PARK_ERROR:
1849 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1851 if (moduleState()->increaseParkingDomeFailureCount())
1859 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1860 moduleState()->resetParkingDomeFailureCount();
1863 else if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1865 if (moduleState()->increaseParkingDomeFailureCount())
1873 moduleState()->setStartupState(STARTUP_ERROR);
1874 moduleState()->resetParkingDomeFailureCount();
1886 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1889 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Checking Startup State (%1)...").
arg(moduleState()->startupState());
1891 switch (moduleState()->startupState())
1895 KSNotification::event(
QLatin1String(
"ObservatoryStartup"),
i18n(
"Observatory is in the startup process"),
1896 KSNotification::Scheduler);
1898 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Startup Idle. Starting startup process...";
1905 if (Options::alwaysExecuteStartupScript() ==
false && moduleState()->ekosCommunicationStatus() == Ekos::Success)
1907 if (moduleState()->startupScriptURL().isEmpty() ==
false)
1910 if (!activeJob() || activeJob()->getLightFramesRequired())
1911 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
1913 moduleState()->setStartupState(STARTUP_COMPLETE);
1917 if (moduleState()->currentProfile() !=
i18n(
"Default"))
1920 profile.
append(moduleState()->currentProfile());
1921 ekosInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setProfile", profile);
1924 if (moduleState()->startupScriptURL().isEmpty() ==
false)
1926 moduleState()->setStartupState(STARTUP_SCRIPT);
1931 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
1935 case STARTUP_SCRIPT:
1938 case STARTUP_UNPARK_DOME:
1942 if (activeJob() ==
nullptr || activeJob()->getLightFramesRequired())
1944 if (Options::schedulerUnparkDome())
1947 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1951 moduleState()->setStartupState(STARTUP_COMPLETE);
1957 case STARTUP_UNPARKING_DOME:
1958 checkDomeParkingStatus();
1961 case STARTUP_UNPARK_MOUNT:
1962 if (Options::schedulerUnparkMount())
1965 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1968 case STARTUP_UNPARKING_MOUNT:
1969 checkMountParkingStatus();
1972 case STARTUP_UNPARK_CAP:
1973 if (Options::schedulerOpenDustCover())
1976 moduleState()->setStartupState(STARTUP_COMPLETE);
1979 case STARTUP_UNPARKING_CAP:
1983 case STARTUP_COMPLETE:
1996 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking shutdown state...";
1998 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2001 switch (moduleState()->shutdownState())
2005 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Starting shutdown process...";
2007 moduleState()->setActiveJob(
nullptr);
2008 moduleState()->setupNextIteration(RUN_SHUTDOWN);
2009 emit shutdownStarted();
2011 if (Options::schedulerWarmCCD())
2018 if (captureInterface())
2020 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Setting coolerControl=false";
2021 captureInterface()->setProperty(
"coolerControl",
false);
2026 if (moduleState()->isINDIConnected())
2028 if (Options::schedulerCloseDustCover())
2030 moduleState()->setShutdownState(SHUTDOWN_PARK_CAP);
2034 if (Options::schedulerParkMount())
2036 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2040 if (Options::schedulerParkDome())
2042 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2046 else appendLogText(
i18n(
"Warning: Bypassing parking procedures, no INDI connection."));
2048 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2050 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2054 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2057 case SHUTDOWN_PARK_CAP:
2058 if (!moduleState()->isINDIConnected())
2060 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2061 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2063 else if (Options::schedulerCloseDustCover())
2066 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2069 case SHUTDOWN_PARKING_CAP:
2073 case SHUTDOWN_PARK_MOUNT:
2074 if (!moduleState()->isINDIConnected())
2076 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2077 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2079 else if (Options::schedulerParkMount())
2082 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2085 case SHUTDOWN_PARKING_MOUNT:
2086 checkMountParkingStatus();
2089 case SHUTDOWN_PARK_DOME:
2090 if (!moduleState()->isINDIConnected())
2092 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2093 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2095 else if (Options::schedulerParkDome())
2098 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2101 case SHUTDOWN_PARKING_DOME:
2102 checkDomeParkingStatus();
2105 case SHUTDOWN_SCRIPT:
2106 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2109 if (moduleState()->ekosState() != EKOS_IDLE && Options::shutdownScriptTerminatesINDI())
2115 moduleState()->setShutdownState(SHUTDOWN_SCRIPT_RUNNING);
2119 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2122 case SHUTDOWN_SCRIPT_RUNNING:
2125 case SHUTDOWN_COMPLETE:
2128 case SHUTDOWN_ERROR:
2138 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2141 if (moduleState()->parkWaitState() == PARKWAIT_IDLE)
2146 switch (moduleState()->parkWaitState())
2152 case PARKWAIT_PARKING:
2153 checkMountParkingStatus();
2156 case PARKWAIT_UNPARK:
2160 case PARKWAIT_UNPARKING:
2161 checkMountParkingStatus();
2165 case PARKWAIT_PARKED:
2166 case PARKWAIT_UNPARKED:
2169 case PARKWAIT_ERROR:
2181 if (moduleState()->startupState() == STARTUP_IDLE
2182 || moduleState()->startupState() == STARTUP_ERROR
2183 || moduleState()->startupState() == STARTUP_COMPLETE)
2187 KSMessageBox::Instance()->disconnect(
this);
2190 moduleState()->setStartupState(STARTUP_IDLE);
2196 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the startup procedure manually?"));
2200 switch (moduleState()->startupState())
2205 case STARTUP_SCRIPT:
2206 scriptProcess().terminate();
2209 case STARTUP_UNPARK_DOME:
2212 case STARTUP_UNPARKING_DOME:
2213 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking dome...";
2217 case STARTUP_UNPARK_MOUNT:
2220 case STARTUP_UNPARKING_MOUNT:
2221 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking mount...";
2225 case STARTUP_UNPARK_CAP:
2228 case STARTUP_UNPARKING_CAP:
2231 case STARTUP_COMPLETE:
2238 moduleState()->setStartupState(STARTUP_IDLE);
2247 if (moduleState()->shutdownState() == SHUTDOWN_IDLE
2248 || moduleState()->shutdownState() == SHUTDOWN_ERROR
2249 || moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
2253 KSMessageBox::Instance()->disconnect(
this);
2255 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2260 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the shutdown procedure manually?"));
2264 switch (moduleState()->shutdownState())
2269 case SHUTDOWN_SCRIPT:
2272 case SHUTDOWN_SCRIPT_RUNNING:
2273 scriptProcess().terminate();
2276 case SHUTDOWN_PARK_DOME:
2279 case SHUTDOWN_PARKING_DOME:
2280 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking dome...";
2284 case SHUTDOWN_PARK_MOUNT:
2287 case SHUTDOWN_PARKING_MOUNT:
2288 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking mount...";
2292 case SHUTDOWN_PARK_CAP:
2293 case SHUTDOWN_PARKING_CAP:
2296 case SHUTDOWN_COMPLETE:
2299 case SHUTDOWN_ERROR:
2303 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2311 moduleState()->setupNextIteration(RUN_NOTHING);
2313 emit schedulerPaused();
2319 for (SchedulerJob * job : moduleState()->jobs())
2322 job->setCompletedCount(0);
2331 auto finished_or_aborted = [](SchedulerJob
const *
const job)
2338 auto neither_scheduled_nor_aborted = [](SchedulerJob
const *
const job)
2346 if (jobs.
isEmpty() || std::all_of(jobs.
begin(), jobs.
end(), neither_scheduled_nor_aborted))
2349 moduleState()->setActiveJob(
nullptr);
2353 else if (std::all_of(jobs.
begin(), jobs.
end(), finished_or_aborted) &&
2354 strategy != ERROR_DONT_RESTART)
2356 appendLogText(
i18n(
"Only aborted jobs left in the scheduler queue after evaluating, rescheduling those."));
2357 std::for_each(jobs.
begin(), jobs.
end(), [](SchedulerJob * job)
2359 if (SCHEDJOB_ABORTED == job->getState())
2360 job->setState(SCHEDJOB_EVALUATION);
2367 SchedulerJob *scheduledJob = getGreedyScheduler()->getScheduledJob();
2371 moduleState()->setActiveJob(
nullptr);
2374 if (activeJob() !=
nullptr && scheduledJob != activeJob())
2377 for (
auto job : m_activeJobs.values())
2379 stopCapturing(job->getOpticalTrain(),
false);
2382 m_activeJobs.clear();
2384 moduleState()->setActiveJob(scheduledJob);
2392 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2396 moduleState()->resetSequenceExecutionCounter();
2404 for (
auto job : moduleState()->jobs())
2408 if (moduleState()->jobs().isEmpty())
2411 if (Options::rememberJobProgress())
2414 moduleState()->calculateDawnDusk();
2416 getGreedyScheduler()->scheduleJobs(moduleState()->jobs(), SchedulerModuleState::getLocalTime(),
2417 moduleState()->capturedFramesCount(),
this);
2421 if (!evaluateOnly && moduleState()->schedulerState() == SCHEDULER_RUNNING)
2426 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos finished evaluating jobs, no job selection required.";
2428 emit jobsUpdated(moduleState()->getJSONJobs());
2433 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2435 if (activeJob() ==
nullptr)
2440 switch (activeJob()->getState())
2456 if (activeJob() ==
nullptr)
2459 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE
2460 || moduleState()->shutdownState() == SHUTDOWN_ERROR)
2466 if (moduleState()->shutdownState() > SHUTDOWN_IDLE)
2469 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
2484 if (
nullptr == activeJob() && moduleState()->checkRepeatSequence())
2493 moduleState()->increaseSequenceExecutionCounter();
2494 appendLogText(
i18n(
"Starting job sequence iteration #%1", moduleState()->sequenceExecutionCounter()));
2500 if (
nullptr == activeJob())
2510 if (moduleState()->startupState() == STARTUP_ERROR)
2518 if ((moduleState()->startupState() == STARTUP_IDLE
2520 || moduleState()->startupState() == STARTUP_SCRIPT)
2536 if (moduleState()->startupState() > STARTUP_SCRIPT
2537 && moduleState()->startupState() < STARTUP_ERROR
2549 emit updateJobTable();
2557 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Get next action...";
2559 switch (activeJob()->getStage())
2561 case SCHEDSTAGE_IDLE:
2562 if (activeJob()->getLightFramesRequired())
2564 if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
2566 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2568 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3485";
2571 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2573 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2586 if (activeJob()->getStepPipeline())
2588 i18n(
"Job '%1' is proceeding directly to capture stage because only calibration frames are pending.",
2589 activeJob()->getName()));
2595 case SCHEDSTAGE_SLEW_COMPLETE:
2596 if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2598 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3514";
2601 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2603 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2609 case SCHEDSTAGE_FOCUS_COMPLETE:
2610 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2612 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2618 case SCHEDSTAGE_ALIGN_COMPLETE:
2619 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING);
2622 case SCHEDSTAGE_RESLEWING_COMPLETE:
2625 if ((activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS) && activeJob()->getInSequenceFocus())
2628 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3544";
2631 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2637 case SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE:
2638 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2644 case SCHEDSTAGE_GUIDING_COMPLETE:
2660 moduleState()->iterationTimer().setSingleShot(
true);
2661 moduleState()->iterationTimer().start(msSleep);
2668 if (moduleState()->startMSecs() == 0)
2669 moduleState()->setStartMSecs(now);
2682 moduleState()->setIterationSetup(
false);
2683 switch (keepTimerState)
2686 changeSleepLabel(
"",
false);
2699 moduleState()->setTimerInterval(-1);
2702 if (!moduleState()->iterationSetup())
2708 moduleState()->setTimerInterval(moduleState()->updatePeriodMs());
2711 return moduleState()->timerInterval();
2716 Q_ASSERT_X(activeJob(), __FUNCTION__,
"Actual current job is required to check job stage");
2720 if (checkJobStageCounter == 0)
2722 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking job stage for" << activeJob()->getName() <<
"startup" <<
2723 activeJob()->getStartupCondition() << activeJob()->getStartupTime().toString() <<
"state" << activeJob()->getState();
2724 if (checkJobStageCounter++ == 30)
2725 checkJobStageCounter = 0;
2728 emit syncGreedyParams();
2729 if (!getGreedyScheduler()->checkJob(moduleState()->leadJobs(), SchedulerModuleState::getLocalTime(), activeJob()))
2736 checkJobStageEpilogue();
2739void SchedulerProcess::checkJobStageEpilogue()
2754 if (!activeJob())
return;
2755 switch (activeJob()->getStage())
2757 case SCHEDSTAGE_IDLE:
2759 emit jobStarted(activeJob()->getName());
2763 case SCHEDSTAGE_ALIGNING:
2765 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(ALIGN_INACTIVITY_TIMEOUT))
2767 QVariant const status = alignInterface()->property(
"status");
2772 if (moduleState()->increaseAlignFailureCount())
2774 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align module timed out. Restarting request...";
2779 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
2785 moduleState()->startCurrentOperationTimer();
2789 case SCHEDSTAGE_CAPTURING:
2791 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(CAPTURE_INACTIVITY_TIMEOUT))
2793 QVariant
const status = captureInterface()->property(
"status");
2798 if (moduleState()->increaseCaptureFailureCount())
2800 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"capture module timed out. Restarting request...";
2805 appendLogText(
i18n(
"Warning: job '%1' capture procedure failed, marking aborted.", activeJob()->getName()));
2810 else moduleState()->startCurrentOperationTimer();
2814 case SCHEDSTAGE_FOCUSING:
2816 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(FOCUS_INACTIVITY_TIMEOUT))
2818 QVariant
const status = focusInterface()->property(
"status");
2819 Ekos::FocusState focusStatus =
static_cast<Ekos::FocusState
>(status.toInt());
2821 if (focusStatus == Ekos::FOCUS_IDLE || focusStatus == Ekos::FOCUS_WAITING)
2823 if (moduleState()->increaseFocusFailureCount())
2825 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus module timed out. Restarting request...";
2830 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
2835 else moduleState()->startCurrentOperationTimer();
2839 case SCHEDSTAGE_GUIDING:
2841 if (moduleState()->getCurrentOperationMsec() > GUIDE_INACTIVITY_TIMEOUT)
2845 if (guideStatus == Ekos::GUIDE_IDLE || guideStatus == Ekos::GUIDE_CONNECTED || guideStatus == Ekos::GUIDE_DISCONNECTED)
2847 if (moduleState()->increaseGuideFailureCount())
2849 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"guide module timed out. Restarting request...";
2854 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
2859 else moduleState()->startCurrentOperationTimer();
2863 case SCHEDSTAGE_SLEWING:
2864 case SCHEDSTAGE_RESLEWING:
2867 QVariant
const slewStatus = mountInterface()->property(
"status");
2873 ISD::Mount::Status
const status =
static_cast<ISD::Mount::Status
>(slewStatus.
toInt());
2874 setMountStatus(status);
2878 appendLogText(
i18n(
"Warning: job '%1' lost connection to the mount, attempting to reconnect.", activeJob()->getName()));
2886 case SCHEDSTAGE_SLEW_COMPLETE:
2887 case SCHEDSTAGE_RESLEWING_COMPLETE:
2889 if (moduleState()->domeReady())
2891 QVariant
const isDomeMoving = domeInterface()->property(
"isMoving");
2895 appendLogText(
i18n(
"Warning: job '%1' lost connection to the dome, attempting to reconnect.", activeJob()->getName()));
2901 if (!isDomeMoving.
value<
bool>())
2914 moduleState()->calculateDawnDusk();
2916 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2928 if (activeJob() == job &&
SCHEDJOB_BUSY == activeJob()->getState())
2931 moduleState()->setActiveJob(job);
2943 else if (0 < SchedulerModuleState::getLocalTime().secsTo(activeJob()->getStartupTime()))
2948 if (job->getCompletionCondition() == FINISH_SEQUENCE && Options::rememberJobProgress())
2949 captureInterface()->setProperty(
"targetName", job->getName());
2951 moduleState()->calculateDawnDusk();
2955 moduleState()->setAutofocusCompleted(
false);
2957 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Executing Job " << activeJob()->getName();
2960 emit jobsUpdated(moduleState()->getJSONJobs());
2962 KSNotification::event(
QLatin1String(
"EkosSchedulerJobStart"),
2963 i18n(
"Ekos job started (%1)", activeJob()->getName()), KSNotification::Scheduler);
2966 moduleState()->setupNextIteration(RUN_JOBCHECK);
2978 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
2987 outstream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<
Qt::endl;
2988 outstream <<
"<SchedulerList version='2.0'>" <<
Qt::endl;
2990 outstream <<
"<Profile>" <<
QString(entityXML(strdup(moduleState()->currentProfile().toStdString().c_str()))) <<
2993 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
2994 bool useMosaicInfo = !tiles->sequenceFile().isEmpty();
2998 outstream <<
"<Mosaic>" <<
Qt::endl;
2999 outstream <<
"<Target>" << tiles->targetName() <<
"</Target>" <<
Qt::endl;
3000 outstream <<
"<Group>" << tiles->group() <<
"</Group>" <<
Qt::endl;
3002 QString ccArg, ccValue = tiles->completionCondition(&ccArg);
3003 if (ccValue ==
"FinishSequence")
3004 outstream <<
"<FinishSequence/>" <<
Qt::endl;
3005 else if (ccValue ==
"FinishLoop")
3006 outstream <<
"<FinishLoop/>" <<
Qt::endl;
3007 else if (ccValue ==
"FinishRepeat")
3008 outstream <<
"<FinishRepeat>" << ccArg <<
"</FinishRepeat>" <<
Qt::endl;
3010 outstream <<
"<Sequence>" << tiles->sequenceFile() <<
"</Sequence>" <<
Qt::endl;
3011 outstream <<
"<Directory>" << tiles->outputDirectory() <<
"</Directory>" <<
Qt::endl;
3013 outstream <<
"<FocusEveryN>" << tiles->focusEveryN() <<
"</FocusEveryN>" <<
Qt::endl;
3014 outstream <<
"<AlignEveryN>" << tiles->alignEveryN() <<
"</AlignEveryN>" <<
Qt::endl;
3015 if (tiles->isTrackChecked())
3016 outstream <<
"<TrackChecked/>" <<
Qt::endl;
3017 if (tiles->isFocusChecked())
3018 outstream <<
"<FocusChecked/>" <<
Qt::endl;
3019 if (tiles->isAlignChecked())
3020 outstream <<
"<AlignChecked/>" <<
Qt::endl;
3021 if (tiles->isGuideChecked())
3022 outstream <<
"<GuideChecked/>" <<
Qt::endl;
3023 outstream <<
"<Overlap>" << cLocale.
toString(tiles->overlap()) <<
"</Overlap>" <<
Qt::endl;
3024 outstream <<
"<CenterRA>" << cLocale.
toString(tiles->ra0().Hours()) <<
"</CenterRA>" <<
Qt::endl;
3025 outstream <<
"<CenterDE>" << cLocale.
toString(tiles->dec0().Degrees()) <<
"</CenterDE>" <<
Qt::endl;
3026 outstream <<
"<GridW>" << tiles->gridSize().width() <<
"</GridW>" <<
Qt::endl;
3027 outstream <<
"<GridH>" << tiles->gridSize().height() <<
"</GridH>" <<
Qt::endl;
3028 outstream <<
"<FOVW>" << cLocale.
toString(tiles->mosaicFOV().width()) <<
"</FOVW>" <<
Qt::endl;
3029 outstream <<
"<FOVH>" << cLocale.
toString(tiles->mosaicFOV().height()) <<
"</FOVH>" <<
Qt::endl;
3030 outstream <<
"<CameraFOVW>" << cLocale.
toString(tiles->cameraFOV().width()) <<
"</CameraFOVW>" <<
Qt::endl;
3031 outstream <<
"<CameraFOVH>" << cLocale.
toString(tiles->cameraFOV().height()) <<
"</CameraFOVH>" <<
Qt::endl;
3032 outstream <<
"</Mosaic>" <<
Qt::endl;
3036 for (
auto &job : moduleState()->jobs())
3041 outstream <<
"<JobType lead='" << (job->isLead() ?
"true" :
"false") <<
"'/>" <<
Qt::endl;
3046 outstream <<
"<Coordinates>" <<
Qt::endl;
3049 outstream <<
"</Coordinates>" <<
Qt::endl;
3052 if (! job->getOpticalTrain().
isEmpty())
3053 outstream <<
"<OpticalTrain>" <<
QString(entityXML(strdup(job->getOpticalTrain().
toStdString().c_str()))) <<
3056 if (job->isLead() && job->getFITSFile().
isValid() && job->getFITSFile().
isEmpty() ==
false)
3059 outstream <<
"<PositionAngle>" << job->getPositionAngle() <<
"</PositionAngle>" <<
Qt::endl;
3061 outstream <<
"<Sequence>" << job->getSequenceFile().
toLocalFile() <<
"</Sequence>" <<
Qt::endl;
3063 if (useMosaicInfo && index < tiles->tiles().size())
3065 auto oneTile = tiles->tiles().at(index++);
3066 outstream <<
"<TileCenter>" <<
Qt::endl;
3067 outstream <<
"<X>" << cLocale.
toString(oneTile->center.x()) <<
"</X>" <<
Qt::endl;
3068 outstream <<
"<Y>" << cLocale.
toString(oneTile->center.y()) <<
"</Y>" <<
Qt::endl;
3069 outstream <<
"<Rotation>" << cLocale.
toString(oneTile->rotation) <<
"</Rotation>" <<
Qt::endl;
3070 outstream <<
"</TileCenter>" <<
Qt::endl;
3075 outstream <<
"<StartupCondition>" <<
Qt::endl;
3076 if (job->getFileStartupCondition() == START_ASAP)
3077 outstream <<
"<Condition>ASAP</Condition>" <<
Qt::endl;
3078 else if (job->getFileStartupCondition() == START_AT)
3079 outstream <<
"<Condition value='" << job->getStartAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3081 outstream <<
"</StartupCondition>" <<
Qt::endl;
3083 outstream <<
"<Constraints>" <<
Qt::endl;
3084 if (job->hasMinAltitude())
3085 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinAltitude()) <<
"'>MinimumAltitude</Constraint>" <<
3087 if (job->getMinMoonSeparation() > 0)
3088 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinMoonSeparation()) <<
"'>MoonSeparation</Constraint>"
3090 if (job->getEnforceWeather())
3091 outstream <<
"<Constraint>EnforceWeather</Constraint>" <<
Qt::endl;
3092 if (job->getEnforceTwilight())
3093 outstream <<
"<Constraint>EnforceTwilight</Constraint>" <<
Qt::endl;
3094 if (job->getEnforceArtificialHorizon())
3095 outstream <<
"<Constraint>EnforceArtificialHorizon</Constraint>" <<
Qt::endl;
3096 outstream <<
"</Constraints>" <<
Qt::endl;
3099 outstream <<
"<CompletionCondition>" <<
Qt::endl;
3100 if (job->getCompletionCondition() == FINISH_SEQUENCE)
3101 outstream <<
"<Condition>Sequence</Condition>" <<
Qt::endl;
3102 else if (job->getCompletionCondition() == FINISH_REPEAT)
3103 outstream <<
"<Condition value='" << cLocale.
toString(job->getRepeatsRequired()) <<
"'>Repeat</Condition>" <<
Qt::endl;
3104 else if (job->getCompletionCondition() == FINISH_LOOP)
3105 outstream <<
"<Condition>Loop</Condition>" <<
Qt::endl;
3106 else if (job->getCompletionCondition() == FINISH_AT)
3107 outstream <<
"<Condition value='" << job->getFinishAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3109 outstream <<
"</CompletionCondition>" <<
Qt::endl;
3113 outstream <<
"<Steps>" <<
Qt::endl;
3114 if (job->getStepPipeline() & SchedulerJob::USE_TRACK)
3115 outstream <<
"<Step>Track</Step>" <<
Qt::endl;
3116 if (job->getStepPipeline() & SchedulerJob::USE_FOCUS)
3117 outstream <<
"<Step>Focus</Step>" <<
Qt::endl;
3118 if (job->getStepPipeline() & SchedulerJob::USE_ALIGN)
3119 outstream <<
"<Step>Align</Step>" <<
Qt::endl;
3120 if (job->getStepPipeline() & SchedulerJob::USE_GUIDE)
3121 outstream <<
"<Step>Guide</Step>" <<
Qt::endl;
3122 outstream <<
"</Steps>" <<
Qt::endl;
3127 outstream <<
"<SchedulerAlgorithm value='" << ALGORITHM_GREEDY <<
"'/>" <<
Qt::endl;
3128 outstream <<
"<ErrorHandlingStrategy value='" << Options::errorHandlingStrategy() <<
"'>" <<
Qt::endl;
3129 if (Options::rescheduleErrors())
3130 outstream <<
"<RescheduleErrors />" <<
Qt::endl;
3131 outstream <<
"<delay>" << Options::errorHandlingStrategyDelay() <<
"</delay>" <<
Qt::endl;
3132 outstream <<
"</ErrorHandlingStrategy>" <<
Qt::endl;
3134 outstream <<
"<StartupProcedure>" <<
Qt::endl;
3135 if (moduleState()->startupScriptURL().isEmpty() ==
false)
3136 outstream <<
"<Procedure value='" << moduleState()->startupScriptURL().toString(
QUrl::PreferLocalFile) <<
3137 "'>StartupScript</Procedure>" <<
Qt::endl;
3138 if (Options::schedulerUnparkDome())
3139 outstream <<
"<Procedure>UnparkDome</Procedure>" <<
Qt::endl;
3140 if (Options::schedulerUnparkMount())
3141 outstream <<
"<Procedure>UnparkMount</Procedure>" <<
Qt::endl;
3142 if (Options::schedulerOpenDustCover())
3143 outstream <<
"<Procedure>UnparkCap</Procedure>" <<
Qt::endl;
3144 outstream <<
"</StartupProcedure>" <<
Qt::endl;
3146 outstream <<
"<ShutdownProcedure>" <<
Qt::endl;
3147 if (Options::schedulerWarmCCD())
3148 outstream <<
"<Procedure>WarmCCD</Procedure>" <<
Qt::endl;
3149 if (Options::schedulerCloseDustCover())
3150 outstream <<
"<Procedure>ParkCap</Procedure>" <<
Qt::endl;
3151 if (Options::schedulerParkMount())
3152 outstream <<
"<Procedure>ParkMount</Procedure>" <<
Qt::endl;
3153 if (Options::schedulerParkDome())
3154 outstream <<
"<Procedure>ParkDome</Procedure>" <<
Qt::endl;
3155 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
3156 outstream <<
"<Procedure value='" << moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile) <<
3157 "'>schedulerStartupScript</Procedure>" <<
3159 outstream <<
"</ShutdownProcedure>" <<
Qt::endl;
3161 outstream <<
"</SchedulerList>" <<
Qt::endl;
3165 moduleState()->setDirty(
false);
3169void SchedulerProcess::checkAlignment(
const QVariantMap &metadata,
const QString &trainname)
3172 if (activeJob() ==
nullptr || (activeJob()->getOpticalTrain() !=
"" && activeJob()->getOpticalTrain() != trainname))
3174 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Ignoring metadata from train =" << trainname <<
"for alignment check.";
3178 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN &&
3179 metadata[
"type"].toInt() == FRAME_LIGHT &&
3180 Options::alignCheckFrequency() > 0 &&
3181 moduleState()->increaseSolverIteration() >= Options::alignCheckFrequency())
3183 moduleState()->resetSolverIteration();
3185 auto filename = metadata[
"filename"].toString();
3186 auto exposure = metadata[
"exposure"].toDouble();
3188 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking alignment on train =" << trainname <<
"for" << filename;
3190 constexpr double minSolverSeconds = 5.0;
3191 double solverTimeout = std::max(exposure - 2, minSolverSeconds);
3192 if (solverTimeout >= minSolverSeconds)
3194 auto profiles = getDefaultAlignOptionsProfiles();
3196 SSolver::Parameters parameters;
3201 parameters = profiles.at(Options::solveOptionsProfile());
3203 catch (std::out_of_range
const &)
3205 parameters = profiles[0];
3209 parameters.search_radius = parameters.search_radius * 2;
3214 auto width = metadata[
"width"].toUInt() / (metadata[
"binx"].isValid() ? metadata[
"binx"].toUInt() : 1);
3215 auto height = metadata[
"height"].toUInt() / (metadata[
"biny"].isValid() ? metadata[
"biny"].toUInt() : 1);
3217 auto lowScale = Options::astrometryImageScaleLow();
3218 auto highScale = Options::astrometryImageScaleHigh();
3221 if (Options::astrometryImageScaleUnits() == SSolver::DEG_WIDTH)
3223 lowScale = (lowScale * 3600) / std::max(width, height);
3224 highScale = (highScale * 3600) / std::min(width, height);
3226 else if (Options::astrometryImageScaleUnits() == SSolver::ARCMIN_WIDTH)
3228 lowScale = (lowScale * 60) / std::max(width, height);
3229 highScale = (highScale * 60) / std::min(width, height);
3232 m_Solver->useScale(Options::astrometryUseImageScale(), lowScale, highScale);
3233 m_Solver->usePosition(Options::astrometryUsePosition(), activeJob()->getTargetCoords().ra().Degrees(),
3234 activeJob()->getTargetCoords().
dec().Degrees());
3235 m_Solver->setHealpix(moduleState()->indexToUse(), moduleState()->healpixToUse());
3236 m_Solver->runSolver(filename);
3241void SchedulerProcess::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
3243 disconnect(m_Solver.get(), &SolverUtils::done,
this, &Ekos::SchedulerProcess::solverDone);
3248 QString healpixString =
"";
3249 if (moduleState()->indexToUse() != -1 || moduleState()->healpixToUse() != -1)
3250 healpixString = QString(
"Healpix %1 Index %2").
arg(moduleState()->healpixToUse()).
arg(moduleState()->indexToUse());
3252 if (timedOut || !success)
3255 moduleState()->setIndexToUse(-1);
3256 moduleState()->setHealpixToUse(-1);
3262 m_Solver->getSolutionHealpix(&index, &healpix);
3263 moduleState()->setIndexToUse(index);
3264 moduleState()->setHealpixToUse(healpix);
3268 appendLogText(
i18n(
"Solver timed out: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3270 appendLogText(
i18n(
"Solver failed: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3273 const double ra = solution.ra;
3274 const double dec = solution.dec;
3276 const auto target = activeJob()->getTargetCoords();
3278 SkyPoint alignCoord;
3279 alignCoord.
setRA0(ra / 15.0);
3283 const double diffRa = (alignCoord.
ra().deltaAngle(target.ra())).Degrees() * 3600;
3284 const double diffDec = (alignCoord.
dec().deltaAngle(target.dec())).Degrees() * 3600;
3287 const double diffTotal = hypot(diffRa, diffDec);
3291 qCDebug(KSTARS_EKOS_SCHEDULER) <<
3292 QString(
"Target Distance: %1\" Target (RA: %2 DE: %3) Current (RA: %4 DE: %5) %6 solved in %7s")
3293 .arg(QString(
"%L1").arg(diffTotal, 0,
'f', 0),
3294 target.ra().toDMSString(),
3295 target.dec().toDMSString(),
3299 QString(
"%L1").arg(elapsedSeconds, 0,
'f', 2));
3300 emit targetDistance(diffTotal);
3303 if (diffTotal / 60 > Options::alignCheckThreshold())
3315 SchedulerState
const old_state = moduleState()->schedulerState();
3316 moduleState()->setSchedulerState(SCHEDULER_LOADING);
3323 QString message =
i18n(
"Unable to open file %1", fileURL);
3324 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3325 moduleState()->setSchedulerState(old_state);
3329 LilXML *xmlParser = newLilXML();
3330 char errmsg[MAXRBUF];
3331 XMLEle *root =
nullptr;
3332 XMLEle *ep =
nullptr;
3333 XMLEle *subEP =
nullptr;
3340 SchedulerJob *lastLead =
nullptr;
3343 const QStringList allTrainNames = OpticalTrainManager::Instance()->getTrainNames();
3348 root = readXMLEle(xmlParser, c, errmsg);
3352 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
3354 const char *tag = tagXMLEle(ep);
3355 if (!strcmp(tag,
"Job"))
3357 SchedulerJob *newJob = SchedulerUtils::createJob(ep, lastLead);
3359 if (newJob->isLead())
3363 remainingTrainNames = allTrainNames;
3366 const QString trainname = newJob->getOpticalTrain();
3367 bool allowedName = (newJob->isLead() && trainname.
isEmpty()) || allTrainNames.
contains(trainname);
3368 bool availableName = (newJob->isLead() && trainname.
isEmpty()) || !remainingTrainNames.
isEmpty();
3370 if (!allowedName && availableName)
3373 i18n(
"Warning: train name is empty, selecting \"%1\".", remainingTrainNames.
first()) :
3374 i18n(
"Warning: train name %2 does not exist, selecting \"%1\".", remainingTrainNames.
first(), trainname);
3380 newJob->setOpticalTrain(remainingTrainNames.
first());
3383 else if (!availableName)
3385 const QString message =
i18n(
"Warning: no available train name for scheduler job, select the optical train name manually.");
3393 emit addJob(newJob);
3395 else if (!strcmp(tag,
"Mosaic"))
3398 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
3399 tiles->fromXML(fileURL);
3401 else if (!strcmp(tag,
"Profile"))
3403 moduleState()->setCurrentProfile(pcdataXMLEle(ep));
3406 else if (!strcmp(tag,
"SchedulerAlgorithm"))
3408 int algIndex = cLocale.
toInt(findXMLAttValu(ep,
"value"));
3409 if (algIndex != ALGORITHM_GREEDY)
3410 appendLogText(
i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
3412 else if (!strcmp(tag,
"ErrorHandlingStrategy"))
3417 subEP = findXMLEle(ep,
"delay");
3420 Options::setErrorHandlingStrategyDelay(cLocale.
toInt(pcdataXMLEle(subEP)));
3422 subEP = findXMLEle(ep,
"RescheduleErrors");
3423 Options::setRescheduleErrors(subEP !=
nullptr);
3425 else if (!strcmp(tag,
"StartupProcedure"))
3428 Options::setSchedulerUnparkDome(
false);
3429 Options::setSchedulerUnparkMount(
false);
3430 Options::setSchedulerOpenDustCover(
false);
3432 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3434 const char *proc = pcdataXMLEle(procedure);
3436 if (!strcmp(proc,
"StartupScript"))
3438 moduleState()->setStartupScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3440 else if (!strcmp(proc,
"UnparkDome"))
3441 Options::setSchedulerUnparkDome(
true);
3442 else if (!strcmp(proc,
"UnparkMount"))
3443 Options::setSchedulerUnparkMount(
true);
3444 else if (!strcmp(proc,
"UnparkCap"))
3445 Options::setSchedulerOpenDustCover(
true);
3448 else if (!strcmp(tag,
"ShutdownProcedure"))
3451 Options::setSchedulerWarmCCD(
false);
3452 Options::setSchedulerParkDome(
false);
3453 Options::setSchedulerParkMount(
false);
3454 Options::setSchedulerCloseDustCover(
false);
3456 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3458 const char *proc = pcdataXMLEle(procedure);
3460 if (!strcmp(proc,
"ShutdownScript"))
3462 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3464 else if (!strcmp(proc,
"WarmCCD"))
3465 Options::setSchedulerWarmCCD(
true);
3466 else if (!strcmp(proc,
"ParkDome"))
3467 Options::setSchedulerParkDome(
true);
3468 else if (!strcmp(proc,
"ParkMount"))
3469 Options::setSchedulerParkMount(
true);
3470 else if (!strcmp(proc,
"ParkCap"))
3471 Options::setSchedulerCloseDustCover(
true);
3476 emit syncGUIToGeneralSettings();
3481 delLilXML(xmlParser);
3482 moduleState()->setSchedulerState(old_state);
3487 moduleState()->setDirty(
false);
3488 delLilXML(xmlParser);
3489 emit updateSchedulerURL(fileURL);
3491 moduleState()->setSchedulerState(old_state);
3498 int const max_log_count = 2000;
3499 if (moduleState()->logText().size() > max_log_count)
3500 moduleState()->logText().removeLast();
3502 moduleState()->logText().prepend(
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
3503 SchedulerModuleState::getLocalTime().toString(
"yyyy-MM-ddThh:mm:ss"), logentry));
3505 qCInfo(KSTARS_EKOS_SCHEDULER) << logentry;
3507 emit newLog(logentry);
3512 moduleState()->logText().clear();
3516void SchedulerProcess::setAlignStatus(
AlignState status)
3518 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3521 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align State" << Ekos::getAlignStatusString(status);
3526 QDateTime const now = SchedulerModuleState::getLocalTime();
3527 if (now < activeJob()->getStartupTime())
3531 if (activeJob()->getStage() == SCHEDSTAGE_ALIGNING)
3537 moduleState()->resetAlignFailureCount();
3539 moduleState()->updateJobStage(SCHEDSTAGE_ALIGN_COMPLETE);
3542 if (activeJob()->getFITSFile().isEmpty() ==
false)
3548 activeJob()->setTargetCoords(
dms(values[0] * 15.0),
dms(values[1]), KStarsData::Instance()->ut().djd());
3555 appendLogText(
i18n(
"Warning: job '%1' alignment failed.", activeJob()->getName()));
3557 if (moduleState()->increaseAlignFailureCount())
3559 if (Options::resetMountModelOnAlignFail() && moduleState()->maxFailureAttempts() - 1 < moduleState()->alignFailureCount())
3561 appendLogText(
i18n(
"Warning: job '%1' forcing mount model reset after failing alignment #%2.", activeJob()->getName(),
3562 moduleState()->alignFailureCount()));
3565 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3570 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
3579void SchedulerProcess::setGuideStatus(GuideState status)
3581 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3584 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Guide State" << Ekos::getGuideStatusString(status);
3589 QDateTime
const now = SchedulerModuleState::getLocalTime();
3590 if (now < activeJob()->getStartupTime())
3594 if (activeJob()->getStage() == SCHEDSTAGE_GUIDING)
3596 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Calibration & Guide stage...";
3599 if (status == Ekos::GUIDE_GUIDING)
3601 appendLogText(
i18n(
"Job '%1' guiding is in progress.", activeJob()->getName()));
3602 moduleState()->resetGuideFailureCount();
3604 moduleState()->cancelGuidingTimer();
3606 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
3609 else if (status == Ekos::GUIDE_CALIBRATION_ERROR ||
3610 status == Ekos::GUIDE_ABORTED)
3612 if (status == Ekos::GUIDE_ABORTED)
3613 appendLogText(
i18n(
"Warning: job '%1' guiding failed.", activeJob()->getName()));
3615 appendLogText(
i18n(
"Warning: job '%1' calibration failed.", activeJob()->getName()));
3621 if (moduleState()->isGuidingTimerActive())
3624 if (moduleState()->increaseGuideFailureCount())
3626 if (status == Ekos::GUIDE_CALIBRATION_ERROR &&
3627 Options::realignAfterCalibrationFailure())
3629 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3634 appendLogText(
i18n(
"Job '%1' is guiding, guiding procedure will be restarted in %2 seconds.", activeJob()->getName(),
3635 (RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount()) / 1000));
3636 moduleState()->startGuidingTimer(RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount());
3641 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3650void SchedulerProcess::setCaptureStatus(
CaptureState status,
const QString &trainname)
3652 if (activeJob() ==
nullptr || !m_activeJobs.contains(trainname))
3655 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Capture State" << Ekos::getCaptureStatusString(status) <<
"train =" << trainname;
3657 SchedulerJob *job = m_activeJobs[trainname];
3662 QDateTime
const now = SchedulerModuleState::getLocalTime();
3663 if (now < job->getStartupTime())
3667 if (job->getStage() == SCHEDSTAGE_CAPTURING)
3679 const SkyPoint targetCoords = activeJob()->getTargetCoords();
3680 QList<QVariant> targetArgs;
3682 alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords", targetArgs);
3687 appendLogText(
i18n(
"[%2] Warning: job '%1' failed to capture target.", job->getName(), trainname));
3692 if (moduleState()->increaseCaptureFailureCount())
3697 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
3701 if (gStatus == Ekos::GUIDE_ABORTED ||
3702 gStatus == Ekos::GUIDE_CALIBRATION_ERROR ||
3703 gStatus == GUIDE_DITHERING_ERROR)
3705 appendLogText(
i18n(
"[%2] Job '%1' is capturing, is restarting its guiding procedure (attempt #%3 of %4).",
3706 activeJob()->getName(), trainname,
3707 moduleState()->captureFailureCount(), moduleState()->maxFailureAttempts()));
3714 appendLogText(
i18n(
"Warning: job '%1' failed its capture procedure, restarting capture.", activeJob()->getName()));
3720 appendLogText(
i18n(
"[%2] Warning: job '%1' failed its capture procedure, marking aborted.", job->getName(), trainname));
3723 stopCapturing(
"",
true);
3730 if (job->leadJob()->getStage() == SCHEDSTAGE_CAPTURING)
3733 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted, is restarting.", job->getName(), trainname));
3735 startSingleCapture(job,
true);
3739 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted.", job->getName(), trainname));
3746 KSNotification::event(QLatin1String(
"EkosScheduledImagingFinished"),
3747 i18n(
"[%2] Job (%1) - Capture finished", job->getName(), trainname), KSNotification::Scheduler);
3759 if (job->getCompletionCondition() == FINISH_LOOP ||
3760 (job->getCompletionCondition() == FINISH_REPEAT && job->getRepeatsRemaining() > 0))
3763 startSingleCapture(job,
false);
3769 job->setStage(SCHEDSTAGE_COMPLETE);
3777 if (Options::rememberJobProgress())
3781 for (
const auto &job : moduleState()->jobs())
3782 SchedulerUtils::estimateJobTime(job, moduleState()->capturedFramesCount(),
this);
3786 activeJob()->setCompletedCount(job->getCompletedCount() + 1);
3790 moduleState()->resetCaptureFailureCount();
3795void SchedulerProcess::setFocusStatus(FocusState status,
const QString &trainname)
3799 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3802 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus State" << Ekos::getFocusStatusString(status);
3807 QDateTime
const now = SchedulerModuleState::getLocalTime();
3808 if (now < activeJob()->getStartupTime())
3812 if (activeJob()->getStage() == SCHEDSTAGE_FOCUSING)
3815 if (status == Ekos::FOCUS_COMPLETE)
3819 moduleState()->setAutofocusCompleted(
true);
3821 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
3825 else if (status == Ekos::FOCUS_FAILED || status == Ekos::FOCUS_ABORTED)
3827 appendLogText(
i18n(
"Warning: job '%1' focusing failed.", activeJob()->getName()));
3829 if (moduleState()->increaseFocusFailureCount())
3831 appendLogText(
i18n(
"Job '%1' is restarting its focusing procedure.", activeJob()->getName()));
3835 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 6883";
3840 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
3849void SchedulerProcess::setMountStatus(ISD::Mount::Status status)
3851 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3854 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount State changed to" << status;
3858 if (
static_cast<QDateTime const
>(SchedulerModuleState::getLocalTime()) < activeJob()->getStartupTime())
3861 switch (activeJob()->getStage())
3863 case SCHEDSTAGE_SLEWING:
3865 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Slewing stage...";
3867 if (status == ISD::Mount::MOUNT_TRACKING)
3870 moduleState()->updateJobStage(SCHEDSTAGE_SLEW_COMPLETE);
3873 else if (status == ISD::Mount::MOUNT_ERROR)
3875 appendLogText(
i18n(
"Warning: job '%1' slew failed, marking terminated due to errors.", activeJob()->getName()));
3879 else if (status == ISD::Mount::MOUNT_IDLE)
3881 appendLogText(
i18n(
"Warning: job '%1' found not slewing, restarting.", activeJob()->getName()));
3882 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
3888 case SCHEDSTAGE_RESLEWING:
3890 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Re-slewing stage...";
3892 if (status == ISD::Mount::MOUNT_TRACKING)
3894 appendLogText(
i18n(
"Job '%1' repositioning is complete.", activeJob()->getName()));
3895 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING_COMPLETE);
3898 else if (status == ISD::Mount::MOUNT_ERROR)
3900 appendLogText(
i18n(
"Warning: job '%1' repositioning failed, marking terminated due to errors.", activeJob()->getName()));
3904 else if (status == ISD::Mount::MOUNT_IDLE)
3906 appendLogText(
i18n(
"Warning: job '%1' found not repositioning, restarting.", activeJob()->getName()));
3907 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
3915 case SCHEDSTAGE_FOCUSING:
3916 case SCHEDSTAGE_ALIGNING:
3917 case SCHEDSTAGE_GUIDING:
3918 if (status == ISD::Mount::MOUNT_PARKED)
3920 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
3927 case SCHEDSTAGE_CAPTURING:
3928 if (status == ISD::Mount::MOUNT_PARKED && activeJob() && activeJob()->getLightFramesRequired()
3929 && activeJob()->getCalibrationMountPark() ==
false)
3931 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
3941void SchedulerProcess::setWeatherStatus(ISD::Weather::Status status)
3943 ISD::Weather::Status newStatus = status;
3945 if (newStatus == moduleState()->weatherStatus())
3948 moduleState()->setWeatherStatus(newStatus);
3952 if (activeJob() && activeJob()->getEnforceWeather() && moduleState()->weatherStatus() == ISD::Weather::WEATHER_ALERT
3953 && moduleState()->schedulerState() != Ekos::SCHEDULER_IDLE && moduleState()->schedulerState() != Ekos::SCHEDULER_SHUTDOWN)
3964 emit newWeatherStatus(status);
3967void SchedulerProcess::checkStartupProcedure()
3973void SchedulerProcess::checkShutdownProcedure()
3978 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
3982 if (Options::stopEkosAfterShutdown())
3985 else if (moduleState()->shutdownState() == SHUTDOWN_ERROR)
3988 moduleState()->setShutdownState(SHUTDOWN_IDLE);
3997void SchedulerProcess::parkCap()
3999 if (capInterface().isNull())
4002 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4006 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4007 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4009 if (parkingStatus.
isValid() ==
false)
4011 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4012 mountInterface()->lastError().
type());
4014 parkingStatus = ISD::PARK_ERROR;
4017 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4019 if (status != ISD::PARK_PARKED)
4021 moduleState()->setShutdownState(SHUTDOWN_PARKING_CAP);
4022 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking dust cap...";
4026 moduleState()->startCurrentOperationTimer();
4031 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
4035void SchedulerProcess::unParkCap()
4037 if (capInterface().isNull())
4039 appendLogText(
i18n(
"Dust cover unpark requested but no dust covers detected."));
4040 moduleState()->setStartupState(STARTUP_ERROR);
4044 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4045 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4047 if (parkingStatus.
isValid() ==
false)
4049 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4050 mountInterface()->lastError().
type());
4052 parkingStatus = ISD::PARK_ERROR;
4055 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4057 if (status != ISD::PARK_UNPARKED)
4059 moduleState()->setStartupState(STARTUP_UNPARKING_CAP);
4063 moduleState()->startCurrentOperationTimer();
4068 moduleState()->setStartupState(STARTUP_COMPLETE);
4072void SchedulerProcess::parkMount()
4074 if (mountInterface().isNull())
4077 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4081 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4082 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4084 if (parkingStatus.
isValid() ==
false)
4086 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4087 mountInterface()->lastError().
type());
4089 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4092 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4096 case ISD::PARK_PARKED:
4097 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4098 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
4100 moduleState()->setParkWaitState(PARKWAIT_PARKED);
4104 case ISD::PARK_UNPARKING:
4110 case ISD::PARK_ERROR:
4111 case ISD::PARK_UNKNOWN:
4112 case ISD::PARK_UNPARKED:
4114 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking mount...";
4115 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"park");
4119 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount park request received DBUS error: %1").arg(
4122 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4124 else moduleState()->startCurrentOperationTimer();
4128 case ISD::PARK_PARKING:
4130 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4131 moduleState()->setShutdownState(SHUTDOWN_PARKING_MOUNT);
4133 moduleState()->setParkWaitState(PARKWAIT_PARKING);
4144void SchedulerProcess::unParkMount()
4146 if (mountInterface().isNull())
4149 moduleState()->setStartupState(STARTUP_ERROR);
4153 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4154 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4156 if (parkingStatus.
isValid() ==
false)
4158 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4159 mountInterface()->lastError().
type());
4161 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4164 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4169 case ISD::PARK_UNPARKED:
4170 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4171 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
4173 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
4178 case ISD::PARK_PARKING:
4184 case ISD::PARK_ERROR:
4185 case ISD::PARK_UNKNOWN:
4186 case ISD::PARK_PARKED:
4188 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"unpark");
4192 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount unpark request received DBUS error: %1").arg(
4195 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4197 else moduleState()->startCurrentOperationTimer();
4202 case ISD::PARK_UNPARKING:
4203 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4204 moduleState()->setStartupState(STARTUP_UNPARKING_MOUNT);
4206 moduleState()->setParkWaitState(PARKWAIT_UNPARKING);
4207 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
4218 if (mountInterface().isNull())
4222 QVariant canPark = mountInterface()->property(
"canPark");
4223 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount can park:" << (!canPark.
isValid() ?
"invalid" : (canPark.
toBool() ?
"T" :
"F"));
4225 if (canPark.
isValid() ==
false)
4227 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount canPark request received DBUS error: %1").
arg(
4228 mountInterface()->lastError().type());
4232 else if (canPark.
toBool() ==
true)
4236 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4237 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4239 if (parkingStatus.
isValid() ==
false)
4241 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parking status property is invalid %1.").
arg(
4242 mountInterface()->lastError().type());
4248 switch (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()))
4252 case ISD::PARK_PARKED:
4267void SchedulerProcess::parkDome()
4270 if (domeInterface().isNull())
4273 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4279 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4280 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4282 if (parkingStatus.
isValid() ==
false)
4284 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4285 mountInterface()->lastError().type());
4287 parkingStatus = ISD::PARK_ERROR;
4290 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4291 if (status != ISD::PARK_PARKED)
4293 moduleState()->setShutdownState(SHUTDOWN_PARKING_DOME);
4297 moduleState()->startCurrentOperationTimer();
4302 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
4306void SchedulerProcess::unParkDome()
4309 if (domeInterface().isNull())
4312 moduleState()->setStartupState(STARTUP_ERROR);
4316 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4317 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4319 if (parkingStatus.
isValid() ==
false)
4321 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
4322 mountInterface()->lastError().
type());
4324 parkingStatus = ISD::PARK_ERROR;
4327 if (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()) != ISD::PARK_UNPARKED)
4329 moduleState()->setStartupState(STARTUP_UNPARKING_DOME);
4333 moduleState()->startCurrentOperationTimer();
4338 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
4344 QVariant guideStatus = guideInterface()->property(
"status");
4345 Ekos::GuideState gStatus =
static_cast<Ekos::GuideState
>(guideStatus.
toInt());
4350const QString &SchedulerProcess::profile()
const
4352 return moduleState()->currentProfile();
4355void SchedulerProcess::setProfile(
const QString &newProfile)
4357 moduleState()->setCurrentProfile(newProfile);
4360QString SchedulerProcess::currentJobName()
4362 auto job = moduleState()->activeJob();
4363 return ( job !=
nullptr ? job->getName() : QString() );
4366QString SchedulerProcess::currentJobJson()
4368 auto job = moduleState()->activeJob();
4369 if( job !=
nullptr )
4371 return QString( QJsonDocument( job->toJson() ).toJson() );
4379QString SchedulerProcess::jsonJobs()
4381 return QString( QJsonDocument( moduleState()->getJSONJobs() ).toJson() );
4384QStringList SchedulerProcess::logText()
4386 return moduleState()->logText();
4391 if (domeInterface().isNull())
4394 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4395 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4397 if (parkingStatus.
isValid() ==
false)
4399 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4400 mountInterface()->lastError().type());
4402 parkingStatus = ISD::PARK_ERROR;
4405 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4407 return status == ISD::PARK_PARKED;
4410void SchedulerProcess::simClockScaleChanged(
float newScale)
4412 if (moduleState()->currentlySleeping())
4415 (moduleState()->iterationTimer().remainingTime())
4416 * KStarsData::Instance()->clock()->scale()
4418 appendLogText(
i18n(
"Sleeping for %1 on simulation clock update until next observation job is ready...",
4419 remainingTimeMs.
toString(
"hh:mm:ss")));
4420 moduleState()->iterationTimer().stop();
4425void SchedulerProcess::simClockTimeChanged()
4427 moduleState()->calculateDawnDusk();
4430 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
4434void SchedulerProcess::setINDICommunicationStatus(CommunicationStatus status)
4436 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler INDI status is" << status;
4438 moduleState()->setIndiCommunicationStatus(status);
4441void SchedulerProcess::setEkosCommunicationStatus(CommunicationStatus status)
4443 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler Ekos status is" << status;
4445 moduleState()->setEkosCommunicationStatus(status);
4450void SchedulerProcess::checkInterfaceReady(QDBusInterface * iface)
4452 if (iface == mountInterface())
4455 moduleState()->setMountReady(
true);
4457 else if (iface == capInterface())
4460 moduleState()->setCapReady(
true);
4462 else if (iface == observatoryInterface())
4464 QVariant status = observatoryInterface()->property(
"status");
4465 if (status.isValid())
4466 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4468 else if (iface == weatherInterface())
4470 QVariant status = weatherInterface()->property(
"status");
4471 if (status.isValid())
4472 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4474 else if (iface == domeInterface())
4477 moduleState()->setDomeReady(
true);
4479 else if (iface == captureInterface())
4482 moduleState()->setCaptureReady(
true);
4485 emit interfaceReady(iface);
4488void SchedulerProcess::registerNewModule(
const QString &name)
4490 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Registering new Module (" <<
name <<
")";
4492 if (name ==
"Focus")
4494 delete focusInterface();
4495 setFocusInterface(
new QDBusInterface(kstarsInterfaceString, focusPathString, focusInterfaceString,
4497 connect(focusInterface(), SIGNAL(newStatus(Ekos::FocusState,
const QString)),
this,
4500 else if (name ==
"Capture")
4502 delete captureInterface();
4503 setCaptureInterface(
new QDBusInterface(kstarsInterfaceString, capturePathString, captureInterfaceString,
4506 connect(captureInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4509 connect(captureInterface(), SIGNAL(captureComplete(QVariantMap,
const QString)),
this, SLOT(checkAlignment(QVariantMap,
4512 checkInterfaceReady(captureInterface());
4514 else if (name ==
"Mount")
4516 delete mountInterface();
4517 setMountInterface(
new QDBusInterface(kstarsInterfaceString, mountPathString, mountInterfaceString,
4520 connect(mountInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4521 connect(mountInterface(), SIGNAL(newStatus(ISD::Mount::Status)),
this, SLOT(setMountStatus(ISD::Mount::Status)),
4524 checkInterfaceReady(mountInterface());
4526 else if (name ==
"Align")
4528 delete alignInterface();
4529 setAlignInterface(
new QDBusInterface(kstarsInterfaceString, alignPathString, alignInterfaceString,
4534 else if (name ==
"Guide")
4536 delete guideInterface();
4537 setGuideInterface(
new QDBusInterface(kstarsInterfaceString, guidePathString, guideInterfaceString,
4539 connect(guideInterface(), SIGNAL(newStatus(Ekos::GuideState)),
this,
4542 else if (name ==
"Observatory")
4544 delete observatoryInterface();
4545 setObservatoryInterface(
new QDBusInterface(kstarsInterfaceString, observatoryPathString, observatoryInterfaceString,
4547 connect(observatoryInterface(), SIGNAL(newStatus(ISD::Weather::Status)),
this,
4549 checkInterfaceReady(observatoryInterface());
4553void SchedulerProcess::registerNewDevice(
const QString &name,
int interface)
4557 if (interface & INDI::BaseDevice::DOME_INTERFACE)
4559 QList<QVariant> dbusargs;
4560 dbusargs.
append(INDI::BaseDevice::DOME_INTERFACE);
4561 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4566 setDomePathString(paths.value().last());
4567 delete domeInterface();
4568 setDomeInterface(
new QDBusInterface(kstarsInterfaceString, domePathString,
4569 domeInterfaceString,
4571 connect(domeInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4572 checkInterfaceReady(domeInterface());
4597 if (interface & INDI::BaseDevice::DUSTCAP_INTERFACE)
4599 QList<QVariant> dbusargs;
4600 dbusargs.
append(INDI::BaseDevice::DUSTCAP_INTERFACE);
4601 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4606 setDustCapPathString(paths.value().last());
4607 delete capInterface();
4608 setCapInterface(
new QDBusInterface(kstarsInterfaceString, dustCapPathString,
4609 dustCapInterfaceString,
4611 connect(capInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4612 checkInterfaceReady(capInterface());
4619 XMLEle *ep =
nullptr;
4620 XMLEle *subEP =
nullptr;
4622 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
4624 if (!strcmp(tagXMLEle(ep),
"Job"))
4626 for (subEP = nextXMLEle(ep, 1); subEP !=
nullptr; subEP = nextXMLEle(ep, 0))
4628 if (!strcmp(tagXMLEle(subEP),
"TargetName"))
4633 else if (!strcmp(tagXMLEle(subEP),
"FITSDirectory"))
4646 if (outputFile ==
nullptr)
4648 QString message =
i18n(
"Unable to write to file %1", filename);
4649 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
4653 fprintf(outputFile,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4654 prXMLEle(outputFile, root, 0);
4668 KSNotification::sorry(
i18n(
"Unable to open file %1", sFile.
fileName()),
4669 i18n(
"Could Not Open File"));
4673 LilXML *xmlParser = newLilXML();
4674 char errmsg[MAXRBUF];
4675 XMLEle *root =
nullptr;
4680 root = readXMLEle(xmlParser, c, errmsg);
4686 delLilXML(xmlParser);
4691void SchedulerProcess::checkProcessExit(
int exitCode)
4697 if (moduleState()->startupState() == STARTUP_SCRIPT)
4698 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
4699 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4700 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
4705 if (moduleState()->startupState() == STARTUP_SCRIPT)
4708 moduleState()->setStartupState(STARTUP_ERROR);
4710 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4713 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4718void SchedulerProcess::readProcessOutput()
4720 appendLogText(scriptProcess().readAllStandardOutput().simplified());
4723bool SchedulerProcess::canCountCaptures(
const SchedulerJob &job)
4725 QList<SequenceJob*> seqjobs;
4726 bool hasAutoFocus =
false;
4727 SchedulerJob tempJob = job;
4728 if (SchedulerUtils::loadSequenceQueue(tempJob.getSequenceFile().toLocalFile(), &tempJob, seqjobs, hasAutoFocus,
4732 for (
const SequenceJob *oneSeqJob : seqjobs)
4734 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
4748 forced |= std::any_of(moduleState()->jobs().begin(),
4749 moduleState()->jobs().end(), [](SchedulerJob * oneJob) ->
bool
4756 moduleState()->capturedFramesCount().clear();
4759 for (SchedulerJob *oneJob : moduleState()->jobs())
4766 bool hasAutoFocus =
false;
4770 if (SchedulerUtils::loadSequenceQueue(oneJob->getSequenceFile().
toLocalFile(), oneJob, seqjobs, hasAutoFocus,
4773 appendLogText(
i18n(
"Warning: job '%1' has inaccessible sequence '%2', marking invalid.", oneJob->getName(),
4779 oneJob->clearProgress();
4781 for (SequenceJob *oneSeqJob : seqjobs)
4785 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
4789 QString const signature = oneSeqJob->getSignature();
4797 const int count = newFramesCount.
constFind(signature).value();
4798 newJobFramesCount[signature] = count;
4799 oneJob->addProgress(count, oneSeqJob);
4805 CapturedFramesMap::const_iterator
const earlierRunIterator =
4806 moduleState()->capturedFramesCount().constFind(signature);
4808 if (moduleState()->capturedFramesCount().constEnd() != earlierRunIterator)
4810 count = earlierRunIterator.value();
4813 count = PlaceholderPath::getCompletedFiles(signature);
4815 newFramesCount[signature] = count;
4816 newJobFramesCount[signature] = count;
4817 oneJob->addProgress(count, oneSeqJob);
4821 SchedulerUtils::updateLightFramesRequired(oneJob, seqjobs, newFramesCount);
4824 moduleState()->setCapturedFramesCount(newFramesCount);
4827 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Frame map summary:";
4828 CapturedFramesMap::const_iterator it = moduleState()->capturedFramesCount().constBegin();
4829 for (; it != moduleState()->capturedFramesCount().constEnd(); it++)
4830 qCDebug(KSTARS_EKOS_SCHEDULER) <<
" " << it.key() <<
':' << it.value();
4834SchedulerJob *SchedulerProcess::activeJob()
4836 return moduleState()->activeJob();
4839void SchedulerProcess::printStates(
const QString &label)
4841 qCDebug(KSTARS_EKOS_SCHEDULER) <<
4842 QString(
"%1 %2 %3%4 %5 %6 %7 %8 %9\n")
4844 .
arg(timerStr(moduleState()->timerState()))
4845 .
arg(getSchedulerStatusString(moduleState()->schedulerState()))
4846 .
arg((moduleState()->timerState() == RUN_JOBCHECK && activeJob() !=
nullptr) ?
4847 QString(
"(%1 %2)").arg(SchedulerJob::jobStatusString(activeJob()->getState()))
4848 .arg(SchedulerJob::jobStageString(activeJob()->getStage())) :
"")
4849 .
arg(ekosStateString(moduleState()->ekosState()))
4850 .
arg(indiStateString(moduleState()->indiState()))
4851 .
arg(startupStateString(moduleState()->startupState()))
4852 .
arg(shutdownStateString(moduleState()->shutdownState()))
4853 .
arg(parkWaitStateString(moduleState()->parkWaitState())).
toLatin1().
data();
4854 foreach (
auto j, moduleState()->jobs())
4855 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"job %1 %2\n").
arg(j->getName()).
arg(SchedulerJob::jobStatusString(
Q_SCRIPTABLE Q_NOREPLY void startAstrometry()
startAstrometry initiation of the capture and solve operation.
bool shouldSchedulerSleep(SchedulerJob *job)
shouldSchedulerSleep Check if the scheduler needs to sleep until the job is ready
Q_SCRIPTABLE Q_NOREPLY void startCapture(bool restart=false)
startCapture The current job file name is solved to an url which is fed to ekos.
void loadProfiles()
loadProfiles Load the existing EKOS profiles
Q_SCRIPTABLE Q_NOREPLY void runStartupProcedure()
runStartupProcedure Execute the startup of the scheduler itself to be prepared for running scheduler ...
void checkCapParkingStatus()
checkDomeParkingStatus check dome parking status and updating corresponding states accordingly.
void getNextAction()
getNextAction Checking for the next appropriate action regarding the current state of the scheduler a...
Q_SCRIPTABLE bool isMountParked()
Q_SCRIPTABLE Q_NOREPLY void startJobEvaluation()
startJobEvaluation Start job evaluation only without starting the scheduler process itself.
Q_SCRIPTABLE Q_NOREPLY void resetJobs()
resetJobs Reset all jobs counters
void selectActiveJob(const QList< SchedulerJob * > &jobs)
selectActiveJob Select the job that should be executed
Q_SCRIPTABLE void wakeUpScheduler()
wakeUpScheduler Wake up scheduler from sleep state
Q_SCRIPTABLE Q_NOREPLY void setPaused()
setPaused pausing the scheduler
Q_SCRIPTABLE bool checkParkWaitState()
checkParkWaitState Check park wait state.
bool executeJob(SchedulerJob *job)
executeJob After the best job is selected, we call this in order to start the process that will execu...
bool createJobSequence(XMLEle *root, const QString &prefix, const QString &outputDir)
createJobSequence Creates a job sequence for the mosaic tool given the prefix and output dir.
void iterate()
Repeatedly runs a scheduler iteration and then sleeps timerInterval millisconds and run the next iter...
Q_SCRIPTABLE Q_NOREPLY void startGuiding(bool resetCalibration=false)
startGuiding After ekos is fed the calibration options, we start the guiding process
void findNextJob()
findNextJob Check if the job met the completion criteria, and if it did, then it search for next job ...
Q_SCRIPTABLE bool appendEkosScheduleList(const QString &fileURL)
appendEkosScheduleList Append the contents of an ESL file to the queue.
Q_SCRIPTABLE void execute()
execute Execute the schedule, start if idle or paused.
Q_SCRIPTABLE bool isDomeParked()
Q_SCRIPTABLE bool saveScheduler(const QUrl &fileURL)
saveScheduler Save scheduler jobs to a file
Q_SCRIPTABLE Q_NOREPLY void appendLogText(const QString &logentry) override
appendLogText Append a new line to the logging.
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS interface function.
Q_SCRIPTABLE Q_NOREPLY void removeAllJobs()
DBUS interface function.
Q_SCRIPTABLE void stopCurrentJobAction()
stopCurrentJobAction Stop whatever action taking place in the current job (eg.
Q_SCRIPTABLE Q_NOREPLY void stopGuiding()
stopGuiding After guiding is done we need to stop the process
bool checkShutdownState()
checkShutdownState Check shutdown procedure stages and make sure all stages are complete.
Q_SCRIPTABLE Q_NOREPLY void startSlew()
startSlew DBus call for initiating slew
bool checkEkosState()
checkEkosState Check ekos startup stages and take whatever action necessary to get Ekos up and runnin...
bool checkStatus()
checkJobStatus Check the overall state of the scheduler, Ekos, and INDI.
bool checkINDIState()
checkINDIState Check INDI startup stages and take whatever action necessary to get INDI devices conne...
XMLEle * getSequenceJobRoot(const QString &filename) const
getSequenceJobRoot Read XML data from capture sequence job
Q_SCRIPTABLE Q_NOREPLY void runShutdownProcedure()
runShutdownProcedure Shutdown the scheduler itself and EKOS (if configured to do so).
Q_SCRIPTABLE Q_NOREPLY void setSequence(const QString &sequenceFileURL)
DBUS interface function.
Q_SCRIPTABLE bool loadScheduler(const QString &fileURL)
DBUS interface function.
Q_SCRIPTABLE Q_NOREPLY void startFocusing()
startFocusing DBus call for feeding ekos the specified settings and initiating focus operation
void checkJobStage()
checkJobStage Check the progress of the job states and make DBUS calls to start the next stage until ...
bool checkStartupState()
checkStartupState Check startup procedure stages and make sure all stages are complete.
void processGuidingTimer()
processGuidingTimer Check the guiding timer, and possibly restart guiding.
GuideState getGuidingStatus()
getGuidingStatus Retrieve the guiding status.
Q_SCRIPTABLE Q_NOREPLY void resetAllJobs()
DBUS interface function.
void applyConfig()
applyConfig Apply configuration changes from the global configuration dialog.
Q_SCRIPTABLE void clearLog()
clearLog Clear log entry
Q_SCRIPTABLE Q_NOREPLY void disconnectINDI()
disconnectINDI disconnect all INDI devices from server.
Q_SCRIPTABLE Q_NOREPLY void stop()
DBUS interface function.
bool manageConnectionLoss()
manageConnectionLoss Mitigate loss of connection with the INDI server.
void updateCompletedJobsCount(bool forced=false)
updateCompletedJobsCount For each scheduler job, examine sequence job storage and count captures.
Q_SCRIPTABLE Q_NOREPLY void evaluateJobs(bool evaluateOnly)
evaluateJobs evaluates the current state of each objects and gives each one a score based on the cons...
int runSchedulerIteration()
Run a single scheduler iteration.
void setSolverAction(Align::GotoMode mode)
setSolverAction set the GOTO mode for the solver
Q_SCRIPTABLE bool completeShutdown()
completeShutdown Try to complete the scheduler shutdown
static KConfigDialog * exists(const QString &name)
void settingsChanged(const QString &dialogName)
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
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