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();
549 moduleState()->tickleTimer().stop();
551 moduleState()->setSchedulerState(SCHEDULER_IDLE);
552 moduleState()->setParkWaitState(PARKWAIT_IDLE);
553 moduleState()->setEkosState(EKOS_IDLE);
554 moduleState()->setIndiState(INDI_IDLE);
558 if (moduleState()->startupState() != STARTUP_COMPLETE || moduleState()->preemptiveShutdown())
560 if (moduleState()->startupState() == STARTUP_SCRIPT)
562 scriptProcess().disconnect();
563 scriptProcess().terminate();
566 moduleState()->setStartupState(STARTUP_IDLE);
571 else if (moduleState()->startupState() == STARTUP_COMPLETE)
573 if (Options::schedulerUnparkDome())
574 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
575 else if (Options::schedulerUnparkMount())
576 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
577 else if (Options::schedulerOpenDustCover())
578 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
581 moduleState()->setShutdownState(SHUTDOWN_IDLE);
583 moduleState()->setActiveJob(
nullptr);
584 moduleState()->resetFailureCounters();
585 moduleState()->resetAutofocusCompleted();
588 if (moduleState()->preemptiveShutdown())
590 QDateTime const now = SchedulerModuleState::getLocalTime();
591 int const nextObservationTime = now.
secsTo(moduleState()->preemptiveShutdownWakeupTime());
592 moduleState()->setupNextIteration(RUN_WAKEUP,
593 std::lround(((nextObservationTime + 1) * 1000)
594 / KStarsData::Instance()->clock()->scale()));
596 emit schedulerStopped();
601 if (captureInterface().isNull() ==
false)
602 captureInterface()->setProperty(
"targetName",
QString());
605 scriptProcess().terminate();
608 emit schedulerStopped();
613 emit clearJobTable();
615 qDeleteAll(moduleState()->jobs());
616 moduleState()->mutlableJobs().clear();
617 moduleState()->setCurrentPosition(-1);
629 emit changeCurrentSequence(sequenceFileURL);
634 if (moduleState()->schedulerState() == SCHEDULER_RUNNING)
638 foreach (SchedulerJob *job, moduleState()->jobs())
639 job->setCompletedCount(0);
647 Q_ASSERT_X(
nullptr != job, __FUNCTION__,
648 "There must be a valid current job for Scheduler to test sleep requirement");
650 if (job->getLightFramesRequired() ==
false)
653 QDateTime const now = SchedulerModuleState::getLocalTime();
654 int const nextObservationTime = now.
secsTo(job->getStartupTime());
658 if (getGreedyScheduler()->getScheduledJob() != job)
662 if (Options::schedulerWeather())
664 ISD::Weather::Status weatherStatus = moduleState()->weatherStatus();
665 if (weatherStatus == ISD::Weather::WEATHER_WARNING || weatherStatus == ISD::Weather::WEATHER_ALERT)
668 if (moduleState()->weatherGracePeriodActive())
670 appendLogText(
i18n(
"Job '%1' cannot start because weather status is %2 and grace period is over.",
671 job->getName(), (weatherStatus == ISD::Weather::WEATHER_WARNING) ?
i18n(
"Warning") :
i18n(
"Alert")));
673 moduleState()->setWeatherGracePeriodActive(
false);
678 QDateTime wakeupTime = SchedulerModuleState::getLocalTime().
addSecs(Options::schedulerWeatherGracePeriod() * 60);
680 appendLogText(
i18n(
"Job '%1' cannot start because weather status is %2. Waiting until weather improves or until %3",
681 job->getName(), (weatherStatus == ISD::Weather::WEATHER_WARNING) ?
i18n(
"Warning") :
i18n(
"Alert"),
685 moduleState()->setWeatherGracePeriodActive(
true);
686 moduleState()->enablePreemptiveShutdown(wakeupTime);
688 emit schedulerSleeping(
true,
true);
693 moduleState()->setWeatherGracePeriodActive(
false);
697 if (moduleState()->startupState() == STARTUP_COMPLETE &&
698 Options::preemptiveShutdown() &&
699 nextObservationTime > (Options::preemptiveShutdownTime() * 3600))
702 "Job '%1' scheduled for execution at %2. "
703 "Observatory scheduled for shutdown until next job is ready.",
704 job->getName(), job->getStartupTime().
toString()));
705 moduleState()->enablePreemptiveShutdown(job->getStartupTime());
707 emit schedulerSleeping(
true,
false);
716 else if (nextObservationTime > Options::leadTime() * 60 &&
717 moduleState()->startupState() == STARTUP_COMPLETE &&
718 moduleState()->parkWaitState() == PARKWAIT_IDLE &&
719 (job->getStepPipeline() & SchedulerJob::USE_TRACK) &&
721 Options::schedulerParkMount())
724 "Job '%1' scheduled for execution at %2. "
725 "Parking the mount until the job is ready.",
726 job->getName(), job->getStartupTime().
toString()));
728 moduleState()->setParkWaitState(PARKWAIT_PARK);
732 else if (nextObservationTime > Options::leadTime() * 60)
734 auto log =
i18n(
"Sleeping until observation job %1 is ready at %2", job->getName(),
737 KSNotification::event(
QLatin1String(
"SchedulerSleeping"), log, KSNotification::Scheduler,
738 KSNotification::Info);
741 if (nextObservationTime > Options::leadTime() * 60 * 12 && !Options::preemptiveShutdown())
743 dms delay(
static_cast<double>(nextObservationTime * 15.0 / 3600.0));
745 "Warning: Job '%1' is %2 away from now, you may want to enable Preemptive Shutdown.",
754 moduleState()->setupNextIteration(RUN_WAKEUP,
755 std::lround(((nextObservationTime + 1) * 1000) / KStarsData::Instance()->clock()->scale()));
757 emit schedulerSleeping(
false,
true);
766 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting slewing must be valid");
771 moduleState()->setParkWaitState(PARKWAIT_UNPARK);
775 if (Options::resetMountModelBeforeJob())
780 SkyPoint target = activeJob()->getTargetCoords();
790 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' slew request received DBUS error: %2").
arg(
797 moduleState()->updateJobStage(SCHEDSTAGE_SLEWING);
804 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting focusing must be valid");
807 if (activeJob()->getStage() == SCHEDSTAGE_RESLEWING_COMPLETE ||
808 activeJob()->getStage() == SCHEDSTAGE_POSTALIGN_FOCUSING)
814 moduleState()->updateJobStage(SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE);
819 if (activeJob()->getOpticalTrain() !=
"")
820 m_activeJobs.insert(activeJob()->getOpticalTrain(), activeJob());
823 QVariant opticalTrain = captureInterface()->property(
"opticalTrain");
825 if (opticalTrain.
isValid() ==
false)
827 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' opticalTrain request failed.").
arg(activeJob()->getName());
836 m_activeJobs.insert(opticalTrain.
toString(), activeJob());
837 activeJob()->setOpticalTrain(opticalTrain.
toString());
843 foreach (
auto follower, activeJob()->followerJobs())
845 m_activeJobs.insert(follower->getOpticalTrain(), follower);
857 dBusArgs.
append(job->getOpticalTrain());
858 boolReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"canAutoFocus", dBusArgs);
862 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' canAutoFocus request received DBUS error: %2").
arg(
872 if (boolReply.value() ==
false)
874 appendLogText(
i18n(
"Warning: job '%1' is unable to proceed with autofocus, not supported.", job->getName()));
875 job->setStepPipeline(
876 static_cast<SchedulerJob::StepPipeline
>(job->getStepPipeline() & ~SchedulerJob::USE_FOCUS));
877 moduleState()->setAutofocusCompleted(job->getOpticalTrain(),
true);
878 if (moduleState()->autofocusCompleted())
880 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
889 if ((reply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"clearAutoFocusHFR",
892 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' clearAutoFocusHFR request received DBUS error: %2").arg(
903 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"resetFrame",
906 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' resetFrame request received DBUS error: %2").arg(
918 if (!job->getInitialFilter().
isEmpty())
921 dBusArgs.
append(job->getInitialFilter());
922 dBusArgs.
append(job->getOpticalTrain());
923 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setFilter",
926 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' setFilter request received DBUS error: %1").arg(
938 dBusArgs.
append(job->getOpticalTrain());
939 boolReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"useFullField", dBusArgs);
943 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' useFullField request received DBUS error: %2").arg(
953 if (boolReply.value() ==
false)
958 dBusArgs.
append(job->getOpticalTrain());
959 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setAutoStarEnabled", dBusArgs)).
type() ==
962 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' setAutoFocusStar request received DBUS error: %1").arg(
975 dBusArgs.
append(job->getOpticalTrain());
979 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' startFocus request received DBUS error: %2").arg(
989 moduleState()->updateJobStage(SCHEDSTAGE_FOCUSING);
991 moduleState()->startCurrentOperationTimer();
996 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting aligning must be valid");
1007 moduleState()->setIndexToUse(-1);
1008 moduleState()->setHealpixToUse(-1);
1011 if (activeJob()->getFITSFile().isEmpty() ==
false)
1017 appendLogText(
i18n(
"Warning: job '%1' target FITS file does not exist.", activeJob()->getName()));
1026 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"loadAndSlew", solveArgs)).type() ==
1029 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request received DBUS error: %2",
1038 else if (reply.
arguments().first().toBool() ==
false)
1040 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request failed.", activeJob()->getName()));
1046 appendLogText(
i18n(
"Job '%1' is plate solving %2.", activeJob()->getName(), activeJob()->getFITSFile().fileName()));
1052 const SkyPoint targetCoords = activeJob()->getTargetCoords();
1055 rotationArgs << activeJob()->getPositionAngle();
1057 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords",
1060 appendLogText(
i18n(
"Warning: job '%1' setTargetCoords request received DBUS error: %2",
1071 if (activeJob()->getPositionAngle() >= -180)
1073 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetPositionAngle",
1076 appendLogText(
i18n(
"Warning: job '%1' setTargetPositionAngle request received DBUS error: %2").arg(
1089 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request received DBUS error: %2").arg(
1098 else if (reply.
arguments().first().toBool() ==
false)
1100 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request failed.", activeJob()->getName()));
1106 appendLogText(
i18n(
"Job '%1' is capturing and plate solving.", activeJob()->getName()));
1110 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
1111 moduleState()->startCurrentOperationTimer();
1116 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting guiding must be valid");
1121 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
1122 appendLogText(
i18n(
"Guiding already running for %1, starting next scheduler action...", activeJob()->getName()));
1124 moduleState()->startCurrentOperationTimer();
1137 if (resetCalibration && Options::resetGuideCalibration())
1144 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1146 appendLogText(
i18n(
"Starting guiding procedure for %1 ...", activeJob()->getName()));
1148 moduleState()->startCurrentOperationTimer();
1153 if (!guideInterface())
1157 if (
nullptr != activeJob() && (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE))
1159 qCInfo(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' is stopping guiding...").
arg(activeJob()->getName());
1161 moduleState()->resetGuideFailureCount();
1163 stopCapturing(
"",
true);
1167 if (moduleState()->isGuidingTimerActive())
1168 moduleState()->cancelGuidingTimer();
1173 if ((moduleState()->restartGuidingInterval() > 0) &&
1174 (moduleState()->restartGuidingTime().msecsTo(KStarsData::Instance()->ut()) > moduleState()->restartGuidingInterval()))
1176 moduleState()->cancelGuidingTimer();
1183 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting capturing must be valid");
1186 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE &&
getGuidingStatus() != GUIDE_GUIDING)
1189 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1194 startSingleCapture(activeJob(), restart);
1195 for (
auto follower : activeJob()->followerJobs())
1198 if (follower->getState() ==
SCHEDJOB_SCHEDULED || (follower->getStage() == SCHEDSTAGE_CAPTURING && follower->isStopped()))
1201 follower->setStage(SCHEDSTAGE_CAPTURING);
1202 startSingleCapture(follower, restart);
1206 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
1208 KSNotification::event(
QLatin1String(
"EkosScheduledImagingStart"),
1209 i18n(
"Ekos job (%1) - Capture started", activeJob()->getName()), KSNotification::Scheduler);
1211 if (moduleState()->captureBatch() > 0)
1212 appendLogText(
i18n(
"Job '%1' capture is in progress (batch #%2)...", activeJob()->getName(),
1213 moduleState()->captureBatch() + 1));
1215 appendLogText(
i18n(
"Job '%1' capture is in progress...", activeJob()->getName()));
1217 moduleState()->startCurrentOperationTimer();
1220void SchedulerProcess::startSingleCapture(SchedulerJob *job,
bool restart)
1222 captureInterface()->setProperty(
"targetName", job->getName());
1225 QVariant train(job->getOpticalTrain());
1227 if (restart ==
false)
1232 QVariant targetName(job->getName());
1236 dbusargs.
append(targetName);
1238 "loadSequenceQueue",
1242 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1243 QString(
"Warning: job '%1' loadSequenceQueue request received DBUS error: %1").
arg(job->getName()).
arg(
1250 else if (captureReply.value() ==
false)
1252 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1253 QString(
"Warning: job '%1' loadSequenceQueue request failed").
arg(job->getName());
1262 for (
auto &e : fMap.keys())
1264 QList<QVariant> dbusargs;
1267 dbusargs.
append(fMap.value(e));
1270 if ((reply = captureInterface()->callWithArgumentList(
QDBus::Block,
"setCapturedFramesMap",
1271 dbusargs)).
type() ==
1274 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1275 QString(
"Warning: job '%1' setCapturedFramesCount request received DBUS error: %1").arg(job->getName()).arg(
1284 QList<QVariant> dbusargs;
1287 QDBusReply<QString>
const startReply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"start",
1292 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1293 QString(
"Warning: job '%1' start request received DBUS error: %1").arg(job->getName()).arg(
1300 QString trainName = startReply.value();
1301 m_activeJobs[trainName] = job;
1307 QVariant gotoMode(
static_cast<int>(mode));
1313 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Loading profiles";
1317 moduleState()->updateProfiles(profiles);
1320void SchedulerProcess::executeScript(
const QString &filename)
1329 checkProcessExit(exitCode);
1333 scriptProcess().
start(filename, arguments);
1338 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1341 switch (moduleState()->ekosState())
1345 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1347 moduleState()->setEkosState(EKOS_READY);
1353 moduleState()->setEkosState(EKOS_STARTING);
1354 moduleState()->startCurrentOperationTimer();
1356 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos communication status is" << moduleState()->ekosCommunicationStatus() <<
1365 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1368 moduleState()->resetEkosConnectFailureCount();
1369 moduleState()->setEkosState(EKOS_READY);
1372 else if (moduleState()->ekosCommunicationStatus() == Ekos::Error)
1374 if (moduleState()->increaseEkosConnectFailureCount())
1385 else if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1388 else if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1390 if (moduleState()->increaseEkosConnectFailureCount())
1397 moduleState()->startCurrentOperationTimer();
1411 if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1414 moduleState()->setEkosState(EKOS_IDLE);
1428 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1431 switch (moduleState()->indiState())
1435 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1437 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1438 moduleState()->resetIndiConnectFailureCount();
1439 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI Properties...";
1443 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Connecting INDI devices...";
1445 moduleState()->setIndiState(INDI_CONNECTING);
1447 moduleState()->startCurrentOperationTimer();
1452 case INDI_CONNECTING:
1454 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1457 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1459 else if (moduleState()->indiCommunicationStatus() == Ekos::Error)
1461 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1468 appendLogText(
i18n(
"One or more INDI devices failed to connect. Check INDI control panel for details."));
1473 else if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1475 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1479 moduleState()->startCurrentOperationTimer();
1483 appendLogText(
i18n(
"One or more INDI devices timed out. Check INDI control panel for details."));
1490 case INDI_DISCONNECTING:
1492 if (moduleState()->indiCommunicationStatus() == Ekos::Idle)
1495 moduleState()->setIndiState(INDI_IDLE);
1501 case INDI_PROPERTY_CHECK:
1503 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI properties.";
1505 if (Options::schedulerUnparkDome() && moduleState()->domeReady() ==
false)
1507 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1509 moduleState()->startCurrentOperationTimer();
1510 appendLogText(
i18n(
"Warning: dome device not ready after timeout, attempting to recover..."));
1520 if (Options::schedulerUnparkMount() && moduleState()->mountReady() ==
false)
1522 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1524 moduleState()->startCurrentOperationTimer();
1525 appendLogText(
i18n(
"Warning: mount device not ready after timeout, attempting to recover..."));
1530 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount unpark required but mount is not yet ready.";
1535 if (Options::schedulerOpenDustCover() && moduleState()->capReady() ==
false)
1537 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1539 moduleState()->startCurrentOperationTimer();
1540 appendLogText(
i18n(
"Warning: cap device not ready after timeout, attempting to recover..."));
1545 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap unpark required but cap is not yet ready.";
1550 if (captureInterface().isNull())
1553 if (moduleState()->captureReady() ==
false)
1555 QVariant hasCoolerControl = captureInterface()->property(
"coolerControl");
1556 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cooler control" << (!hasCoolerControl.
isValid() ?
"invalid" :
1557 (hasCoolerControl.
toBool() ?
"True" :
"Faklse"));
1558 if (hasCoolerControl.
isValid())
1559 moduleState()->setCaptureReady(
true);
1561 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"Capture module is not ready yet...";
1564 moduleState()->setIndiState(INDI_READY);
1565 moduleState()->resetIndiConnectFailureCount();
1579 if (moduleState()->indiState() == INDI_DISCONNECTING
1584 if (moduleState()->weatherGracePeriodActive() ==
false)
1587 if (moduleState()->indiState() != INDI_IDLE && Options::stopEkosAfterShutdown())
1594 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
1598 if (moduleState()->ekosState() != EKOS_IDLE && Options::stopEkosAfterShutdown())
1605 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
1618 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Disconnecting INDI...";
1619 moduleState()->setIndiState(INDI_DISCONNECTING);
1623void SchedulerProcess::stopEkos()
1625 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Stopping Ekos...";
1626 moduleState()->setEkosState(EKOS_STOPPING);
1627 moduleState()->resetEkosConnectFailureCount();
1629 moduleState()->setMountReady(
false);
1630 moduleState()->setCaptureReady(
false);
1631 moduleState()->setDomeReady(
false);
1632 moduleState()->setCapReady(
false);
1637 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
1641 switch (moduleState()->ekosState())
1652 switch (moduleState()->indiState())
1655 case INDI_DISCONNECTING:
1664 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1666 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Ekos is currently connected, checking INDI before mitigating connection loss.");
1669 if (moduleState()->isINDIConnected())
1672 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"INDI is currently connected, no connection loss mitigation needed.");
1691 if (capInterface().isNull())
1694 QVariant parkingStatus = capInterface()->property(
"parkStatus");
1695 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1697 if (parkingStatus.
isValid() ==
false)
1699 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
1700 capInterface()->lastError().type());
1702 parkingStatus = ISD::PARK_ERROR;
1705 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1709 case ISD::PARK_PARKED:
1710 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1713 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
1715 moduleState()->resetParkingCapFailureCount();
1718 case ISD::PARK_UNPARKED:
1719 if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1721 moduleState()->setStartupState(STARTUP_COMPLETE);
1724 moduleState()->resetParkingCapFailureCount();
1727 case ISD::PARK_PARKING:
1728 case ISD::PARK_UNPARKING:
1730 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1732 if (moduleState()->increaseParkingCapFailureCount())
1735 if (status == ISD::PARK_PARKING)
1744 case ISD::PARK_ERROR:
1745 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1748 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1750 else if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1753 moduleState()->setStartupState(STARTUP_ERROR);
1755 moduleState()->resetParkingCapFailureCount();
1763void SchedulerProcess::checkMountParkingStatus()
1765 if (mountInterface().isNull())
1768 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
1769 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1771 if (parkingStatus.
isValid() ==
false)
1773 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
1774 mountInterface()->lastError().type());
1776 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1779 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1784 case ISD::PARK_PARKED:
1787 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1788 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1791 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1792 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1795 moduleState()->resetParkingMountFailureCount();
1799 case ISD::PARK_UNPARKED:
1802 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1803 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1806 if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1807 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1810 moduleState()->resetParkingMountFailureCount();
1816 case ISD::PARK_UNPARKING:
1817 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1819 if (moduleState()->increaseParkingMountFailureCount())
1821 appendLogText(
i18n(
"Warning: mount unpark operation timed out on attempt %1/%2. Restarting operation...",
1822 moduleState()->parkingMountFailureCount(), moduleState()->maxFailureAttempts()));
1827 appendLogText(
i18n(
"Warning: mount unpark operation timed out on last attempt."));
1828 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1831 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
1836 case ISD::PARK_PARKING:
1837 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1839 if (moduleState()->increaseParkingMountFailureCount())
1841 appendLogText(
i18n(
"Warning: mount park operation timed out on attempt %1/%2. Restarting operation...",
1842 moduleState()->parkingMountFailureCount(),
1843 moduleState()->maxFailureAttempts()));
1848 appendLogText(
i18n(
"Warning: mount park operation timed out on last attempt."));
1849 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1852 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Parking mount in progress...";
1857 case ISD::PARK_ERROR:
1858 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1861 moduleState()->setStartupState(STARTUP_ERROR);
1862 moduleState()->resetParkingMountFailureCount();
1864 else if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1866 if (moduleState()->increaseParkingMountFailureCount())
1868 appendLogText(
i18n(
"Warning: mount park operation failed on attempt %1/%2. Restarting operation...",
1869 moduleState()->parkingMountFailureCount(),
1870 moduleState()->maxFailureAttempts()));
1876 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1877 moduleState()->resetParkingMountFailureCount();
1881 else if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1884 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1885 moduleState()->resetParkingMountFailureCount();
1887 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1890 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1891 moduleState()->resetParkingMountFailureCount();
1897 case ISD::PARK_UNKNOWN:
1899 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1900 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1903 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1904 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1907 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1908 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1909 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1910 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1912 moduleState()->resetParkingMountFailureCount();
1917void SchedulerProcess::checkDomeParkingStatus()
1919 if (domeInterface().isNull())
1922 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
1923 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1925 if (parkingStatus.
isValid() ==
false)
1927 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
1928 mountInterface()->lastError().
type());
1930 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1933 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1937 case ISD::PARK_PARKED:
1938 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1942 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
1944 moduleState()->resetParkingDomeFailureCount();
1947 case ISD::PARK_UNPARKED:
1948 if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1950 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1953 moduleState()->resetParkingDomeFailureCount();
1956 case ISD::PARK_PARKING:
1957 case ISD::PARK_UNPARKING:
1959 if (moduleState()->getCurrentOperationMsec() > (120 * 1000))
1961 if (moduleState()->increaseParkingDomeFailureCount())
1964 if (status == ISD::PARK_PARKING)
1973 case ISD::PARK_ERROR:
1974 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1976 if (moduleState()->increaseParkingDomeFailureCount())
1984 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1985 moduleState()->resetParkingDomeFailureCount();
1988 else if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1990 if (moduleState()->increaseParkingDomeFailureCount())
1998 moduleState()->setStartupState(STARTUP_ERROR);
1999 moduleState()->resetParkingDomeFailureCount();
2011 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2014 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Checking Startup State (%1)...").
arg(moduleState()->startupState());
2016 switch (moduleState()->startupState())
2020 KSNotification::event(
QLatin1String(
"ObservatoryStartup"),
i18n(
"Observatory is in the startup process"),
2021 KSNotification::Scheduler);
2023 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Startup Idle. Starting startup process...";
2030 if (Options::alwaysExecuteStartupScript() ==
false && moduleState()->ekosCommunicationStatus() == Ekos::Success)
2032 if (moduleState()->startupScriptURL().isEmpty() ==
false)
2035 if (!activeJob() || activeJob()->getLightFramesRequired())
2036 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
2038 moduleState()->setStartupState(STARTUP_COMPLETE);
2042 if (moduleState()->currentProfile() !=
i18n(
"Default"))
2045 profile.
append(moduleState()->currentProfile());
2046 ekosInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setProfile", profile);
2049 if (moduleState()->startupScriptURL().isEmpty() ==
false)
2051 moduleState()->setStartupState(STARTUP_SCRIPT);
2056 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
2060 case STARTUP_SCRIPT:
2063 case STARTUP_UNPARK_DOME:
2067 if (activeJob() ==
nullptr || activeJob()->getLightFramesRequired())
2069 if (Options::schedulerUnparkDome())
2072 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
2076 moduleState()->setStartupState(STARTUP_COMPLETE);
2082 case STARTUP_UNPARKING_DOME:
2083 checkDomeParkingStatus();
2086 case STARTUP_UNPARK_MOUNT:
2087 if (Options::schedulerUnparkMount())
2090 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
2093 case STARTUP_UNPARKING_MOUNT:
2094 checkMountParkingStatus();
2097 case STARTUP_UNPARK_CAP:
2098 if (Options::schedulerOpenDustCover())
2101 moduleState()->setStartupState(STARTUP_COMPLETE);
2104 case STARTUP_UNPARKING_CAP:
2108 case STARTUP_COMPLETE:
2121 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking shutdown state...";
2123 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2126 switch (moduleState()->shutdownState())
2130 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Starting shutdown process...";
2132 moduleState()->setActiveJob(
nullptr);
2133 moduleState()->setupNextIteration(RUN_SHUTDOWN);
2134 emit shutdownStarted();
2136 if (Options::schedulerWarmCCD())
2143 if (captureInterface())
2145 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Setting coolerControl=false";
2146 captureInterface()->setProperty(
"coolerControl",
false);
2151 if (moduleState()->isINDIConnected())
2153 if (Options::schedulerCloseDustCover())
2155 moduleState()->setShutdownState(SHUTDOWN_PARK_CAP);
2159 if (Options::schedulerParkMount())
2161 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2165 if (Options::schedulerParkDome())
2167 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2171 else appendLogText(
i18n(
"Warning: Bypassing parking procedures, no INDI connection."));
2173 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2175 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2179 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2182 case SHUTDOWN_PARK_CAP:
2183 if (!moduleState()->isINDIConnected())
2185 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2186 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2188 else if (Options::schedulerCloseDustCover())
2191 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2194 case SHUTDOWN_PARKING_CAP:
2198 case SHUTDOWN_PARK_MOUNT:
2199 if (!moduleState()->isINDIConnected())
2201 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2202 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2204 else if (Options::schedulerParkMount())
2207 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2210 case SHUTDOWN_PARKING_MOUNT:
2211 checkMountParkingStatus();
2214 case SHUTDOWN_PARK_DOME:
2215 if (!moduleState()->isINDIConnected())
2217 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2218 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2220 else if (Options::schedulerParkDome())
2223 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2226 case SHUTDOWN_PARKING_DOME:
2227 checkDomeParkingStatus();
2230 case SHUTDOWN_SCRIPT:
2231 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2234 if (moduleState()->ekosState() != EKOS_IDLE && Options::shutdownScriptTerminatesINDI())
2240 moduleState()->setShutdownState(SHUTDOWN_SCRIPT_RUNNING);
2244 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2247 case SHUTDOWN_SCRIPT_RUNNING:
2250 case SHUTDOWN_COMPLETE:
2253 case SHUTDOWN_ERROR:
2263 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2266 if (moduleState()->parkWaitState() == PARKWAIT_IDLE)
2271 switch (moduleState()->parkWaitState())
2277 case PARKWAIT_PARKING:
2278 checkMountParkingStatus();
2281 case PARKWAIT_UNPARK:
2285 case PARKWAIT_UNPARKING:
2286 checkMountParkingStatus();
2290 case PARKWAIT_PARKED:
2291 case PARKWAIT_UNPARKED:
2294 case PARKWAIT_ERROR:
2306 if (moduleState()->startupState() == STARTUP_IDLE
2307 || moduleState()->startupState() == STARTUP_ERROR
2308 || moduleState()->startupState() == STARTUP_COMPLETE)
2312 KSMessageBox::Instance()->disconnect(
this);
2315 moduleState()->setStartupState(STARTUP_IDLE);
2321 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the startup procedure manually?"));
2325 switch (moduleState()->startupState())
2330 case STARTUP_SCRIPT:
2331 scriptProcess().terminate();
2334 case STARTUP_UNPARK_DOME:
2337 case STARTUP_UNPARKING_DOME:
2338 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking dome...";
2342 case STARTUP_UNPARK_MOUNT:
2345 case STARTUP_UNPARKING_MOUNT:
2346 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking mount...";
2350 case STARTUP_UNPARK_CAP:
2353 case STARTUP_UNPARKING_CAP:
2356 case STARTUP_COMPLETE:
2363 moduleState()->setStartupState(STARTUP_IDLE);
2372 if (moduleState()->shutdownState() == SHUTDOWN_IDLE
2373 || moduleState()->shutdownState() == SHUTDOWN_ERROR
2374 || moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
2378 KSMessageBox::Instance()->disconnect(
this);
2380 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2385 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the shutdown procedure manually?"));
2389 switch (moduleState()->shutdownState())
2394 case SHUTDOWN_SCRIPT:
2397 case SHUTDOWN_SCRIPT_RUNNING:
2398 scriptProcess().terminate();
2401 case SHUTDOWN_PARK_DOME:
2404 case SHUTDOWN_PARKING_DOME:
2405 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking dome...";
2409 case SHUTDOWN_PARK_MOUNT:
2412 case SHUTDOWN_PARKING_MOUNT:
2413 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking mount...";
2417 case SHUTDOWN_PARK_CAP:
2418 case SHUTDOWN_PARKING_CAP:
2421 case SHUTDOWN_COMPLETE:
2424 case SHUTDOWN_ERROR:
2428 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2436 moduleState()->setupNextIteration(RUN_NOTHING);
2438 emit schedulerPaused();
2444 for (SchedulerJob * job : moduleState()->jobs())
2447 job->setCompletedCount(0);
2456 auto finished_or_aborted = [](SchedulerJob
const *
const job)
2463 auto neither_scheduled_nor_aborted = [](SchedulerJob
const *
const job)
2471 if (jobs.
isEmpty() || std::all_of(jobs.
begin(), jobs.
end(), neither_scheduled_nor_aborted))
2474 moduleState()->setActiveJob(
nullptr);
2478 else if (std::all_of(jobs.
begin(), jobs.
end(), finished_or_aborted) &&
2479 strategy != ERROR_DONT_RESTART)
2481 appendLogText(
i18n(
"Only aborted jobs left in the scheduler queue after evaluating, rescheduling those."));
2482 std::for_each(jobs.
begin(), jobs.
end(), [](SchedulerJob * job)
2484 if (SCHEDJOB_ABORTED == job->getState())
2485 job->setState(SCHEDJOB_EVALUATION);
2492 SchedulerJob *scheduledJob = getGreedyScheduler()->getScheduledJob();
2496 moduleState()->setActiveJob(
nullptr);
2499 if (activeJob() !=
nullptr && scheduledJob != activeJob())
2502 for (
auto job : m_activeJobs.values())
2504 stopCapturing(job->getOpticalTrain(),
false);
2507 m_activeJobs.clear();
2509 moduleState()->setActiveJob(scheduledJob);
2517 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2521 moduleState()->resetSequenceExecutionCounter();
2529 for (
auto job : moduleState()->jobs())
2533 if (moduleState()->jobs().isEmpty())
2536 if (Options::rememberJobProgress())
2539 moduleState()->calculateDawnDusk();
2541 getGreedyScheduler()->scheduleJobs(moduleState()->jobs(), SchedulerModuleState::getLocalTime(),
2542 moduleState()->capturedFramesCount(),
this);
2546 if (!evaluateOnly && moduleState()->schedulerState() == SCHEDULER_RUNNING)
2551 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos finished evaluating jobs, no job selection required.";
2553 emit jobsUpdated(moduleState()->getJSONJobs());
2558 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2560 if (activeJob() ==
nullptr)
2565 switch (activeJob()->getState())
2581 if (activeJob() ==
nullptr)
2584 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE
2585 || moduleState()->shutdownState() == SHUTDOWN_ERROR)
2591 if (moduleState()->shutdownState() > SHUTDOWN_IDLE)
2594 if (moduleState()->ekosState() == EKOS_STOPPING &&
checkEkosState() ==
false)
2609 if (
nullptr == activeJob() && moduleState()->checkRepeatSequence())
2618 moduleState()->increaseSequenceExecutionCounter();
2619 appendLogText(
i18n(
"Starting job sequence iteration #%1", moduleState()->sequenceExecutionCounter()));
2625 if (
nullptr == activeJob())
2635 if (moduleState()->startupState() == STARTUP_ERROR)
2643 if ((moduleState()->startupState() == STARTUP_IDLE
2645 || moduleState()->startupState() == STARTUP_SCRIPT)
2661 if (moduleState()->startupState() > STARTUP_SCRIPT
2662 && moduleState()->startupState() < STARTUP_ERROR
2674 emit updateJobTable();
2682 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Get next action...";
2684 switch (activeJob()->getStage())
2686 case SCHEDSTAGE_IDLE:
2687 if (activeJob()->getLightFramesRequired())
2689 if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
2691 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2693 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3485";
2696 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2698 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2711 if (activeJob()->getStepPipeline())
2713 i18n(
"Job '%1' is proceeding directly to capture stage because only calibration frames are pending.",
2714 activeJob()->getName()));
2720 case SCHEDSTAGE_SLEW_COMPLETE:
2721 if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2723 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3514";
2726 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2728 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2734 case SCHEDSTAGE_FOCUS_COMPLETE:
2735 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2737 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2743 case SCHEDSTAGE_ALIGN_COMPLETE:
2744 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING);
2747 case SCHEDSTAGE_RESLEWING_COMPLETE:
2750 if ((activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS) && activeJob()->getInSequenceFocus())
2753 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3544";
2756 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2762 case SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE:
2763 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2769 case SCHEDSTAGE_GUIDING_COMPLETE:
2787 constexpr int oneHour = 1000 * 3600;
2788 moduleState()->tickleTimer().stop();
2790 if (msSleep > oneHour)
2794 if (moduleState() && moduleState()->currentlySleeping())
2796 moduleState()->tickleTimer().start(oneHour);
2797 emit updateJobTable(
nullptr);
2800 moduleState()->tickleTimer().setSingleShot(
true);
2801 moduleState()->tickleTimer().start(oneHour);
2803 moduleState()->iterationTimer().setSingleShot(
true);
2804 moduleState()->iterationTimer().start(msSleep);
2811 if (moduleState()->startMSecs() == 0)
2812 moduleState()->setStartMSecs(now);
2825 moduleState()->setIterationSetup(
false);
2826 switch (keepTimerState)
2829 changeSleepLabel(
"",
false);
2842 moduleState()->setTimerInterval(-1);
2845 if (!moduleState()->iterationSetup())
2851 moduleState()->setTimerInterval(moduleState()->updatePeriodMs());
2854 return moduleState()->timerInterval();
2859 Q_ASSERT_X(activeJob(), __FUNCTION__,
"Actual current job is required to check job stage");
2863 if (checkJobStageCounter == 0)
2865 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking job stage for" << activeJob()->getName() <<
"startup" <<
2866 activeJob()->getStartupCondition() << activeJob()->getStartupTime().toString() <<
"state" << activeJob()->getState();
2867 if (checkJobStageCounter++ == 30)
2868 checkJobStageCounter = 0;
2871 emit syncGreedyParams();
2872 if (!getGreedyScheduler()->checkJob(moduleState()->leadJobs(), SchedulerModuleState::getLocalTime(), activeJob()))
2879 checkJobStageEpilogue();
2882void SchedulerProcess::checkJobStageEpilogue()
2897 if (!activeJob())
return;
2898 switch (activeJob()->getStage())
2900 case SCHEDSTAGE_IDLE:
2902 emit jobStarted(activeJob()->getName());
2906 case SCHEDSTAGE_ALIGNING:
2908 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(ALIGN_INACTIVITY_TIMEOUT))
2910 QVariant const status = alignInterface()->property(
"status");
2915 if (moduleState()->increaseAlignFailureCount())
2917 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align module timed out. Restarting request...";
2922 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
2928 moduleState()->startCurrentOperationTimer();
2932 case SCHEDSTAGE_CAPTURING:
2934 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(CAPTURE_INACTIVITY_TIMEOUT))
2936 QVariant
const status = captureInterface()->property(
"status");
2941 if (moduleState()->increaseCaptureFailureCount())
2943 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"capture module timed out. Restarting request...";
2948 appendLogText(
i18n(
"Warning: job '%1' capture procedure failed, marking aborted.", activeJob()->getName()));
2953 else moduleState()->startCurrentOperationTimer();
2957 case SCHEDSTAGE_FOCUSING:
2959 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(FOCUS_INACTIVITY_TIMEOUT))
2961 bool success =
true;
2962 foreach (
const QString trainname, m_activeJobs.keys())
2964 QList<QVariant> dbusargs;
2965 dbusargs.
append(trainname);
2966 QDBusReply<Ekos::FocusState> statusReply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"status", dbusargs);
2969 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: job '%1' status request received DBUS error: %2").arg(
2979 Ekos::FocusState focusStatus = statusReply.value();
2980 if (focusStatus == Ekos::FOCUS_IDLE || focusStatus == Ekos::FOCUS_WAITING)
2982 if (moduleState()->increaseFocusFailureCount(trainname))
2984 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus module timed out. Restarting request...";
2992 if (success ==
false)
2994 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
2999 else moduleState()->startCurrentOperationTimer();
3002 case SCHEDSTAGE_GUIDING:
3004 if (moduleState()->getCurrentOperationMsec() > GUIDE_INACTIVITY_TIMEOUT)
3008 if (guideStatus == Ekos::GUIDE_IDLE || guideStatus == Ekos::GUIDE_CONNECTED || guideStatus == Ekos::GUIDE_DISCONNECTED)
3010 if (moduleState()->increaseGuideFailureCount())
3012 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"guide module timed out. Restarting request...";
3017 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3022 else moduleState()->startCurrentOperationTimer();
3026 case SCHEDSTAGE_SLEWING:
3027 case SCHEDSTAGE_RESLEWING:
3030 QVariant
const slewStatus = mountInterface()->property(
"status");
3036 ISD::Mount::Status
const status =
static_cast<ISD::Mount::Status
>(slewStatus.
toInt());
3037 setMountStatus(status);
3041 appendLogText(
i18n(
"Warning: job '%1' lost connection to the mount, attempting to reconnect.", activeJob()->getName()));
3049 case SCHEDSTAGE_SLEW_COMPLETE:
3050 case SCHEDSTAGE_RESLEWING_COMPLETE:
3052 if (moduleState()->domeReady())
3054 QVariant
const isDomeMoving = domeInterface()->property(
"isMoving");
3058 appendLogText(
i18n(
"Warning: job '%1' lost connection to the dome, attempting to reconnect.", activeJob()->getName()));
3064 if (!isDomeMoving.
value<
bool>())
3077 moduleState()->calculateDawnDusk();
3079 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
3091 if (activeJob() == job &&
SCHEDJOB_BUSY == activeJob()->getState())
3094 moduleState()->setActiveJob(job);
3106 else if (0 < SchedulerModuleState::getLocalTime().secsTo(activeJob()->getStartupTime()))
3111 if (job->getCompletionCondition() == FINISH_SEQUENCE && Options::rememberJobProgress())
3112 captureInterface()->setProperty(
"targetName", job->getName());
3114 moduleState()->calculateDawnDusk();
3118 moduleState()->setAutofocusCompleted(job->getOpticalTrain(),
false);
3120 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Executing Job " << activeJob()->getName();
3123 emit jobsUpdated(moduleState()->getJSONJobs());
3125 KSNotification::event(
QLatin1String(
"EkosSchedulerJobStart"),
3126 i18n(
"Ekos job started (%1)", activeJob()->getName()), KSNotification::Scheduler);
3129 moduleState()->setupNextIteration(RUN_JOBCHECK);
3141 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3150 outstream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<
Qt::endl;
3151 outstream <<
"<SchedulerList version='2.1'>" <<
Qt::endl;
3153 outstream <<
"<Profile>" <<
QString(entityXML(strdup(moduleState()->currentProfile().toStdString().c_str()))) <<
3156 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
3157 bool useMosaicInfo = !tiles->sequenceFile().isEmpty();
3161 outstream <<
"<Mosaic>" <<
Qt::endl;
3162 outstream <<
"<Target>" << tiles->targetName() <<
"</Target>" <<
Qt::endl;
3163 outstream <<
"<Group>" << tiles->group() <<
"</Group>" <<
Qt::endl;
3165 QString ccArg, ccValue = tiles->completionCondition(&ccArg);
3166 if (ccValue ==
"FinishSequence")
3167 outstream <<
"<FinishSequence/>" <<
Qt::endl;
3168 else if (ccValue ==
"FinishLoop")
3169 outstream <<
"<FinishLoop/>" <<
Qt::endl;
3170 else if (ccValue ==
"FinishRepeat")
3171 outstream <<
"<FinishRepeat>" << ccArg <<
"</FinishRepeat>" <<
Qt::endl;
3173 outstream <<
"<Sequence>" << tiles->sequenceFile() <<
"</Sequence>" <<
Qt::endl;
3174 outstream <<
"<Directory>" << tiles->outputDirectory() <<
"</Directory>" <<
Qt::endl;
3176 outstream <<
"<FocusEveryN>" << tiles->focusEveryN() <<
"</FocusEveryN>" <<
Qt::endl;
3177 outstream <<
"<AlignEveryN>" << tiles->alignEveryN() <<
"</AlignEveryN>" <<
Qt::endl;
3178 if (tiles->isTrackChecked())
3179 outstream <<
"<TrackChecked/>" <<
Qt::endl;
3180 if (tiles->isFocusChecked())
3181 outstream <<
"<FocusChecked/>" <<
Qt::endl;
3182 if (tiles->isAlignChecked())
3183 outstream <<
"<AlignChecked/>" <<
Qt::endl;
3184 if (tiles->isGuideChecked())
3185 outstream <<
"<GuideChecked/>" <<
Qt::endl;
3186 outstream <<
"<Overlap>" << cLocale.
toString(tiles->overlap()) <<
"</Overlap>" <<
Qt::endl;
3187 outstream <<
"<CenterRA>" << cLocale.
toString(tiles->ra0().Hours()) <<
"</CenterRA>" <<
Qt::endl;
3188 outstream <<
"<CenterDE>" << cLocale.
toString(tiles->dec0().Degrees()) <<
"</CenterDE>" <<
Qt::endl;
3189 outstream <<
"<GridW>" << tiles->gridSize().width() <<
"</GridW>" <<
Qt::endl;
3190 outstream <<
"<GridH>" << tiles->gridSize().height() <<
"</GridH>" <<
Qt::endl;
3191 outstream <<
"<FOVW>" << cLocale.
toString(tiles->mosaicFOV().width()) <<
"</FOVW>" <<
Qt::endl;
3192 outstream <<
"<FOVH>" << cLocale.
toString(tiles->mosaicFOV().height()) <<
"</FOVH>" <<
Qt::endl;
3193 outstream <<
"<CameraFOVW>" << cLocale.
toString(tiles->cameraFOV().width()) <<
"</CameraFOVW>" <<
Qt::endl;
3194 outstream <<
"<CameraFOVH>" << cLocale.
toString(tiles->cameraFOV().height()) <<
"</CameraFOVH>" <<
Qt::endl;
3195 outstream <<
"</Mosaic>" <<
Qt::endl;
3199 for (
auto &job : moduleState()->jobs())
3204 outstream <<
"<JobType lead='" << (job->isLead() ?
"true" :
"false") <<
"'/>" <<
Qt::endl;
3209 outstream <<
"<Coordinates>" <<
Qt::endl;
3212 outstream <<
"</Coordinates>" <<
Qt::endl;
3215 if (! job->getOpticalTrain().
isEmpty())
3216 outstream <<
"<OpticalTrain>" <<
QString(entityXML(strdup(job->getOpticalTrain().
toStdString().c_str()))) <<
3219 if (job->isLead() && job->getFITSFile().
isValid() && job->getFITSFile().
isEmpty() ==
false)
3222 outstream <<
"<PositionAngle>" << job->getPositionAngle() <<
"</PositionAngle>" <<
Qt::endl;
3224 outstream <<
"<Sequence>" << job->getSequenceFile().
toLocalFile() <<
"</Sequence>" <<
Qt::endl;
3226 if (useMosaicInfo && index < tiles->tiles().size())
3228 auto oneTile = tiles->tiles().at(index++);
3229 outstream <<
"<TileCenter>" <<
Qt::endl;
3230 outstream <<
"<X>" << cLocale.
toString(oneTile->center.x()) <<
"</X>" <<
Qt::endl;
3231 outstream <<
"<Y>" << cLocale.
toString(oneTile->center.y()) <<
"</Y>" <<
Qt::endl;
3232 outstream <<
"<Rotation>" << cLocale.
toString(oneTile->rotation) <<
"</Rotation>" <<
Qt::endl;
3233 outstream <<
"</TileCenter>" <<
Qt::endl;
3238 outstream <<
"<StartupCondition>" <<
Qt::endl;
3239 if (job->getFileStartupCondition() == START_ASAP)
3240 outstream <<
"<Condition>ASAP</Condition>" <<
Qt::endl;
3241 else if (job->getFileStartupCondition() == START_AT)
3242 outstream <<
"<Condition value='" << job->getStartAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3244 outstream <<
"</StartupCondition>" <<
Qt::endl;
3246 outstream <<
"<Constraints>" <<
Qt::endl;
3247 if (job->hasMinAltitude())
3248 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinAltitude()) <<
"'>MinimumAltitude</Constraint>" <<
3250 if (job->getMinMoonSeparation() > 0)
3251 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinMoonSeparation()) <<
"'>MoonSeparation</Constraint>"
3253 if (job->getMaxMoonAltitude() < 90)
3254 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMaxMoonAltitude()) <<
"'>MoonMaxAltitude</Constraint>"
3256 if (job->getEnforceTwilight())
3257 outstream <<
"<Constraint>EnforceTwilight</Constraint>" <<
Qt::endl;
3258 if (job->getEnforceArtificialHorizon())
3259 outstream <<
"<Constraint>EnforceArtificialHorizon</Constraint>" <<
Qt::endl;
3260 outstream <<
"</Constraints>" <<
Qt::endl;
3263 outstream <<
"<CompletionCondition>" <<
Qt::endl;
3264 if (job->getCompletionCondition() == FINISH_SEQUENCE)
3265 outstream <<
"<Condition>Sequence</Condition>" <<
Qt::endl;
3266 else if (job->getCompletionCondition() == FINISH_REPEAT)
3267 outstream <<
"<Condition value='" << cLocale.
toString(job->getRepeatsRequired()) <<
"'>Repeat</Condition>" <<
Qt::endl;
3268 else if (job->getCompletionCondition() == FINISH_LOOP)
3269 outstream <<
"<Condition>Loop</Condition>" <<
Qt::endl;
3270 else if (job->getCompletionCondition() == FINISH_AT)
3271 outstream <<
"<Condition value='" << job->getFinishAtTime().
toString(
Qt::ISODate) <<
"'>At</Condition>"
3273 outstream <<
"</CompletionCondition>" <<
Qt::endl;
3277 outstream <<
"<Steps>" <<
Qt::endl;
3278 if (job->getStepPipeline() & SchedulerJob::USE_TRACK)
3279 outstream <<
"<Step>Track</Step>" <<
Qt::endl;
3280 if (job->getStepPipeline() & SchedulerJob::USE_FOCUS)
3281 outstream <<
"<Step>Focus</Step>" <<
Qt::endl;
3282 if (job->getStepPipeline() & SchedulerJob::USE_ALIGN)
3283 outstream <<
"<Step>Align</Step>" <<
Qt::endl;
3284 if (job->getStepPipeline() & SchedulerJob::USE_GUIDE)
3285 outstream <<
"<Step>Guide</Step>" <<
Qt::endl;
3286 outstream <<
"</Steps>" <<
Qt::endl;
3291 outstream <<
"<SchedulerAlgorithm value='" << ALGORITHM_GREEDY <<
"'/>" <<
Qt::endl;
3292 outstream <<
"<ErrorHandlingStrategy value='" << Options::errorHandlingStrategy() <<
"'>" <<
Qt::endl;
3293 if (Options::rescheduleErrors())
3294 outstream <<
"<RescheduleErrors />" <<
Qt::endl;
3295 outstream <<
"<delay>" << Options::errorHandlingStrategyDelay() <<
"</delay>" <<
Qt::endl;
3296 outstream <<
"</ErrorHandlingStrategy>" <<
Qt::endl;
3298 outstream <<
"<StartupProcedure>" <<
Qt::endl;
3299 if (moduleState()->startupScriptURL().isEmpty() ==
false)
3300 outstream <<
"<Procedure value='" << moduleState()->startupScriptURL().toString(
QUrl::PreferLocalFile) <<
3301 "'>StartupScript</Procedure>" <<
Qt::endl;
3302 if (Options::schedulerUnparkDome())
3303 outstream <<
"<Procedure>UnparkDome</Procedure>" <<
Qt::endl;
3304 if (Options::schedulerUnparkMount())
3305 outstream <<
"<Procedure>UnparkMount</Procedure>" <<
Qt::endl;
3306 if (Options::schedulerOpenDustCover())
3307 outstream <<
"<Procedure>UnparkCap</Procedure>" <<
Qt::endl;
3308 outstream <<
"</StartupProcedure>" <<
Qt::endl;
3310 outstream <<
"<ShutdownProcedure>" <<
Qt::endl;
3311 if (Options::schedulerWarmCCD())
3312 outstream <<
"<Procedure>WarmCCD</Procedure>" <<
Qt::endl;
3313 if (Options::schedulerCloseDustCover())
3314 outstream <<
"<Procedure>ParkCap</Procedure>" <<
Qt::endl;
3315 if (Options::schedulerParkMount())
3316 outstream <<
"<Procedure>ParkMount</Procedure>" <<
Qt::endl;
3317 if (Options::schedulerParkDome())
3318 outstream <<
"<Procedure>ParkDome</Procedure>" <<
Qt::endl;
3319 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
3320 outstream <<
"<Procedure value='" << moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile) <<
3321 "'>schedulerStartupScript</Procedure>" <<
3323 outstream <<
"</ShutdownProcedure>" <<
Qt::endl;
3325 outstream <<
"</SchedulerList>" <<
Qt::endl;
3329 moduleState()->setDirty(
false);
3333void SchedulerProcess::checkAlignment(
const QVariantMap &metadata,
const QString &trainname)
3336 if (activeJob() ==
nullptr || (activeJob()->getOpticalTrain() !=
"" && activeJob()->getOpticalTrain() != trainname))
3338 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Ignoring metadata from train =" << trainname <<
"for alignment check.";
3342 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN &&
3343 metadata[
"type"].toInt() == FRAME_LIGHT &&
3344 Options::alignCheckFrequency() > 0 &&
3345 moduleState()->increaseSolverIteration() >= Options::alignCheckFrequency())
3347 moduleState()->resetSolverIteration();
3349 auto filename = metadata[
"filename"].toString();
3350 auto exposure = metadata[
"exposure"].toDouble();
3352 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking alignment on train =" << trainname <<
"for" << filename;
3354 constexpr double minSolverSeconds = 5.0;
3355 double solverTimeout = std::max(exposure - 2, minSolverSeconds);
3356 if (solverTimeout >= minSolverSeconds)
3358 auto profiles = getDefaultAlignOptionsProfiles();
3360 SSolver::Parameters parameters;
3365 parameters = profiles.at(Options::solveOptionsProfile());
3367 catch (std::out_of_range
const &)
3369 parameters = profiles[0];
3373 parameters.search_radius = parameters.search_radius * 2;
3378 auto width = metadata[
"width"].toUInt() / (metadata[
"binx"].isValid() ? metadata[
"binx"].toUInt() : 1);
3379 auto height = metadata[
"height"].toUInt() / (metadata[
"biny"].isValid() ? metadata[
"biny"].toUInt() : 1);
3381 auto lowScale = Options::astrometryImageScaleLow();
3382 auto highScale = Options::astrometryImageScaleHigh();
3385 if (Options::astrometryImageScaleUnits() == SSolver::DEG_WIDTH)
3387 lowScale = (lowScale * 3600) / std::max(width, height);
3388 highScale = (highScale * 3600) / std::min(width, height);
3390 else if (Options::astrometryImageScaleUnits() == SSolver::ARCMIN_WIDTH)
3392 lowScale = (lowScale * 60) / std::max(width, height);
3393 highScale = (highScale * 60) / std::min(width, height);
3396 m_Solver->useScale(Options::astrometryUseImageScale(), lowScale, highScale);
3397 m_Solver->usePosition(Options::astrometryUsePosition(), activeJob()->getTargetCoords().ra().Degrees(),
3398 activeJob()->getTargetCoords().
dec().Degrees());
3399 m_Solver->setHealpix(moduleState()->indexToUse(), moduleState()->healpixToUse());
3400 m_Solver->runSolver(filename);
3405void SchedulerProcess::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
3407 disconnect(m_Solver.get(), &SolverUtils::done,
this, &Ekos::SchedulerProcess::solverDone);
3412 QString healpixString =
"";
3413 if (moduleState()->indexToUse() != -1 || moduleState()->healpixToUse() != -1)
3414 healpixString = QString(
"Healpix %1 Index %2").
arg(moduleState()->healpixToUse()).
arg(moduleState()->indexToUse());
3416 if (timedOut || !success)
3419 moduleState()->setIndexToUse(-1);
3420 moduleState()->setHealpixToUse(-1);
3426 m_Solver->getSolutionHealpix(&index, &healpix);
3427 moduleState()->setIndexToUse(index);
3428 moduleState()->setHealpixToUse(healpix);
3432 appendLogText(
i18n(
"Solver timed out: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3434 appendLogText(
i18n(
"Solver failed: %1s %2", QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3437 const double ra = solution.ra;
3438 const double dec = solution.dec;
3440 const auto target = activeJob()->getTargetCoords();
3442 SkyPoint alignCoord;
3443 alignCoord.
setRA0(ra / 15.0);
3447 const double diffRa = (alignCoord.
ra().deltaAngle(target.ra())).Degrees() * 3600;
3448 const double diffDec = (alignCoord.
dec().deltaAngle(target.dec())).Degrees() * 3600;
3451 const double diffTotal = hypot(diffRa, diffDec);
3455 qCDebug(KSTARS_EKOS_SCHEDULER) <<
3456 QString(
"Target Distance: %1\" Target (RA: %2 DE: %3) Current (RA: %4 DE: %5) %6 solved in %7s")
3457 .arg(QString(
"%L1").arg(diffTotal, 0,
'f', 0),
3458 target.ra().toDMSString(),
3459 target.dec().toDMSString(),
3463 QString(
"%L1").arg(elapsedSeconds, 0,
'f', 2));
3464 emit targetDistance(diffTotal);
3467 if (diffTotal / 60 > Options::alignCheckThreshold())
3479 SchedulerState
const old_state = moduleState()->schedulerState();
3480 moduleState()->setSchedulerState(SCHEDULER_LOADING);
3487 QString message =
i18n(
"Unable to open file %1", fileURL);
3488 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3489 moduleState()->setSchedulerState(old_state);
3493 LilXML *xmlParser = newLilXML();
3494 char errmsg[MAXRBUF];
3495 XMLEle *root =
nullptr;
3496 XMLEle *ep =
nullptr;
3497 XMLEle *subEP =
nullptr;
3504 SchedulerJob *lastLead =
nullptr;
3507 const QStringList allTrainNames = OpticalTrainManager::Instance()->getTrainNames();
3512 root = readXMLEle(xmlParser, c, errmsg);
3516 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
3518 const char *tag = tagXMLEle(ep);
3519 if (!strcmp(tag,
"Job"))
3521 SchedulerJob *newJob = SchedulerUtils::createJob(ep, lastLead);
3523 if (newJob->isLead())
3527 remainingTrainNames = allTrainNames;
3530 const QString trainname = newJob->getOpticalTrain();
3531 bool allowedName = (newJob->isLead() && trainname.
isEmpty()) || allTrainNames.
contains(trainname);
3532 bool availableName = (newJob->isLead() && trainname.
isEmpty()) || !remainingTrainNames.
isEmpty();
3534 if (!allowedName && availableName)
3537 i18n(
"Warning: train name is empty, selecting \"%1\".", remainingTrainNames.
first()) :
3538 i18n(
"Warning: train name %2 does not exist, selecting \"%1\".", remainingTrainNames.
first(), trainname);
3544 newJob->setOpticalTrain(remainingTrainNames.
first());
3547 else if (!availableName)
3549 const QString message =
i18n(
"Warning: no available train name for scheduler job, select the optical train name manually.");
3557 emit addJob(newJob);
3559 else if (!strcmp(tag,
"Mosaic"))
3562 auto tiles = KStarsData::Instance()->skyComposite()->mosaicComponent()->tiles();
3563 tiles->fromXML(fileURL);
3565 else if (!strcmp(tag,
"Profile"))
3567 moduleState()->setCurrentProfile(pcdataXMLEle(ep));
3570 else if (!strcmp(tag,
"SchedulerAlgorithm"))
3572 int algIndex = cLocale.
toInt(findXMLAttValu(ep,
"value"));
3573 if (algIndex != ALGORITHM_GREEDY)
3574 appendLogText(
i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
3576 else if (!strcmp(tag,
"ErrorHandlingStrategy"))
3581 subEP = findXMLEle(ep,
"delay");
3584 Options::setErrorHandlingStrategyDelay(cLocale.
toInt(pcdataXMLEle(subEP)));
3586 subEP = findXMLEle(ep,
"RescheduleErrors");
3587 Options::setRescheduleErrors(subEP !=
nullptr);
3589 else if (!strcmp(tag,
"StartupProcedure"))
3592 Options::setSchedulerUnparkDome(
false);
3593 Options::setSchedulerUnparkMount(
false);
3594 Options::setSchedulerOpenDustCover(
false);
3596 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3598 const char *proc = pcdataXMLEle(procedure);
3600 if (!strcmp(proc,
"StartupScript"))
3602 moduleState()->setStartupScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3604 else if (!strcmp(proc,
"UnparkDome"))
3605 Options::setSchedulerUnparkDome(
true);
3606 else if (!strcmp(proc,
"UnparkMount"))
3607 Options::setSchedulerUnparkMount(
true);
3608 else if (!strcmp(proc,
"UnparkCap"))
3609 Options::setSchedulerOpenDustCover(
true);
3612 else if (!strcmp(tag,
"ShutdownProcedure"))
3615 Options::setSchedulerWarmCCD(
false);
3616 Options::setSchedulerParkDome(
false);
3617 Options::setSchedulerParkMount(
false);
3618 Options::setSchedulerCloseDustCover(
false);
3620 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3622 const char *proc = pcdataXMLEle(procedure);
3624 if (!strcmp(proc,
"ShutdownScript"))
3626 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3628 else if (!strcmp(proc,
"WarmCCD"))
3629 Options::setSchedulerWarmCCD(
true);
3630 else if (!strcmp(proc,
"ParkDome"))
3631 Options::setSchedulerParkDome(
true);
3632 else if (!strcmp(proc,
"ParkMount"))
3633 Options::setSchedulerParkMount(
true);
3634 else if (!strcmp(proc,
"ParkCap"))
3635 Options::setSchedulerCloseDustCover(
true);
3640 emit syncGUIToGeneralSettings();
3645 delLilXML(xmlParser);
3646 moduleState()->setSchedulerState(old_state);
3651 moduleState()->setDirty(
false);
3652 delLilXML(xmlParser);
3653 emit updateSchedulerURL(fileURL);
3655 moduleState()->setSchedulerState(old_state);
3665 int const max_log_count = 2000;
3666 if (moduleState()->logText().size() > max_log_count)
3667 moduleState()->logText().removeLast();
3669 moduleState()->logText().prepend(
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
3670 SchedulerModuleState::getLocalTime().toString(
"yyyy-MM-ddThh:mm:ss"), logentry));
3672 qCInfo(KSTARS_EKOS_SCHEDULER) << logentry;
3674 emit newLog(logentry);
3679 moduleState()->logText().clear();
3683void SchedulerProcess::setAlignStatus(
AlignState status)
3685 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3688 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align State" << Ekos::getAlignStatusString(status);
3693 QDateTime const now = SchedulerModuleState::getLocalTime();
3694 if (now < activeJob()->getStartupTime())
3698 if (activeJob()->getStage() == SCHEDSTAGE_ALIGNING)
3704 moduleState()->resetAlignFailureCount();
3706 moduleState()->updateJobStage(SCHEDSTAGE_ALIGN_COMPLETE);
3709 if (activeJob()->getFITSFile().isEmpty() ==
false)
3715 activeJob()->setTargetCoords(
dms(values[0] * 15.0),
dms(values[1]), KStarsData::Instance()->ut().djd());
3722 appendLogText(
i18n(
"Warning: job '%1' alignment failed.", activeJob()->getName()));
3724 if (moduleState()->increaseAlignFailureCount())
3726 if (Options::resetMountModelOnAlignFail() && moduleState()->maxFailureAttempts() - 1 < moduleState()->alignFailureCount())
3728 appendLogText(
i18n(
"Warning: job '%1' forcing mount model reset after failing alignment #%2.", activeJob()->getName(),
3729 moduleState()->alignFailureCount()));
3732 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3737 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
3746void SchedulerProcess::setGuideStatus(GuideState status)
3748 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3751 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Guide State" << Ekos::getGuideStatusString(status);
3756 QDateTime
const now = SchedulerModuleState::getLocalTime();
3757 if (now < activeJob()->getStartupTime())
3761 if (activeJob()->getStage() == SCHEDSTAGE_GUIDING)
3763 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Calibration & Guide stage...";
3766 if (status == Ekos::GUIDE_GUIDING)
3768 appendLogText(
i18n(
"Job '%1' guiding is in progress.", activeJob()->getName()));
3769 moduleState()->resetGuideFailureCount();
3771 moduleState()->cancelGuidingTimer();
3773 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
3776 else if (status == Ekos::GUIDE_CALIBRATION_ERROR ||
3777 status == Ekos::GUIDE_ABORTED)
3779 if (status == Ekos::GUIDE_ABORTED)
3780 appendLogText(
i18n(
"Warning: job '%1' guiding failed.", activeJob()->getName()));
3782 appendLogText(
i18n(
"Warning: job '%1' calibration failed.", activeJob()->getName()));
3788 if (moduleState()->isGuidingTimerActive())
3791 if (moduleState()->increaseGuideFailureCount())
3793 if (status == Ekos::GUIDE_CALIBRATION_ERROR &&
3794 Options::realignAfterCalibrationFailure())
3796 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3801 appendLogText(
i18n(
"Job '%1' is guiding, guiding procedure will be restarted in %2 seconds.", activeJob()->getName(),
3802 (RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount()) / 1000));
3803 moduleState()->startGuidingTimer(RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount());
3808 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3817void SchedulerProcess::setCaptureStatus(
CaptureState status,
const QString &trainname)
3819 if (activeJob() ==
nullptr || !m_activeJobs.contains(trainname))
3822 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Capture State" << Ekos::getCaptureStatusString(status) <<
"train =" << trainname;
3824 SchedulerJob *job = m_activeJobs[trainname];
3829 QDateTime
const now = SchedulerModuleState::getLocalTime();
3830 if (now < job->getStartupTime())
3834 if (job->getStage() == SCHEDSTAGE_CAPTURING)
3846 const SkyPoint targetCoords = activeJob()->getTargetCoords();
3847 QList<QVariant> targetArgs;
3849 alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords", targetArgs);
3854 appendLogText(
i18n(
"[%2] Warning: job '%1' failed to capture target.", job->getName(), trainname));
3859 if (moduleState()->increaseCaptureFailureCount())
3864 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
3868 if (gStatus == Ekos::GUIDE_ABORTED ||
3869 gStatus == Ekos::GUIDE_CALIBRATION_ERROR ||
3870 gStatus == GUIDE_DITHERING_ERROR)
3872 appendLogText(
i18n(
"[%2] Job '%1' is capturing, is restarting its guiding procedure (attempt #%3 of %4).",
3873 activeJob()->getName(), trainname,
3874 moduleState()->captureFailureCount(), moduleState()->maxFailureAttempts()));
3881 appendLogText(
i18n(
"Warning: job '%1' failed its capture procedure, restarting capture.", activeJob()->getName()));
3887 appendLogText(
i18n(
"[%2] Warning: job '%1' failed its capture procedure, marking aborted.", job->getName(), trainname));
3890 stopCapturing(
"",
true);
3897 if (job->leadJob()->getStage() == SCHEDSTAGE_CAPTURING)
3900 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted, is restarting.", job->getName(), trainname));
3902 startSingleCapture(job,
true);
3906 appendLogText(
i18n(
"[%2] Follower job '%1' has been aborted.", job->getName(), trainname));
3913 KSNotification::event(QLatin1String(
"EkosScheduledImagingFinished"),
3914 i18n(
"[%2] Job (%1) - Capture finished", job->getName(), trainname), KSNotification::Scheduler);
3926 if (job->getCompletionCondition() == FINISH_LOOP ||
3927 (job->getCompletionCondition() == FINISH_REPEAT && job->getRepeatsRemaining() > 0))
3930 startSingleCapture(job,
false);
3936 job->setStage(SCHEDSTAGE_COMPLETE);
3944 if (Options::rememberJobProgress())
3948 for (
const auto &job : moduleState()->jobs())
3949 SchedulerUtils::estimateJobTime(job, moduleState()->capturedFramesCount(),
this);
3953 activeJob()->setCompletedCount(job->getCompletedCount() + 1);
3957 moduleState()->resetCaptureFailureCount();
3962void SchedulerProcess::setFocusStatus(FocusState status,
const QString &trainname)
3964 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3967 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Train " << trainname <<
"focus state" << Ekos::getFocusStatusString(status);
3970 if (m_activeJobs.contains(trainname) ==
false)
3973 SchedulerJob *currentJob = m_activeJobs[trainname];
3978 QDateTime
const now = SchedulerModuleState::getLocalTime();
3979 if (now < activeJob()->getStartupTime())
3983 if (activeJob()->getStage() == SCHEDSTAGE_FOCUSING)
3986 if (status == Ekos::FOCUS_COMPLETE)
3988 appendLogText(
i18n(
"Job '%1' focusing train '%2' is complete.", currentJob->getName(), trainname));
3990 moduleState()->setAutofocusCompleted(trainname,
true);
3992 if (moduleState()->autofocusCompleted())
3994 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
3998 else if (status == Ekos::FOCUS_FAILED || status == Ekos::FOCUS_ABORTED)
4000 appendLogText(
i18n(
"Warning: job '%1' focusing failed.", currentJob->getName()));
4002 if (moduleState()->increaseFocusFailureCount(trainname))
4004 appendLogText(
i18n(
"Job '%1' for train '%2' is restarting its focusing procedure.", currentJob->getName(), trainname));
4009 appendLogText(
i18n(
"Warning: job '%1' on train '%2' focusing procedure failed, marking aborted.", activeJob()->getName(),
4019void SchedulerProcess::setMountStatus(ISD::Mount::Status status)
4021 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
4024 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount State changed to" << status;
4028 if (
static_cast<QDateTime const
>(SchedulerModuleState::getLocalTime()) < activeJob()->getStartupTime())
4031 switch (activeJob()->getStage())
4033 case SCHEDSTAGE_SLEWING:
4035 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Slewing stage...";
4037 if (status == ISD::Mount::MOUNT_TRACKING)
4040 moduleState()->updateJobStage(SCHEDSTAGE_SLEW_COMPLETE);
4043 else if (status == ISD::Mount::MOUNT_ERROR)
4045 appendLogText(
i18n(
"Warning: job '%1' slew failed, marking terminated due to errors.", activeJob()->getName()));
4049 else if (status == ISD::Mount::MOUNT_IDLE)
4051 appendLogText(
i18n(
"Warning: job '%1' found not slewing, restarting.", activeJob()->getName()));
4052 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
4058 case SCHEDSTAGE_RESLEWING:
4060 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Re-slewing stage...";
4062 if (status == ISD::Mount::MOUNT_TRACKING)
4064 appendLogText(
i18n(
"Job '%1' repositioning is complete.", activeJob()->getName()));
4065 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING_COMPLETE);
4068 else if (status == ISD::Mount::MOUNT_ERROR)
4070 appendLogText(
i18n(
"Warning: job '%1' repositioning failed, marking terminated due to errors.", activeJob()->getName()));
4074 else if (status == ISD::Mount::MOUNT_IDLE)
4076 appendLogText(
i18n(
"Warning: job '%1' found not repositioning, restarting.", activeJob()->getName()));
4077 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
4085 case SCHEDSTAGE_FOCUSING:
4086 case SCHEDSTAGE_ALIGNING:
4087 case SCHEDSTAGE_GUIDING:
4088 if (status == ISD::Mount::MOUNT_PARKED)
4090 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
4097 case SCHEDSTAGE_CAPTURING:
4098 if (status == ISD::Mount::MOUNT_PARKED && activeJob() && activeJob()->getLightFramesRequired()
4099 && activeJob()->getCalibrationMountPark() ==
false)
4101 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
4111void SchedulerProcess::setWeatherStatus(ISD::Weather::Status status)
4113 ISD::Weather::Status newStatus = status;
4115 if (newStatus == moduleState()->weatherStatus())
4118 ISD::Weather::Status oldStatus = moduleState()->weatherStatus();
4119 moduleState()->setWeatherStatus(newStatus);
4122 if (moduleState()->preemptiveShutdown() &&
4123 oldStatus != ISD::Weather::WEATHER_OK &&
4124 newStatus == ISD::Weather::WEATHER_OK)
4127 moduleState()->setWeatherGracePeriodActive(
false);
4131 else if (activeJob() && Options::schedulerWeather() && (newStatus == ISD::Weather::WEATHER_ALERT &&
4132 moduleState()->schedulerState() != Ekos::SCHEDULER_IDLE &&
4133 moduleState()->schedulerState() != Ekos::SCHEDULER_SHUTDOWN))
4135 appendLogText(
i18n(
"Weather alert detected. Starting soft shutdown procedure."));
4146 QDateTime wakeupTime = SchedulerModuleState::getLocalTime().addSecs(Options::schedulerWeatherGracePeriod() * 60);
4147 moduleState()->setWeatherGracePeriodActive(
true);
4148 moduleState()->enablePreemptiveShutdown(wakeupTime);
4150 appendLogText(
i18n(
"Observatory scheduled for soft shutdown until weather improves or until %1.",
4154 emit schedulerSleeping(
true,
true);
4159 emit newWeatherStatus(status);
4162void SchedulerProcess::checkStartupProcedure()
4168void SchedulerProcess::checkShutdownProcedure()
4173 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
4177 if (Options::stopEkosAfterShutdown())
4180 else if (moduleState()->shutdownState() == SHUTDOWN_ERROR)
4183 moduleState()->setShutdownState(SHUTDOWN_IDLE);
4192void SchedulerProcess::parkCap()
4194 if (capInterface().isNull())
4197 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4201 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4202 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4204 if (parkingStatus.
isValid() ==
false)
4206 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4207 mountInterface()->lastError().
type());
4209 parkingStatus = ISD::PARK_ERROR;
4212 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4214 if (status != ISD::PARK_PARKED)
4216 moduleState()->setShutdownState(SHUTDOWN_PARKING_CAP);
4217 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking dust cap...";
4221 moduleState()->startCurrentOperationTimer();
4226 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
4230void SchedulerProcess::unParkCap()
4232 if (capInterface().isNull())
4234 appendLogText(
i18n(
"Dust cover unpark requested but no dust covers detected."));
4235 moduleState()->setStartupState(STARTUP_ERROR);
4239 QVariant parkingStatus = capInterface()->property(
"parkStatus");
4240 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4242 if (parkingStatus.
isValid() ==
false)
4244 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: cap parkStatus request received DBUS error: %1").arg(
4245 mountInterface()->lastError().
type());
4247 parkingStatus = ISD::PARK_ERROR;
4250 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4252 if (status != ISD::PARK_UNPARKED)
4254 moduleState()->setStartupState(STARTUP_UNPARKING_CAP);
4258 moduleState()->startCurrentOperationTimer();
4263 moduleState()->setStartupState(STARTUP_COMPLETE);
4267void SchedulerProcess::parkMount()
4269 if (mountInterface().isNull())
4272 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4276 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4277 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4279 if (parkingStatus.
isValid() ==
false)
4281 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4282 mountInterface()->lastError().
type());
4284 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4287 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4291 case ISD::PARK_PARKED:
4292 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4293 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
4295 moduleState()->setParkWaitState(PARKWAIT_PARKED);
4299 case ISD::PARK_UNPARKING:
4305 case ISD::PARK_ERROR:
4306 case ISD::PARK_UNKNOWN:
4307 case ISD::PARK_UNPARKED:
4309 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking mount...";
4310 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"park");
4314 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount park request received DBUS error: %1").arg(
4317 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4319 else moduleState()->startCurrentOperationTimer();
4323 case ISD::PARK_PARKING:
4325 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
4326 moduleState()->setShutdownState(SHUTDOWN_PARKING_MOUNT);
4328 moduleState()->setParkWaitState(PARKWAIT_PARKING);
4339void SchedulerProcess::unParkMount()
4341 if (mountInterface().isNull())
4344 moduleState()->setStartupState(STARTUP_ERROR);
4348 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4349 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4351 if (parkingStatus.
isValid() ==
false)
4353 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount parkStatus request received DBUS error: %1").arg(
4354 mountInterface()->lastError().
type());
4356 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4359 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4364 case ISD::PARK_UNPARKED:
4365 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4366 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
4368 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
4373 case ISD::PARK_PARKING:
4379 case ISD::PARK_ERROR:
4380 case ISD::PARK_UNKNOWN:
4381 case ISD::PARK_PARKED:
4383 QDBusReply<bool>
const mountReply = mountInterface()->call(
QDBus::AutoDetect,
"unpark");
4387 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: mount unpark request received DBUS error: %1").arg(
4390 moduleState()->setParkWaitState(PARKWAIT_ERROR);
4392 else moduleState()->startCurrentOperationTimer();
4397 case ISD::PARK_UNPARKING:
4398 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4399 moduleState()->setStartupState(STARTUP_UNPARKING_MOUNT);
4401 moduleState()->setParkWaitState(PARKWAIT_UNPARKING);
4402 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
4413 QVariant var = mountInterface()->property(
"equatorialCoords");
4416 if (var.isValid() ==
false || var.canConvert<
QList<double >> () ==
false)
4418 qCCritical(KSTARS_EKOS_SCHEDULER) <<
"Warning: reading equatorial coordinates received an unexpected value:" << var;
4423 if (coords.
size() != 2)
4425 qCCritical(KSTARS_EKOS_SCHEDULER) <<
"Warning: reading equatorial coordinates received" << coords.
size() <<
4426 "instead of 2 values: " << coords;
4430 return SkyPoint(coords[0], coords[1]);
4435 if (mountInterface().isNull())
4439 QVariant canPark = mountInterface()->property(
"canPark");
4440 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount can park:" << (!canPark.
isValid() ?
"invalid" : (canPark.
toBool() ?
"T" :
"F"));
4442 if (canPark.
isValid() ==
false)
4444 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount canPark request received DBUS error: %1").
arg(
4445 mountInterface()->lastError().type());
4449 else if (canPark.
toBool() ==
true)
4453 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4454 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4456 if (parkingStatus.
isValid() ==
false)
4458 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parking status property is invalid %1.").
arg(
4459 mountInterface()->lastError().type());
4465 switch (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()))
4469 case ISD::PARK_PARKED:
4484void SchedulerProcess::parkDome()
4487 if (domeInterface().isNull())
4490 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4496 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4497 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4499 if (parkingStatus.
isValid() ==
false)
4501 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4502 mountInterface()->lastError().type());
4504 parkingStatus = ISD::PARK_ERROR;
4507 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4508 if (status != ISD::PARK_PARKED)
4510 moduleState()->setShutdownState(SHUTDOWN_PARKING_DOME);
4514 moduleState()->startCurrentOperationTimer();
4519 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
4523void SchedulerProcess::unParkDome()
4526 if (domeInterface().isNull())
4529 moduleState()->setStartupState(STARTUP_ERROR);
4533 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4534 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4536 if (parkingStatus.
isValid() ==
false)
4538 qCCritical(KSTARS_EKOS_SCHEDULER) << QString(
"Warning: dome parkStatus request received DBUS error: %1").arg(
4539 mountInterface()->lastError().
type());
4541 parkingStatus = ISD::PARK_ERROR;
4544 if (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()) != ISD::PARK_UNPARKED)
4546 moduleState()->setStartupState(STARTUP_UNPARKING_DOME);
4550 moduleState()->startCurrentOperationTimer();
4555 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
4561 QVariant guideStatus = guideInterface()->property(
"status");
4562 Ekos::GuideState gStatus =
static_cast<Ekos::GuideState
>(guideStatus.
toInt());
4567const QString &SchedulerProcess::profile()
const
4569 return moduleState()->currentProfile();
4572void SchedulerProcess::setProfile(
const QString &newProfile)
4574 moduleState()->setCurrentProfile(newProfile);
4577QString SchedulerProcess::currentJobName()
4579 auto job = moduleState()->activeJob();
4580 return ( job !=
nullptr ? job->getName() : QString() );
4583QString SchedulerProcess::currentJobJson()
4585 auto job = moduleState()->activeJob();
4586 if( job !=
nullptr )
4588 return QString( QJsonDocument( job->toJson() ).toJson() );
4596QString SchedulerProcess::jsonJobs()
4598 return QString( QJsonDocument( moduleState()->getJSONJobs() ).toJson() );
4601QStringList SchedulerProcess::logText()
4603 return moduleState()->logText();
4608 if (domeInterface().isNull())
4611 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4612 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4614 if (parkingStatus.
isValid() ==
false)
4616 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4617 mountInterface()->lastError().type());
4619 parkingStatus = ISD::PARK_ERROR;
4622 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4624 return status == ISD::PARK_PARKED;
4627void SchedulerProcess::simClockScaleChanged(
float newScale)
4629 if (moduleState()->currentlySleeping())
4632 (moduleState()->iterationTimer().remainingTime())
4633 * KStarsData::Instance()->clock()->scale()
4635 appendLogText(
i18n(
"Sleeping for %1 on simulation clock update until next observation job is ready...",
4636 remainingTimeMs.
toString(
"hh:mm:ss")));
4637 moduleState()->iterationTimer().stop();
4640 moduleState()->tickleTimer().stop();
4643void SchedulerProcess::simClockTimeChanged()
4645 moduleState()->calculateDawnDusk();
4648 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
4652void SchedulerProcess::setINDICommunicationStatus(CommunicationStatus status)
4654 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler INDI status is" << status;
4656 moduleState()->setIndiCommunicationStatus(status);
4659void SchedulerProcess::setEkosCommunicationStatus(CommunicationStatus status)
4661 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler Ekos status is" << status;
4663 moduleState()->setEkosCommunicationStatus(status);
4668void SchedulerProcess::checkInterfaceReady(QDBusInterface * iface)
4670 if (iface == mountInterface())
4673 moduleState()->setMountReady(
true);
4675 else if (iface == capInterface())
4678 moduleState()->setCapReady(
true);
4680 else if (iface == observatoryInterface())
4682 QVariant status = observatoryInterface()->property(
"status");
4683 if (status.isValid())
4684 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4686 else if (iface == weatherInterface())
4688 QVariant status = weatherInterface()->property(
"status");
4689 if (status.isValid())
4690 setWeatherStatus(
static_cast<ISD::Weather::Status
>(status.toInt()));
4692 else if (iface == domeInterface())
4695 moduleState()->setDomeReady(
true);
4697 else if (iface == captureInterface())
4700 moduleState()->setCaptureReady(
true);
4703 emit interfaceReady(iface);
4706void SchedulerProcess::registerNewModule(
const QString &name)
4708 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Registering new Module (" <<
name <<
")";
4710 if (name ==
"Focus")
4712 delete focusInterface();
4713 setFocusInterface(
new QDBusInterface(kstarsInterfaceString, focusPathString, focusInterfaceString,
4715 connect(focusInterface(), SIGNAL(newStatus(Ekos::FocusState,
const QString)),
this,
4718 else if (name ==
"Capture")
4720 delete captureInterface();
4721 setCaptureInterface(
new QDBusInterface(kstarsInterfaceString, capturePathString, captureInterfaceString,
4724 connect(captureInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4727 connect(captureInterface(), SIGNAL(captureComplete(QVariantMap,
const QString)),
this, SLOT(checkAlignment(QVariantMap,
4730 checkInterfaceReady(captureInterface());
4732 else if (name ==
"Mount")
4734 delete mountInterface();
4735 setMountInterface(
new QDBusInterface(kstarsInterfaceString, mountPathString, mountInterfaceString,
4738 connect(mountInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4739 connect(mountInterface(), SIGNAL(newStatus(ISD::Mount::Status)),
this, SLOT(setMountStatus(ISD::Mount::Status)),
4742 checkInterfaceReady(mountInterface());
4744 else if (name ==
"Align")
4746 delete alignInterface();
4747 setAlignInterface(
new QDBusInterface(kstarsInterfaceString, alignPathString, alignInterfaceString,
4752 else if (name ==
"Guide")
4754 delete guideInterface();
4755 setGuideInterface(
new QDBusInterface(kstarsInterfaceString, guidePathString, guideInterfaceString,
4757 connect(guideInterface(), SIGNAL(newStatus(Ekos::GuideState)),
this,
4760 else if (name ==
"Observatory")
4762 delete observatoryInterface();
4763 setObservatoryInterface(
new QDBusInterface(kstarsInterfaceString, observatoryPathString, observatoryInterfaceString,
4765 connect(observatoryInterface(), SIGNAL(newStatus(ISD::Weather::Status)),
this,
4767 checkInterfaceReady(observatoryInterface());
4771void SchedulerProcess::registerNewDevice(
const QString &name,
int interface)
4775 if (interface & INDI::BaseDevice::DOME_INTERFACE)
4777 QList<QVariant> dbusargs;
4778 dbusargs.
append(INDI::BaseDevice::DOME_INTERFACE);
4779 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4784 setDomePathString(paths.value().last());
4785 delete domeInterface();
4786 setDomeInterface(
new QDBusInterface(kstarsInterfaceString, domePathString,
4787 domeInterfaceString,
4789 connect(domeInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4790 checkInterfaceReady(domeInterface());
4815 if (interface & INDI::BaseDevice::DUSTCAP_INTERFACE)
4817 QList<QVariant> dbusargs;
4818 dbusargs.
append(INDI::BaseDevice::DUSTCAP_INTERFACE);
4819 QDBusReply<QStringList> paths = indiInterface()->callWithArgumentList(
QDBus::AutoDetect,
"getDevicesPaths",
4824 setDustCapPathString(paths.value().last());
4825 delete capInterface();
4826 setCapInterface(
new QDBusInterface(kstarsInterfaceString, dustCapPathString,
4827 dustCapInterfaceString,
4829 connect(capInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4830 checkInterfaceReady(capInterface());
4837 XMLEle *ep =
nullptr;
4838 XMLEle *subEP =
nullptr;
4840 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
4842 if (!strcmp(tagXMLEle(ep),
"Job"))
4844 for (subEP = nextXMLEle(ep, 1); subEP !=
nullptr; subEP = nextXMLEle(ep, 0))
4846 if (!strcmp(tagXMLEle(subEP),
"TargetName"))
4851 else if (!strcmp(tagXMLEle(subEP),
"FITSDirectory"))
4864 if (outputFile ==
nullptr)
4866 QString message =
i18n(
"Unable to write to file %1", filename);
4867 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
4871 fprintf(outputFile,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4872 prXMLEle(outputFile, root, 0);
4886 KSNotification::sorry(
i18n(
"Unable to open file %1", sFile.
fileName()),
4887 i18n(
"Could Not Open File"));
4891 LilXML *xmlParser = newLilXML();
4892 char errmsg[MAXRBUF];
4893 XMLEle *root =
nullptr;
4898 root = readXMLEle(xmlParser, c, errmsg);
4904 delLilXML(xmlParser);
4909void SchedulerProcess::checkProcessExit(
int exitCode)
4915 if (moduleState()->startupState() == STARTUP_SCRIPT)
4916 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
4917 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4918 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
4923 if (moduleState()->startupState() == STARTUP_SCRIPT)
4926 moduleState()->setStartupState(STARTUP_ERROR);
4928 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4931 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4936void SchedulerProcess::readProcessOutput()
4938 appendLogText(scriptProcess().readAllStandardOutput().simplified());
4941bool SchedulerProcess::canCountCaptures(
const SchedulerJob &job)
4943 QList<QSharedPointer<SequenceJob >> seqjobs;
4944 bool hasAutoFocus =
false;
4945 SchedulerJob tempJob = job;
4946 if (SchedulerUtils::loadSequenceQueue(tempJob.getSequenceFile().toLocalFile(), &tempJob, seqjobs, hasAutoFocus,
4950 for (
auto oneSeqJob : seqjobs)
4952 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
4966 forced |= std::any_of(moduleState()->jobs().begin(),
4967 moduleState()->jobs().end(), [](SchedulerJob * oneJob) ->
bool
4974 moduleState()->capturedFramesCount().clear();
4977 for (SchedulerJob *oneJob : moduleState()->jobs())
4984 bool hasAutoFocus =
false;
4988 if (SchedulerUtils::loadSequenceQueue(oneJob->getSequenceFile().
toLocalFile(), oneJob, seqjobs, hasAutoFocus,
4991 appendLogText(
i18n(
"Warning: job '%1' has inaccessible sequence '%2', marking invalid.", oneJob->getName(),
4997 oneJob->clearProgress();
4999 for (
auto oneSeqJob : seqjobs)
5003 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_REMOTE)
5007 QString const signature = oneSeqJob->getSignature();
5015 const int count = newFramesCount.
constFind(signature).value();
5016 newJobFramesCount[signature] = count;
5017 oneJob->addProgress(count, oneSeqJob);
5023 CapturedFramesMap::const_iterator
const earlierRunIterator =
5024 moduleState()->capturedFramesCount().constFind(signature);
5026 if (moduleState()->capturedFramesCount().constEnd() != earlierRunIterator)
5028 count = earlierRunIterator.value();
5031 count = PlaceholderPath::getCompletedFiles(signature);
5033 newFramesCount[signature] = count;
5034 newJobFramesCount[signature] = count;
5035 oneJob->addProgress(count, oneSeqJob);
5039 SchedulerUtils::updateLightFramesRequired(oneJob, seqjobs, newFramesCount);
5042 moduleState()->setCapturedFramesCount(newFramesCount);
5045 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Frame map summary:";
5046 CapturedFramesMap::const_iterator it = moduleState()->capturedFramesCount().constBegin();
5047 for (; it != moduleState()->capturedFramesCount().constEnd(); it++)
5048 qCDebug(KSTARS_EKOS_SCHEDULER) <<
" " << it.key() <<
':' << it.value();
5052SchedulerJob *SchedulerProcess::activeJob()
5054 return moduleState()->activeJob();
5057void SchedulerProcess::printStates(
const QString &label)
5059 qCDebug(KSTARS_EKOS_SCHEDULER) <<
5060 QString(
"%1 %2 %3%4 %5 %6 %7 %8 %9\n")
5062 .
arg(timerStr(moduleState()->timerState()))
5063 .
arg(getSchedulerStatusString(moduleState()->schedulerState()))
5064 .
arg((moduleState()->timerState() == RUN_JOBCHECK && activeJob() !=
nullptr) ?
5065 QString(
"(%1 %2)").arg(SchedulerJob::jobStatusString(activeJob()->getState()))
5066 .arg(SchedulerJob::jobStageString(activeJob()->getStage())) :
"")
5067 .
arg(ekosStateString(moduleState()->ekosState()))
5068 .
arg(indiStateString(moduleState()->indiState()))
5069 .
arg(startupStateString(moduleState()->startupState()))
5070 .
arg(shutdownStateString(moduleState()->shutdownState()))
5071 .
arg(parkWaitStateString(moduleState()->parkWaitState())).
toLatin1().
data();
5072 foreach (
auto j, moduleState()->jobs())
5073 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"job %1 %2\n").
arg(j->getName()).
arg(SchedulerJob::jobStatusString(
Q_SCRIPTABLE Q_NOREPLY void startAstrometry()
startAstrometry initiation of the capture and solve operation.
bool shouldSchedulerSleep(SchedulerJob *job)
shouldSchedulerSleep Check if the scheduler needs to sleep until the job is ready
Q_SCRIPTABLE Q_NOREPLY void startCapture(bool restart=false)
startCapture The current job file name is solved to an url which is fed to ekos.
void loadProfiles()
loadProfiles Load the existing EKOS profiles
Q_SCRIPTABLE Q_NOREPLY void runStartupProcedure()
runStartupProcedure Execute the startup of the scheduler itself to be prepared for running scheduler ...
void checkCapParkingStatus()
checkDomeParkingStatus check dome parking status and updating corresponding states accordingly.
void getNextAction()
getNextAction Checking for the next appropriate action regarding the current state of the scheduler a...
Q_SCRIPTABLE bool isMountParked()
Q_SCRIPTABLE Q_NOREPLY void startJobEvaluation()
startJobEvaluation Start job evaluation only without starting the scheduler process itself.
Q_SCRIPTABLE Q_NOREPLY void resetJobs()
resetJobs Reset all jobs counters
void selectActiveJob(const QList< SchedulerJob * > &jobs)
selectActiveJob Select the job that should be executed
Q_SCRIPTABLE void wakeUpScheduler()
wakeUpScheduler Wake up scheduler from sleep state
Q_SCRIPTABLE Q_NOREPLY void setPaused()
setPaused pausing the scheduler
Q_SCRIPTABLE bool checkParkWaitState()
checkParkWaitState Check park wait state.
bool executeJob(SchedulerJob *job)
executeJob After the best job is selected, we call this in order to start the process that will execu...
bool createJobSequence(XMLEle *root, const QString &prefix, const QString &outputDir)
createJobSequence Creates a job sequence for the mosaic tool given the prefix and output dir.
void iterate()
Repeatedly runs a scheduler iteration and then sleeps timerInterval millisconds and run the next iter...
Q_SCRIPTABLE Q_NOREPLY void startGuiding(bool resetCalibration=false)
startGuiding After ekos is fed the calibration options, we start the guiding process
void findNextJob()
findNextJob Check if the job met the completion criteria, and if it did, then it search for next job ...
Q_SCRIPTABLE bool appendEkosScheduleList(const QString &fileURL)
appendEkosScheduleList Append the contents of an ESL file to the queue.
Q_SCRIPTABLE void execute()
execute Execute the schedule, start if idle or paused.
Q_SCRIPTABLE bool isDomeParked()
Q_SCRIPTABLE bool saveScheduler(const QUrl &fileURL)
saveScheduler Save scheduler jobs to a file
Q_SCRIPTABLE Q_NOREPLY void appendLogText(const QString &logentry) override
appendLogText Append a new line to the logging.
Q_SCRIPTABLE Q_NOREPLY void start()
DBUS interface function.
Q_SCRIPTABLE Q_NOREPLY void removeAllJobs()
DBUS interface function.
Q_SCRIPTABLE void stopCurrentJobAction()
stopCurrentJobAction Stop whatever action taking place in the current job (eg.
Q_SCRIPTABLE Q_NOREPLY void stopGuiding()
stopGuiding After guiding is done we need to stop the process
bool checkShutdownState()
checkShutdownState Check shutdown procedure stages and make sure all stages are complete.
Q_SCRIPTABLE Q_NOREPLY void startSlew()
startSlew DBus call for initiating slew
bool checkEkosState()
checkEkosState Check ekos startup stages and take whatever action necessary to get Ekos up and runnin...
bool checkStatus()
checkJobStatus Check the overall state of the scheduler, Ekos, and INDI.
bool checkINDIState()
checkINDIState Check INDI startup stages and take whatever action necessary to get INDI devices conne...
XMLEle * getSequenceJobRoot(const QString &filename) const
getSequenceJobRoot Read XML data from capture sequence job
Q_SCRIPTABLE Q_NOREPLY void runShutdownProcedure()
runShutdownProcedure Shutdown the scheduler itself and EKOS (if configured to do so).
Q_SCRIPTABLE Q_NOREPLY void setSequence(const QString &sequenceFileURL)
DBUS interface function.
Q_SCRIPTABLE bool loadScheduler(const QString &fileURL)
DBUS interface function.
Q_SCRIPTABLE Q_NOREPLY void startFocusing()
startFocusing DBus call for feeding ekos the specified settings and initiating focus operation
void checkJobStage()
checkJobStage Check the progress of the job states and make DBUS calls to start the next stage until ...
bool checkStartupState()
checkStartupState Check startup procedure stages and make sure all stages are complete.
void processGuidingTimer()
processGuidingTimer Check the guiding timer, and possibly restart guiding.
SkyPoint mountCoords()
mountCoords read the equatorial coordinates from the mount
GuideState getGuidingStatus()
getGuidingStatus Retrieve the guiding status.
Q_SCRIPTABLE Q_NOREPLY void resetAllJobs()
DBUS interface function.
void applyConfig()
applyConfig Apply configuration changes from the global configuration dialog.
Q_SCRIPTABLE void clearLog()
clearLog Clear log entry
Q_SCRIPTABLE Q_NOREPLY void disconnectINDI()
disconnectINDI disconnect all INDI devices from server.
Q_SCRIPTABLE Q_NOREPLY void stop()
DBUS interface function.
bool manageConnectionLoss()
manageConnectionLoss Mitigate loss of connection with the INDI server.
void updateCompletedJobsCount(bool forced=false)
updateCompletedJobsCount For each scheduler job, examine sequence job storage and count captures.
Q_SCRIPTABLE Q_NOREPLY void evaluateJobs(bool evaluateOnly)
evaluateJobs evaluates the current state of each objects and gives each one a score based on the cons...
int runSchedulerIteration()
Run a single scheduler iteration.
void setSolverAction(Align::GotoMode mode)
setSolverAction set the GOTO mode for the solver
Q_SCRIPTABLE bool completeShutdown()
completeShutdown Try to complete the scheduler shutdown
static KConfigDialog * exists(const QString &name)
void settingsChanged(const QString &dialogName)
static KStars * Instance()
The SchedulerState class holds all attributes defining the scheduler's state.
void timeChanged()
The time has changed (emitted by setUTC() )
void scaleChanged(float)
The timestep has changed.
The sky coordinates of a point in the sky.
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
const CachingDms & dec() const
const CachingDms & ra0() const
const CachingDms & ra() const
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
const CachingDms & dec0() const
void setDec0(dms d)
Sets Dec0, the catalog Declination.
An angle, stored as degrees, but expressible in many ways.
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
const double & Degrees() const
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
Ekos is an advanced Astrophotography tool for Linux.
SchedulerJobStatus
States of a SchedulerJob.
@ SCHEDJOB_ABORTED
Job encountered a transitory issue while processing, and will be rescheduled.
@ SCHEDJOB_INVALID
Job has an incorrect configuration, and cannot proceed.
@ SCHEDJOB_ERROR
Job encountered a fatal issue while processing, and must be reset manually.
@ SCHEDJOB_COMPLETE
Job finished all required captures.
@ SCHEDJOB_EVALUATION
Job is being evaluated.
@ SCHEDJOB_SCHEDULED
Job was evaluated, and has a schedule.
@ SCHEDJOB_BUSY
Job is being processed.
@ SCHEDJOB_IDLE
Job was just created, and is not evaluated yet.
QMap< QString, uint16_t > CapturedFramesMap
mapping signature --> frames count
ErrorHandlingStrategy
options what should happen if an error or abort occurs
@ ALIGN_FAILED
Alignment failed.
@ ALIGN_ABORTED
Alignment aborted by user or agent.
@ ALIGN_IDLE
No ongoing operations.
@ ALIGN_COMPLETE
Alignment successfully completed.
CaptureState
Capture states.
SchedulerTimerState
IterationTypes, the different types of scheduler iterations that are run.
GeoCoordinates geo(const QVariant &location)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
bool isValid(QStringView ifopt)
QString name(StandardAction id)
const char * constData() const const
QDateTime addSecs(qint64 s) const const
qint64 currentMSecsSinceEpoch()
qint64 secsTo(const QDateTime &other) const const
QString toString(QStringView format, QCalendar cal) const const
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QDBusConnection sessionBus()
void unregisterObject(const QString &path, UnregisterMode mode)
QString errorString(ErrorType error)
QString message() const const
ErrorType type() const const
QList< QVariant > arguments() const const
QString errorMessage() const const
const QDBusError & error()
bool isValid() const const
bool mkpath(const QString &dirPath) const const
bool exists(const QString &fileName)
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
void append(QList< T > &&value)
bool isEmpty() const const
qsizetype size() const const
T value(qsizetype i) const const
int toInt(QStringView s, bool *ok) const const
QString toString(QDate date, FormatType format) const const
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
QList< Key > keys() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QVariant property(const char *name) const const
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void readyReadStandardOutput()
void start(OpenMode mode)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QByteArray toLatin1() const const
std::string toStdString() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QTextStream & dec(QTextStream &stream)
QTextStream & endl(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QTime fromMSecsSinceStartOfDay(int msecs)
int msecsSinceStartOfDay() const const
QString toString(QStringView format) const const
bool isEmpty() const const
bool isValid() const const
QString toLocalFile() const const
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const
QString toString() const const