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/stellarsolverprofile.h"
23#include <ekos_scheduler_debug.h>
26#include <QDBusInterface>
28#define RESTART_GUIDING_DELAY_MS 5000
40 setObjectName(
"SchedulerProcess");
41 m_moduleState = state;
42 m_GreedyScheduler =
new GreedyScheduler();
50 connect(moduleState().data(), &SchedulerModuleState::schedulerStateChanged,
this, &SchedulerProcess::newStatus);
51 connect(moduleState().data(), &SchedulerModuleState::newLog,
this, &SchedulerProcess::appendLogText);
54 new SchedulerAdaptor(
this);
57 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"SchedulerProcess failed to register with dbus");
59 setEkosInterface(
new QDBusInterface(kstarsInterfaceString, ekosPathStr, ekosInterfaceStr,
61 setIndiInterface(
new QDBusInterface(kstarsInterfaceString, INDIPathString, INDIInterfaceString,
64 this, SLOT(setINDICommunicationStatus(Ekos::CommunicationStatus)));
66 this, SLOT(setEkosCommunicationStatus(Ekos::CommunicationStatus)));
68 SLOT(registerNewModule(
QString)));
70 SLOT(registerNewDevice(
QString,
int)));
75 return moduleState()->schedulerState();
78void SchedulerProcess::execute()
80 switch (moduleState()->schedulerState())
84 if (!moduleState()->startupScriptURL().isEmpty() && ! moduleState()->startupScriptURL().isValid())
86 appendLogText(
i18n(
"Warning: startup script URL %1 is not valid.",
92 if (!moduleState()->shutdownScriptURL().isEmpty() && !moduleState()->shutdownScriptURL().isValid())
94 appendLogText(
i18n(
"Warning: shutdown script URL %1 is not valid.",
100 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Scheduler is starting...";
102 moduleState()->setSchedulerState(SCHEDULER_RUNNING);
103 moduleState()->setupNextIteration(RUN_SCHEDULER);
105 appendLogText(
i18n(
"Scheduler started."));
106 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler started.";
109 case SCHEDULER_PAUSED:
110 moduleState()->setSchedulerState(SCHEDULER_RUNNING);
111 moduleState()->setupNextIteration(RUN_SCHEDULER);
113 appendLogText(
i18n(
"Scheduler resuming."));
114 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler resuming.";
127void SchedulerProcess::findNextJob()
129 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
140 __FUNCTION__,
"Finding next job requires current to be in error, aborted, idle or complete");
143 moduleState()->resetAlignFailureCount();
144 moduleState()->resetGuideFailureCount();
145 moduleState()->resetFocusFailureCount();
146 moduleState()->resetCaptureFailureCount();
150 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
151 moduleState()->resetCaptureBatch();
156 appendLogText(
i18n(
"Job '%1' is terminated due to errors.", activeJob()->getName()));
158 appendLogText(
i18n(
"Job '%1' is aborted.", activeJob()->getName()));
161 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
164 if (Options::errorHandlingStrategy() == ERROR_RESTART_IMMEDIATELY &&
166 (activeJob()->getState() ==
SCHEDJOB_ERROR && Options::rescheduleErrors())))
171 appendLogText(
i18n(
"Waiting %1 seconds to restart job '%2'.", Options::errorHandlingStrategyDelay(),
172 activeJob()->getName()));
175 moduleState()->setupNextIteration(RUN_WAKEUP, std::lround((Options::errorHandlingStrategyDelay() * 1000) /
176 KStarsData::Instance()->clock()->scale()));
177 emit changeSleepLabel(
i18n(
"Scheduler waits for a retry."));
182 moduleState()->setActiveJob(
nullptr);
183 moduleState()->setupNextIteration(RUN_SCHEDULER);
187 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
190 moduleState()->setActiveJob(
nullptr);
191 moduleState()->setupNextIteration(RUN_SCHEDULER);
195 else if (activeJob()->getCompletionCondition() == FINISH_SEQUENCE)
197 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
200 if (Options::rememberJobProgress())
202 foreach(SchedulerJob *a_job, moduleState()->jobs())
203 if (a_job == activeJob() || a_job->isDuplicateOf(activeJob()))
207 moduleState()->resetCaptureBatch();
211 appendLogText(
i18n(
"Job '%1' is complete.", activeJob()->getName()));
214 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
218 if (!canCountCaptures(*activeJob()))
221 moduleState()->setActiveJob(
nullptr);
222 moduleState()->setupNextIteration(RUN_SCHEDULER);
224 else if (activeJob()->getCompletionCondition() == FINISH_REPEAT &&
225 (activeJob()->getRepeatsRemaining() <= 1))
228 if (activeJob()->getRepeatsRemaining() > 0)
231 if (!Options::rememberJobProgress())
233 activeJob()->setRepeatsRemaining(activeJob()->getRepeatsRemaining() - 1);
234 activeJob()->setCompletedIterations(activeJob()->getCompletedIterations() + 1);
236 activeJob()->setStartupTime(
QDateTime());
240 foreach(SchedulerJob *a_job, moduleState()->jobs())
241 if (a_job == activeJob() || a_job->isDuplicateOf(activeJob()))
248 if (activeJob() ==
nullptr || activeJob()->getRepeatsRemaining() == 0)
250 stopCurrentJobAction();
252 if (activeJob() !=
nullptr)
254 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
255 appendLogText(
i18np(
"Job '%1' is complete after #%2 batch.",
256 "Job '%1' is complete after #%2 batches.",
257 activeJob()->getName(), activeJob()->getRepeatsRequired()));
258 if (!canCountCaptures(*activeJob()))
260 moduleState()->setActiveJob(
nullptr);
262 moduleState()->setupNextIteration(RUN_SCHEDULER);
268 if (executeJob(activeJob()) ==
false)
272 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN && Options::forceAlignmentBeforeJob())
275 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
279 else if ( (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE) )
281 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
285 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
287 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
291 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
293 moduleState()->updateJobStage(SCHEDSTAGE_SLEWING);
299 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
303 appendLogText(
i18np(
"Job '%1' is repeating, #%2 batch remaining.",
304 "Job '%1' is repeating, #%2 batches remaining.",
305 activeJob()->getName(), activeJob()->getRepeatsRemaining()));
307 moduleState()->setupNextIteration(RUN_JOBCHECK);
310 else if ((activeJob()->getCompletionCondition() == FINISH_LOOP) ||
311 (activeJob()->getCompletionCondition() == FINISH_REPEAT &&
312 activeJob()->getRepeatsRemaining() > 0))
315 if ((activeJob()->getCompletionCondition() == FINISH_REPEAT) &&
316 (activeJob()->getRepeatsRemaining() > 1))
319 if (!Options::rememberJobProgress())
321 activeJob()->setRepeatsRemaining(activeJob()->getRepeatsRemaining() - 1);
322 activeJob()->setCompletedIterations(activeJob()->getCompletedIterations() + 1);
324 activeJob()->setStartupTime(
QDateTime());
327 if (executeJob(activeJob()) ==
false)
330 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN && Options::forceAlignmentBeforeJob())
333 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
338 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
342 moduleState()->increaseCaptureBatch();
344 if (activeJob()->getCompletionCondition() == FINISH_REPEAT )
345 appendLogText(
i18np(
"Job '%1' is repeating, #%2 batch remaining.",
346 "Job '%1' is repeating, #%2 batches remaining.",
347 activeJob()->getName(), activeJob()->getRepeatsRemaining()));
349 appendLogText(
i18n(
"Job '%1' is repeating, looping indefinitely.", activeJob()->getName()));
352 moduleState()->setupNextIteration(RUN_JOBCHECK);
354 else if (activeJob()->getCompletionCondition() == FINISH_AT)
356 if (SchedulerModuleState::getLocalTime().secsTo(activeJob()->getCompletionTime()) <= 0)
358 emit jobEnded(activeJob()->getName(), activeJob()->getStopReason());
361 foreach(SchedulerJob *a_job, moduleState()->jobs())
362 if (a_job == activeJob() || a_job->isDuplicateOf(activeJob()))
364 stopCurrentJobAction();
366 moduleState()->resetCaptureBatch();
368 appendLogText(
i18np(
"Job '%1' stopping, reached completion time with #%2 batch done.",
369 "Job '%1' stopping, reached completion time with #%2 batches done.",
370 activeJob()->getName(), moduleState()->captureBatch() + 1));
373 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
375 moduleState()->setActiveJob(
nullptr);
376 moduleState()->setupNextIteration(RUN_SCHEDULER);
380 if (executeJob(activeJob()) ==
false)
383 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN && Options::forceAlignmentBeforeJob())
386 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
391 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
395 moduleState()->increaseCaptureBatch();
397 appendLogText(
i18np(
"Job '%1' completed #%2 batch before completion time, restarted.",
398 "Job '%1' completed #%2 batches before completion time, restarted.",
399 activeJob()->getName(), moduleState()->captureBatch()));
401 moduleState()->setupNextIteration(RUN_JOBCHECK);
407 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"BUGBUG! Job '" << activeJob()->getName() <<
408 "' timer elapsed, but no action to be taken.";
411 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
413 moduleState()->setActiveJob(
nullptr);
414 moduleState()->setupNextIteration(RUN_SCHEDULER);
418void SchedulerProcess::stopCurrentJobAction()
420 if (
nullptr != activeJob())
422 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Job '" << activeJob()->getName() <<
"' is stopping current action..." <<
423 activeJob()->getStage();
425 switch (activeJob()->getStage())
427 case SCHEDSTAGE_IDLE:
430 case SCHEDSTAGE_SLEWING:
434 case SCHEDSTAGE_FOCUSING:
438 case SCHEDSTAGE_ALIGNING:
444 case SCHEDSTAGE_CAPTURING:
453 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
460void SchedulerProcess::wakeUpScheduler()
462 if (moduleState()->preemptiveShutdown())
464 moduleState()->disablePreemptiveShutdown();
465 appendLogText(
i18n(
"Scheduler is awake."));
470 if (moduleState()->schedulerState() == SCHEDULER_RUNNING)
471 appendLogText(
i18n(
"Scheduler is awake. Jobs shall be started when ready..."));
473 appendLogText(
i18n(
"Scheduler is awake. Jobs shall be started when scheduler is resumed."));
475 moduleState()->setupNextIteration(RUN_SCHEDULER);
479void SchedulerProcess::start()
482 foreach (
auto j, moduleState()->jobs())
485 emit updateJobTable(j);
487 moduleState()->init();
491void SchedulerProcess::stop()
494 if (moduleState()->schedulerState() != SCHEDULER_RUNNING)
497 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Scheduler is stopping...";
501 if (!moduleState()->preemptiveShutdown())
503 for (
auto &oneJob : moduleState()->jobs())
505 if (oneJob == activeJob())
506 stopCurrentJobAction();
510 appendLogText(
i18n(
"Job '%1' has not been processed upon scheduler stop, marking aborted.", oneJob->getName()));
516 moduleState()->setupNextIteration(RUN_NOTHING);
517 moduleState()->cancelGuidingTimer();
519 moduleState()->setSchedulerState(SCHEDULER_IDLE);
520 moduleState()->setParkWaitState(PARKWAIT_IDLE);
521 moduleState()->setEkosState(EKOS_IDLE);
522 moduleState()->setIndiState(INDI_IDLE);
526 if (moduleState()->startupState() != STARTUP_COMPLETE || moduleState()->preemptiveShutdown())
528 if (moduleState()->startupState() == STARTUP_SCRIPT)
530 scriptProcess().disconnect();
531 scriptProcess().terminate();
534 moduleState()->setStartupState(STARTUP_IDLE);
539 else if (moduleState()->startupState() == STARTUP_COMPLETE)
541 if (Options::schedulerUnparkDome())
542 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
543 else if (Options::schedulerUnparkMount())
544 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
545 else if (Options::schedulerOpenDustCover())
546 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
549 moduleState()->setShutdownState(SHUTDOWN_IDLE);
551 moduleState()->setActiveJob(
nullptr);
552 moduleState()->resetFailureCounters();
553 moduleState()->setAutofocusCompleted(
false);
556 if (moduleState()->preemptiveShutdown())
558 QDateTime const now = SchedulerModuleState::getLocalTime();
559 int const nextObservationTime = now.
secsTo(moduleState()->preemptiveShutdownWakeupTime());
560 moduleState()->setupNextIteration(RUN_WAKEUP,
561 std::lround(((nextObservationTime + 1) * 1000)
562 / KStarsData::Instance()->clock()->scale()));
564 emit schedulerStopped();
569 if (captureInterface().isNull() ==
false)
570 captureInterface()->setProperty(
"targetName",
QString());
573 scriptProcess().terminate();
576 emit schedulerStopped();
579void SchedulerProcess::removeAllJobs()
581 emit clearJobTable();
583 qDeleteAll(moduleState()->jobs());
584 moduleState()->mutlableJobs().clear();
585 moduleState()->setCurrentPosition(-1);
589bool SchedulerProcess::loadScheduler(
const QString &fileURL)
592 return appendEkosScheduleList(fileURL);
595void SchedulerProcess::setSequence(
const QString &sequenceFileURL)
597 emit changeCurrentSequence(sequenceFileURL);
600void SchedulerProcess::resetAllJobs()
602 if (moduleState()->schedulerState() == SCHEDULER_RUNNING)
606 foreach (SchedulerJob *job, moduleState()->jobs())
607 job->setCompletedCount(0);
610 startJobEvaluation();
613bool SchedulerProcess::shouldSchedulerSleep(SchedulerJob * job)
615 Q_ASSERT_X(
nullptr != job, __FUNCTION__,
616 "There must be a valid current job for Scheduler to test sleep requirement");
618 if (job->getLightFramesRequired() ==
false)
621 QDateTime const now = SchedulerModuleState::getLocalTime();
622 int const nextObservationTime = now.
secsTo(job->getStartupTime());
626 if (getGreedyScheduler()->getScheduledJob() != job)
631 if (moduleState()->startupState() == STARTUP_COMPLETE &&
632 Options::preemptiveShutdown() &&
633 nextObservationTime > (Options::preemptiveShutdownTime() * 3600))
636 "Job '%1' scheduled for execution at %2. "
637 "Observatory scheduled for shutdown until next job is ready.",
638 job->getName(), job->getStartupTime().
toString()));
639 moduleState()->enablePreemptiveShutdown(job->getStartupTime());
640 checkShutdownState();
641 emit schedulerSleeping(
true,
false);
650 else if (nextObservationTime > Options::leadTime() * 60 &&
651 moduleState()->startupState() == STARTUP_COMPLETE &&
652 moduleState()->parkWaitState() == PARKWAIT_IDLE &&
653 (job->getStepPipeline() & SchedulerJob::USE_TRACK) &&
655 Options::schedulerParkMount())
658 "Job '%1' scheduled for execution at %2. "
659 "Parking the mount until the job is ready.",
660 job->getName(), job->getStartupTime().
toString()));
662 moduleState()->setParkWaitState(PARKWAIT_PARK);
666 else if (nextObservationTime > Options::leadTime() * 60)
668 appendLogText(
i18n(
"Sleeping until observation job %1 is ready at %2...", job->getName(),
672 if (nextObservationTime > Options::leadTime() * 60 * 12 && !Options::preemptiveShutdown())
674 dms delay(
static_cast<double>(nextObservationTime * 15.0 / 3600.0));
676 "Warning: Job '%1' is %2 away from now, you may want to enable Preemptive Shutdown.",
685 moduleState()->setupNextIteration(RUN_WAKEUP,
686 std::lround(((nextObservationTime + 1) * 1000) / KStarsData::Instance()->clock()->scale()));
688 emit schedulerSleeping(
false,
true);
695void SchedulerProcess::startSlew()
697 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting slewing must be valid");
702 moduleState()->setParkWaitState(PARKWAIT_UNPARK);
706 if (Options::resetMountModelBeforeJob())
711 SkyPoint target = activeJob()->getTargetCoords();
721 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' slew request received DBUS error: %2").
arg(
723 if (!manageConnectionLoss())
728 moduleState()->updateJobStage(SCHEDSTAGE_SLEWING);
729 appendLogText(
i18n(
"Job '%1' is slewing to target.", activeJob()->getName()));
733void SchedulerProcess::startFocusing()
735 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting focusing must be valid");
739 if (activeJob()->getStage() == SCHEDSTAGE_RESLEWING_COMPLETE ||
740 activeJob()->getStage() == SCHEDSTAGE_POSTALIGN_FOCUSING)
746 moduleState()->updateJobStage(SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE);
757 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' canAutoFocus request received DBUS error: %2").
arg(
759 if (!manageConnectionLoss())
767 if (focusModeReply.value() ==
false)
769 appendLogText(
i18n(
"Warning: job '%1' is unable to proceed with autofocus, not supported.", activeJob()->getName()));
770 activeJob()->setStepPipeline(
771 static_cast<SchedulerJob::StepPipeline
>(activeJob()->getStepPipeline() & ~SchedulerJob::USE_FOCUS));
772 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
785 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' resetFrame request received DBUS error: %2").
arg(
787 if (!manageConnectionLoss())
797 if (!activeJob()->getInitialFilter().isEmpty())
799 focusInterface()->setProperty(
"filter", activeJob()->getInitialFilter());
803 if (Options::focusUseFullField() ==
false)
807 if ((reply = focusInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setAutoStarEnabled", autoStar)).type() ==
810 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' setAutoFocusStar request received DBUS error: %1").
arg(
812 if (!manageConnectionLoss())
824 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: job '%1' startFocus request received DBUS error: %2").
arg(
826 if (!manageConnectionLoss())
834 moduleState()->updateJobStage(SCHEDSTAGE_FOCUSING);
835 appendLogText(
i18n(
"Job '%1' is focusing.", activeJob()->getName()));
836 moduleState()->startCurrentOperationTimer();
839void SchedulerProcess::startAstrometry()
841 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting aligning must be valid");
844 setSolverAction(Align::GOTO_SLEW);
852 moduleState()->setIndexToUse(-1);
853 moduleState()->setHealpixToUse(-1);
856 if (activeJob()->getFITSFile().isEmpty() ==
false)
862 appendLogText(
i18n(
"Warning: job '%1' target FITS file does not exist.", activeJob()->getName()));
871 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"loadAndSlew", solveArgs)).type() ==
874 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request received DBUS error: %2",
876 if (!manageConnectionLoss())
883 else if (reply.
arguments().first().toBool() ==
false)
885 appendLogText(
i18n(
"Warning: job '%1' loadAndSlew request failed.", activeJob()->getName()));
891 appendLogText(
i18n(
"Job '%1' is plate solving %2.", activeJob()->getName(), activeJob()->getFITSFile().fileName()));
897 const SkyPoint targetCoords = activeJob()->getTargetCoords();
900 rotationArgs << activeJob()->getPositionAngle();
902 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords",
905 appendLogText(
i18n(
"Warning: job '%1' setTargetCoords request received DBUS error: %2",
907 if (!manageConnectionLoss())
916 if (activeJob()->getPositionAngle() >= -180)
918 if ((reply = alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetPositionAngle",
921 appendLogText(
i18n(
"Warning: job '%1' setTargetPositionAngle request received DBUS error: %2").arg(
923 if (!manageConnectionLoss())
934 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request received DBUS error: %2").arg(
936 if (!manageConnectionLoss())
943 else if (reply.
arguments().first().toBool() ==
false)
945 appendLogText(
i18n(
"Warning: job '%1' captureAndSolve request failed.", activeJob()->getName()));
951 appendLogText(
i18n(
"Job '%1' is capturing and plate solving.", activeJob()->getName()));
955 moduleState()->updateJobStage(SCHEDSTAGE_ALIGNING);
956 moduleState()->startCurrentOperationTimer();
959void SchedulerProcess::startGuiding(
bool resetCalibration)
961 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting guiding must be valid");
964 if (resetCalibration ==
false && getGuidingStatus() == GUIDE_GUIDING)
966 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
967 appendLogText(
i18n(
"Guiding already running for %1, starting next scheduler action...", activeJob()->getName()));
969 moduleState()->startCurrentOperationTimer();
982 if (resetCalibration && Options::resetGuideCalibration())
989 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
991 appendLogText(
i18n(
"Starting guiding procedure for %1 ...", activeJob()->getName()));
993 moduleState()->startCurrentOperationTimer();
996void SchedulerProcess::stopGuiding()
998 if (!guideInterface())
1002 if (
nullptr != activeJob() && (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE))
1004 qCInfo(KSTARS_EKOS_SCHEDULER) <<
QString(
"Job '%1' is stopping guiding...").
arg(activeJob()->getName());
1006 moduleState()->resetGuideFailureCount();
1010 if (moduleState()->isGuidingTimerActive())
1011 moduleState()->cancelGuidingTimer();
1014void SchedulerProcess::processGuidingTimer()
1016 if ((moduleState()->restartGuidingInterval() > 0) &&
1017 (moduleState()->restartGuidingTime().msecsTo(KStarsData::Instance()->ut()) > moduleState()->restartGuidingInterval()))
1019 moduleState()->cancelGuidingTimer();
1024void SchedulerProcess::startCapture(
bool restart)
1026 Q_ASSERT_X(
nullptr != activeJob(), __FUNCTION__,
"Job starting capturing must be valid");
1029 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE && getGuidingStatus() != GUIDE_GUIDING)
1032 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING);
1037 captureInterface()->setProperty(
"targetName", activeJob()->getName());
1039 QString url = activeJob()->getSequenceFile().toLocalFile();
1041 if (restart ==
false)
1046 QVariant targetName(activeJob()->getName());
1047 dbusargs.
append(targetName);
1049 "loadSequenceQueue",
1053 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1054 QString(
"Warning: job '%1' loadSequenceQueue request received DBUS error: %1").
arg(activeJob()->getName()).
arg(
1056 if (!manageConnectionLoss())
1061 else if (captureReply.value() ==
false)
1063 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1064 QString(
"Warning: job '%1' loadSequenceQueue request failed").
arg(activeJob()->getName());
1065 if (!manageConnectionLoss())
1074 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1075 QString(
"Warning: job '%1' mainCameraDeviceName request received DBUS error: %1").
arg(activeJob()->getName()).
arg(
1077 if (!manageConnectionLoss())
1083 moduleState()->setMainCameraDeviceName(nameReply.value());
1089 for (
auto &e : fMap.
keys())
1096 if ((reply = captureInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setCapturedFramesMap",
1097 dbusargs)).type() ==
1100 qCCritical(KSTARS_EKOS_SCHEDULER) <<
1101 QString(
"Warning: job '%1' setCapturedFramesCount request received DBUS error: %1").
arg(activeJob()->getName()).
arg(
1103 if (!manageConnectionLoss())
1112 moduleState()->updateJobStage(SCHEDSTAGE_CAPTURING);
1114 KSNotification::event(
QLatin1String(
"EkosScheduledImagingStart"),
1115 i18n(
"Ekos job (%1) - Capture started", activeJob()->getName()), KSNotification::Scheduler);
1117 if (moduleState()->captureBatch() > 0)
1118 appendLogText(
i18n(
"Job '%1' capture is in progress (batch #%2)...", activeJob()->getName(),
1119 moduleState()->captureBatch() + 1));
1121 appendLogText(
i18n(
"Job '%1' capture is in progress...", activeJob()->getName()));
1123 moduleState()->startCurrentOperationTimer();
1126void SchedulerProcess::setSolverAction(Align::GotoMode mode)
1128 QVariant gotoMode(
static_cast<int>(mode));
1132void SchedulerProcess::loadProfiles()
1134 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Loading profiles";
1138 moduleState()->updateProfiles(profiles);
1141void SchedulerProcess::executeScript(
const QString &filename)
1143 appendLogText(
i18n(
"Executing script %1...", filename));
1150 checkProcessExit(exitCode);
1154 scriptProcess().start(filename, arguments);
1157bool SchedulerProcess::checkEkosState()
1159 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1162 switch (moduleState()->ekosState())
1166 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1168 moduleState()->setEkosState(EKOS_READY);
1174 moduleState()->setEkosState(EKOS_STARTING);
1175 moduleState()->startCurrentOperationTimer();
1177 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos communication status is" << moduleState()->ekosCommunicationStatus() <<
1186 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1188 appendLogText(
i18n(
"Ekos started."));
1189 moduleState()->resetEkosConnectFailureCount();
1190 moduleState()->setEkosState(EKOS_READY);
1193 else if (moduleState()->ekosCommunicationStatus() == Ekos::Error)
1195 if (moduleState()->increaseEkosConnectFailureCount())
1197 appendLogText(
i18n(
"Starting Ekos failed. Retrying..."));
1202 appendLogText(
i18n(
"Starting Ekos failed."));
1206 else if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1209 else if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1211 if (moduleState()->increaseEkosConnectFailureCount())
1213 appendLogText(
i18n(
"Starting Ekos timed out. Retrying..."));
1218 moduleState()->startCurrentOperationTimer();
1223 appendLogText(
i18n(
"Starting Ekos timed out."));
1232 if (moduleState()->ekosCommunicationStatus() == Ekos::Idle)
1234 appendLogText(
i18n(
"Ekos stopped."));
1235 moduleState()->setEkosState(EKOS_IDLE);
1247bool SchedulerProcess::checkINDIState()
1249 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1252 switch (moduleState()->indiState())
1256 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1258 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1259 moduleState()->resetIndiConnectFailureCount();
1260 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI Properties...";
1264 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Connecting INDI devices...";
1266 moduleState()->setIndiState(INDI_CONNECTING);
1268 moduleState()->startCurrentOperationTimer();
1273 case INDI_CONNECTING:
1275 if (moduleState()->indiCommunicationStatus() == Ekos::Success)
1277 appendLogText(
i18n(
"INDI devices connected."));
1278 moduleState()->setIndiState(INDI_PROPERTY_CHECK);
1280 else if (moduleState()->indiCommunicationStatus() == Ekos::Error)
1282 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1284 appendLogText(
i18n(
"One or more INDI devices failed to connect. Retrying..."));
1289 appendLogText(
i18n(
"One or more INDI devices failed to connect. Check INDI control panel for details."));
1294 else if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1296 if (moduleState()->increaseIndiConnectFailureCount() <= moduleState()->maxFailureAttempts())
1298 appendLogText(
i18n(
"One or more INDI devices timed out. Retrying..."));
1300 moduleState()->startCurrentOperationTimer();
1304 appendLogText(
i18n(
"One or more INDI devices timed out. Check INDI control panel for details."));
1311 case INDI_DISCONNECTING:
1313 if (moduleState()->indiCommunicationStatus() == Ekos::Idle)
1315 appendLogText(
i18n(
"INDI devices disconnected."));
1316 moduleState()->setIndiState(INDI_IDLE);
1322 case INDI_PROPERTY_CHECK:
1324 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking INDI properties.";
1326 if (Options::schedulerUnparkDome() && moduleState()->domeReady() ==
false)
1328 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1330 moduleState()->startCurrentOperationTimer();
1331 appendLogText(
i18n(
"Warning: dome device not ready after timeout, attempting to recover..."));
1336 appendLogText(
i18n(
"Dome unpark required but dome is not yet ready."));
1341 if (Options::schedulerUnparkMount() && moduleState()->mountReady() ==
false)
1343 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1345 moduleState()->startCurrentOperationTimer();
1346 appendLogText(
i18n(
"Warning: mount device not ready after timeout, attempting to recover..."));
1351 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount unpark required but mount is not yet ready.";
1356 if (Options::schedulerOpenDustCover() && moduleState()->capReady() ==
false)
1358 if (moduleState()->getCurrentOperationMsec() > (30 * 1000))
1360 moduleState()->startCurrentOperationTimer();
1361 appendLogText(
i18n(
"Warning: cap device not ready after timeout, attempting to recover..."));
1366 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap unpark required but cap is not yet ready.";
1371 if (captureInterface().isNull())
1374 if (moduleState()->captureReady() ==
false)
1376 QVariant hasCoolerControl = captureInterface()->property(
"coolerControl");
1377 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cooler control" << (!hasCoolerControl.
isValid() ?
"invalid" :
1378 (hasCoolerControl.
toBool() ?
"True" :
"Faklse"));
1379 if (hasCoolerControl.
isValid())
1380 moduleState()->setCaptureReady(
true);
1382 qCWarning(KSTARS_EKOS_SCHEDULER) <<
"Capture module is not ready yet...";
1385 moduleState()->setIndiState(INDI_READY);
1386 moduleState()->resetIndiConnectFailureCount();
1397bool SchedulerProcess::completeShutdown()
1400 if (moduleState()->indiState() == INDI_DISCONNECTING
1401 && checkINDIState() ==
false)
1405 if (moduleState()->indiState() != INDI_IDLE && Options::stopEkosAfterShutdown())
1412 if (moduleState()->ekosState() == EKOS_STOPPING && checkEkosState() ==
false)
1416 if (moduleState()->ekosState() != EKOS_IDLE && Options::stopEkosAfterShutdown())
1422 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
1423 appendLogText(
i18n(
"Shutdown complete."));
1425 appendLogText(
i18n(
"Shutdown procedure failed, aborting..."));
1433void SchedulerProcess::disconnectINDI()
1435 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Disconnecting INDI...";
1436 moduleState()->setIndiState(INDI_DISCONNECTING);
1440void SchedulerProcess::stopEkos()
1442 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Stopping Ekos...";
1443 moduleState()->setEkosState(EKOS_STOPPING);
1444 moduleState()->resetEkosConnectFailureCount();
1446 moduleState()->setMountReady(
false);
1447 moduleState()->setCaptureReady(
false);
1448 moduleState()->setDomeReady(
false);
1449 moduleState()->setCapReady(
false);
1452bool SchedulerProcess::manageConnectionLoss()
1454 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
1458 switch (moduleState()->ekosState())
1469 switch (moduleState()->indiState())
1472 case INDI_DISCONNECTING:
1481 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1483 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Ekos is currently connected, checking INDI before mitigating connection loss.");
1486 if (moduleState()->isINDIConnected())
1489 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"INDI is currently connected, no connection loss mitigation needed.");
1495 stopCurrentJobAction();
1506void SchedulerProcess::checkCapParkingStatus()
1508 if (capInterface().isNull())
1511 QVariant parkingStatus = capInterface()->property(
"parkStatus");
1512 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1514 if (parkingStatus.
isValid() ==
false)
1516 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
1517 capInterface()->lastError().type());
1518 if (!manageConnectionLoss())
1519 parkingStatus = ISD::PARK_ERROR;
1522 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1526 case ISD::PARK_PARKED:
1527 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1529 appendLogText(
i18n(
"Cap parked."));
1530 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
1532 moduleState()->resetParkingCapFailureCount();
1535 case ISD::PARK_UNPARKED:
1536 if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1538 moduleState()->setStartupState(STARTUP_COMPLETE);
1539 appendLogText(
i18n(
"Cap unparked."));
1541 moduleState()->resetParkingCapFailureCount();
1544 case ISD::PARK_PARKING:
1545 case ISD::PARK_UNPARKING:
1547 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1549 if (moduleState()->increaseParkingCapFailureCount())
1551 appendLogText(
i18n(
"Operation timeout. Restarting operation..."));
1552 if (status == ISD::PARK_PARKING)
1561 case ISD::PARK_ERROR:
1562 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_CAP)
1564 appendLogText(
i18n(
"Cap parking error."));
1565 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1567 else if (moduleState()->startupState() == STARTUP_UNPARKING_CAP)
1569 appendLogText(
i18n(
"Cap unparking error."));
1570 moduleState()->setStartupState(STARTUP_ERROR);
1572 moduleState()->resetParkingCapFailureCount();
1580void SchedulerProcess::checkMountParkingStatus()
1582 if (mountInterface().isNull())
1585 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
1586 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1588 if (parkingStatus.
isValid() ==
false)
1590 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
1591 mountInterface()->lastError().type());
1592 if (!manageConnectionLoss())
1593 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1596 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1601 case ISD::PARK_PARKED:
1604 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1605 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1608 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1609 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1611 appendLogText(
i18n(
"Mount parked."));
1612 moduleState()->resetParkingMountFailureCount();
1616 case ISD::PARK_UNPARKED:
1619 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1620 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1623 if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1624 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1626 appendLogText(
i18n(
"Mount unparked."));
1627 moduleState()->resetParkingMountFailureCount();
1633 case ISD::PARK_UNPARKING:
1634 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1636 if (moduleState()->increaseParkingMountFailureCount())
1638 appendLogText(
i18n(
"Warning: mount unpark operation timed out on attempt %1/%2. Restarting operation...",
1639 moduleState()->parkingMountFailureCount(), moduleState()->maxFailureAttempts()));
1644 appendLogText(
i18n(
"Warning: mount unpark operation timed out on last attempt."));
1645 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1648 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
1653 case ISD::PARK_PARKING:
1654 if (moduleState()->getCurrentOperationMsec() > (60 * 1000))
1656 if (moduleState()->increaseParkingMountFailureCount())
1658 appendLogText(
i18n(
"Warning: mount park operation timed out on attempt %1/%2. Restarting operation...",
1659 moduleState()->parkingMountFailureCount(),
1660 moduleState()->maxFailureAttempts()));
1665 appendLogText(
i18n(
"Warning: mount park operation timed out on last attempt."));
1666 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1669 else qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Parking mount in progress...";
1674 case ISD::PARK_ERROR:
1675 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1677 appendLogText(
i18n(
"Mount unparking error."));
1678 moduleState()->setStartupState(STARTUP_ERROR);
1679 moduleState()->resetParkingMountFailureCount();
1681 else if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1683 if (moduleState()->increaseParkingMountFailureCount())
1685 appendLogText(
i18n(
"Warning: mount park operation failed on attempt %1/%2. Restarting operation...",
1686 moduleState()->parkingMountFailureCount(),
1687 moduleState()->maxFailureAttempts()));
1692 appendLogText(
i18n(
"Mount parking error."));
1693 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1694 moduleState()->resetParkingMountFailureCount();
1698 else if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1700 appendLogText(
i18n(
"Mount parking error."));
1701 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1702 moduleState()->resetParkingMountFailureCount();
1704 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1706 appendLogText(
i18n(
"Mount unparking error."));
1707 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1708 moduleState()->resetParkingMountFailureCount();
1714 case ISD::PARK_UNKNOWN:
1716 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_MOUNT)
1717 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1720 if (moduleState()->startupState() == STARTUP_UNPARKING_MOUNT)
1721 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1724 if (moduleState()->parkWaitState() == PARKWAIT_PARKING)
1725 moduleState()->setParkWaitState(PARKWAIT_PARKED);
1726 else if (moduleState()->parkWaitState() == PARKWAIT_UNPARKING)
1727 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
1729 moduleState()->resetParkingMountFailureCount();
1734void SchedulerProcess::checkDomeParkingStatus()
1736 if (domeInterface().isNull())
1739 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
1740 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
1742 if (parkingStatus.
isValid() ==
false)
1744 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
1745 mountInterface()->lastError().
type());
1746 if (!manageConnectionLoss())
1747 moduleState()->setParkWaitState(PARKWAIT_ERROR);
1750 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
1754 case ISD::PARK_PARKED:
1755 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1757 appendLogText(
i18n(
"Dome parked."));
1759 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
1761 moduleState()->resetParkingDomeFailureCount();
1764 case ISD::PARK_UNPARKED:
1765 if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1767 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1768 appendLogText(
i18n(
"Dome unparked."));
1770 moduleState()->resetParkingDomeFailureCount();
1773 case ISD::PARK_PARKING:
1774 case ISD::PARK_UNPARKING:
1776 if (moduleState()->getCurrentOperationMsec() > (120 * 1000))
1778 if (moduleState()->increaseParkingDomeFailureCount())
1780 appendLogText(
i18n(
"Operation timeout. Restarting operation..."));
1781 if (status == ISD::PARK_PARKING)
1790 case ISD::PARK_ERROR:
1791 if (moduleState()->shutdownState() == SHUTDOWN_PARKING_DOME)
1793 if (moduleState()->increaseParkingDomeFailureCount())
1795 appendLogText(
i18n(
"Dome parking failed. Restarting operation..."));
1800 appendLogText(
i18n(
"Dome parking error."));
1801 moduleState()->setShutdownState(SHUTDOWN_ERROR);
1802 moduleState()->resetParkingDomeFailureCount();
1805 else if (moduleState()->startupState() == STARTUP_UNPARKING_DOME)
1807 if (moduleState()->increaseParkingDomeFailureCount())
1809 appendLogText(
i18n(
"Dome unparking failed. Restarting operation..."));
1814 appendLogText(
i18n(
"Dome unparking error."));
1815 moduleState()->setStartupState(STARTUP_ERROR);
1816 moduleState()->resetParkingDomeFailureCount();
1826bool SchedulerProcess::checkStartupState()
1828 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1831 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"Checking Startup State (%1)...").
arg(moduleState()->startupState());
1833 switch (moduleState()->startupState())
1837 KSNotification::event(
QLatin1String(
"ObservatoryStartup"),
i18n(
"Observatory is in the startup process"),
1838 KSNotification::Scheduler);
1840 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Startup Idle. Starting startup process...";
1847 if (moduleState()->ekosCommunicationStatus() == Ekos::Success)
1849 if (moduleState()->startupScriptURL().isEmpty() ==
false)
1850 appendLogText(
i18n(
"Ekos is already started, skipping startup script..."));
1852 if (!activeJob() || activeJob()->getLightFramesRequired())
1853 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
1855 moduleState()->setStartupState(STARTUP_COMPLETE);
1859 if (moduleState()->currentProfile() !=
i18n(
"Default"))
1862 profile.
append(moduleState()->currentProfile());
1863 ekosInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setProfile", profile);
1866 if (moduleState()->startupScriptURL().isEmpty() ==
false)
1868 moduleState()->setStartupState(STARTUP_SCRIPT);
1873 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
1877 case STARTUP_SCRIPT:
1880 case STARTUP_UNPARK_DOME:
1884 if (activeJob() ==
nullptr || activeJob()->getLightFramesRequired())
1886 if (Options::schedulerUnparkDome())
1889 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
1893 moduleState()->setStartupState(STARTUP_COMPLETE);
1899 case STARTUP_UNPARKING_DOME:
1900 checkDomeParkingStatus();
1903 case STARTUP_UNPARK_MOUNT:
1904 if (Options::schedulerUnparkMount())
1907 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
1910 case STARTUP_UNPARKING_MOUNT:
1911 checkMountParkingStatus();
1914 case STARTUP_UNPARK_CAP:
1915 if (Options::schedulerOpenDustCover())
1918 moduleState()->setStartupState(STARTUP_COMPLETE);
1921 case STARTUP_UNPARKING_CAP:
1922 checkCapParkingStatus();
1925 case STARTUP_COMPLETE:
1936bool SchedulerProcess::checkShutdownState()
1938 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking shutdown state...";
1940 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
1943 switch (moduleState()->shutdownState())
1947 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Starting shutdown process...";
1949 moduleState()->setActiveJob(
nullptr);
1950 moduleState()->setupNextIteration(RUN_SHUTDOWN);
1951 emit shutdownStarted();
1953 if (Options::schedulerWarmCCD())
1955 appendLogText(
i18n(
"Warming up CCD..."));
1960 if (captureInterface())
1962 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Setting coolerControl=false";
1963 captureInterface()->setProperty(
"coolerControl",
false);
1968 if (moduleState()->isINDIConnected())
1970 if (Options::schedulerCloseDustCover())
1972 moduleState()->setShutdownState(SHUTDOWN_PARK_CAP);
1976 if (Options::schedulerParkMount())
1978 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
1982 if (Options::schedulerParkDome())
1984 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
1988 else appendLogText(
i18n(
"Warning: Bypassing parking procedures, no INDI connection."));
1990 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
1992 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
1996 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
1999 case SHUTDOWN_PARK_CAP:
2000 if (!moduleState()->isINDIConnected())
2002 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2003 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2005 else if (Options::schedulerCloseDustCover())
2008 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
2011 case SHUTDOWN_PARKING_CAP:
2012 checkCapParkingStatus();
2015 case SHUTDOWN_PARK_MOUNT:
2016 if (!moduleState()->isINDIConnected())
2018 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2019 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2021 else if (Options::schedulerParkMount())
2024 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
2027 case SHUTDOWN_PARKING_MOUNT:
2028 checkMountParkingStatus();
2031 case SHUTDOWN_PARK_DOME:
2032 if (!moduleState()->isINDIConnected())
2034 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Bypassing shutdown step 'park cap', no INDI connection.";
2035 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2037 else if (Options::schedulerParkDome())
2040 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
2043 case SHUTDOWN_PARKING_DOME:
2044 checkDomeParkingStatus();
2047 case SHUTDOWN_SCRIPT:
2048 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
2051 if (moduleState()->ekosState() != EKOS_IDLE && Options::shutdownScriptTerminatesINDI())
2057 moduleState()->setShutdownState(SHUTDOWN_SCRIPT_RUNNING);
2061 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
2064 case SHUTDOWN_SCRIPT_RUNNING:
2067 case SHUTDOWN_COMPLETE:
2068 return completeShutdown();
2070 case SHUTDOWN_ERROR:
2078bool SchedulerProcess::checkParkWaitState()
2080 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2083 if (moduleState()->parkWaitState() == PARKWAIT_IDLE)
2088 switch (moduleState()->parkWaitState())
2094 case PARKWAIT_PARKING:
2095 checkMountParkingStatus();
2098 case PARKWAIT_UNPARK:
2102 case PARKWAIT_UNPARKING:
2103 checkMountParkingStatus();
2107 case PARKWAIT_PARKED:
2108 case PARKWAIT_UNPARKED:
2111 case PARKWAIT_ERROR:
2112 appendLogText(
i18n(
"park/unpark wait procedure failed, aborting..."));
2121void SchedulerProcess::runStartupProcedure()
2123 if (moduleState()->startupState() == STARTUP_IDLE
2124 || moduleState()->startupState() == STARTUP_ERROR
2125 || moduleState()->startupState() == STARTUP_COMPLETE)
2131 appendLogText(
i18n(
"Warning: executing startup procedure manually..."));
2132 moduleState()->setStartupState(STARTUP_IDLE);
2133 checkStartupState();
2138 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the startup procedure manually?"));
2142 switch (moduleState()->startupState())
2147 case STARTUP_SCRIPT:
2148 scriptProcess().terminate();
2151 case STARTUP_UNPARK_DOME:
2154 case STARTUP_UNPARKING_DOME:
2155 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking dome...";
2159 case STARTUP_UNPARK_MOUNT:
2162 case STARTUP_UNPARKING_MOUNT:
2163 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting unparking mount...";
2167 case STARTUP_UNPARK_CAP:
2170 case STARTUP_UNPARKING_CAP:
2173 case STARTUP_COMPLETE:
2180 moduleState()->setStartupState(STARTUP_IDLE);
2182 appendLogText(
i18n(
"Startup procedure terminated."));
2187void SchedulerProcess::runShutdownProcedure()
2189 if (moduleState()->shutdownState() == SHUTDOWN_IDLE
2190 || moduleState()->shutdownState() == SHUTDOWN_ERROR
2191 || moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
2196 appendLogText(
i18n(
"Warning: executing shutdown procedure manually..."));
2197 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2198 checkShutdownState();
2202 KSMessageBox::Instance()->questionYesNo(
i18n(
"Are you sure you want to execute the shutdown procedure manually?"));
2206 switch (moduleState()->shutdownState())
2211 case SHUTDOWN_SCRIPT:
2214 case SHUTDOWN_SCRIPT_RUNNING:
2215 scriptProcess().terminate();
2218 case SHUTDOWN_PARK_DOME:
2221 case SHUTDOWN_PARKING_DOME:
2222 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking dome...";
2226 case SHUTDOWN_PARK_MOUNT:
2229 case SHUTDOWN_PARKING_MOUNT:
2230 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Aborting parking mount...";
2234 case SHUTDOWN_PARK_CAP:
2235 case SHUTDOWN_PARKING_CAP:
2238 case SHUTDOWN_COMPLETE:
2241 case SHUTDOWN_ERROR:
2245 moduleState()->setShutdownState(SHUTDOWN_IDLE);
2247 appendLogText(
i18n(
"Shutdown procedure terminated."));
2251void SchedulerProcess::setPaused()
2253 moduleState()->setupNextIteration(RUN_NOTHING);
2254 appendLogText(
i18n(
"Scheduler paused."));
2255 emit schedulerPaused();
2258void SchedulerProcess::resetJobs()
2261 for (SchedulerJob * job : moduleState()->jobs())
2264 job->setCompletedCount(0);
2268 updateCompletedJobsCount(
true);
2273 auto finished_or_aborted = [](SchedulerJob
const *
const job)
2280 auto neither_scheduled_nor_aborted = [](SchedulerJob
const *
const job)
2288 if (jobs.
isEmpty() || std::all_of(jobs.
begin(), jobs.
end(), neither_scheduled_nor_aborted))
2290 appendLogText(
i18n(
"No jobs left in the scheduler queue after evaluating."));
2291 moduleState()->setActiveJob(
nullptr);
2295 else if (std::all_of(jobs.
begin(), jobs.
end(), finished_or_aborted) &&
2296 strategy != ERROR_DONT_RESTART)
2298 appendLogText(
i18n(
"Only aborted jobs left in the scheduler queue after evaluating, rescheduling those."));
2299 std::for_each(jobs.
begin(), jobs.
end(), [](SchedulerJob * job)
2301 if (SCHEDJOB_ABORTED == job->getState())
2302 job->setState(SCHEDJOB_EVALUATION);
2309 SchedulerJob *scheduledJob = getGreedyScheduler()->getScheduledJob();
2312 appendLogText(
i18n(
"No jobs scheduled."));
2313 moduleState()->setActiveJob(
nullptr);
2316 moduleState()->setActiveJob(scheduledJob);
2320void SchedulerProcess::startJobEvaluation()
2324 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2328 moduleState()->resetSequenceExecutionCounter();
2334void SchedulerProcess::evaluateJobs(
bool evaluateOnly)
2336 for (
auto job : moduleState()->jobs())
2340 if (moduleState()->jobs().isEmpty())
2343 if (Options::rememberJobProgress())
2344 updateCompletedJobsCount();
2346 moduleState()->calculateDawnDusk();
2348 getGreedyScheduler()->scheduleJobs(moduleState()->jobs(), SchedulerModuleState::getLocalTime(),
2349 moduleState()->capturedFramesCount(),
this);
2352 if (!evaluateOnly && moduleState()->schedulerState() == SCHEDULER_RUNNING)
2355 selectActiveJob(moduleState()->jobs());
2357 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Ekos finished evaluating jobs, no job selection required.";
2359 emit jobsUpdated(moduleState()->getJSONJobs());
2362bool SchedulerProcess::checkStatus()
2364 if (moduleState()->schedulerState() == SCHEDULER_PAUSED)
2366 if (activeJob() ==
nullptr)
2371 switch (activeJob()->getState())
2387 if (activeJob() ==
nullptr)
2390 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE
2391 || moduleState()->shutdownState() == SHUTDOWN_ERROR)
2393 return completeShutdown();
2397 if (moduleState()->shutdownState() > SHUTDOWN_IDLE)
2400 if (moduleState()->ekosState() == EKOS_STOPPING && checkEkosState() ==
false)
2403 checkShutdownState();
2408 if (checkParkWaitState() ==
false)
2412 evaluateJobs(
false);
2415 if (
nullptr == activeJob() && moduleState()->checkRepeatSequence())
2420 evaluateJobs(
false);
2424 moduleState()->increaseSequenceExecutionCounter();
2425 appendLogText(
i18n(
"Starting job sequence iteration #%1", moduleState()->sequenceExecutionCounter()));
2431 if (
nullptr == activeJob())
2433 checkShutdownState();
2438 else if (shouldSchedulerSleep(activeJob()) ==
false)
2441 if (moduleState()->startupState() == STARTUP_ERROR)
2449 if ((moduleState()->startupState() == STARTUP_IDLE
2450 && checkStartupState() ==
false)
2451 || moduleState()->startupState() == STARTUP_SCRIPT)
2455 if (checkEkosState() ==
false)
2459 if (checkINDIState() ==
false)
2463 if (checkParkWaitState() ==
false)
2467 if (moduleState()->startupState() > STARTUP_SCRIPT
2468 && moduleState()->startupState() < STARTUP_ERROR
2469 && checkStartupState() ==
false)
2479 executeJob(activeJob());
2480 emit updateJobTable();
2486void SchedulerProcess::getNextAction()
2488 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Get next action...";
2490 switch (activeJob()->getStage())
2492 case SCHEDSTAGE_IDLE:
2493 if (activeJob()->getLightFramesRequired())
2495 if (activeJob()->getStepPipeline() & SchedulerJob::USE_TRACK)
2497 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2499 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3485";
2502 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2504 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2505 if (getGuidingStatus() == GUIDE_GUIDING)
2507 appendLogText(
i18n(
"Guiding already running, directly start capturing."));
2517 if (activeJob()->getStepPipeline())
2519 i18n(
"Job '%1' is proceeding directly to capture stage because only calibration frames are pending.",
2520 activeJob()->getName()));
2526 case SCHEDSTAGE_SLEW_COMPLETE:
2527 if (activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS && moduleState()->autofocusCompleted() ==
false)
2529 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3514";
2532 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2534 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2540 case SCHEDSTAGE_FOCUS_COMPLETE:
2541 if (activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN)
2543 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2549 case SCHEDSTAGE_ALIGN_COMPLETE:
2550 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING);
2553 case SCHEDSTAGE_RESLEWING_COMPLETE:
2556 if ((activeJob()->getStepPipeline() & SchedulerJob::USE_FOCUS) && activeJob()->getInSequenceFocus())
2559 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 3544";
2562 else if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2568 case SCHEDSTAGE_POSTALIGN_FOCUSING_COMPLETE:
2569 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
2575 case SCHEDSTAGE_GUIDING_COMPLETE:
2584void SchedulerProcess::iterate()
2586 const int msSleep = runSchedulerIteration();
2591 moduleState()->iterationTimer().setSingleShot(
true);
2592 moduleState()->iterationTimer().start(msSleep);
2596int SchedulerProcess::runSchedulerIteration()
2599 if (moduleState()->startMSecs() == 0)
2600 moduleState()->setStartMSecs(now);
2613 moduleState()->setIterationSetup(
false);
2614 switch (keepTimerState)
2617 changeSleepLabel(
"",
false);
2627 checkShutdownState();
2630 moduleState()->setTimerInterval(-1);
2633 if (!moduleState()->iterationSetup())
2639 moduleState()->setTimerInterval(moduleState()->updatePeriodMs());
2642 return moduleState()->timerInterval();
2645void SchedulerProcess::checkJobStage()
2647 Q_ASSERT_X(activeJob(), __FUNCTION__,
"Actual current job is required to check job stage");
2651 if (checkJobStageCounter == 0)
2653 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Checking job stage for" << activeJob()->getName() <<
"startup" <<
2654 activeJob()->getStartupCondition() << activeJob()->getStartupTime().toString() <<
"state" << activeJob()->getState();
2655 if (checkJobStageCounter++ == 30)
2656 checkJobStageCounter = 0;
2659 emit syncGreedyParams();
2660 if (!getGreedyScheduler()->checkJob(moduleState()->jobs(), SchedulerModuleState::getLocalTime(), activeJob()))
2663 stopCurrentJobAction();
2667 checkJobStageEpilogue();
2670void SchedulerProcess::checkJobStageEpilogue()
2681 processGuidingTimer();
2685 if (!activeJob())
return;
2686 switch (activeJob()->getStage())
2688 case SCHEDSTAGE_IDLE:
2690 emit jobStarted(activeJob()->getName());
2694 case SCHEDSTAGE_ALIGNING:
2696 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(ALIGN_INACTIVITY_TIMEOUT))
2698 QVariant const status = alignInterface()->property(
"status");
2703 if (moduleState()->increaseAlignFailureCount())
2705 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align module timed out. Restarting request...";
2710 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
2711 activeJob()->setState(SCHEDJOB_ABORTED);
2716 moduleState()->startCurrentOperationTimer();
2720 case SCHEDSTAGE_CAPTURING:
2722 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(CAPTURE_INACTIVITY_TIMEOUT))
2729 if (moduleState()->increaseCaptureFailureCount())
2731 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"capture module timed out. Restarting request...";
2736 appendLogText(
i18n(
"Warning: job '%1' capture procedure failed, marking aborted.", activeJob()->getName()));
2737 activeJob()->setState(SCHEDJOB_ABORTED);
2741 else moduleState()->startCurrentOperationTimer();
2745 case SCHEDSTAGE_FOCUSING:
2747 if (moduleState()->getCurrentOperationMsec() >
static_cast<int>(FOCUS_INACTIVITY_TIMEOUT))
2750 Ekos::FocusState focusStatus =
static_cast<Ekos::FocusState
>(
status.toInt());
2752 if (focusStatus == Ekos::FOCUS_IDLE || focusStatus == Ekos::FOCUS_WAITING)
2754 if (moduleState()->increaseFocusFailureCount())
2756 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus module timed out. Restarting request...";
2761 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
2762 activeJob()->setState(SCHEDJOB_ABORTED);
2766 else moduleState()->startCurrentOperationTimer();
2770 case SCHEDSTAGE_GUIDING:
2772 if (moduleState()->getCurrentOperationMsec() > GUIDE_INACTIVITY_TIMEOUT)
2774 GuideState guideStatus = getGuidingStatus();
2776 if (guideStatus == Ekos::GUIDE_IDLE || guideStatus == Ekos::GUIDE_CONNECTED || guideStatus == Ekos::GUIDE_DISCONNECTED)
2778 if (moduleState()->increaseGuideFailureCount())
2780 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"guide module timed out. Restarting request...";
2785 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
2786 activeJob()->setState(SCHEDJOB_ABORTED);
2790 else moduleState()->startCurrentOperationTimer();
2794 case SCHEDSTAGE_SLEWING:
2795 case SCHEDSTAGE_RESLEWING:
2798 QVariant const slewStatus = mountInterface()->property(
"status");
2804 ISD::Mount::Status
const status =
static_cast<ISD::Mount::Status
>(slewStatus.
toInt());
2805 setMountStatus(status);
2809 appendLogText(
i18n(
"Warning: job '%1' lost connection to the mount, attempting to reconnect.", activeJob()->getName()));
2810 if (!manageConnectionLoss())
2811 activeJob()->setState(SCHEDJOB_ERROR);
2817 case SCHEDSTAGE_SLEW_COMPLETE:
2818 case SCHEDSTAGE_RESLEWING_COMPLETE:
2820 if (moduleState()->domeReady())
2822 QVariant const isDomeMoving = domeInterface()->property(
"isMoving");
2826 appendLogText(
i18n(
"Warning: job '%1' lost connection to the dome, attempting to reconnect.", activeJob()->getName()));
2827 if (!manageConnectionLoss())
2828 activeJob()->setState(SCHEDJOB_ERROR);
2832 if (!isDomeMoving.
value<
bool>())
2835 else getNextAction();
2843void SchedulerProcess::applyConfig()
2845 moduleState()->calculateDawnDusk();
2847 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
2853bool SchedulerProcess::executeJob(SchedulerJob * job)
2859 if (activeJob() == job &&
SCHEDJOB_BUSY == activeJob()->getState())
2862 moduleState()->setActiveJob(job);
2871 if (shouldSchedulerSleep(activeJob()))
2874 else if (0 < SchedulerModuleState::getLocalTime().secsTo(activeJob()->getStartupTime()))
2879 if (job->getCompletionCondition() == FINISH_SEQUENCE && Options::rememberJobProgress())
2880 captureInterface()->setProperty(
"targetName", job->getName());
2882 moduleState()->calculateDawnDusk();
2886 moduleState()->setAutofocusCompleted(
false);
2888 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Executing Job " << activeJob()->getName();
2891 emit jobsUpdated(moduleState()->getJSONJobs());
2893 KSNotification::event(
QLatin1String(
"EkosSchedulerJobStart"),
2894 i18n(
"Ekos job started (%1)", activeJob()->getName()), KSNotification::Scheduler);
2897 moduleState()->setupNextIteration(RUN_JOBCHECK);
2901bool SchedulerProcess::saveScheduler(
const QUrl &fileURL)
2909 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
2918 outstream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<
Qt::endl;
2919 outstream <<
"<SchedulerList version='1.6'>" <<
Qt::endl;
2921 outstream <<
"<Profile>" <<
QString(entityXML(strdup(moduleState()->currentProfile().toStdString().c_str()))) <<
2924 auto tiles = KStarsData::Instance()->
skyComposite()->mosaicComponent()->tiles();
2925 bool useMosaicInfo = !tiles->sequenceFile().isEmpty();
2929 outstream <<
"<Mosaic>" <<
Qt::endl;
2930 outstream <<
"<Target>" << tiles->targetName() <<
"</Target>" <<
Qt::endl;
2931 outstream <<
"<Group>" << tiles->group() <<
"</Group>" <<
Qt::endl;
2933 QString ccArg, ccValue = tiles->completionCondition(&ccArg);
2934 if (ccValue ==
"FinishSequence")
2935 outstream <<
"<FinishSequence/>" <<
Qt::endl;
2936 else if (ccValue ==
"FinishLoop")
2937 outstream <<
"<FinishLoop/>" <<
Qt::endl;
2938 else if (ccValue ==
"FinishRepeat")
2939 outstream <<
"<FinishRepeat>" << ccArg <<
"</FinishRepeat>" <<
Qt::endl;
2941 outstream <<
"<Sequence>" << tiles->sequenceFile() <<
"</Sequence>" <<
Qt::endl;
2942 outstream <<
"<Directory>" << tiles->outputDirectory() <<
"</Directory>" <<
Qt::endl;
2944 outstream <<
"<FocusEveryN>" << tiles->focusEveryN() <<
"</FocusEveryN>" <<
Qt::endl;
2945 outstream <<
"<AlignEveryN>" << tiles->alignEveryN() <<
"</AlignEveryN>" <<
Qt::endl;
2946 if (tiles->isTrackChecked())
2947 outstream <<
"<TrackChecked/>" <<
Qt::endl;
2948 if (tiles->isFocusChecked())
2949 outstream <<
"<FocusChecked/>" <<
Qt::endl;
2950 if (tiles->isAlignChecked())
2951 outstream <<
"<AlignChecked/>" <<
Qt::endl;
2952 if (tiles->isGuideChecked())
2953 outstream <<
"<GuideChecked/>" <<
Qt::endl;
2954 outstream <<
"<Overlap>" << cLocale.
toString(tiles->overlap()) <<
"</Overlap>" <<
Qt::endl;
2955 outstream <<
"<CenterRA>" << cLocale.
toString(tiles->ra0().Hours()) <<
"</CenterRA>" <<
Qt::endl;
2956 outstream <<
"<CenterDE>" << cLocale.
toString(tiles->dec0().Degrees()) <<
"</CenterDE>" <<
Qt::endl;
2957 outstream <<
"<GridW>" << tiles->gridSize().width() <<
"</GridW>" <<
Qt::endl;
2958 outstream <<
"<GridH>" << tiles->gridSize().height() <<
"</GridH>" <<
Qt::endl;
2959 outstream <<
"<FOVW>" << cLocale.
toString(tiles->mosaicFOV().width()) <<
"</FOVW>" <<
Qt::endl;
2960 outstream <<
"<FOVH>" << cLocale.
toString(tiles->mosaicFOV().height()) <<
"</FOVH>" <<
Qt::endl;
2961 outstream <<
"<CameraFOVW>" << cLocale.
toString(tiles->cameraFOV().width()) <<
"</CameraFOVW>" <<
Qt::endl;
2962 outstream <<
"<CameraFOVH>" << cLocale.
toString(tiles->cameraFOV().height()) <<
"</CameraFOVH>" <<
Qt::endl;
2963 outstream <<
"</Mosaic>" <<
Qt::endl;
2967 for (
auto &job : moduleState()->jobs())
2972 outstream <<
"<Name>" <<
QString(entityXML(strdup(job->getName().toStdString().c_str()))) <<
"</Name>" <<
Qt::endl;
2973 outstream <<
"<Group>" <<
QString(entityXML(strdup(job->getGroup().toStdString().c_str()))) <<
"</Group>" <<
Qt::endl;
2974 outstream <<
"<Coordinates>" <<
Qt::endl;
2975 outstream <<
"<J2000RA>" << cLocale.
toString(job->getTargetCoords().ra0().Hours()) <<
"</J2000RA>" <<
Qt::endl;
2976 outstream <<
"<J2000DE>" << cLocale.
toString(job->getTargetCoords().dec0().Degrees()) <<
"</J2000DE>" <<
Qt::endl;
2977 outstream <<
"</Coordinates>" <<
Qt::endl;
2979 if (job->getFITSFile().isValid() && job->getFITSFile().isEmpty() ==
false)
2980 outstream <<
"<FITS>" << job->getFITSFile().toLocalFile() <<
"</FITS>" <<
Qt::endl;
2982 outstream <<
"<PositionAngle>" << job->getPositionAngle() <<
"</PositionAngle>" <<
Qt::endl;
2984 outstream <<
"<Sequence>" << job->getSequenceFile().toLocalFile() <<
"</Sequence>" <<
Qt::endl;
2986 if (useMosaicInfo && index < tiles->tiles().size())
2988 auto oneTile = tiles->tiles().at(index++);
2989 outstream <<
"<TileCenter>" <<
Qt::endl;
2990 outstream <<
"<X>" << cLocale.
toString(oneTile->center.x()) <<
"</X>" <<
Qt::endl;
2991 outstream <<
"<Y>" << cLocale.
toString(oneTile->center.y()) <<
"</Y>" <<
Qt::endl;
2992 outstream <<
"<Rotation>" << cLocale.
toString(oneTile->rotation) <<
"</Rotation>" <<
Qt::endl;
2993 outstream <<
"</TileCenter>" <<
Qt::endl;
2996 outstream <<
"<StartupCondition>" <<
Qt::endl;
2997 if (job->getFileStartupCondition() == START_ASAP)
2998 outstream <<
"<Condition>ASAP</Condition>" <<
Qt::endl;
2999 else if (job->getFileStartupCondition() == START_AT)
3000 outstream <<
"<Condition value='" << job->getFileStartupTime().toString(
Qt::ISODate) <<
"'>At</Condition>"
3002 outstream <<
"</StartupCondition>" <<
Qt::endl;
3004 outstream <<
"<Constraints>" <<
Qt::endl;
3005 if (job->hasMinAltitude())
3006 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinAltitude()) <<
"'>MinimumAltitude</Constraint>" <<
3008 if (job->getMinMoonSeparation() > 0)
3009 outstream <<
"<Constraint value='" << cLocale.
toString(job->getMinMoonSeparation()) <<
"'>MoonSeparation</Constraint>"
3011 if (job->getEnforceWeather())
3012 outstream <<
"<Constraint>EnforceWeather</Constraint>" <<
Qt::endl;
3013 if (job->getEnforceTwilight())
3014 outstream <<
"<Constraint>EnforceTwilight</Constraint>" <<
Qt::endl;
3015 if (job->getEnforceArtificialHorizon())
3016 outstream <<
"<Constraint>EnforceArtificialHorizon</Constraint>" <<
Qt::endl;
3017 outstream <<
"</Constraints>" <<
Qt::endl;
3019 outstream <<
"<CompletionCondition>" <<
Qt::endl;
3020 if (job->getCompletionCondition() == FINISH_SEQUENCE)
3021 outstream <<
"<Condition>Sequence</Condition>" <<
Qt::endl;
3022 else if (job->getCompletionCondition() == FINISH_REPEAT)
3023 outstream <<
"<Condition value='" << cLocale.
toString(job->getRepeatsRequired()) <<
"'>Repeat</Condition>" <<
Qt::endl;
3024 else if (job->getCompletionCondition() == FINISH_LOOP)
3025 outstream <<
"<Condition>Loop</Condition>" <<
Qt::endl;
3026 else if (job->getCompletionCondition() == FINISH_AT)
3027 outstream <<
"<Condition value='" << job->getCompletionTime().toString(
Qt::ISODate) <<
"'>At</Condition>"
3029 outstream <<
"</CompletionCondition>" <<
Qt::endl;
3031 outstream <<
"<Steps>" <<
Qt::endl;
3032 if (job->getStepPipeline() & SchedulerJob::USE_TRACK)
3033 outstream <<
"<Step>Track</Step>" <<
Qt::endl;
3034 if (job->getStepPipeline() & SchedulerJob::USE_FOCUS)
3035 outstream <<
"<Step>Focus</Step>" <<
Qt::endl;
3036 if (job->getStepPipeline() & SchedulerJob::USE_ALIGN)
3037 outstream <<
"<Step>Align</Step>" <<
Qt::endl;
3038 if (job->getStepPipeline() & SchedulerJob::USE_GUIDE)
3039 outstream <<
"<Step>Guide</Step>" <<
Qt::endl;
3040 outstream <<
"</Steps>" <<
Qt::endl;
3045 outstream <<
"<SchedulerAlgorithm value='" << ALGORITHM_GREEDY <<
"'/>" <<
Qt::endl;
3046 outstream <<
"<ErrorHandlingStrategy value='" << Options::errorHandlingStrategy() <<
"'>" <<
Qt::endl;
3047 if (Options::rescheduleErrors())
3048 outstream <<
"<RescheduleErrors />" <<
Qt::endl;
3049 outstream <<
"<delay>" << Options::errorHandlingStrategyDelay() <<
"</delay>" <<
Qt::endl;
3050 outstream <<
"</ErrorHandlingStrategy>" <<
Qt::endl;
3052 outstream <<
"<StartupProcedure>" <<
Qt::endl;
3053 if (moduleState()->startupScriptURL().isEmpty() ==
false)
3054 outstream <<
"<Procedure value='" << moduleState()->startupScriptURL().toString(
QUrl::PreferLocalFile) <<
3055 "'>StartupScript</Procedure>" <<
Qt::endl;
3056 if (Options::schedulerUnparkDome())
3057 outstream <<
"<Procedure>UnparkDome</Procedure>" <<
Qt::endl;
3058 if (Options::schedulerUnparkMount())
3059 outstream <<
"<Procedure>UnparkMount</Procedure>" <<
Qt::endl;
3060 if (Options::schedulerOpenDustCover())
3061 outstream <<
"<Procedure>UnparkCap</Procedure>" <<
Qt::endl;
3062 outstream <<
"</StartupProcedure>" <<
Qt::endl;
3064 outstream <<
"<ShutdownProcedure>" <<
Qt::endl;
3065 if (Options::schedulerWarmCCD())
3066 outstream <<
"<Procedure>WarmCCD</Procedure>" <<
Qt::endl;
3067 if (Options::schedulerCloseDustCover())
3068 outstream <<
"<Procedure>ParkCap</Procedure>" <<
Qt::endl;
3069 if (Options::schedulerParkMount())
3070 outstream <<
"<Procedure>ParkMount</Procedure>" <<
Qt::endl;
3071 if (Options::schedulerParkDome())
3072 outstream <<
"<Procedure>ParkDome</Procedure>" <<
Qt::endl;
3073 if (moduleState()->shutdownScriptURL().isEmpty() ==
false)
3074 outstream <<
"<Procedure value='" << moduleState()->shutdownScriptURL().toString(
QUrl::PreferLocalFile) <<
3075 "'>schedulerStartupScript</Procedure>" <<
3077 outstream <<
"</ShutdownProcedure>" <<
Qt::endl;
3079 outstream <<
"</SchedulerList>" <<
Qt::endl;
3081 appendLogText(
i18n(
"Scheduler list saved to %1", fileURL.
toLocalFile()));
3083 moduleState()->setDirty(
false);
3087void SchedulerProcess::checkAlignment(
const QVariantMap &metadata)
3090 activeJob()->getStepPipeline() & SchedulerJob::USE_ALIGN &&
3091 metadata[
"type"].toInt() == FRAME_LIGHT &&
3092 Options::alignCheckFrequency() > 0 &&
3093 moduleState()->increaseSolverIteration() >= Options::alignCheckFrequency())
3095 moduleState()->resetSolverIteration();
3097 auto filename = metadata[
"filename"].toString();
3098 auto exposure = metadata[
"exposure"].toDouble();
3100 constexpr double minSolverSeconds = 5.0;
3101 double solverTimeout = std::max(exposure - 2, minSolverSeconds);
3102 if (solverTimeout >= minSolverSeconds)
3104 auto profiles = getDefaultAlignOptionsProfiles();
3105 auto parameters = profiles.at(Options::solveOptionsProfile());
3107 parameters.search_radius = parameters.search_radius * 2;
3109 connect(m_Solver.get(), &SolverUtils::done,
this, &Ekos::SchedulerProcess::solverDone,
Qt::UniqueConnection);
3112 auto width = metadata[
"width"].toUInt();
3113 auto height = metadata[
"height"].toUInt();
3115 auto lowScale = Options::astrometryImageScaleLow();
3116 auto highScale = Options::astrometryImageScaleHigh();
3119 if (Options::astrometryImageScaleUnits() == SSolver::DEG_WIDTH)
3121 lowScale = (lowScale * 3600) / std::max(width, height);
3122 highScale = (highScale * 3600) / std::min(width, height);
3124 else if (Options::astrometryImageScaleUnits() == SSolver::ARCMIN_WIDTH)
3126 lowScale = (lowScale * 60) / std::max(width, height);
3127 highScale = (highScale * 60) / std::min(width, height);
3130 m_Solver->useScale(Options::astrometryUseImageScale(), lowScale, highScale);
3131 m_Solver->usePosition(Options::astrometryUsePosition(), activeJob()->getTargetCoords().ra().Degrees(),
3132 activeJob()->getTargetCoords().dec().Degrees());
3133 m_Solver->setHealpix(moduleState()->indexToUse(), moduleState()->healpixToUse());
3134 m_Solver->runSolver(filename);
3139void SchedulerProcess::solverDone(
bool timedOut,
bool success,
const FITSImage::Solution &solution,
double elapsedSeconds)
3141 disconnect(m_Solver.get(), &SolverUtils::done,
this, &Ekos::SchedulerProcess::solverDone);
3147 if (moduleState()->indexToUse() != -1 || moduleState()->healpixToUse() != -1)
3148 healpixString =
QString(
"Healpix %1 Index %2").
arg(moduleState()->healpixToUse()).
arg(moduleState()->indexToUse());
3150 if (timedOut || !success)
3153 moduleState()->setIndexToUse(-1);
3154 moduleState()->setHealpixToUse(-1);
3160 m_Solver->getSolutionHealpix(&index, &healpix);
3161 moduleState()->setIndexToUse(index);
3162 moduleState()->setHealpixToUse(healpix);
3166 appendLogText(
i18n(
"Solver timed out: %1s %2",
QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3168 appendLogText(
i18n(
"Solver failed: %1s %2",
QString(
"%L1").arg(elapsedSeconds, 0,
'f', 1), healpixString));
3171 const double ra = solution.ra;
3172 const double dec = solution.dec;
3174 const auto target = activeJob()->getTargetCoords();
3177 alignCoord.
setRA0(ra / 15.0);
3181 const double diffRa = (alignCoord.
ra().
deltaAngle(target.ra())).Degrees() * 3600;
3182 const double diffDec = (alignCoord.
dec().
deltaAngle(target.dec())).Degrees() * 3600;
3185 const double diffTotal = hypot(diffRa, diffDec);
3189 qCDebug(KSTARS_EKOS_SCHEDULER) <<
3190 QString(
"Target Distance: %1\" Target (RA: %2 DE: %3) Current (RA: %4 DE: %5) %6 solved in %7s")
3191 .
arg(
QString(
"%L1").arg(diffTotal, 0,
'f', 0),
3192 target.ra().toDMSString(),
3193 target.dec().toDMSString(),
3197 QString(
"%L1").arg(elapsedSeconds, 0,
'f', 2));
3198 emit targetDistance(diffTotal);
3201 if (diffTotal / 60 > Options::alignCheckThreshold())
3203 appendLogText(
i18n(
"Captured frame is %1 arcminutes away from target, re-aligning...",
QString::number(diffTotal / 60.0,
3205 stopCurrentJobAction();
3211bool SchedulerProcess::appendEkosScheduleList(
const QString &fileURL)
3213 SchedulerState const old_state = moduleState()->schedulerState();
3214 moduleState()->setSchedulerState(SCHEDULER_LOADING);
3221 QString message =
i18n(
"Unable to open file %1", fileURL);
3222 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
3223 moduleState()->setSchedulerState(old_state);
3227 LilXML *xmlParser = newLilXML();
3228 char errmsg[MAXRBUF];
3229 XMLEle *root =
nullptr;
3230 XMLEle *ep =
nullptr;
3231 XMLEle *subEP =
nullptr;
3239 root = readXMLEle(xmlParser, c, errmsg);
3243 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
3245 const char *tag = tagXMLEle(ep);
3246 if (!strcmp(tag,
"Job"))
3248 emit addJob(SchedulerUtils::createJob(ep));
3250 else if (!strcmp(tag,
"Mosaic"))
3253 auto tiles = KStarsData::Instance()->
skyComposite()->mosaicComponent()->tiles();
3254 tiles->fromXML(fileURL);
3256 else if (!strcmp(tag,
"Profile"))
3258 moduleState()->setCurrentProfile(pcdataXMLEle(ep));
3261 else if (!strcmp(tag,
"SchedulerAlgorithm"))
3263 int algIndex = cLocale.
toInt(findXMLAttValu(ep,
"value"));
3264 if (algIndex != ALGORITHM_GREEDY)
3265 appendLogText(
i18n(
"Warning: The Classic scheduler algorithm has been retired. Switching you to the Greedy algorithm."));
3267 else if (!strcmp(tag,
"ErrorHandlingStrategy"))
3272 subEP = findXMLEle(ep,
"delay");
3275 Options::setErrorHandlingStrategyDelay(cLocale.
toInt(pcdataXMLEle(subEP)));
3277 subEP = findXMLEle(ep,
"RescheduleErrors");
3278 Options::setRescheduleErrors(subEP !=
nullptr);
3280 else if (!strcmp(tag,
"StartupProcedure"))
3283 Options::setSchedulerUnparkDome(
false);
3284 Options::setSchedulerUnparkMount(
false);
3285 Options::setSchedulerOpenDustCover(
false);
3287 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3289 const char *proc = pcdataXMLEle(procedure);
3291 if (!strcmp(proc,
"StartupScript"))
3293 moduleState()->setStartupScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3295 else if (!strcmp(proc,
"UnparkDome"))
3296 Options::setSchedulerUnparkDome(
true);
3297 else if (!strcmp(proc,
"UnparkMount"))
3298 Options::setSchedulerUnparkMount(
true);
3299 else if (!strcmp(proc,
"UnparkCap"))
3300 Options::setSchedulerOpenDustCover(
true);
3303 else if (!strcmp(tag,
"ShutdownProcedure"))
3306 Options::setSchedulerWarmCCD(
false);
3307 Options::setSchedulerParkDome(
false);
3308 Options::setSchedulerParkMount(
false);
3309 Options::setSchedulerCloseDustCover(
false);
3311 for (procedure = nextXMLEle(ep, 1); procedure !=
nullptr; procedure = nextXMLEle(ep, 0))
3313 const char *proc = pcdataXMLEle(procedure);
3315 if (!strcmp(proc,
"ShutdownScript"))
3317 moduleState()->setShutdownScriptURL(
QUrl::fromUserInput(findXMLAttValu(procedure,
"value")));
3319 else if (!strcmp(proc,
"WarmCCD"))
3320 Options::setSchedulerWarmCCD(
true);
3321 else if (!strcmp(proc,
"ParkDome"))
3322 Options::setSchedulerParkDome(
true);
3323 else if (!strcmp(proc,
"ParkMount"))
3324 Options::setSchedulerParkMount(
true);
3325 else if (!strcmp(proc,
"ParkCap"))
3326 Options::setSchedulerCloseDustCover(
true);
3331 emit syncGUIToGeneralSettings();
3335 appendLogText(
QString(errmsg));
3336 delLilXML(xmlParser);
3337 moduleState()->setSchedulerState(old_state);
3342 moduleState()->setDirty(
false);
3343 delLilXML(xmlParser);
3344 emit updateSchedulerURL(fileURL);
3346 moduleState()->setSchedulerState(old_state);
3350void SchedulerProcess::appendLogText(
const QString &logentry)
3353 int const max_log_count = 2000;
3354 if (moduleState()->logText().size() > max_log_count)
3355 moduleState()->logText().removeLast();
3357 moduleState()->logText().prepend(
i18nc(
"log entry; %1 is the date, %2 is the text",
"%1 %2",
3358 SchedulerModuleState::getLocalTime().toString(
"yyyy-MM-ddThh:mm:ss"), logentry));
3360 qCInfo(KSTARS_EKOS_SCHEDULER) << logentry;
3362 emit newLog(logentry);
3365void SchedulerProcess::clearLog()
3367 moduleState()->logText().clear();
3371void SchedulerProcess::setAlignStatus(
AlignState status)
3373 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3376 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Align State" << Ekos::getAlignStatusString(status);
3379 if (SCHEDJOB_SCHEDULED == activeJob()->getState())
3381 QDateTime const now = SchedulerModuleState::getLocalTime();
3382 if (now < activeJob()->getStartupTime())
3386 if (activeJob()->getStage() == SCHEDSTAGE_ALIGNING)
3391 appendLogText(
i18n(
"Job '%1' alignment is complete.", activeJob()->getName()));
3392 moduleState()->resetAlignFailureCount();
3394 moduleState()->updateJobStage(SCHEDSTAGE_ALIGN_COMPLETE);
3397 if (activeJob()->getFITSFile().isEmpty() ==
false)
3403 activeJob()->setTargetCoords(
dms(values[0] * 15.0),
dms(values[1]), KStarsData::Instance()->ut().djd());
3410 appendLogText(
i18n(
"Warning: job '%1' alignment failed.", activeJob()->getName()));
3412 if (moduleState()->increaseAlignFailureCount())
3414 if (Options::resetMountModelOnAlignFail() && moduleState()->maxFailureAttempts() - 1 < moduleState()->alignFailureCount())
3416 appendLogText(
i18n(
"Warning: job '%1' forcing mount model reset after failing alignment #%2.", activeJob()->getName(),
3417 moduleState()->alignFailureCount()));
3420 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3425 appendLogText(
i18n(
"Warning: job '%1' alignment procedure failed, marking aborted.", activeJob()->getName()));
3426 activeJob()->setState(SCHEDJOB_ABORTED);
3434void SchedulerProcess::setGuideStatus(GuideState status)
3436 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3439 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Guide State" << Ekos::getGuideStatusString(status);
3442 if (SCHEDJOB_SCHEDULED == activeJob()->getState())
3444 QDateTime const now = SchedulerModuleState::getLocalTime();
3445 if (now < activeJob()->getStartupTime())
3449 if (activeJob()->getStage() == SCHEDSTAGE_GUIDING)
3451 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Calibration & Guide stage...";
3454 if (status == Ekos::GUIDE_GUIDING)
3456 appendLogText(
i18n(
"Job '%1' guiding is in progress.", activeJob()->getName()));
3457 moduleState()->resetGuideFailureCount();
3459 moduleState()->cancelGuidingTimer();
3461 moduleState()->updateJobStage(SCHEDSTAGE_GUIDING_COMPLETE);
3464 else if (status == Ekos::GUIDE_CALIBRATION_ERROR ||
3465 status == Ekos::GUIDE_ABORTED)
3467 if (status == Ekos::GUIDE_ABORTED)
3468 appendLogText(
i18n(
"Warning: job '%1' guiding failed.", activeJob()->getName()));
3470 appendLogText(
i18n(
"Warning: job '%1' calibration failed.", activeJob()->getName()));
3476 if (moduleState()->isGuidingTimerActive())
3479 if (moduleState()->increaseGuideFailureCount())
3481 if (status == Ekos::GUIDE_CALIBRATION_ERROR &&
3482 Options::realignAfterCalibrationFailure())
3484 appendLogText(
i18n(
"Restarting %1 alignment procedure...", activeJob()->getName()));
3489 appendLogText(
i18n(
"Job '%1' is guiding, guiding procedure will be restarted in %2 seconds.", activeJob()->getName(),
3490 (RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount()) / 1000));
3491 moduleState()->startGuidingTimer(RESTART_GUIDING_DELAY_MS * moduleState()->guideFailureCount());
3496 appendLogText(
i18n(
"Warning: job '%1' guiding procedure failed, marking aborted.", activeJob()->getName()));
3497 activeJob()->setState(SCHEDJOB_ABORTED);
3505void SchedulerProcess::setCaptureStatus(CaptureState status,
const QString &devicename)
3507 if (activeJob() ==
nullptr || devicename != moduleState()->mainCameraDeviceName())
3510 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Capture State" << Ekos::getCaptureStatusString(status);
3513 if (SCHEDJOB_SCHEDULED == activeJob()->getState())
3515 QDateTime const now = SchedulerModuleState::getLocalTime();
3516 if (now < activeJob()->getStartupTime())
3520 if (activeJob()->getStage() == SCHEDSTAGE_CAPTURING)
3529 const SkyPoint targetCoords = activeJob()->getTargetCoords();
3532 alignInterface()->callWithArgumentList(
QDBus::AutoDetect,
"setTargetCoords", targetArgs);
3536 appendLogText(
i18n(
"Warning: job '%1' failed to capture target.", activeJob()->getName()));
3538 if (moduleState()->increaseCaptureFailureCount())
3541 if (activeJob()->getStepPipeline() & SchedulerJob::USE_GUIDE)
3544 Ekos::GuideState gStatus = getGuidingStatus();
3545 if (gStatus == Ekos::GUIDE_ABORTED ||
3546 gStatus == Ekos::GUIDE_CALIBRATION_ERROR ||
3547 gStatus == GUIDE_DITHERING_ERROR)
3549 appendLogText(
i18n(
"Job '%1' is capturing, is restarting its guiding procedure (attempt #%2 of %3).",
3550 activeJob()->getName(),
3551 moduleState()->captureFailureCount(), moduleState()->maxFailureAttempts()));
3558 appendLogText(
i18n(
"Warning: job '%1' failed its capture procedure, restarting capture.", activeJob()->getName()));
3564 appendLogText(
i18n(
"Warning: job '%1' failed its capture procedure, marking aborted.", activeJob()->getName()));
3565 activeJob()->setState(SCHEDJOB_ABORTED);
3572 KSNotification::event(
QLatin1String(
"EkosScheduledImagingFinished"),
3573 i18n(
"Ekos job (%1) - Capture finished", activeJob()->getName()), KSNotification::Scheduler);
3575 activeJob()->setState(SCHEDJOB_COMPLETE);
3582 if (Options::rememberJobProgress())
3584 updateCompletedJobsCount(
true);
3586 for (
const auto &job : moduleState()->jobs())
3587 SchedulerUtils::estimateJobTime(job, moduleState()->capturedFramesCount(), this);
3591 activeJob()->setCompletedCount(activeJob()->getCompletedCount() + 1);
3593 moduleState()->resetCaptureFailureCount();
3598void SchedulerProcess::setFocusStatus(FocusState status)
3600 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3603 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Focus State" << Ekos::getFocusStatusString(status);
3606 if (SCHEDJOB_SCHEDULED == activeJob()->getState())
3608 QDateTime const now = SchedulerModuleState::getLocalTime();
3609 if (now < activeJob()->getStartupTime())
3613 if (activeJob()->getStage() == SCHEDSTAGE_FOCUSING)
3616 if (status == Ekos::FOCUS_COMPLETE)
3618 appendLogText(
i18n(
"Job '%1' focusing is complete.", activeJob()->getName()));
3620 moduleState()->setAutofocusCompleted(
true);
3622 moduleState()->updateJobStage(SCHEDSTAGE_FOCUS_COMPLETE);
3626 else if (status == Ekos::FOCUS_FAILED || status == Ekos::FOCUS_ABORTED)
3628 appendLogText(
i18n(
"Warning: job '%1' focusing failed.", activeJob()->getName()));
3630 if (moduleState()->increaseFocusFailureCount())
3632 appendLogText(
i18n(
"Job '%1' is restarting its focusing procedure.", activeJob()->getName()));
3636 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"startFocusing on 6883";
3641 appendLogText(
i18n(
"Warning: job '%1' focusing procedure failed, marking aborted.", activeJob()->getName()));
3642 activeJob()->setState(SCHEDJOB_ABORTED);
3650void SchedulerProcess::setMountStatus(ISD::Mount::Status status)
3652 if (moduleState()->schedulerState() == SCHEDULER_PAUSED || activeJob() ==
nullptr)
3655 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount State changed to" <<
status;
3658 if (SCHEDJOB_SCHEDULED == activeJob()->getState())
3659 if (
static_cast<QDateTime const
>(SchedulerModuleState::getLocalTime()) < activeJob()->getStartupTime())
3662 switch (activeJob()->getStage())
3664 case SCHEDSTAGE_SLEWING:
3666 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Slewing stage...";
3668 if (status == ISD::Mount::MOUNT_TRACKING)
3670 appendLogText(
i18n(
"Job '%1' slew is complete.", activeJob()->getName()));
3671 moduleState()->updateJobStage(SCHEDSTAGE_SLEW_COMPLETE);
3674 else if (status == ISD::Mount::MOUNT_ERROR)
3676 appendLogText(
i18n(
"Warning: job '%1' slew failed, marking terminated due to errors.", activeJob()->getName()));
3677 activeJob()->setState(SCHEDJOB_ERROR);
3680 else if (status == ISD::Mount::MOUNT_IDLE)
3682 appendLogText(
i18n(
"Warning: job '%1' found not slewing, restarting.", activeJob()->getName()));
3683 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
3689 case SCHEDSTAGE_RESLEWING:
3691 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Re-slewing stage...";
3693 if (status == ISD::Mount::MOUNT_TRACKING)
3695 appendLogText(
i18n(
"Job '%1' repositioning is complete.", activeJob()->getName()));
3696 moduleState()->updateJobStage(SCHEDSTAGE_RESLEWING_COMPLETE);
3699 else if (status == ISD::Mount::MOUNT_ERROR)
3701 appendLogText(
i18n(
"Warning: job '%1' repositioning failed, marking terminated due to errors.", activeJob()->getName()));
3702 activeJob()->setState(SCHEDJOB_ERROR);
3705 else if (status == ISD::Mount::MOUNT_IDLE)
3707 appendLogText(
i18n(
"Warning: job '%1' found not repositioning, restarting.", activeJob()->getName()));
3708 moduleState()->updateJobStage(SCHEDSTAGE_IDLE);
3716 case SCHEDSTAGE_FOCUSING:
3717 case SCHEDSTAGE_ALIGNING:
3718 case SCHEDSTAGE_GUIDING:
3719 if (status == ISD::Mount::MOUNT_PARKED)
3721 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
3728 case SCHEDSTAGE_CAPTURING:
3729 if (status == ISD::Mount::MOUNT_PARKED && activeJob() && activeJob()->getLightFramesRequired()
3730 && activeJob()->getCalibrationMountPark() ==
false)
3732 appendLogText(
i18n(
"Warning: Mount is parked while scheduler for job '%1' is active. Aborting.", activeJob()->getName()));
3742void SchedulerProcess::setWeatherStatus(ISD::Weather::Status status)
3744 ISD::Weather::Status newStatus =
status;
3746 if (newStatus == moduleState()->weatherStatus())
3749 moduleState()->setWeatherStatus(newStatus);
3753 if (activeJob() && activeJob()->getEnforceWeather() && moduleState()->weatherStatus() == ISD::Weather::WEATHER_ALERT
3754 && moduleState()->schedulerState() != Ekos::SCHEDULER_IDLE && moduleState()->schedulerState() != Ekos::SCHEDULER_SHUTDOWN)
3756 appendLogText(
i18n(
"Starting shutdown procedure due to severe weather."));
3759 activeJob()->setState(SCHEDJOB_ABORTED);
3760 stopCurrentJobAction();
3762 checkShutdownState();
3765 emit newWeatherStatus(status);
3768void SchedulerProcess::checkStartupProcedure()
3770 if (checkStartupState() ==
false)
3774void SchedulerProcess::checkShutdownProcedure()
3776 if (checkShutdownState())
3779 if (moduleState()->shutdownState() == SHUTDOWN_COMPLETE)
3781 appendLogText(
i18n(
"Manual shutdown procedure completed successfully."));
3783 if (Options::stopEkosAfterShutdown())
3786 else if (moduleState()->shutdownState() == SHUTDOWN_ERROR)
3787 appendLogText(
i18n(
"Manual shutdown procedure terminated due to errors."));
3789 moduleState()->setShutdownState(SHUTDOWN_IDLE);
3798void SchedulerProcess::parkCap()
3800 if (capInterface().isNull())
3802 appendLogText(
i18n(
"Dust cover park requested but no dust covers detected."));
3803 moduleState()->setShutdownState(SHUTDOWN_ERROR);
3807 QVariant parkingStatus = capInterface()->property(
"parkStatus");
3808 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
3810 if (parkingStatus.
isValid() ==
false)
3812 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
3813 mountInterface()->lastError().
type());
3814 if (!manageConnectionLoss())
3815 parkingStatus = ISD::PARK_ERROR;
3818 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
3820 if (status != ISD::PARK_PARKED)
3822 moduleState()->setShutdownState(SHUTDOWN_PARKING_CAP);
3823 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking dust cap...";
3825 appendLogText(
i18n(
"Parking Cap..."));
3827 moduleState()->startCurrentOperationTimer();
3831 appendLogText(
i18n(
"Cap already parked."));
3832 moduleState()->setShutdownState(SHUTDOWN_PARK_MOUNT);
3836void SchedulerProcess::unParkCap()
3838 if (capInterface().isNull())
3840 appendLogText(
i18n(
"Dust cover unpark requested but no dust covers detected."));
3841 moduleState()->setStartupState(STARTUP_ERROR);
3845 QVariant parkingStatus = capInterface()->property(
"parkStatus");
3846 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Cap parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
3848 if (parkingStatus.
isValid() ==
false)
3850 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: cap parkStatus request received DBUS error: %1").
arg(
3851 mountInterface()->lastError().
type());
3852 if (!manageConnectionLoss())
3853 parkingStatus = ISD::PARK_ERROR;
3856 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
3858 if (status != ISD::PARK_UNPARKED)
3860 moduleState()->setStartupState(STARTUP_UNPARKING_CAP);
3862 appendLogText(
i18n(
"Unparking cap..."));
3864 moduleState()->startCurrentOperationTimer();
3868 appendLogText(
i18n(
"Cap already unparked."));
3869 moduleState()->setStartupState(STARTUP_COMPLETE);
3873void SchedulerProcess::parkMount()
3875 if (mountInterface().isNull())
3877 appendLogText(
i18n(
"Mount park requested but no mounts detected."));
3878 moduleState()->setShutdownState(SHUTDOWN_ERROR);
3882 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
3883 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
3885 if (parkingStatus.
isValid() ==
false)
3887 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
3888 mountInterface()->lastError().
type());
3889 if (!manageConnectionLoss())
3890 moduleState()->setParkWaitState(PARKWAIT_ERROR);
3893 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
3897 case ISD::PARK_PARKED:
3898 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
3899 moduleState()->setShutdownState(SHUTDOWN_PARK_DOME);
3901 moduleState()->setParkWaitState(PARKWAIT_PARKED);
3902 appendLogText(
i18n(
"Mount already parked."));
3905 case ISD::PARK_UNPARKING:
3911 case ISD::PARK_ERROR:
3912 case ISD::PARK_UNKNOWN:
3913 case ISD::PARK_UNPARKED:
3915 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Parking mount...";
3920 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount park request received DBUS error: %1").
arg(
3922 if (!manageConnectionLoss())
3923 moduleState()->setParkWaitState(PARKWAIT_ERROR);
3925 else moduleState()->startCurrentOperationTimer();
3929 case ISD::PARK_PARKING:
3931 if (moduleState()->shutdownState() == SHUTDOWN_PARK_MOUNT)
3932 moduleState()->setShutdownState(SHUTDOWN_PARKING_MOUNT);
3934 moduleState()->setParkWaitState(PARKWAIT_PARKING);
3935 appendLogText(
i18n(
"Parking mount in progress..."));
3945void SchedulerProcess::unParkMount()
3947 if (mountInterface().isNull())
3949 appendLogText(
i18n(
"Mount unpark requested but no mounts detected."));
3950 moduleState()->setStartupState(STARTUP_ERROR);
3954 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
3955 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
3957 if (parkingStatus.
isValid() ==
false)
3959 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parkStatus request received DBUS error: %1").
arg(
3960 mountInterface()->lastError().
type());
3961 if (!manageConnectionLoss())
3962 moduleState()->setParkWaitState(PARKWAIT_ERROR);
3965 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
3970 case ISD::PARK_UNPARKED:
3971 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
3972 moduleState()->setStartupState(STARTUP_UNPARK_CAP);
3974 moduleState()->setParkWaitState(PARKWAIT_UNPARKED);
3975 appendLogText(
i18n(
"Mount already unparked."));
3979 case ISD::PARK_PARKING:
3985 case ISD::PARK_ERROR:
3986 case ISD::PARK_UNKNOWN:
3987 case ISD::PARK_PARKED:
3993 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount unpark request received DBUS error: %1").
arg(
3995 if (!manageConnectionLoss())
3996 moduleState()->setParkWaitState(PARKWAIT_ERROR);
3998 else moduleState()->startCurrentOperationTimer();
4003 case ISD::PARK_UNPARKING:
4004 if (moduleState()->startupState() == STARTUP_UNPARK_MOUNT)
4005 moduleState()->setStartupState(STARTUP_UNPARKING_MOUNT);
4007 moduleState()->setParkWaitState(PARKWAIT_UNPARKING);
4008 qCInfo(KSTARS_EKOS_SCHEDULER) <<
"Unparking mount in progress...";
4017bool SchedulerProcess::isMountParked()
4019 if (mountInterface().isNull())
4023 QVariant canPark = mountInterface()->property(
"canPark");
4024 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount can park:" << (!canPark.
isValid() ?
"invalid" : (canPark.
toBool() ?
"T" :
"F"));
4026 if (canPark.
isValid() ==
false)
4028 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount canPark request received DBUS error: %1").
arg(
4029 mountInterface()->lastError().type());
4030 manageConnectionLoss();
4033 else if (canPark.
toBool() ==
true)
4037 QVariant parkingStatus = mountInterface()->property(
"parkStatus");
4038 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Mount parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4040 if (parkingStatus.
isValid() ==
false)
4042 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: mount parking status property is invalid %1.").
arg(
4043 mountInterface()->lastError().type());
4044 manageConnectionLoss();
4049 switch (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()))
4053 case ISD::PARK_PARKED:
4068void SchedulerProcess::parkDome()
4071 if (domeInterface().isNull())
4073 appendLogText(
i18n(
"Dome park requested but no domes detected."));
4074 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4080 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4081 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4083 if (parkingStatus.
isValid() ==
false)
4085 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4086 mountInterface()->lastError().type());
4087 if (!manageConnectionLoss())
4088 parkingStatus = ISD::PARK_ERROR;
4091 ISD::ParkStatus
status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4092 if (status != ISD::PARK_PARKED)
4094 moduleState()->setShutdownState(SHUTDOWN_PARKING_DOME);
4096 appendLogText(
i18n(
"Parking dome..."));
4098 moduleState()->startCurrentOperationTimer();
4102 appendLogText(
i18n(
"Dome already parked."));
4103 moduleState()->setShutdownState(SHUTDOWN_SCRIPT);
4107void SchedulerProcess::unParkDome()
4110 if (domeInterface().isNull())
4112 appendLogText(
i18n(
"Dome unpark requested but no domes detected."));
4113 moduleState()->setStartupState(STARTUP_ERROR);
4117 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4118 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4120 if (parkingStatus.
isValid() ==
false)
4122 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4123 mountInterface()->lastError().
type());
4124 if (!manageConnectionLoss())
4125 parkingStatus = ISD::PARK_ERROR;
4128 if (
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt()) != ISD::PARK_UNPARKED)
4130 moduleState()->setStartupState(STARTUP_UNPARKING_DOME);
4132 appendLogText(
i18n(
"Unparking dome..."));
4134 moduleState()->startCurrentOperationTimer();
4138 appendLogText(
i18n(
"Dome already unparked."));
4139 moduleState()->setStartupState(STARTUP_UNPARK_MOUNT);
4143GuideState SchedulerProcess::getGuidingStatus()
4145 QVariant guideStatus = guideInterface()->property(
"status");
4146 Ekos::GuideState gStatus =
static_cast<Ekos::GuideState
>(guideStatus.
toInt());
4151const QString &SchedulerProcess::profile()
const
4153 return moduleState()->currentProfile();
4156void SchedulerProcess::setProfile(
const QString &newProfile)
4158 moduleState()->setCurrentProfile(newProfile);
4161QString SchedulerProcess::currentJobName()
4163 auto job = moduleState()->activeJob();
4164 return ( job !=
nullptr ? job->getName() :
QString() );
4167QString SchedulerProcess::currentJobJson()
4169 auto job = moduleState()->activeJob();
4170 if( job !=
nullptr )
4180QString SchedulerProcess::jsonJobs()
4187 return moduleState()->logText();
4190bool SchedulerProcess::isDomeParked()
4192 if (domeInterface().isNull())
4195 QVariant parkingStatus = domeInterface()->property(
"parkStatus");
4196 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Dome parking status" << (!parkingStatus.
isValid() ? -1 : parkingStatus.
toInt());
4198 if (parkingStatus.
isValid() ==
false)
4200 qCCritical(KSTARS_EKOS_SCHEDULER) <<
QString(
"Warning: dome parkStatus request received DBUS error: %1").
arg(
4201 mountInterface()->lastError().type());
4202 if (!manageConnectionLoss())
4203 parkingStatus = ISD::PARK_ERROR;
4206 ISD::ParkStatus status =
static_cast<ISD::ParkStatus
>(parkingStatus.
toInt());
4208 return status == ISD::PARK_PARKED;
4211void SchedulerProcess::simClockScaleChanged(
float newScale)
4213 if (moduleState()->currentlySleeping())
4216 (moduleState()->iterationTimer().remainingTime())
4217 * KStarsData::Instance()->clock()->scale()
4219 appendLogText(
i18n(
"Sleeping for %1 on simulation clock update until next observation job is ready...",
4220 remainingTimeMs.
toString(
"hh:mm:ss")));
4221 moduleState()->iterationTimer().stop();
4226void SchedulerProcess::simClockTimeChanged()
4228 moduleState()->calculateDawnDusk();
4231 if (SCHEDULER_RUNNING != moduleState()->schedulerState())
4232 startJobEvaluation();
4235void SchedulerProcess::setINDICommunicationStatus(CommunicationStatus status)
4237 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler INDI status is" <<
status;
4239 moduleState()->setIndiCommunicationStatus(status);
4242void SchedulerProcess::setEkosCommunicationStatus(CommunicationStatus status)
4244 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Scheduler Ekos status is" <<
status;
4246 moduleState()->setEkosCommunicationStatus(status);
4251void SchedulerProcess::checkInterfaceReady(
QDBusInterface * iface)
4253 if (iface == mountInterface())
4255 if (mountInterface()->property(
"canPark").
isValid())
4256 moduleState()->setMountReady(
true);
4258 else if (iface == capInterface())
4260 if (capInterface()->property(
"canPark").
isValid())
4261 moduleState()->setCapReady(
true);
4263 else if (iface == observatoryInterface())
4267 setWeatherStatus(
static_cast<ISD::Weather::Status
>(
status.toInt()));
4269 else if (iface == weatherInterface())
4273 setWeatherStatus(
static_cast<ISD::Weather::Status
>(
status.toInt()));
4275 else if (iface == domeInterface())
4277 if (domeInterface()->property(
"canPark").
isValid())
4278 moduleState()->setDomeReady(
true);
4280 else if (iface == captureInterface())
4282 if (captureInterface()->property(
"coolerControl").
isValid())
4283 moduleState()->setCaptureReady(
true);
4286 emit interfaceReady(iface);
4289void SchedulerProcess::registerNewModule(
const QString &name)
4291 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Registering new Module (" <<
name <<
")";
4293 if (name ==
"Focus")
4295 delete focusInterface();
4296 setFocusInterface(
new QDBusInterface(kstarsInterfaceString, focusPathString, focusInterfaceString,
4298 connect(focusInterface(), SIGNAL(newStatus(Ekos::FocusState)),
this,
4301 else if (name ==
"Capture")
4303 delete captureInterface();
4304 setCaptureInterface(
new QDBusInterface(kstarsInterfaceString, capturePathString, captureInterfaceString,
4307 connect(captureInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4311 connect(captureInterface(), SIGNAL(captureComplete(QVariantMap)),
this, SLOT(checkAlignment(QVariantMap)),
4313 checkInterfaceReady(captureInterface());
4315 else if (name ==
"Mount")
4317 delete mountInterface();
4318 setMountInterface(
new QDBusInterface(kstarsInterfaceString, mountPathString, mountInterfaceString,
4321 connect(mountInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4322 connect(mountInterface(), SIGNAL(newStatus(ISD::Mount::Status)),
this, SLOT(setMountStatus(ISD::Mount::Status)),
4325 checkInterfaceReady(mountInterface());
4327 else if (name ==
"Align")
4329 delete alignInterface();
4330 setAlignInterface(
new QDBusInterface(kstarsInterfaceString, alignPathString, alignInterfaceString,
4335 else if (name ==
"Guide")
4337 delete guideInterface();
4338 setGuideInterface(
new QDBusInterface(kstarsInterfaceString, guidePathString, guideInterfaceString,
4340 connect(guideInterface(), SIGNAL(newStatus(Ekos::GuideState)),
this,
4343 else if (name ==
"Observatory")
4345 delete observatoryInterface();
4346 setObservatoryInterface(
new QDBusInterface(kstarsInterfaceString, observatoryPathString, observatoryInterfaceString,
4348 connect(observatoryInterface(), SIGNAL(newStatus(ISD::Weather::Status)),
this,
4350 checkInterfaceReady(observatoryInterface());
4354void SchedulerProcess::registerNewDevice(
const QString &name,
int interface)
4358 if (interface & INDI::BaseDevice::DOME_INTERFACE)
4361 dbusargs.
append(INDI::BaseDevice::DOME_INTERFACE);
4367 setDomePathString(paths.value().last());
4368 delete domeInterface();
4369 setDomeInterface(
new QDBusInterface(kstarsInterfaceString, domePathString,
4370 domeInterfaceString,
4372 connect(domeInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4373 checkInterfaceReady(domeInterface());
4398 if (interface & INDI::BaseDevice::DUSTCAP_INTERFACE)
4401 dbusargs.
append(INDI::BaseDevice::DUSTCAP_INTERFACE);
4407 setDustCapPathString(paths.value().last());
4408 delete capInterface();
4409 setCapInterface(
new QDBusInterface(kstarsInterfaceString, dustCapPathString,
4410 dustCapInterfaceString,
4412 connect(capInterface(), SIGNAL(ready()),
this, SLOT(syncProperties()));
4413 checkInterfaceReady(capInterface());
4418bool SchedulerProcess::createJobSequence(XMLEle * root,
const QString &prefix,
const QString &outputDir)
4420 XMLEle *ep =
nullptr;
4421 XMLEle *subEP =
nullptr;
4423 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
4425 if (!strcmp(tagXMLEle(ep),
"Job"))
4427 for (subEP = nextXMLEle(ep, 1); subEP !=
nullptr; subEP = nextXMLEle(ep, 0))
4429 if (!strcmp(tagXMLEle(subEP),
"Prefix"))
4431 XMLEle *rawPrefix = findXMLEle(subEP,
"RawPrefix");
4437 else if (!strcmp(tagXMLEle(subEP),
"FITSDirectory"))
4450 if (outputFile ==
nullptr)
4452 QString message =
i18n(
"Unable to write to file %1", filename);
4453 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
4457 fprintf(outputFile,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
4458 prXMLEle(outputFile, root, 0);
4465XMLEle *SchedulerProcess::getSequenceJobRoot(
const QString &filename)
const
4472 KSNotification::sorry(
i18n(
"Unable to open file %1", sFile.
fileName()),
4473 i18n(
"Could Not Open File"));
4477 LilXML *xmlParser = newLilXML();
4478 char errmsg[MAXRBUF];
4479 XMLEle *root =
nullptr;
4484 root = readXMLEle(xmlParser, c, errmsg);
4490 delLilXML(xmlParser);
4495void SchedulerProcess::checkProcessExit(
int exitCode)
4497 scriptProcess().disconnect();
4501 if (moduleState()->startupState() == STARTUP_SCRIPT)
4502 moduleState()->setStartupState(STARTUP_UNPARK_DOME);
4503 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4504 moduleState()->setShutdownState(SHUTDOWN_COMPLETE);
4509 if (moduleState()->startupState() == STARTUP_SCRIPT)
4511 appendLogText(
i18n(
"Startup script failed, aborting..."));
4512 moduleState()->setStartupState(STARTUP_ERROR);
4514 else if (moduleState()->shutdownState() == SHUTDOWN_SCRIPT_RUNNING)
4516 appendLogText(
i18n(
"Shutdown script failed, aborting..."));
4517 moduleState()->setShutdownState(SHUTDOWN_ERROR);
4522void SchedulerProcess::readProcessOutput()
4524 appendLogText(scriptProcess().readAllStandardOutput().simplified());
4527bool SchedulerProcess::canCountCaptures(
const SchedulerJob &job)
4530 bool hasAutoFocus =
false;
4531 SchedulerJob tempJob = job;
4532 if (SchedulerUtils::loadSequenceQueue(tempJob.getSequenceFile().toLocalFile(), &tempJob, seqjobs, hasAutoFocus,
4538 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_LOCAL)
4544void SchedulerProcess::updateCompletedJobsCount(
bool forced)
4552 forced |= std::any_of(moduleState()->jobs().begin(),
4553 moduleState()->jobs().end(), [](SchedulerJob * oneJob) ->
bool
4560 moduleState()->capturedFramesCount().clear();
4563 for (SchedulerJob *oneJob : moduleState()->jobs())
4570 bool hasAutoFocus =
false;
4574 if (SchedulerUtils::loadSequenceQueue(oneJob->getSequenceFile().
toLocalFile(), oneJob, seqjobs, hasAutoFocus,
4577 appendLogText(
i18n(
"Warning: job '%1' has inaccessible sequence '%2', marking invalid.", oneJob->getName(),
4583 oneJob->clearProgress();
4585 for (SequenceJob *oneSeqJob : seqjobs)
4589 if (oneSeqJob->getUploadMode() == ISD::Camera::UPLOAD_LOCAL)
4593 QString const signature = oneSeqJob->getSignature();
4601 const int count = newFramesCount.
constFind(signature).value();
4602 newJobFramesCount[signature] = count;
4603 oneJob->addProgress(count, oneSeqJob);
4610 moduleState()->capturedFramesCount().constFind(signature);
4612 if (moduleState()->capturedFramesCount().constEnd() != earlierRunIterator)
4614 count = earlierRunIterator.value();
4617 count = PlaceholderPath::getCompletedFiles(signature);
4619 newFramesCount[signature] = count;
4620 newJobFramesCount[signature] = count;
4621 oneJob->addProgress(count, oneSeqJob);
4625 SchedulerUtils::updateLightFramesRequired(oneJob, seqjobs, newFramesCount);
4628 moduleState()->setCapturedFramesCount(newFramesCount);
4631 qCDebug(KSTARS_EKOS_SCHEDULER) <<
"Frame map summary:";
4633 for (; it != moduleState()->capturedFramesCount().constEnd(); it++)
4634 qCDebug(KSTARS_EKOS_SCHEDULER) <<
" " << it.key() <<
':' << it.value();
4638SchedulerJob *SchedulerProcess::activeJob()
4640 return moduleState()->activeJob();
4643void SchedulerProcess::printStates(
const QString &label)
4645 qCDebug(KSTARS_EKOS_SCHEDULER) <<
4646 QString(
"%1 %2 %3%4 %5 %6 %7 %8 %9\n")
4648 .
arg(timerStr(moduleState()->timerState()))
4649 .
arg(getSchedulerStatusString(moduleState()->schedulerState()))
4650 .
arg((moduleState()->timerState() == RUN_JOBCHECK && activeJob() !=
nullptr) ?
4651 QString(
"(%1 %2)").arg(SchedulerJob::jobStatusString(activeJob()->getState()))
4652 .arg(SchedulerJob::jobStageString(activeJob()->getStage())) :
"")
4653 .arg(ekosStateString(moduleState()->ekosState()))
4654 .arg(indiStateString(moduleState()->indiState()))
4655 .arg(startupStateString(moduleState()->startupState()))
4656 .arg(shutdownStateString(moduleState()->shutdownState()))
4657 .arg(parkWaitStateString(moduleState()->parkWaitState())).toLatin1().data();
4658 foreach (
auto j, moduleState()->jobs())
4659 qCDebug(KSTARS_EKOS_SCHEDULER) <<
QString(
"job %1 %2\n").
arg(j->getName()).
arg(SchedulerJob::jobStatusString(
static KConfigDialog * exists(const QString &name)
void settingsChanged(const QString &dialogName)
SkyMapComposite * skyComposite()
This is the main window for KStars.
static KStars * Instance()
The SchedulerState class holds all attributes defining the scheduler's state.
Sequence Job is a container for the details required to capture a series of images.
void timeChanged()
The time has changed (emitted by setUTC() )
void scaleChanged(float)
The timestep has changed.
The sky coordinates of a point in the sky.
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
const CachingDms & dec() const
const CachingDms & ra0() const
const CachingDms & ra() const
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
const CachingDms & dec0() const
void setDec0(dms d)
Sets Dec0, the catalog Declination.
An angle, stored as degrees, but expressible in many ways.
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
const dms deltaAngle(dms angle) const
deltaAngle Return the shortest difference (path) between this angle and the supplied angle.
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
const double & Degrees() const
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
SchedulerJobStatus
States of a SchedulerJob.
@ SCHEDJOB_ABORTED
Job encountered a transitory issue while processing, and will be rescheduled.
@ SCHEDJOB_INVALID
Job has an incorrect configuration, and cannot proceed.
@ SCHEDJOB_ERROR
Job encountered a fatal issue while processing, and must be reset manually.
@ SCHEDJOB_COMPLETE
Job finished all required captures.
@ SCHEDJOB_EVALUATION
Job is being evaluated.
@ SCHEDJOB_SCHEDULED
Job was evaluated, and has a schedule.
@ SCHEDJOB_BUSY
Job is being processed.
@ SCHEDJOB_IDLE
Job was just created, and is not evaluated yet.
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.
QString name(GameStandardAction id)
GeoCoordinates geo(const QVariant &location)
bool isValid(QStringView ifopt)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
const char * constData() const const
QDateTime addSecs(qint64 s) const const
qint64 currentMSecsSinceEpoch()
qint64 secsTo(const QDateTime &other) const const
QString toString(QStringView format, QCalendar cal) const const
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QDBusConnection sessionBus()
void unregisterObject(const QString &path, UnregisterMode mode)
QString errorString(ErrorType error)
QString message() const const
ErrorType type() const const
QList< QVariant > arguments() const const
QString errorMessage() const const
const QDBusError & error()
bool isValid() const const
bool mkpath(const QString &dirPath) const const
bool exists() const const
virtual QString fileName() const const override
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QByteArray toJson(JsonFormat format) const const
void append(QList< T > &&value)
bool isEmpty() const const
int toInt(QStringView s, bool *ok) const const
QString toString(QDate date, FormatType format) const const
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
QList< Key > keys() const const
T value(const Key &key, const T &defaultValue) const const
bool disconnect(const QMetaObject::Connection &connection)
void finished(int exitCode, QProcess::ExitStatus exitStatus)
void readyReadStandardOutput()
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QByteArray toLatin1() 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
QString toLocalFile() const const
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const