7#include "greedyscheduler.h"
9#include <ekos_scheduler_debug.h>
13#include "schedulermodulestate.h"
15#include "ui_scheduler.h"
16#include "schedulerjob.h"
17#include "schedulerutils.h"
19#define TEST_PRINT if (false) fprintf
22constexpr int SCHEDULE_RESOLUTION_MINUTES = 2;
27GreedyScheduler::GreedyScheduler()
31void GreedyScheduler::setParams(
bool restartImmediately,
bool restartQueue,
32 bool rescheduleErrors,
int abortDelay,
33 int errorHandlingDelay)
35 setRescheduleAbortsImmediate(restartImmediately);
36 setRescheduleAbortsQueue(restartQueue);
37 setRescheduleErrors(rescheduleErrors);
38 setAbortDelaySeconds(abortDelay);
39 setErrorDelaySeconds(errorHandlingDelay);
48void GreedyScheduler::scheduleJobs(
const QList<SchedulerJob *> &jobs,
50 const QMap<QString, uint16_t> &capturedFramesCount,
59 scheduledJob =
nullptr;
62 prepareJobsForEvaluation(jobs, now, capturedFramesCount, logger);
65 const QList<SchedulerJob *> leadJobs = SchedulerUtils::filterLeadJobs(jobs);
67 scheduledJob = selectNextJob(leadJobs, now,
nullptr, SIMULATE, &when,
nullptr,
nullptr, &capturedFramesCount);
68 auto schedule = getSchedule();
69 if (logger !=
nullptr)
71 if (!schedule.empty())
76 for (
int i = schedule.size() - 1; i >= 0; i--)
77 logger->appendLogText(GreedyScheduler::jobScheduleString(schedule[i]));
78 logger->appendLogText(QString(
"Scheduler plan for the next 48 hours starting %1 (%2)s:")
85 const int numJobs = jobs.size();
87 auto now = SchedulerModuleState::getLocalTime().
addSecs(12 * 3600);
88 QDateTime soon = now.
addSecs(3600);
89 for (
int i = numJobs; i > 0; i--)
91 const auto &job = jobs[i - 1];
92 QDateTime nextEnd = job->getNextEndTime(now, SCHEDULE_RESOLUTION_MINUTES, &reason, soon);
93 logger->appendLogText(QString(
"(%1) %2: cannot run because: %3").arg(i).arg(job->getName()).arg(reason));
95 logger->appendLogText(QString(
"*****************In 12 Hours******************"));
96 now = SchedulerModuleState::getLocalTime();
98 for (
int i = numJobs; i > 0; i--)
100 const auto &job = jobs[i - 1];
101 QDateTime nextEnd = job->getNextEndTime(now, SCHEDULE_RESOLUTION_MINUTES, &reason, soon);
102 logger->appendLogText(QString(
"(%1) %2: cannot run because: %3").arg(i).arg(job->getName()).arg(reason));
104 logger->appendLogText(QString(
"******************** Now ********************"));
105 logger->appendLogText(QString(
"To debug, set the time to a time when you believe a job should be runnable."));
107 logger->appendLogText(QString(
"Scheduler: Sorry, no jobs are runnable for the next 3 days."));
110 if (scheduledJob !=
nullptr)
112 qCDebug(KSTARS_EKOS_SCHEDULER)
113 << QString(
"Greedy Scheduler scheduling next job %1 at %2")
114 .arg(scheduledJob->getName(), when.
toString(
"hh:mm"));
116 scheduledJob->setStartupTime(when);
119 for (
auto job : jobs)
126bool GreedyScheduler::checkJob(
const QList<SchedulerJob *> &jobs,
127 const QDateTime &now,
128 const SchedulerJob *
const currentJob)
131 if (currentJob && currentJob->getStateTime().secsTo(now) < 5)
138 SimulationType simType = SIMULATE_EACH_JOB_ONCE;
139 if (m_SimSeconds > 0.5 ||
140 (m_LastCheckJobSim.isValid() && m_LastCheckJobSim.secsTo(now) < 60))
141 simType = DONT_SIMULATE;
143 const SchedulerJob *
next = selectNextJob(jobs, now, currentJob, simType, &startTime);
144 if (next == currentJob && now.
secsTo(startTime) <= 1)
146 if (simType != DONT_SIMULATE)
147 m_LastCheckJobSim = now;
154 qCDebug(KSTARS_EKOS_SCHEDULER)
155 << QString(
"Greedy Scheduler bumping current job %1 for %2 at %3")
156 .arg(currentJob->getName(), next ?
next->getName() :
"---", now.
toString(
"hh:mm"));
175void GreedyScheduler::prepareJobsForEvaluation(
176 const QList<SchedulerJob *> &jobs,
const QDateTime &now,
177 const QMap<QString, uint16_t> &capturedFramesCount, ModuleLogger *logger,
bool reestimateJobTimes)
const
180 foreach (SchedulerJob *job, jobs)
182 job->clearSimulatedSchedule();
183 switch (job->getCompletionCondition())
187 if (job->getFinishAtTime().isValid() && job->getFinishAtTime() < now)
197 if (job->getRepeatsRemaining() == 0)
199 if (logger !=
nullptr)
logger->appendLogText(
i18n(
"Job '%1' has no more batches remaining.", job->getName()));
201 job->setEstimatedTime(0);
212 foreach (SchedulerJob *job, jobs)
214 switch (job->getState())
236 foreach (SchedulerJob *job, jobs)
244 if (reestimateJobTimes)
246 job->setEstimatedTime(-1);
247 if (SchedulerUtils::estimateJobTime(job, capturedFramesCount, logger) ==
false)
253 if (job->getEstimatedTime() == 0)
255 job->setRepeatsRemaining(0);
260 TEST_PRINT(stderr,
"JOB %s estimated time: %ld state %d\n", job->getName().toLatin1().data(), job->getEstimatedTime(),
270bool allowJob(
const SchedulerJob *job,
bool rescheduleAbortsImmediate,
bool rescheduleAbortsQueue,
bool rescheduleErrors)
274 if (job->getState() ==
SCHEDJOB_ABORTED && !rescheduleAbortsImmediate && !rescheduleAbortsQueue)
284QDateTime firstPossibleStart(
const SchedulerJob *job,
const QDateTime &now,
285 bool rescheduleAbortsQueue,
int abortDelaySeconds,
286 bool rescheduleErrors,
int errorDelaySeconds)
288 QDateTime possibleStart = now;
289 const QDateTime &abortTime = job->getLastAbortTime();
290 const QDateTime &errorTime = job->getLastErrorTime();
292 if (abortTime.
isValid() && rescheduleAbortsQueue)
294 auto abortStartTime = abortTime.
addSecs(abortDelaySeconds);
295 if (abortStartTime > now)
296 possibleStart = abortStartTime;
300 if (errorTime.
isValid() && rescheduleErrors)
302 auto errorStartTime = errorTime.
addSecs(errorDelaySeconds);
303 if (errorStartTime > now)
304 possibleStart = errorStartTime;
307 if (!possibleStart.
isValid() || possibleStart < now)
309 return possibleStart;
331SchedulerJob *GreedyScheduler::selectNextJob(
const QList<SchedulerJob *> &jobs,
const QDateTime &now,
332 const SchedulerJob *
const currentJob, SimulationType simType, QDateTime *when,
333 QDateTime *nextInterruption, QString *interruptReason,
334 const QMap<QString, uint16_t> *capturedFramesCount)
338 constexpr int MIN_RUN_SECS = 10 * 60;
341 constexpr int MAX_INTERRUPT_SECS = 30;
344 bool currentJobIsStartAt = (currentJob && currentJob->getFileStartupCondition() == START_AT &&
345 currentJob->getStartAtTime().isValid());
347 SchedulerJob * nextJob =
nullptr;
348 QString interruptStr;
350 for (
int i = 0; i < jobs.
size(); ++i)
352 SchedulerJob *
const job = jobs[i];
353 const bool evaluatingCurrentJob = (currentJob && (job == currentJob));
355 TEST_PRINT(stderr,
" considering %s (%s)\n", job->getName().toLatin1().data(), evaluatingCurrentJob ?
"evaluating" :
"");
357 if (!allowJob(job, rescheduleAbortsImmediate, rescheduleAbortsQueue, rescheduleErrors))
359 TEST_PRINT(stderr,
" not allowed\n");
364 QDateTime startSearchingtAt = firstPossibleStart(
365 job, now, rescheduleAbortsQueue, abortDelaySeconds, rescheduleErrors, errorDelaySeconds);
371 const QDateTime startTime = job->getNextPossibleStartTime(startSearchingtAt, SCHEDULE_RESOLUTION_MINUTES,
372 evaluatingCurrentJob);
377 if (nextJob ==
nullptr)
380 nextStart = startTime;
382 if (nextInterruption) *nextInterruption = QDateTime();
385 else if (Options::greedyScheduling())
389 const int runSecs = evaluatingCurrentJob ? MAX_INTERRUPT_SECS : MIN_RUN_SECS;
392 if (evaluatingCurrentJob && currentJobIsStartAt)
394 if (nextInterruption) *nextInterruption = QDateTime();
395 nextStart = startTime;
399 else if (startTime.
secsTo(nextStart) > runSecs)
403 if (nextInterruption) *nextInterruption = nextStart;
404 interruptStr = QString(
"interrupted by %1").
arg(nextJob->getName());
405 nextStart = startTime;
411 if (!currentJob && nextStart.
isValid() && now.
secsTo(nextStart) < MIN_RUN_SECS)
414 else if (evaluatingCurrentJob)
422 if (evaluatingCurrentJob)
break;
424 if (nextJob !=
nullptr)
430 for (
int i = 0; i < jobs.
size(); ++i)
432 SchedulerJob *
const atJob = jobs[i];
433 if (atJob == nextJob)
435 const QDateTime atTime = atJob->getStartAtTime();
436 if (atJob->getFileStartupCondition() == START_AT && atTime.
isValid())
438 if (!allowJob(atJob, rescheduleAbortsImmediate, rescheduleAbortsQueue, rescheduleErrors))
441 QDateTime startSearchingtAt = firstPossibleStart(
442 atJob, now, rescheduleAbortsQueue, abortDelaySeconds, rescheduleErrors,
446 const QDateTime atJobStartTime = atJob->getNextPossibleStartTime(startSearchingtAt, SCHEDULE_RESOLUTION_MINUTES, currentJob
447 && (atJob == currentJob));
451 const double startDelta = atJobStartTime.
secsTo(atTime);
452 if (fabs(startDelta) < 20 * 60)
458 const int gap = currentJob ==
nullptr ? MIN_RUN_SECS : 30;
459 if (nextStart.
secsTo(atJobStartTime) <= gap)
462 nextStart = atJobStartTime;
463 if (nextInterruption) *nextInterruption = QDateTime();
465 else if (nextInterruption)
469 if (!nextInterruption->
isValid() ||
470 atJobStartTime.
secsTo(*nextInterruption) < 0)
472 *nextInterruption = atJobStartTime;
473 interruptStr = QString(
"interrupted by %1").
arg(atJob->getName());
485 if (nextJob && !nextJob->getGroup().isEmpty() && Options::greedyScheduling() && nextJob->getCompletedIterations() > 0)
487 TEST_PRINT(stderr,
" Considering GROUPS (%d jobs) selected %s\n", jobs.
size(), nextJob->getName().toLatin1().data());
489 bool foundSelectedJob =
false;
490 for (
int i = 0; i < jobs.
size(); ++i)
492 SchedulerJob *
const job = jobs[i];
495 foundSelectedJob =
true;
499 TEST_PRINT(stderr,
" Job %s (group %s) %s (%d vs %d iterations) %s\n",
500 job->getName().toLatin1().data(), (job->getGroup() != nextJob->getGroup()) ?
"Different" :
"Same",
501 foundSelectedJob ?
"Found" :
"not found yet",
502 job->getCompletedIterations(), nextJob->getCompletedIterations(),
503 allowJob(job, rescheduleAbortsImmediate, rescheduleAbortsQueue, rescheduleErrors) ?
"allowed" :
"not allowed");
508 if (!foundSelectedJob ||
509 (job->getGroup() != nextJob->getGroup()) ||
510 (job->getCompletedIterations() >= nextJob->getCompletedIterations()) ||
511 !allowJob(job, rescheduleAbortsImmediate, rescheduleAbortsQueue, rescheduleErrors))
514 const bool evaluatingCurrentJob = (currentJob && (job == currentJob));
517 QDateTime startSearchingtAt = firstPossibleStart(
518 job, now, rescheduleAbortsQueue, abortDelaySeconds, rescheduleErrors, errorDelaySeconds);
521 const QDateTime startTime = job->getNextPossibleStartTime(startSearchingtAt, SCHEDULE_RESOLUTION_MINUTES,
522 evaluatingCurrentJob);
525 if (!startTime.
isValid() || startTime.
secsTo(nextStart) > MAX_INTERRUPT_SECS)
529 if (evaluatingCurrentJob && currentJobIsStartAt)
531 if (nextInterruption) *nextInterruption = QDateTime();
532 nextStart = startTime;
536 else if (startTime.
secsTo(nextStart) >= -MAX_INTERRUPT_SECS)
539 nextStart = startTime;
545 if (when !=
nullptr) *when = nextStart;
546 if (interruptReason !=
nullptr) *interruptReason = interruptStr;
553 unsetEvaluation(jobs);
555 QElapsedTimer simTimer;
557 const int simDays = SIM_HOURS * 3600;
558 if (simType != DONT_SIMULATE && nextJob !=
nullptr)
560 QDateTime simulationLimit = now.
addSecs(simDays);
562 QDateTime simEnd = simulate(jobs, now, simulationLimit, capturedFramesCount, simType);
566 if (!Options::rememberJobProgress() && Options::schedulerRepeatEverything())
568 int repeats = 0, maxRepeats = 5;
569 while (simEnd.
isValid() && simEnd.
secsTo(simulationLimit) > 0 && ++repeats < maxRepeats)
572 simEnd = simulate(jobs, simEnd, simulationLimit,
nullptr, simType);
575 m_SimSeconds = simTimer.
elapsed() / 1000.0;
576 TEST_PRINT(stderr,
"********************************* simulate(%s,%d) took %.3fs\n",
577 simType == SIMULATE ?
"SIM" :
"ONLY_1", SIM_HOURS, m_SimSeconds);
584QDateTime GreedyScheduler::simulate(
const QList<SchedulerJob *> &jobs,
const QDateTime &time,
const QDateTime &endTime,
585 const QMap<QString, uint16_t> *capturedFramesCount, SimulationType simType)
587 TEST_PRINT(stderr,
"%d simulate()\n", __LINE__);
589 QList<SchedulerJob *> copiedJobs;
590 QList<SchedulerJob *> scheduledJobs;
591 QDateTime simEndTime;
593 foreach (SchedulerJob *job, jobs)
595 SchedulerJob *newJob =
new SchedulerJob();
599 newJob->followerJobs().clear();
600 newJob->clearSimulatedSchedule();
601 copiedJobs.
append(newJob);
602 job->setStopTime(QDateTime());
607 int numStartupCandidates = 0, numStartups = 0;
609 foreach (SchedulerJob *job, copiedJobs)
611 job->setStartupTime(QDateTime());
612 const auto state = job->getState();
615 numStartupCandidates++;
618 QMap<QString, uint16_t> capturedFramesCopy;
619 if (capturedFramesCount !=
nullptr)
620 capturedFramesCopy = *capturedFramesCount;
621 QList<SchedulerJob *>simJobs = copiedJobs;
622 prepareJobsForEvaluation(copiedJobs, time, capturedFramesCopy,
nullptr,
false);
624 QDateTime simTime = time;
626 bool exceededIterations =
false;
627 QHash<SchedulerJob*, int> workDone;
628 QHash<SchedulerJob*, int> originalIteration, originalSecsLeftIteration;
630 for(
int i = 0; i < simJobs.
size(); ++i)
631 workDone[simJobs[i]] = 0.0;
635 QDateTime jobStartTime;
636 QDateTime jobInterruptTime;
637 QString interruptReason;
641 SchedulerJob *selectedJob =
642 selectNextJob(simJobs, simTime,
nullptr, DONT_SIMULATE, &jobStartTime, &jobInterruptTime, &interruptReason);
643 if (selectedJob ==
nullptr)
646 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
"%1 starting at %2 interrupted at \"%3\" reason \"%4\"")
647 .arg(selectedJob->getName()).arg(jobStartTime.
toString(
"MM/dd hh:mm"))
648 .arg(jobInterruptTime.
toString(
"MM/dd hh:mm")).arg(interruptReason).toLatin1().data());
650 if (endTime.
isValid() && jobStartTime.
secsTo(endTime) < 0)
break;
655 QDateTime nextStartAtTime;
656 foreach (SchedulerJob *job, simJobs)
658 if (job != selectedJob &&
659 job->getStartupCondition() == START_AT &&
660 jobStartTime.
secsTo(job->getStartupTime()) > 0 &&
664 QDateTime startAtTime = job->getStartupTime();
665 if (!nextStartAtTime.
isValid() || nextStartAtTime.
secsTo(startAtTime) < 0)
666 nextStartAtTime = startAtTime;
670 QDateTime constraintStopTime = jobInterruptTime;
671 if (nextStartAtTime.
isValid() &&
672 (!constraintStopTime.
isValid() ||
673 nextStartAtTime.
secsTo(constraintStopTime) < 0))
675 constraintStopTime = nextStartAtTime;
676 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" job will be interrupted by a START_AT job").toLatin1().data());
679 QString constraintReason;
681 QDateTime jobConstraintTime = selectedJob->getNextEndTime(jobStartTime, SCHEDULE_RESOLUTION_MINUTES, &constraintReason,
684 std::abs(jobConstraintTime.
secsTo(nextStartAtTime)) < 2 * SCHEDULE_RESOLUTION_MINUTES)
685 constraintReason =
"interrupted by start-at job";
686 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" constraint \"%1\" reason \"%2\"")
687 .arg(jobConstraintTime.
toString(
"MM/dd hh:mm")).arg(constraintReason).toLatin1().data());
688 QDateTime jobCompletionTime;
689 TEST_PRINT(stderr,
"%d %s\n", __LINE__,
690 QString(
" estimated time = %1").arg(selectedJob->getEstimatedTime()).toLatin1().data());
691 if (selectedJob->getEstimatedTime() > 0)
694 const int timeLeft = selectedJob->getEstimatedTime() - workDone[selectedJob];
695 jobCompletionTime = jobStartTime.
addSecs(timeLeft);
696 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" completion \"%1\" time left %2s")
697 .arg(jobCompletionTime.
toString(
"MM/dd hh:mm")).arg(timeLeft).toLatin1().data());
701 QDateTime jobStopTime = jobInterruptTime;
702 QString stopReason = jobStopTime.
isValid() ? interruptReason :
"";
703 if (jobConstraintTime.
isValid() && (!jobStopTime.
isValid() || jobStopTime.
secsTo(jobConstraintTime) < 0))
705 stopReason = constraintReason;
706 jobStopTime = jobConstraintTime;
707 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" picked constraint").toLatin1().data());
709 if (jobCompletionTime.
isValid() && (!jobStopTime.
isValid() || jobStopTime.
secsTo(jobCompletionTime) < 0))
711 stopReason =
"job completion";
712 jobStopTime = jobCompletionTime;
713 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" picked completion").toLatin1().data());
718 if (!selectedJob->getGroup().isEmpty() &&
719 (selectedJob->getCompletionCondition() == FINISH_LOOP ||
720 selectedJob->getCompletionCondition() == FINISH_REPEAT ||
721 selectedJob->getCompletionCondition() == FINISH_AT))
723 if (originalIteration.
find(selectedJob) == originalIteration.
end())
724 originalIteration[selectedJob] = selectedJob->getCompletedIterations();
725 if (originalSecsLeftIteration.
find(selectedJob) == originalSecsLeftIteration.
end())
726 originalSecsLeftIteration[selectedJob] = selectedJob->getEstimatedTimeLeftThisRepeat();
729 int leftThisRepeat = selectedJob->getEstimatedTimeLeftThisRepeat();
730 int secsPerRepeat = selectedJob->getEstimatedTimePerRepeat();
731 int secsLeftThisRepeat = (workDone[selectedJob] < leftThisRepeat) ?
732 leftThisRepeat - workDone[selectedJob] : secsPerRepeat;
734 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" sec per repeat %1 sec left this repeat %2")
735 .arg(secsPerRepeat).arg(secsLeftThisRepeat).toLatin1().data());
737 if (workDone[selectedJob] == 0)
739 secsLeftThisRepeat += selectedJob->getEstimatedStartupTime();
740 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" adding %1 to secsLeftThisRepeat")
741 .arg(selectedJob->getEstimatedStartupTime()).arg(secsLeftThisRepeat).toLatin1().data());
745 if (secsLeftThisRepeat > 0 &&
746 (!jobStopTime.
isValid() || secsLeftThisRepeat < jobStartTime.
secsTo(jobStopTime)))
748 auto tempStart = jobStartTime;
749 auto tempInterrupt = jobInterruptTime;
750 auto tempReason = stopReason;
751 SchedulerJob keepJob = *selectedJob;
753 auto t = jobStartTime.
addSecs(secsLeftThisRepeat);
754 int iteration = selectedJob->getCompletedIterations();
755 int iters = 0, maxIters = 20;
756 while ((!jobStopTime.
isValid() || t.secsTo(jobStopTime) > 0) && iters++ < maxIters)
758 selectedJob->setCompletedIterations(++iteration);
759 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" iteration=%1").arg(iteration).toLatin1().data());
760 SchedulerJob *
next = selectNextJob(simJobs, t,
nullptr, DONT_SIMULATE, &tempStart, &tempInterrupt, &tempReason);
761 if (next != selectedJob)
763 stopReason =
"interrupted for group member";
765 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" switched to group member %1 at %2")
766 .arg(next ==
nullptr ?
"null" :
next->getName()).arg(t.toString(
"MM/dd hh:mm")).toLatin1().data());
770 t = t.addSecs(secsPerRepeat);
772 *selectedJob = keepJob;
779 const int secondsRun = jobStartTime.
secsTo(jobStopTime);
780 workDone[selectedJob] += secondsRun;
782 if ((originalIteration.
find(selectedJob) != originalIteration.
end()) &&
783 (originalSecsLeftIteration.
find(selectedJob) != originalSecsLeftIteration.
end()))
785 int completedIterations = originalIteration[selectedJob];
786 if (workDone[selectedJob] >= originalSecsLeftIteration[selectedJob] &&
787 selectedJob->getEstimatedTimePerRepeat() > 0)
788 completedIterations +=
789 1 + (workDone[selectedJob] - originalSecsLeftIteration[selectedJob]) / selectedJob->getEstimatedTimePerRepeat();
790 TEST_PRINT(stderr,
"%d %s\n", __LINE__,
791 QString(
" work sets interations=%1").arg(completedIterations).toLatin1().data());
792 selectedJob->setCompletedIterations(completedIterations);
798 if (!selectedJob->getStartupTime().isValid())
801 selectedJob->setStartupTime(jobStartTime);
802 selectedJob->setStopTime(jobStopTime);
803 selectedJob->setStopReason(stopReason);
805 scheduledJobs.
append(selectedJob);
806 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" Scheduled: %1 %2 -> %3 %4 work done %5s")
807 .arg(selectedJob->getName()).arg(selectedJob->getStartupTime().toString(
"MM/dd hh:mm"))
808 .arg(selectedJob->getStopTime().toString(
"MM/dd hh:mm")).arg(selectedJob->getStopReason())
809 .arg(workDone[selectedJob]).toLatin1().data());
813 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" Added: %1 %2 -> %3 %4 work done %5s")
814 .arg(selectedJob->getName()).arg(jobStartTime.
toString(
"MM/dd hh:mm"))
815 .arg(jobStopTime.
toString(
"MM/dd hh:mm")).arg(stopReason)
816 .arg(workDone[selectedJob]).toLatin1().data());
820 if (selectedJob->getEstimatedTime() >= 0 &&
821 workDone[selectedJob] >= selectedJob->getEstimatedTime())
824 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
" job %1 is complete")
825 .arg(selectedJob->getName()).toLatin1().data());
827 selectedJob->appendSimulatedSchedule(JobSchedule(
nullptr, jobStartTime, jobStopTime, stopReason));
828 schedule.
append(JobSchedule(jobs[copiedJobs.
indexOf(selectedJob)], jobStartTime, jobStopTime, stopReason));
829 simEndTime = jobStopTime;
830 simTime = jobStopTime.
addSecs(60);
837 if (++iterations > std::max(20, numStartupCandidates))
839 exceededIterations =
true;
840 TEST_PRINT(stderr,
"%d %s\n", __LINE__, QString(
"ending simulation after %1 iterations")
841 .arg(iterations).toLatin1().data());
845 if (simType == SIMULATE_EACH_JOB_ONCE)
847 bool allJobsProcessedOnce =
true;
848 for (
const auto job : simJobs)
850 if (allowJob(job, rescheduleAbortsImmediate, rescheduleAbortsQueue, rescheduleErrors) &&
851 !job->getStartupTime().isValid())
853 allJobsProcessedOnce =
false;
857 if (allJobsProcessedOnce)
859 TEST_PRINT(stderr,
"%d ending simulation, all jobs processed once\n", __LINE__);
868 for (
int i = 0; i < jobs.
size(); ++i)
870 if (scheduledJobs.
indexOf(copiedJobs[i]) >= 0)
876 jobs[i]->setStartupTime(copiedJobs[i]->getStartupTime());
879 jobs[i]->setStopTime(copiedJobs[i]->getStopTime());
880 jobs[i]->setStopReason(copiedJobs[i]->getStopReason());
881 if (simType == SIMULATE)
882 jobs[i]->setSimulatedSchedule(copiedJobs[i]->getSimulatedSchedule());
887 unsetEvaluation(jobs);
889 return exceededIterations ? QDateTime() : simEndTime;
892void GreedyScheduler::unsetEvaluation(
const QList<SchedulerJob *> &jobs)
const
894 for (
int i = 0; i < jobs.
size(); ++i)
901QString GreedyScheduler::jobScheduleString(
const JobSchedule &jobSchedule)
903 return QString(
"%1\t%2 --> %3 \t%4")
904 .arg(jobSchedule.job->getName(), -10)
905 .arg(jobSchedule.startTime.toString(
"MM/dd hh:mm"),
906 jobSchedule.stopTime.toString(
"hh:mm"), jobSchedule.stopReason);
909void GreedyScheduler::printSchedule(
const QList<JobSchedule> &schedule)
911 foreach (
auto &line, schedule)
913 fprintf(stderr,
"%s\n", QString(
"%1 %2 --> %3 (%4)")
914 .arg(jobScheduleString(line)).toLatin1().data());
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
@ 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.
const QList< QKeySequence > & next()
QCA_EXPORT Logger * logger()
QDateTime addSecs(qint64 s) const const
bool isValid() const const
qint64 secsTo(const QDateTime &other) const const
QString toString(QStringView format, QCalendar cal) const const
qint64 elapsed() const const
iterator find(const Key &key)
void append(QList< T > &&value)
qsizetype indexOf(const AT &value, qsizetype from) const const
qsizetype size() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
QByteArray toLatin1() const const