Commit 0a5cddbb authored by Jasem Mutlaq's avatar Jasem Mutlaq
Browse files

If job is complete and next pending job is in the future try to start it...

If job is complete and next pending job is in the future try to start it earliar if it is was intended to run ASAP and current time is suitable. Re-writing the scheduler time slot allocation algorithm but it needs more testing with lots of case scenarios to see if it holds in those cases
parent a7acbdcd
......@@ -50,6 +50,14 @@ bool priorityHigherThan(SchedulerJob *job1, SchedulerJob *job2)
return job1->getPriority() < job2->getPriority();
}
bool altitudeHigherThan(SchedulerJob *job1, SchedulerJob *job2)
{
double job1_altitude = Scheduler::findAltitude(job1->getTargetCoords(), job1->getStartupTime());
double job2_altitude = Scheduler::findAltitude(job2->getTargetCoords(), job2->getStartupTime());
return job1_altitude > job2_altitude;
}
Scheduler::Scheduler()
{
setupUi(this);
......@@ -938,6 +946,15 @@ void Scheduler::evaluateJobs()
else if (isWeatherOK(job) == false)
score += BAD_SCORE;
}
// If it is in the future and originally was designated as ASAP job
// Job must be less than 12 hours away to be considered for re-evaluation
else if (timeUntil > (Options::leadTime() * 60) && (timeUntil < 12 * 3600) && job->getFileStartupCondition() == SchedulerJob::START_ASAP)
{
QDateTime nextJobTime = now.addSecs(Options::leadTime() * 60);
if (now > duskDateTime && now < preDawnDateTime)
job->setStartupTime(nextJobTime);
score += BAD_SCORE;
}
// If time is far in the future, we make the score negative
else
score += BAD_SCORE;
......@@ -1014,93 +1031,63 @@ void Scheduler::evaluateJobs()
return;
}
int maxScore=0, maxPriority=1e6;
SchedulerJob *bestCandidate = NULL;
updatePreDawn();
// Order by altitude first
qSort(jobs.begin(), jobs.end(), altitudeHigherThan);
// Then by priority
qSort(jobs.begin(), jobs.end(), priorityHigherThan);
SchedulerJob *firstJob = jobs.first();
QDateTime firstStartTime = firstJob->getStartupTime();
QDateTime lastStartTime = firstJob->getStartupTime();
double lastJobEstimatedTime = firstJob->getEstimatedTime();
int daysCount = 0;
// Make sure no two jobs have the same scheduled time or overlap with other jobs
foreach(SchedulerJob *job, jobs)
{
{
// If this job is not scheduled, continue
// If this job startup conditon is not to start at a specific time, continue
if (job->getState() != SchedulerJob::JOB_SCHEDULED || job->getStartupCondition() != SchedulerJob::START_AT)
if (job == firstJob || job->getState() != SchedulerJob::JOB_SCHEDULED || job->getStartupCondition() != SchedulerJob::START_AT)
continue;
uint8_t job_priority = job->getPriority();
double timeBetweenJobs = fabs(firstStartTime.secsTo(job->getStartupTime()));
foreach(SchedulerJob *other_job, jobs)
// If there are within 5 minutes of each other, try to advance scheduling time of the lower altitude one
if (timeBetweenJobs < (Options::leadTime())*60)
{
// If we already allocated time slot for this, continue
if (other_job == job || other_job->getState() != SchedulerJob::JOB_SCHEDULED || other_job->getStartupCondition() != SchedulerJob::START_AT || other_job->getTimeSlotAllocated())
continue;
double delayJob = timeBetweenJobs + lastJobEstimatedTime;
uint8_t otherjob_priority = other_job->getPriority();
if (delayJob < (Options::leadTime()*60))
delayJob = Options::leadTime()*60;
double timeBetweenJobs = fabs(job->getStartupTime().secsTo(other_job->getStartupTime()));
// If there are within 5 minutes of each other, try to advance scheduling time of the lower altitude one
if (timeBetweenJobs < (Options::leadTime())*60)
QDateTime otherjob_time = lastStartTime.addSecs(delayJob);
// If other jobs starts after pre-dawn limit, then we scheduler it to the next day.
if (otherjob_time >= preDawnDateTime.addDays(daysCount))
{
double job_altitude = findAltitude(job->getTargetCoords(), job->getStartupTime());
double other_job_altitude = findAltitude(other_job->getTargetCoords(), other_job->getStartupTime());
// If first job is higher than second job, then we scheduler second job to later
if (job_altitude > other_job_altitude || job_priority < otherjob_priority)
{
double delayJob = timeBetweenJobs + job->getEstimatedTime();
daysCount++;
if (delayJob < (Options::leadTime()*60))
delayJob = Options::leadTime()*60;
QDateTime otherjob_time = other_job->getStartupTime().addSecs(delayJob);
// If other jobs starts after pre-dawn limit, then we scheduler it to the next day.
if (otherjob_time >= preDawnDateTime)
other_job->setStartupTime(other_job->getStartupTime().addDays(1));
else
other_job->setStartupTime(other_job->getStartupTime().addSecs(delayJob));
other_job->setState(SchedulerJob::JOB_SCHEDULED);
if (job_priority < otherjob_priority)
appendLogText(i18n("Observation jobs %1 and %2 have close start up times. %1 priority %3 is higher than %2 priority %4. Selecting %1 and rescheduling %2 to %5.",
job->getName(), other_job->getName(), job->getPriority(), other_job->getPriority(), other_job->getStartupTime().toString()));
else
appendLogText(i18n("Observation jobs %1 and %2 have close start up times. At %3, %1 altitude is %4 while %2 altitude is %5. Selecting %1 and rescheduling %2 to %6.",
job->getName(), other_job->getName(), job->getStartupTime().toString(), job_altitude, other_job_altitude, other_job->getStartupTime().toString()));
//return;
//continue;
}
else
{
double delayJob = timeBetweenJobs + other_job->getEstimatedTime();
if (delayJob < (Options::leadTime()*60))
delayJob = Options::leadTime()*60;
QDateTime job_time = job->getStartupTime().addSecs(delayJob);
// If other jobs starts after pre-dawn limit, then we scheduler it to the next day.
if (job_time >= preDawnDateTime)
job->setStartupTime(job->getStartupTime().addDays(1));
else
job->setStartupTime(job->getStartupTime().addSecs(delayJob));
job->setState(SchedulerJob::JOB_SCHEDULED);
if (job_priority < otherjob_priority)
appendLogText(i18n("Observation jobs %1 and %2 have close start up times. %1 priority %3 is higher than %2 priority %4. Selecting %1 and rescheduling %2 to %5.",
other_job->getName(), job->getName(), other_job->getPriority(), job->getPriority(), job->getStartupTime().toString()));
else
appendLogText(i18n("Observation jobs %1 and %2 have close start up times. At %3, %1 altitude is %4 while %2 altitude is %5. Selecting %1 and rescheduling %2 to %6.",
other_job->getName(), job->getName(), other_job->getStartupTime().toString(), other_job_altitude, job_altitude, job->getStartupTime().toString()));
lastStartTime = job->getStartupTime().addDays(daysCount);
job->setStartupTime(lastStartTime);
lastStartTime.addSecs(delayJob);
}
else
{
lastStartTime = lastStartTime.addSecs(delayJob);
job->setStartupTime(lastStartTime);
}
job->setState(SchedulerJob::JOB_SCHEDULED);
//return;
//continue;
}
}
appendLogText(i18n("Observation jobs %1 and %2 have close start up times. %2 is rescheduled to %3.", firstJob->getName(), job->getName(), job->getStartupTime().toString()));
}
job->setTimeSlotAllocated(true);
lastJobEstimatedTime = job->getEstimatedTime();
}
if (jobEvaluationOnly)
......@@ -1253,12 +1240,12 @@ double Scheduler::findAltitude(const SkyPoint & target, const QDateTime when)
// Make a copy
SkyPoint p = target;
QDateTime lt( when.date(), QTime() );
KStarsDateTime ut = geo->LTtoUT( lt );
KStarsDateTime ut = KStarsData::Instance()->geo()->LTtoUT( lt );
KStarsDateTime myUT = ut.addSecs(when.time().msecsSinceStartOfDay()/1000);
dms LST = geo->GSTtoLST( myUT.gst() );
p.EquatorialToHorizontal( &LST, geo->lat() );
dms LST = KStarsData::Instance()->geo()->GSTtoLST( myUT.gst() );
p.EquatorialToHorizontal( &LST, KStarsData::Instance()->geo()->lat() );
return p.alt().Degrees();
}
......@@ -1623,6 +1610,9 @@ void Scheduler::calculateDawnDusk()
QTime dawn = QTime(0,0,0).addSecs(Dawn*24*3600);
QTime dusk = QTime(0,0,0).addSecs(Dusk*24*3600);
duskDateTime.setDate(KStars::Instance()->data()->lt().date());
duskDateTime.setTime(dusk);
appendLogText(i18n("Dawn is at %1, Dusk is at %2, and current time is %3", dawn.toString(), dusk.toString(), now.toString()));
}
......@@ -3335,6 +3325,7 @@ bool Scheduler::estimateJobTime(SchedulerJob *job)
double sequenceEstimatedTime = 0;
bool inSequenceFocus = false;
int jobExposureCount=0;
double seqCompletePercentage=0;
while ( sFile.getChar(&c))
{
......@@ -3342,6 +3333,55 @@ bool Scheduler::estimateJobTime(SchedulerJob *job)
if (root)
{
// If the job finishes with the sequence, we check if all the sequence files were captured or not first
// If all are captured, then job is complete, otherwise we continuce to estimate time
if (job->getCompletionCondition() == SchedulerJob::FINISH_SEQUENCE)
{
int totalSequenceCount=0;
int totalSequenceCompleted=0;
QStringList fitsDirectories;
for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0))
{
if (!strcmp(tagXMLEle(ep), "Job"))
{
XMLEle *subEP=NULL;
for (subEP = nextXMLEle(ep, 1) ; subEP != NULL ; subEP = nextXMLEle(ep, 0))
{
if (!strcmp(tagXMLEle(subEP), "Count"))
totalSequenceCount += atoi(pcdataXMLEle(subEP));
else if (!strcmp(tagXMLEle(subEP), "FITSDirectory"))
fitsDirectories << QString(pcdataXMLEle(subEP));
}
}
}
fitsDirectories.removeDuplicates();
foreach(QString dir, fitsDirectories)
{
QDir fitsDir(dir);
totalSequenceCompleted += fitsDir.entryInfoList(QStringList("*.fits"), QDir::Files).count();
QStringList folderList = fitsDir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
foreach (QString oneFolder, folderList)
{
QDir folder(dir + QDir::separator() + oneFolder);
totalSequenceCompleted += folder.entryInfoList(QStringList("*.fits"), QDir::Files).count();
}
}
if (totalSequenceCompleted > 0 && totalSequenceCompleted >= totalSequenceCount)
{
appendLogText(i18n("%1 observation job is already complete.", job->getName()));
job->setEstimatedTime(0);
return true;
}
seqCompletePercentage = (double) totalSequenceCompleted / (double) totalSequenceCount;
}
for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0))
{
if (!strcmp(tagXMLEle(ep), "Autofocus"))
......@@ -3364,18 +3404,24 @@ bool Scheduler::estimateJobTime(SchedulerJob *job)
sFile.close();
break;
}
else if (oneJobEstimation == 0)
continue;
sequenceEstimatedTime += oneJobEstimation;
// If inSequenceFocus is true
if (inSequenceFocus)
// Wild guess that each in sequence auto focus takes an average of 20 seconds. It can take any where from 2 seconds to 2+ minutes.
sequenceEstimatedTime += jobExposureCount * 20;
// If we're dithering after each exposure, that's another 10-20 seconds
if (Options::useDither())
sequenceEstimatedTime += jobExposureCount * 15;
XMLEle *frameEP = findXMLEle(ep, "Type");
QString frameType = "Light";
if (frameEP)
frameType = QString(pcdataXMLEle(frameEP));
if (frameType == "Light")
{
// If inSequenceFocus is true
if (inSequenceFocus)
// Wild guess that each in sequence auto focus takes an average of 20 seconds. It can take any where from 2 seconds to 2+ minutes.
sequenceEstimatedTime += jobExposureCount * 20;
// If we're dithering after each exposure, that's another 10-20 seconds
if (Options::useDither())
sequenceEstimatedTime += jobExposureCount * 15;
}
}
}
......@@ -3394,12 +3440,8 @@ bool Scheduler::estimateJobTime(SchedulerJob *job)
appendLogText(i18n("Failed to estimate time for %1 observation job.", job->getName()));
return false;
}
else if (sequenceEstimatedTime == 0)
{
appendLogText(i18n("%1 observation job is already complete.", job->getName()));
job->setEstimatedTime(0);
return true;
}
sequenceEstimatedTime = sequenceEstimatedTime - (sequenceEstimatedTime * seqCompletePercentage);
// Are we doing tracking? It takes about 30 seconds
if (job->getStepPipeline() & SchedulerJob::USE_TRACK)
......@@ -3432,8 +3474,6 @@ double Scheduler::estimateSequenceTime(XMLEle *root, int *totalCount)
int count=0;
double exposure=0, delay=0;
QString frameType;
QString fitsDir;
for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0))
{
......@@ -3443,24 +3483,11 @@ double Scheduler::estimateSequenceTime(XMLEle *root, int *totalCount)
count = *totalCount = atoi(pcdataXMLEle(ep));
else if (!strcmp(tagXMLEle(ep), "Delay"))
delay = atoi(pcdataXMLEle(ep));
else if (!strcmp(tagXMLEle(ep), "Type"))
frameType = QString(pcdataXMLEle(ep));
else if (!strcmp(tagXMLEle(ep), "FITSDirectory"))
fitsDir = QString(pcdataXMLEle(ep));
}
fitsDir += "/" + frameType;
QDir targetDir(fitsDir);
QFileInfoList fileList = targetDir.entryInfoList(QStringList("*.fits"), QDir::Files);
int completed = fileList.count();
count -= completed;
totalTime = (exposure + delay) * count;
return totalTime;
}
void Scheduler::parkMount()
......
......@@ -113,6 +113,14 @@ public:
*/
void stop();
/**
* @brief findAltitude Find altitude given a specific time
* @param target Target
* @param when date time to find altitude
* @return Altitude of the target at the specific date and time given.
*/
static double findAltitude(const SkyPoint & target, const QDateTime when);
protected slots:
/**
......@@ -395,14 +403,6 @@ private:
*/
bool processJobInfo(XMLEle *root);
/**
* @brief findAltitude Find altitude given a specific time
* @param target Target
* @param when date time to find altitude
* @return Altitude of the target at the specific date and time given.
*/
double findAltitude(const SkyPoint & target, const QDateTime when);
/**
* @brief getCurrentMoonSeparation Get current moon separation in degrees at current time for the given job
* @param job scheduler job
......@@ -480,6 +480,7 @@ private:
double Dawn, Dusk; // Store day fraction of dawn and dusk to calculate dark skies range
QDateTime preDawnDateTime; // Pre-dawn is where we stop all jobs, it is a user-configurable value before Dawn.
QDateTime duskDateTime; // Dusk date time
bool mDirty; // Was job modified and needs saving?
IPState weatherStatus; // Keep watch of weather status
uint8_t noWeatherCounter; // Keep track of how many times we didn't receive weather updates
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment