7 #include "sequencejob.h"
10 #include "kstarsdata.h"
12 #include "indi/driverinfo.h"
13 #include "indi/clientmanager.h"
14 #include "ekos/scheduler/schedulerjob.h"
16 #include <KNotifications/KNotification>
17 #include <ekos_capture_debug.h>
19 #define MF_TIMER_TIMEOUT 90000
20 #define MF_RA_DIFF_LIMIT 4
24 QString const &SequenceJob::ISOMarker(
"_ISO8601");
31 SequenceJob::SequenceJob()
34 m_CoreProperties[SJ_Exposure] = -1;
35 m_CoreProperties[SJ_Gain] = -1;
36 m_CoreProperties[SJ_Offset] = -1;
37 m_CoreProperties[SJ_ISOIndex] = -1;
38 m_CoreProperties[SJ_Count] = -1;
39 m_CoreProperties[SJ_Delay] = -1;
40 m_CoreProperties[SJ_Binning] =
QPoint(1, 1);
41 m_CoreProperties[SJ_ROI] =
QRect(0, 0, 0, 0);
42 m_CoreProperties[SJ_ExpPrefixEnabled] =
false;
43 m_CoreProperties[SJ_TimeStampPrefixEnabled] =
false;
44 m_CoreProperties[SJ_EnforceTemperature] =
false;
45 m_CoreProperties[SJ_FilterPrefixEnabled] =
false;
46 m_CoreProperties[SJ_DarkFlat] =
false;
47 m_CoreProperties[SJ_GuiderActive] =
false;
48 m_CoreProperties[SJ_Encoding] =
"FITS";
54 captureDeviceAdaptor = cp;
56 stateMachine =
new SequenceJobState(sharedState);
59 connect(
this, &SequenceJob::updateGuiderDrift, stateMachine, &SequenceJobState::setCurrentGuiderDrift);
60 connect(stateMachine, &SequenceJobState::prepareState,
this, &SequenceJob::prepareState);
61 connect(stateMachine, &SequenceJobState::prepareComplete,
this, &SequenceJob::processPrepareComplete);
62 connect(stateMachine, &SequenceJobState::abortCapture,
this, &SequenceJob::processAbortCapture);
63 connect(stateMachine, &SequenceJobState::newLog,
this, &SequenceJob::newLog);
70 SequenceJob::SequenceJob(XMLEle *root):
SequenceJob()
73 XMLEle *subEP =
nullptr;
77 sharedState.
reset(
new SequenceJobState::CaptureState);
78 stateMachine =
new SequenceJobState(sharedState);
85 {
"Light", FRAME_LIGHT }, {
"Dark", FRAME_DARK }, {
"Bias", FRAME_BIAS }, {
"Flat", FRAME_FLAT }
88 setFrameType(FRAME_NONE);
89 setCoreProperty(SJ_Exposure, 0);
97 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
99 if (!strcmp(tagXMLEle(ep),
"Exposure"))
101 setCoreProperty(SJ_Exposure, atof(pcdataXMLEle(ep)));
103 if (!strcmp(tagXMLEle(ep),
"Format"))
105 setCoreProperty(SJ_Format, pcdataXMLEle(ep));
107 if (!strcmp(tagXMLEle(ep),
"Encoding"))
109 setCoreProperty(SJ_Encoding, pcdataXMLEle(ep));
111 else if (!strcmp(tagXMLEle(ep),
"Filter"))
113 setCoreProperty(SJ_Filter,
QString(pcdataXMLEle(ep)));
115 else if (!strcmp(tagXMLEle(ep),
"Type"))
119 if (frameTypes.
contains(frameTypeStr))
121 setFrameType(frameTypes[frameTypeStr]);
126 else if (!strcmp(tagXMLEle(ep),
"Prefix"))
128 subEP = findXMLEle(ep,
"RawPrefix");
130 setCoreProperty(SJ_RawPrefix,
QString(pcdataXMLEle(subEP)));
132 subEP = findXMLEle(ep,
"FilterEnabled");
134 setCoreProperty(SJ_FilterPrefixEnabled, !strcmp(
"1", pcdataXMLEle(subEP)));
136 subEP = findXMLEle(ep,
"ExpEnabled");
138 setCoreProperty(SJ_ExpPrefixEnabled, (!strcmp(
"1", pcdataXMLEle(subEP))));
140 subEP = findXMLEle(ep,
"TimeStampEnabled");
142 setCoreProperty(SJ_TimeStampPrefixEnabled, (!strcmp(
"1", pcdataXMLEle(subEP))));
145 else if (!strcmp(tagXMLEle(ep),
"Count"))
147 setCoreProperty(SJ_Count, atoi(pcdataXMLEle(ep)));
149 else if (!strcmp(tagXMLEle(ep),
"Delay"))
151 setCoreProperty(SJ_Delay, atoi(pcdataXMLEle(ep)));
153 else if (!strcmp(tagXMLEle(ep),
"FITSDirectory"))
155 setCoreProperty(SJ_LocalDirectory, (pcdataXMLEle(ep)));
157 else if (!strcmp(tagXMLEle(ep),
"RemoteDirectory"))
159 setCoreProperty(SJ_RemoteDirectory, (pcdataXMLEle(ep)));
161 else if (!strcmp(tagXMLEle(ep),
"UploadMode"))
163 setUploadMode(
static_cast<ISD::Camera::UploadMode
>(atoi(pcdataXMLEle(ep))));
165 else if (!strcmp(tagXMLEle(ep),
"Calibration"))
167 subEP = findXMLEle(ep,
"FlatSource");
170 XMLEle * typeEP = findXMLEle(subEP,
"Type");
173 if (!strcmp(pcdataXMLEle(typeEP),
"Manual"))
174 setFlatFieldSource(SOURCE_MANUAL);
175 else if (!strcmp(pcdataXMLEle(typeEP),
"FlatCap"))
176 setFlatFieldSource(SOURCE_FLATCAP);
177 else if (!strcmp(pcdataXMLEle(typeEP),
"DarkCap"))
178 setFlatFieldSource(SOURCE_DARKCAP);
179 else if (!strcmp(pcdataXMLEle(typeEP),
"Wall"))
181 XMLEle * azEP = findXMLEle(subEP,
"Az");
182 XMLEle * altEP = findXMLEle(subEP,
"Alt");
186 setFlatFieldSource(SOURCE_WALL);
190 setWallCoord(wallCoord);
194 setFlatFieldSource(SOURCE_DAWN_DUSK);
198 subEP = findXMLEle(ep,
"FlatDuration");
201 const char * dark = findXMLAttValu(subEP,
"dark");
202 setCoreProperty(SJ_DarkFlat, !strcmp(dark,
"true"));
204 XMLEle * typeEP = findXMLEle(subEP,
"Type");
208 if (!strcmp(pcdataXMLEle(typeEP),
"Manual"))
209 setFlatFieldDuration(DURATION_MANUAL);
212 XMLEle * aduEP = findXMLEle(subEP,
"Value");
215 setFlatFieldDuration(DURATION_ADU);
216 setCoreProperty(SJ_TargetADU, cLocale.
toDouble(pcdataXMLEle(aduEP)));
219 aduEP = findXMLEle(subEP,
"Tolerance");
222 setCoreProperty(SJ_TargetADUTolerance, cLocale.
toDouble(pcdataXMLEle(aduEP)));
226 subEP = findXMLEle(ep,
"PreMountPark");
229 setPreMountPark(!strcmp(pcdataXMLEle(subEP),
"True"));
232 subEP = findXMLEle(ep,
"PreDomePark");
235 setPreDomePark(!strcmp(pcdataXMLEle(subEP),
"True"));
241 void SequenceJob::resetStatus(JOBStatus status)
244 setCalibrationStage(SequenceJobState::CAL_NONE);
257 m_CaptureRetires = 0;
258 m_JobProgressIgnored =
false;
266 void SequenceJob::abort()
268 setStatus(JOB_ABORTED);
269 if (captureDeviceAdaptor.data()->getActiveChip()->canAbort())
270 captureDeviceAdaptor.data()->getActiveChip()->abortExposure();
271 captureDeviceAdaptor.data()->getActiveChip()->setBatchMode(
false);
274 void SequenceJob::done()
279 int SequenceJob::getJobRemainingTime(
double estimatedDownloadTime)
281 double remaining = (getCoreProperty(SJ_Exposure).toDouble() +
282 estimatedDownloadTime +
283 getCoreProperty(SJ_Delay).toDouble() / 1000) *
284 (getCoreProperty(SJ_Count).toDouble() - getCompleted());
286 if (getStatus() == JOB_BUSY)
288 if (getExposeLeft() > 0.0)
289 remaining -= getCoreProperty(SJ_Exposure).toDouble() - getExposeLeft();
291 remaining += getExposeLeft() + estimatedDownloadTime;
294 return static_cast<int>(std::round(remaining));
297 void SequenceJob::setStatus(JOBStatus
const in_status)
299 stateMachine->reset(in_status);
300 if( !getCoreProperty(SequenceJob::SJ_Preview).toBool() &&
nullptr != statusCell)
301 statusCell->setText(StatusStrings[in_status]);
311 setStatus(getStatus());
315 void SequenceJob::setCompleted(
int value)
318 if( !getCoreProperty(SequenceJob::SJ_Preview).toBool() &&
nullptr != countCell)
319 countCell->setText(
QString(
"%L1/%L2").arg(value).arg(getCoreProperty(SJ_Count).toInt()));
322 int SequenceJob::getCompleted()
const
334 setCoreProperty(SJ_Count, getCoreProperty(SJ_Count));
342 return m_CustomProperties;
347 m_CustomProperties = value;
360 QString SequenceJob::getScript(ScriptTypes type)
const
362 return m_Scripts[
type];
365 void SequenceJob::setScript(ScriptTypes type,
const QString &value)
367 m_Scripts[
type] = value;
370 void SequenceJob::connectDeviceAdaptor()
372 captureDeviceAdaptor->setCurrentSequenceJobState(stateMachine);
373 captureDeviceAdaptor->connectRotator();
374 captureDeviceAdaptor->connectActiveCamera();
375 captureDeviceAdaptor->connectTelescope();
376 captureDeviceAdaptor->connectDome();
377 captureDeviceAdaptor->connectDustCap();
379 connect(stateMachine, &SequenceJobState::readCurrentState, captureDeviceAdaptor.data(),
380 &CaptureDeviceAdaptor::readCurrentState);
381 connect(stateMachine, &SequenceJobState::flatSyncFocus, captureDeviceAdaptor.data(), &CaptureDeviceAdaptor::flatSyncFocus);
383 connect(captureDeviceAdaptor.data(), &CaptureDeviceAdaptor::flatSyncFocusChanged, stateMachine,
384 &SequenceJobState::flatSyncFocusChanged);
387 void SequenceJob::disconnectDeviceAdaptor()
389 captureDeviceAdaptor->disconnectRotator();
390 captureDeviceAdaptor->disconnectActiveCamera();
391 captureDeviceAdaptor->disconnectTelescope();
392 captureDeviceAdaptor->disconnectDome();
393 captureDeviceAdaptor->disconnectDustCap();
394 disconnect(stateMachine, &SequenceJobState::readCurrentState, captureDeviceAdaptor.data(),
395 &CaptureDeviceAdaptor::readCurrentState);
396 disconnect(stateMachine, &SequenceJobState::flatSyncFocus, captureDeviceAdaptor.data(),
397 &CaptureDeviceAdaptor::flatSyncFocus);
398 disconnect(captureDeviceAdaptor.data(), &CaptureDeviceAdaptor::flatSyncFocusChanged, stateMachine,
399 &SequenceJobState::flatSyncFocusChanged);
402 CAPTUREResult SequenceJob::capture(
bool autofocusReady, FITSMode mode)
404 captureDeviceAdaptor.data()->getActiveChip()->setBatchMode(!getCoreProperty(SequenceJob::SJ_Preview).toBool());
405 captureDeviceAdaptor.data()->getActiveCamera()->setISOMode(getCoreProperty(SJ_TimeStampPrefixEnabled).toBool());
406 captureDeviceAdaptor.data()->getActiveCamera()->setSeqPrefix(getCoreProperty(SJ_FullPrefix).
toString());
408 auto placeholderPath = Ekos::PlaceholderPath(getCoreProperty(SJ_LocalDirectory).
toString() +
"/sequence.esq");
409 placeholderPath.setGenerateFilenameSettings(*
this);
410 captureDeviceAdaptor.data()->getActiveCamera()->setPlaceholderPath(placeholderPath);
412 if (getCoreProperty(SequenceJob::SJ_Preview).toBool())
414 if (captureDeviceAdaptor.data()->getActiveCamera()->getUploadMode() != ISD::Camera::UPLOAD_CLIENT)
415 captureDeviceAdaptor.data()->getActiveCamera()->setUploadMode(ISD::Camera::UPLOAD_CLIENT);
418 captureDeviceAdaptor.data()->getActiveCamera()->setUploadMode(m_UploadMode);
424 INDI::Property *customProp = captureDeviceAdaptor.data()->getActiveCamera()->getProperty(i.key());
430 switch (customProp->getType())
434 auto sp = customProp->getSwitch();
438 auto oneSwitch = sp->findWidgetByName(j.key().toLatin1().data());
440 oneSwitch->setState(
static_cast<ISState
>(j.value().toInt()));
442 captureDeviceAdaptor.data()->getActiveCamera()->getDriverInfo()->getClientManager()->sendNewSwitch(sp);
447 auto tp = customProp->getText();
451 auto oneText = tp->findWidgetByName(j.key().toLatin1().data());
453 oneText->setText(j.value().toString().toLatin1().constData());
455 captureDeviceAdaptor.data()->getActiveCamera()->getDriverInfo()->getClientManager()->sendNewText(tp);
460 auto np = customProp->getNumber();
464 auto oneNumber = np->findWidgetByName(j.key().toLatin1().data());
466 oneNumber->setValue(j.value().toDouble());
468 captureDeviceAdaptor.data()->getActiveCamera()->getDriverInfo()->getClientManager()->sendNewNumber(np);
477 const auto remoteDirectory = getCoreProperty(SJ_RemoteDirectory).toString();
478 if (captureDeviceAdaptor.data()->getActiveChip()->isBatchMode() && remoteDirectory.isEmpty() ==
false)
479 captureDeviceAdaptor.data()->getActiveCamera()->updateUploadSettings(remoteDirectory + getCoreProperty(
482 const int ISOIndex = getCoreProperty(SJ_ISOIndex).toInt();
485 if (ISOIndex != captureDeviceAdaptor.data()->getActiveChip()->getISOIndex())
486 captureDeviceAdaptor.data()->getActiveChip()->setISOIndex(ISOIndex);
489 const auto gain = getCoreProperty(SJ_Gain).toDouble();
492 captureDeviceAdaptor.data()->getActiveCamera()->setGain(gain);
495 const auto offset = getCoreProperty(SJ_Offset).toDouble();
498 captureDeviceAdaptor.data()->getActiveCamera()->setOffset(offset);
501 const auto frameType = getFrameType();
504 if (stateMachine->targetFilterID != -1 && captureDeviceAdaptor.data()->getFilterWheel() !=
nullptr &&
505 (frameType == FRAME_FLAT || frameType == FRAME_LIGHT))
507 if (stateMachine->targetFilterID != stateMachine->m_CaptureState->currentFilterID)
509 emit prepareState(CAPTURE_CHANGING_FILTER);
511 FilterManager::FilterPolicy policy = FilterManager::ALL_POLICIES;
513 if (getCoreProperty(SequenceJob::SJ_Preview).toBool() || frameType != FRAME_LIGHT || autofocusReady ==
false)
514 policy =
static_cast<FilterManager::FilterPolicy
>(policy & ~FilterManager::AUTOFOCUS_POLICY);
516 m_FilterManager->setFilterPosition(stateMachine->targetFilterID, policy);
517 return CAPTURE_FILTER_BUSY;
522 if (captureDeviceAdaptor.data()->getActiveCamera()->getEncodingFormat() ==
QLatin1String(
"FITS"))
524 int currentBinX = 1, currentBinY = 1;
525 captureDeviceAdaptor.data()->getActiveChip()->getBinning(¤tBinX, ¤tBinY);
527 const auto binning = getCoreProperty(SJ_Binning).toPoint();
531 if (captureDeviceAdaptor.data()->getActiveChip()->canBin()
532 && captureDeviceAdaptor.data()->getActiveChip()->setBinning(binning.x(), binning.y()) ==
false)
534 setStatus(JOB_ERROR);
535 return CAPTURE_BIN_ERROR;
538 const auto roi = getCoreProperty(SJ_ROI).toRect();
540 if ((roi.width() > 0 && roi.height() > 0) && captureDeviceAdaptor.data()->getActiveChip()->canSubframe()
541 && captureDeviceAdaptor.data()->getActiveChip()->setFrame(roi.x(),
545 currentBinX != binning.x()) ==
false)
547 setStatus(JOB_ERROR);
548 return CAPTURE_FRAME_ERROR;
552 captureDeviceAdaptor.data()->getActiveCamera()->setCaptureFormat(getCoreProperty(SJ_Format).
toString());
553 captureDeviceAdaptor.data()->getActiveCamera()->setEncodingFormat(getCoreProperty(SJ_Encoding).
toString());
554 captureDeviceAdaptor.data()->getActiveChip()->setFrameType(getFrameType());
561 captureDeviceAdaptor.data()->getActiveChip()->setCaptureMode(mode);
562 captureDeviceAdaptor.data()->getActiveChip()->setCaptureFilter(FITS_NONE);
564 setStatus(getStatus());
566 const auto exposure = getCoreProperty(SJ_Exposure).toDouble();
567 m_ExposeLeft = exposure;
568 captureDeviceAdaptor.data()->getActiveChip()->capture(exposure);
573 void SequenceJob::setTargetFilter(
int pos,
const QString &name)
575 stateMachine->targetFilterID = pos;
576 setCoreProperty(SJ_Filter, name);
579 double SequenceJob::getExposeLeft()
const
584 void SequenceJob::setExposeLeft(
double value)
586 m_ExposeLeft = value;
590 int SequenceJob::getCaptureRetires()
const
592 return m_CaptureRetires;
595 void SequenceJob::setCaptureRetires(
int value)
597 m_CaptureRetires = value;
600 int SequenceJob::getCurrentFilter()
const
602 return stateMachine->m_CaptureState->currentFilterID;
605 void SequenceJob::setCurrentFilter(
int value)
608 stateMachine->setCurrentFilterID(value);
612 void SequenceJob::setUploadMode(ISD::Camera::UploadMode value)
614 m_UploadMode = value;
617 ISD::Camera::UploadMode SequenceJob::getUploadMode()
const
623 void SequenceJob::setFlatFieldSource(FlatFieldSource value)
625 stateMachine->flatFieldSource = value;
628 FlatFieldSource SequenceJob::getFlatFieldSource()
const
630 return stateMachine->flatFieldSource;
633 void SequenceJob::setWallCoord(
const SkyPoint &value)
635 stateMachine->wallCoord = value;
638 const SkyPoint &SequenceJob::getWallCoord()
const
640 return stateMachine->wallCoord;
644 void SequenceJob::setFlatFieldDuration(FlatFieldDuration value)
646 m_FlatFieldDuration = value;
650 FlatFieldDuration SequenceJob::getFlatFieldDuration()
const
652 return m_FlatFieldDuration;
655 void SequenceJob::setJobProgressIgnored(
bool value)
657 m_JobProgressIgnored = value;
660 bool SequenceJob::getJobProgressIgnored()
const
662 return m_JobProgressIgnored;
667 stateMachine->m_CaptureState->hasLightBox = (lightBox !=
nullptr);
672 stateMachine->m_CaptureState->hasDustCap = (dustCap !=
nullptr);
677 stateMachine->m_CaptureState->hasTelescope = (scope !=
nullptr);
680 void SequenceJob::setDome(
ISD::Dome *dome)
682 stateMachine->m_CaptureState->hasDome = (dome !=
nullptr);
687 m_FilterManager = manager;
690 void SequenceJob::setFrameType(CCDFrameType value)
694 stateMachine->setFrameType(value);
698 CCDFrameType SequenceJob::getFrameType()
const
703 QString SequenceJob::getSignature()
705 auto localDirectory = getCoreProperty(SJ_LocalDirectory).toString();
706 auto directoryPostfix = getCoreProperty(SJ_DirectoryPostfix).toString();
707 auto fullPrefix = getCoreProperty(SJ_FullPrefix).toString();
713 void SequenceJob::prepareCapture()
716 connectDeviceAdaptor();
718 switch (getFrameType())
721 stateMachine->prepareLightFrameCapture(getCoreProperty(SJ_EnforceTemperature).toBool(),
722 getCoreProperty(SJ_EnforceStartGuiderDrift).toBool() && getCoreProperty(SJ_GuiderActive).toBool(),
723 getCoreProperty(SJ_Preview).toBool());
726 stateMachine->prepareFlatFrameCapture(getCoreProperty(SJ_EnforceTemperature).toBool(),
727 getCoreProperty(SJ_Preview).toBool());
730 stateMachine->prepareDarkFrameCapture(getCoreProperty(SJ_EnforceTemperature).toBool(),
731 getCoreProperty(SJ_Preview).toBool());
734 stateMachine->prepareBiasFrameCapture(getCoreProperty(SJ_EnforceTemperature).toBool(),
735 getCoreProperty(SJ_Preview).toBool());
739 processPrepareComplete();
744 void SequenceJob::processPrepareComplete()
746 disconnectDeviceAdaptor();
747 emit prepareComplete();
750 void SequenceJob::processAbortCapture()
752 disconnectDeviceAdaptor();
756 IPState SequenceJob::checkFlatFramePendingTasksCompleted()
762 void SequenceJob::setCoreProperty(PropertyID
id,
const QVariant &value)
768 if( !getCoreProperty(SequenceJob::SJ_Preview).toBool() &&
nullptr != countCell)
769 countCell->setText(
QString(
"%L1/%L2").arg(m_Completed).arg(value.
toInt()));
772 case SJ_RemoteDirectory:
775 if (remoteDir.endsWith(
'/'))
778 m_CoreProperties[id] = remoteDir;
783 case SJ_GuiderActive:
788 if (m_CoreProperties[SJ_GuiderActive] != value)
790 stateMachine->setEnforceInitialGuidingDrift(value.
toBool() &&
791 m_CoreProperties[SJ_EnforceStartGuiderDrift].toBool());
798 m_CoreProperties[id] = value;
801 QVariant SequenceJob::getCoreProperty(PropertyID
id)
const
803 return m_CoreProperties[id];