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