File indexing completed on 2024-04-28 03:43:19

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "sequencequeue.h"
0008 
0009 #include <KNotifications/KNotification>
0010 #include <ekos_capture_debug.h>
0011 
0012 #include "capturemodulestate.h"
0013 #include "capturedeviceadaptor.h"
0014 #include "ksnotification.h"
0015 #include "sequencejob.h"
0016 
0017 // Current Sequence File Format:
0018 constexpr double SQ_FORMAT_VERSION = 2.6;
0019 // We accept file formats with version back to:
0020 constexpr double SQ_COMPAT_VERSION = 2.0;
0021 
0022 namespace Ekos
0023 {
0024 
0025 bool SequenceQueue::load(const QString &fileURL, const QString &targetName,
0026                          const QSharedPointer<CaptureDeviceAdaptor> devices,
0027                          const QSharedPointer<CaptureModuleState> state)
0028 {
0029     QFile sFile(fileURL);
0030     if (!sFile.open(QIODevice::ReadOnly))
0031     {
0032         QString message = i18n("Unable to open file %1", fileURL);
0033         KSNotification::sorry(message, i18n("Could Not Open File"));
0034         return false;
0035     }
0036 
0037     LilXML * xmlParser = newLilXML();
0038 
0039     char errmsg[MAXRBUF];
0040     XMLEle * root = nullptr;
0041     XMLEle * ep   = nullptr;
0042     char c;
0043 
0044     // We expect all data read from the XML to be in the C locale - QLocale::c().
0045     QLocale cLocale = QLocale::c();
0046 
0047     while (sFile.getChar(&c))
0048     {
0049         root = readXMLEle(xmlParser, c, errmsg);
0050 
0051         if (root)
0052         {
0053             double sqVersion = cLocale.toDouble(findXMLAttValu(root, "version"));
0054             if (sqVersion < SQ_COMPAT_VERSION)
0055             {
0056                 emit newLog(i18n("Deprecated sequence file format version %1. Please construct a new sequence file.",
0057                                  sqVersion));
0058                 return false;
0059             }
0060 
0061             for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
0062             {
0063                 if (!strcmp(tagXMLEle(ep), "Observer"))
0064                 {
0065                     state->setObserverName(QString(pcdataXMLEle(ep)));
0066                 }
0067                 else if (!strcmp(tagXMLEle(ep), "GuideDeviation"))
0068                 {
0069                     m_GuideDeviationSet = true;
0070                     m_EnforceGuideDeviation = !strcmp(findXMLAttValu(ep, "enabled"), "true");
0071                     m_GuideDeviation = cLocale.toDouble(pcdataXMLEle(ep));
0072                 }
0073                 else if (!strcmp(tagXMLEle(ep), "CCD"))
0074                 {
0075                     // Old field in some files. Without this empty test, it would fall through to the else condition and create a job.
0076                 }
0077                 else if (!strcmp(tagXMLEle(ep), "FilterWheel"))
0078                 {
0079                     // Old field in some files. Without this empty test, it would fall through to the else condition and create a job.
0080                 }
0081                 else if (!strcmp(tagXMLEle(ep), "GuideStartDeviation"))
0082                 {
0083                     m_GuideStartDeviationSet = true;
0084                     m_EnforceStartGuiderDrift = !strcmp(findXMLAttValu(ep, "enabled"), "true");
0085                     m_StartGuideDeviation = cLocale.toDouble(pcdataXMLEle(ep));
0086                 }
0087                 else if (!strcmp(tagXMLEle(ep), "Autofocus"))
0088                 {
0089                     // Old field in some files. Without this empty test, it would fall through to the else condition and create a job.
0090                 }
0091                 else if (!strcmp(tagXMLEle(ep), "HFRCheck"))
0092                 {
0093                     m_AutofocusSet = true;
0094                     m_EnforceAutofocusHFR = !strcmp(findXMLAttValu(ep, "enabled"), "true");
0095 
0096                     XMLEle *epHFR;
0097                     //Set default values in case of malformed XML
0098                     m_HFRDeviation = 0.0;
0099                     m_HFRCheckAlgorithm = HFR_CHECK_LAST_AUTOFOCUS;
0100                     m_HFRCheckThresholdPercentage = HFR_CHECK_DEFAULT_THRESHOLD;
0101                     m_HFRCheckFrames = 1;
0102 
0103                     for (epHFR = nextXMLEle(ep, 1); epHFR != nullptr; epHFR = nextXMLEle(ep, 0))
0104                     {
0105                         if (!strcmp(tagXMLEle(epHFR), "HFRDeviation"))
0106                         {
0107                             double const HFRValue = cLocale.toDouble(pcdataXMLEle(epHFR));
0108                             // Set the HFR value from XML, or reset it to zero, don't let another unrelated older HFR be used
0109                             if (HFRValue >= 0.0)
0110                                 m_HFRDeviation = HFRValue;
0111                         }
0112 
0113                         if (!strcmp(tagXMLEle(epHFR), "HFRCheckAlgorithm"))
0114                         {
0115                             int HFRCheckAlgo = cLocale.toInt(pcdataXMLEle(epHFR));
0116                             // Set the HFR Check Algo from XML, or reset it to Last Autofocus
0117                             if (HFRCheckAlgo >= 0 && HFRCheckAlgo < HFR_CHECK_MAX_ALGO)
0118                                 m_HFRCheckAlgorithm = static_cast<HFR_CHECK_ALGORITHM>(HFRCheckAlgo);
0119                         }
0120                         else if (!strcmp(tagXMLEle(epHFR), "HFRCheckThreshold"))
0121                         {
0122                             double const hFRCheckThreshold = cLocale.toDouble(pcdataXMLEle(epHFR));
0123                             // Set the HFR Threshold Percentage from XML, or reset it to 10%, don't let another unrelated older value be used
0124                             if (hFRCheckThreshold >= 0.0)
0125                                 m_HFRCheckThresholdPercentage = hFRCheckThreshold;
0126                         }
0127                         else if (!strcmp(tagXMLEle(epHFR), "HFRCheckFrames"))
0128                         {
0129                             int const hFRCheckFrames = cLocale.toInt(pcdataXMLEle(epHFR));
0130                             // Set the HFR Frames from XML, or reset it to 1, don't let another unrelated older value be used
0131                             if (hFRCheckFrames > 1)
0132                                 m_HFRCheckFrames = hFRCheckFrames;
0133                         }
0134                     }
0135                 }
0136                 else if (!strcmp(tagXMLEle(ep), "RefocusOnTemperatureDelta"))
0137                 {
0138                     m_RefocusOnTemperatureDeltaSet = true;
0139                     m_EnforceAutofocusOnTemperature = !strcmp(findXMLAttValu(ep, "enabled"), "true");
0140                     double const deltaValue = cLocale.toDouble(pcdataXMLEle(ep));
0141                     m_MaxFocusTemperatureDelta = deltaValue;
0142                 }
0143                 else if (!strcmp(tagXMLEle(ep), "RefocusEveryN"))
0144                 {
0145                     m_RefocusEveryNSet = true;
0146                     m_EnforceRefocusEveryN = !strcmp(findXMLAttValu(ep, "enabled"), "true");
0147                     int const minutesValue = cLocale.toInt(pcdataXMLEle(ep));
0148                     // Set the refocus period from XML, or reset it to zero, don't let another unrelated older refocus period be used.
0149                     m_RefocusEveryN = minutesValue > 0 ? minutesValue : 0;
0150                 }
0151                 else if (!strcmp(tagXMLEle(ep), "RefocusOnMeridianFlip"))
0152                 {
0153                     m_RefocusOnMeridianFlipSet = true;
0154                     m_RefocusAfterMeridianFlip = !strcmp(findXMLAttValu(ep, "enabled"), "true");
0155                 }
0156                 else if (!strcmp(tagXMLEle(ep), "MeridianFlip"))
0157                 {
0158                     // meridian flip is managed by the mount only
0159                     // older files might nevertheless contain MF settings
0160                     if (! strcmp(findXMLAttValu(ep, "enabled"), "true"))
0161                         emit newLog(
0162                             i18n("Meridian flip configuration has been shifted to the mount module. Please configure the meridian flip there."));
0163                 }
0164                 else
0165                 {
0166                     auto job = new SequenceJob(devices, state, SequenceJob::JOBTYPE_BATCH, ep, targetName);
0167                     m_allJobs.append(job);
0168                 }
0169             }
0170             delXMLEle(root);
0171         }
0172         else if (errmsg[0])
0173         {
0174             emit newLog(QString(errmsg));
0175             delLilXML(xmlParser);
0176             return false;
0177         }
0178     }
0179 
0180     state->setSequenceURL(QUrl::fromLocalFile(fileURL));
0181     state->setDirty(false);
0182     delLilXML(xmlParser);
0183     return true;
0184 }
0185 
0186 void SequenceQueue::setOptions()
0187 {
0188     if (m_GuideDeviationSet)
0189     {
0190         Options::setEnforceGuideDeviation(m_EnforceGuideDeviation);
0191         Options::setGuideDeviation(m_GuideDeviation);
0192     }
0193     if (m_GuideStartDeviationSet)
0194     {
0195         Options::setEnforceStartGuiderDrift(m_EnforceStartGuiderDrift);
0196         Options::setStartGuideDeviation(m_StartGuideDeviation);
0197     }
0198     if (m_AutofocusSet)
0199     {
0200         Options::setEnforceAutofocusHFR(m_EnforceAutofocusHFR);
0201         Options::setHFRCheckAlgorithm(m_HFRCheckAlgorithm);
0202         Options::setHFRThresholdPercentage(m_HFRCheckThresholdPercentage);
0203         Options::setInSequenceCheckFrames(m_HFRCheckFrames);
0204         Options::setHFRDeviation(m_HFRDeviation);
0205     }
0206     if (m_RefocusOnTemperatureDeltaSet)
0207     {
0208         Options::setEnforceAutofocusOnTemperature(m_EnforceAutofocusOnTemperature);
0209         Options::setMaxFocusTemperatureDelta(m_MaxFocusTemperatureDelta);
0210     }
0211     if (m_RefocusEveryNSet)
0212     {
0213         Options::setEnforceRefocusEveryN(m_EnforceRefocusEveryN);
0214         Options::setRefocusEveryN(m_RefocusEveryN);
0215     }
0216     if (m_RefocusOnMeridianFlipSet)
0217     {
0218         Options::setRefocusAfterMeridianFlip(m_RefocusAfterMeridianFlip);
0219     }
0220 }
0221 
0222 void SequenceQueue::loadOptions()
0223 {
0224     m_GuideDeviationSet = true;
0225 
0226     m_EnforceGuideDeviation = Options::enforceGuideDeviation();
0227     m_GuideDeviation = Options::guideDeviation();
0228 
0229     m_GuideStartDeviationSet = true;
0230     m_EnforceStartGuiderDrift = Options::enforceStartGuiderDrift();
0231     m_StartGuideDeviation = Options::startGuideDeviation();
0232 
0233     m_AutofocusSet = true;
0234     m_EnforceAutofocusHFR = Options::enforceAutofocusHFR();
0235     m_HFRCheckAlgorithm = static_cast<HFR_CHECK_ALGORITHM>(Options::hFRCheckAlgorithm());
0236     m_HFRCheckThresholdPercentage = Options::hFRThresholdPercentage();
0237     m_HFRCheckFrames = Options::inSequenceCheckFrames();
0238     m_HFRDeviation = Options::hFRDeviation();
0239 
0240     m_RefocusOnTemperatureDeltaSet = true;
0241     m_EnforceAutofocusOnTemperature = Options::enforceAutofocusOnTemperature();
0242     m_MaxFocusTemperatureDelta = Options::maxFocusTemperatureDelta();
0243 
0244     m_RefocusEveryNSet = true;
0245     m_EnforceRefocusEveryN = Options::enforceRefocusEveryN();
0246     m_RefocusEveryN = Options::refocusEveryN();
0247 
0248     m_RefocusOnMeridianFlipSet = true;
0249     m_RefocusAfterMeridianFlip = Options::refocusAfterMeridianFlip();
0250 }
0251 
0252 bool SequenceQueue::save(const QString &path, const QString &observerName)
0253 {
0254     QFile file;
0255     file.setFileName(path);
0256 
0257     if (!file.open(QIODevice::WriteOnly))
0258     {
0259         QString message = i18n("Unable to write to file %1", path);
0260         KSNotification::sorry(message, i18n("Could not open file"));
0261         return false;
0262     }
0263 
0264     QTextStream outstream(&file);
0265 
0266     // We serialize sequence data to XML using the C locale
0267     QLocale cLocale = QLocale::c();
0268 
0269     outstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << Qt::endl;
0270     outstream << "<SequenceQueue version='" << SQ_FORMAT_VERSION << "'>" << Qt::endl;
0271     if (observerName.isEmpty() == false)
0272         outstream << "<Observer>" << observerName << "</Observer>" << Qt::endl;
0273     outstream << "<GuideDeviation enabled='" << (m_EnforceGuideDeviation ? "true" : "false") << "'>"
0274               << cLocale.toString(m_GuideDeviation) << "</GuideDeviation>" << Qt::endl;
0275     outstream << "<GuideStartDeviation enabled='" << (m_EnforceStartGuiderDrift ? "true" : "false") << "'>"
0276               << cLocale.toString(m_StartGuideDeviation) << "</GuideStartDeviation>" << Qt::endl;
0277     outstream << "<HFRCheck enabled='" << (m_EnforceAutofocusHFR ? "true" : "false") << "'>" << Qt::endl;
0278     outstream << "<HFRDeviation>" << cLocale.toString(m_HFRDeviation) << "</HFRDeviation>" << Qt::endl;
0279     outstream << "<HFRCheckAlgorithm>" << m_HFRCheckAlgorithm << "</HFRCheckAlgorithm>" << Qt::endl;
0280     outstream << "<HFRCheckThreshold>" << cLocale.toString(m_HFRCheckThresholdPercentage) << "</HFRCheckThreshold>" << Qt::endl;
0281     outstream << "<HFRCheckFrames>" << cLocale.toString(m_HFRCheckFrames) << "</HFRCheckFrames>" << Qt::endl;
0282     outstream << "</HFRCheck>" << Qt::endl;
0283     outstream << "<RefocusOnTemperatureDelta enabled='" << (m_EnforceAutofocusOnTemperature ? "true" : "false") <<
0284               "'>"
0285               << cLocale.toString(m_MaxFocusTemperatureDelta) << "</RefocusOnTemperatureDelta>" << Qt::endl;
0286     outstream << "<RefocusEveryN enabled='" << (m_EnforceRefocusEveryN ? "true" : "false") << "'>"
0287               << cLocale.toString(m_RefocusEveryN) << "</RefocusEveryN>" << Qt::endl;
0288     outstream << "<RefocusOnMeridianFlip enabled='" << (m_RefocusAfterMeridianFlip ? "true" : "false") << "'/>"
0289               << Qt::endl;
0290 
0291     for (auto &job : m_allJobs)
0292     {
0293         job->saveTo(outstream, cLocale);
0294     }
0295 
0296     outstream << "</SequenceQueue>" << Qt::endl;
0297 
0298     emit newLog(i18n("Sequence queue saved to %1", path));
0299     file.flush();
0300     file.close();
0301 
0302     return true;
0303 }
0304 
0305 }
0306