File indexing completed on 2024-04-21 03:43:37

0001 /*
0002     SPDX-FileCopyrightText: 2023 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QObject>
0010 #include <QProcess>
0011 #include "ekos/ekos.h"
0012 #include "indi/indiweather.h"
0013 #include "kstarsdatetime.h"
0014 #include "geolocation.h"
0015 #include "schedulertypes.h"
0016 #include <QDateTime>
0017 #include <QUrl>
0018 
0019 class SchedulerJob;
0020 
0021 namespace Ekos
0022 {
0023 
0024 class SchedulerProcess;
0025 class SchedulerJob;
0026 
0027 /**
0028  * @class SchedulerState
0029  * @brief The SchedulerState class holds all attributes defining the scheduler's state.
0030  */
0031 class SchedulerModuleState : public QObject
0032 {
0033     Q_OBJECT
0034 
0035 public:
0036 
0037 
0038     SchedulerModuleState();
0039 
0040     // ////////////////////////////////////////////////////////////////////
0041     // Overall scheduler state
0042     // ////////////////////////////////////////////////////////////////////
0043     /**
0044      * @brief init Set initial conditions that need to be set before starting
0045      */
0046     void init();
0047 
0048     // ////////////////////////////////////////////////////////////////////
0049     // profiles and scheduler jobs
0050     // ////////////////////////////////////////////////////////////////////
0051 
0052     const QString &currentProfile() const
0053     {
0054         return m_currentProfile;
0055     }
0056     /**
0057      * @brief setCurrentProfile Set the current profile name.
0058      * @param newName new current profile name
0059      * @param signal send an update efent if true
0060      */
0061     void setCurrentProfile(const QString &newName, bool signal = true);
0062 
0063     const QStringList &profiles() const
0064     {
0065         return m_profiles;
0066     }
0067     void updateProfiles(const QStringList &newProfiles);
0068 
0069     SchedulerJob *activeJob() const
0070     {
0071         return m_activeJob;
0072     }
0073     void setActiveJob(SchedulerJob *newActiveJob);
0074 
0075     QList<SchedulerJob *> &mutlableJobs()
0076     {
0077         return m_jobs;
0078     }
0079     const QList<SchedulerJob *> &jobs() const
0080     {
0081         return m_jobs;
0082     }
0083 
0084     void setJobs(QList<SchedulerJob *> &newJobs)
0085     {
0086         m_jobs = newJobs;
0087     }
0088 
0089     /**
0090      * @brief updateStage Helper function that updates the stage label of the active job.
0091      */
0092     void updateJobStage(SchedulerJobStage stage);
0093 
0094     /**
0095      * @brief getJSONJobs get jobs in JSON format
0096      * @return
0097      */
0098     QJsonArray getJSONJobs();
0099 
0100 
0101     // ////////////////////////////////////////////////////////////////////
0102     // state attributes accessors
0103     // ////////////////////////////////////////////////////////////////////
0104 
0105     bool dirty() const
0106     {
0107         return m_dirty;
0108     }
0109     void setDirty(bool value)
0110     {
0111         m_dirty = value;
0112     }
0113 
0114     // (coarse grained) execution state of the scheduler
0115     const SchedulerState &schedulerState() const
0116     {
0117         return m_schedulerState;
0118     }
0119     void setSchedulerState(const SchedulerState &newState);
0120 
0121     const StartupState &startupState() const
0122     {
0123         return m_startupState;
0124     }
0125 
0126     int currentPosition() const
0127     {
0128         return m_currentPosition;
0129     }
0130     void setCurrentPosition(int newCurrentPosition);
0131 
0132     void setStartupState(StartupState state);
0133 
0134     const QUrl &startupScriptURL() const
0135     {
0136         return m_startupScriptURL;
0137     }
0138     void setStartupScriptURL(const QUrl &newURL)
0139     {
0140         m_startupScriptURL = newURL;
0141     }
0142 
0143     const ShutdownState &shutdownState() const
0144     {
0145         return m_shutdownState;
0146     }
0147     void setShutdownState(ShutdownState state);    
0148 
0149     const QUrl &shutdownScriptURL() const
0150     {
0151         return m_shutdownScriptURL;
0152     }
0153     void setShutdownScriptURL(const QUrl &newShutdownScriptURL)
0154     {
0155         m_shutdownScriptURL = newShutdownScriptURL;
0156     }
0157 
0158     const ParkWaitState &parkWaitState() const
0159     {
0160         return m_parkWaitState;
0161     }
0162     void setParkWaitState(ParkWaitState state);
0163 
0164     /**
0165      * @brief True if the scheduler is between iterations and delaying longer than the typical update period.
0166      */
0167     bool currentlySleeping()
0168     {
0169         return iterationTimer().isActive() && timerState() == RUN_WAKEUP;
0170     }
0171 
0172     // ////////////////////////////////////////////////////////////////////
0173     // job handling
0174     // ////////////////////////////////////////////////////////////////////
0175 
0176     /**
0177      * @brief removeJob Remove the job from the job list at the given position.
0178      * If this is the currently active job, don't remove it and return false.
0179      * @return true iff removing succeeded
0180      */
0181     bool removeJob(const int currentRow);
0182 
0183 
0184     // ////////////////////////////////////////////////////////////////////
0185     // Controls for the preemptive shutdown feature.
0186     // ////////////////////////////////////////////////////////////////////
0187     // Is the scheduler shutting down until later when it will resume a job?
0188     void enablePreemptiveShutdown(const QDateTime &wakeupTime);
0189     void disablePreemptiveShutdown();
0190     const QDateTime &preemptiveShutdownWakeupTime() const;
0191     bool preemptiveShutdown() const;
0192 
0193 
0194     // ////////////////////////////////////////////////////////////////////
0195     // overall EKOS state
0196     // ////////////////////////////////////////////////////////////////////    
0197     EkosState ekosState() const
0198     {
0199         return m_ekosState;
0200     }
0201     void setEkosState(EkosState state);
0202     // last communication result with EKOS
0203     CommunicationStatus ekosCommunicationStatus() const
0204     {
0205         return m_EkosCommunicationStatus;
0206     }
0207     void setEkosCommunicationStatus(CommunicationStatus newEkosCommunicationStatus)
0208     {
0209         m_EkosCommunicationStatus = newEkosCommunicationStatus;
0210     }
0211     // counter for failed EKOS connection attempts
0212     void resetEkosConnectFailureCount(uint8_t newEkosConnectFailureCount = 0)
0213     {
0214         m_ekosConnectFailureCount = newEkosConnectFailureCount;
0215     }
0216     bool increaseEkosConnectFailureCount();
0217 
0218     void resetParkingCapFailureCount(uint8_t value = 0)
0219     {
0220         m_parkingCapFailureCount = value;
0221     }
0222     bool increaseParkingCapFailureCount();
0223     void resetParkingMountFailureCount(uint8_t value = 0)
0224     {
0225         m_parkingMountFailureCount = value;
0226     }
0227     bool increaseParkingMountFailureCount();
0228     uint8_t parkingMountFailureCount() const
0229     {
0230         return m_parkingMountFailureCount;
0231     }
0232     void resetParkingDomeFailureCount(uint8_t value = 0)
0233     {
0234         m_parkingDomeFailureCount = value;
0235     }
0236     bool increaseParkingDomeFailureCount();
0237 
0238     int indexToUse() const
0239     {
0240         return m_IndexToUse;
0241     }
0242     void setIndexToUse(int newIndexToUse)
0243     {
0244         m_IndexToUse = newIndexToUse;
0245     }
0246 
0247     int healpixToUse() const
0248     {
0249         return m_HealpixToUse;
0250     }
0251     void setHealpixToUse(int newHealpixToUse)
0252     {
0253         m_HealpixToUse = newHealpixToUse;
0254     }
0255 
0256     QMap<QString, uint16_t> &capturedFramesCount()
0257     {
0258         return m_CapturedFramesCount;
0259     }
0260 
0261     void setCapturedFramesCount(const QMap<QString, uint16_t> &newCapturedFramesCount)
0262     {
0263         m_CapturedFramesCount = newCapturedFramesCount;
0264     }
0265 
0266     /**
0267      * @brief resetFailureCounters Reset all failure counters
0268      */
0269     void resetFailureCounters();
0270 
0271     // ////////////////////////////////////////////////////////////////////
0272     // overall INDI state
0273     // ////////////////////////////////////////////////////////////////////
0274     INDIState indiState() const
0275     {
0276         return m_indiState;
0277     }
0278     void setIndiState(INDIState state);
0279     // last communication result with INDI
0280     CommunicationStatus indiCommunicationStatus() const
0281     {
0282         return m_INDICommunicationStatus;
0283     }
0284     void setIndiCommunicationStatus(CommunicationStatus newINDICommunicationStatus)
0285     {
0286         m_INDICommunicationStatus = newINDICommunicationStatus;
0287     }
0288     // counters for failed INDI connection attempts
0289     void resetIndiConnectFailureCount(uint8_t newIndiConnectFailureCount = 0)
0290     {
0291         m_indiConnectFailureCount = newIndiConnectFailureCount;
0292     }
0293     bool increaseIndiConnectFailureCount();
0294     /**
0295      * @brief isINDIConnected Determines the status of the INDI connection.
0296      * @return True if INDI connection is up and usable, else false.
0297      */
0298     bool isINDIConnected() const
0299     {
0300         return (indiCommunicationStatus() == Ekos::Success);
0301     }
0302     // ////////////////////////////////////////////////////////////////////
0303     // device states
0304     // ////////////////////////////////////////////////////////////////////
0305     bool mountReady() const
0306     {
0307         return m_MountReady;
0308     }
0309     void setMountReady(bool readiness)
0310     {
0311         m_MountReady = readiness;
0312     }
0313     bool captureReady() const
0314     {
0315         return m_CaptureReady;
0316     }
0317     void setCaptureReady(bool readiness)
0318     {
0319         m_CaptureReady = readiness;
0320     }
0321     bool domeReady() const
0322     {
0323         return m_DomeReady;
0324     }
0325     void setDomeReady(bool readiness)
0326     {
0327         m_DomeReady = readiness;
0328     }
0329     bool capReady() const
0330     {
0331         return m_CapReady;
0332     }
0333     void setCapReady(bool readiness)
0334     {
0335         m_CapReady = readiness;
0336     }
0337 
0338     uint16_t captureBatch() const
0339     {
0340         return m_captureBatch;
0341     }
0342     void resetCaptureBatch()
0343     {
0344         m_captureBatch = 0;
0345     }
0346     uint16_t increaseCaptureBatch()
0347     {
0348         return m_captureBatch++;
0349     }
0350 
0351     uint8_t captureFailureCount() const
0352     {
0353         return m_captureFailureCount;
0354     }
0355     void resetCaptureFailureCount()
0356     {
0357         m_captureFailureCount = 0;
0358     }
0359     bool increaseCaptureFailureCount();
0360 
0361     uint8_t focusFailureCount() const
0362     {
0363         return m_focusFailureCount;
0364     }
0365     void resetFocusFailureCount()
0366     {
0367         m_focusFailureCount = 0;
0368     }
0369     bool increaseFocusFailureCount();
0370 
0371     bool autofocusCompleted() const
0372     {
0373         return m_autofocusCompleted;
0374     }
0375     void setAutofocusCompleted(bool value)
0376     {
0377         m_autofocusCompleted = value;
0378     }
0379 
0380     uint8_t guideFailureCount() const
0381     {
0382         return m_guideFailureCount;
0383     }
0384     void resetGuideFailureCount()
0385     {
0386         m_guideFailureCount = 0;
0387     }
0388     bool increaseGuideFailureCount();
0389 
0390     uint8_t alignFailureCount() const
0391     {
0392         return m_alignFailureCount;
0393     }
0394     void resetAlignFailureCount()
0395     {
0396         m_alignFailureCount = 0;
0397     }
0398     bool increaseAlignFailureCount();
0399 
0400     int restartGuidingInterval() const
0401     {
0402         return m_restartGuidingInterval;
0403     }
0404 
0405     const KStarsDateTime &restartGuidingTime() const
0406     {
0407         return m_restartGuidingTime;
0408     }
0409 
0410     ISD::Weather::Status weatherStatus() const
0411     {
0412         return m_weatherStatus;
0413     }
0414     void setWeatherStatus(ISD::Weather::Status newWeatherStatus)
0415     {
0416         m_weatherStatus = newWeatherStatus;
0417     }
0418 
0419     // ////////////////////////////////////////////////////////////////////
0420     // Timers and time
0421     // ////////////////////////////////////////////////////////////////////
0422     // Returns milliseconds since startCurrentOperationTImer() was called.
0423     qint64 getCurrentOperationMsec() const;
0424     // Starts the above operation timer.
0425     // TODO. It would be better to make this a class and give each operation its own timer.
0426     // TODO. These should be disabled once no longer relevant.
0427     // These are implement with a KStarsDateTime instead of a QTimer type class
0428     // so that the simulated clock can be used.
0429     void startCurrentOperationTimer();
0430 
0431     // Controls for the guiding timer, which restarts guiding after failure.
0432     void cancelGuidingTimer();
0433     bool isGuidingTimerActive();
0434     void startGuidingTimer(int milliseconds);
0435 
0436     /** @brief Setter used in testing to fix the local time. Otherwise getter gets from KStars instance. */
0437     /** @{ */
0438     static KStarsDateTime getLocalTime();
0439     static void setLocalTime(KStarsDateTime *time)
0440     {
0441         storedLocalTime = time;
0442     }
0443     static bool hasLocalTime()
0444     {
0445         return storedLocalTime != nullptr;
0446     }
0447 
0448     /** @} */
0449 
0450 
0451     // ////////////////////////////////////////////////////////////////////
0452     // Astronomical calculations
0453     // ////////////////////////////////////////////////////////////////////
0454     /**
0455      * @brief calculateDawnDusk find the next astronomical dawn and dusk after the current date and time of observation
0456      */
0457     static void calculateDawnDusk(QDateTime const &when, QDateTime &nDawn, QDateTime &nDusk);
0458 
0459     /**
0460      * @brief calculateDawnDusk Calculate dawn and dusk times for today
0461      */
0462     void calculateDawnDusk();
0463 
0464     static QDateTime Dawn()
0465     {
0466         return m_Dawn;
0467     }
0468     static QDateTime Dusk()
0469     {
0470         return m_Dusk;
0471     }
0472     static QDateTime PreDawnDateTime()
0473     {
0474         return m_PreDawnDateTime;
0475     }
0476 
0477     /** @brief Setter used in testing to fix the geo location. Otherwise getter gets from KStars instance. */
0478     /** @{ */
0479     static const GeoLocation *getGeo();
0480     static void setGeo(GeoLocation *geo)
0481     {
0482         storedGeo = geo;
0483     }
0484     static bool hasGeo();
0485 
0486     // ////////////////////////////////////////////////////////////////////
0487     // Scheduler iterations
0488     // ////////////////////////////////////////////////////////////////////
0489 
0490     // Setup the parameters for the next scheduler iteration.
0491     // When milliseconds is not passed in, it uses m_UpdatePeriodMs.
0492     void setupNextIteration(SchedulerTimerState nextState);
0493     void setupNextIteration(SchedulerTimerState nextState, int milliseconds);
0494 
0495     SchedulerTimerState timerState() const
0496     {
0497         return m_timerState;
0498     }
0499 
0500     void setTimerState(SchedulerTimerState newTimerState)
0501     {
0502         m_timerState = newTimerState;
0503     }
0504 
0505     QTimer &iterationTimer()
0506     {
0507         return m_iterationTimer;
0508     }
0509 
0510     bool iterationSetup() const
0511     {
0512         return m_iterationSetup;
0513     }
0514     void setIterationSetup(bool setup)
0515     {
0516         m_iterationSetup = setup;
0517     }
0518 
0519     qint64 startMSecs() const
0520     {
0521         return m_startMSecs;
0522     }
0523     void setStartMSecs(qint64 value)
0524     {
0525         m_startMSecs = value;
0526     }
0527     int increaseSchedulerIteration()
0528     {
0529         return ++m_schedulerIteration;
0530     }
0531     void resetSchedulerIteration()
0532     {
0533         m_schedulerIteration = 0;
0534     }
0535 
0536     int timerInterval() const
0537     {
0538         return m_timerInterval;
0539     }
0540     void setTimerInterval(int value)
0541     {
0542         m_timerInterval = value;
0543     }
0544 
0545     void setUpdatePeriodMs(int ms)
0546     {
0547         m_UpdatePeriodMs = ms;
0548     }
0549      int updatePeriodMs() const
0550     {
0551         return m_UpdatePeriodMs;
0552     }
0553 
0554      uint sequenceExecutionCounter() const
0555      {
0556          return m_sequenceExecutionCounter;
0557      }
0558      void resetSequenceExecutionCounter()
0559      {
0560          m_sequenceExecutionCounter = 1;
0561      }
0562      void increaseSequenceExecutionCounter()
0563      {
0564          m_sequenceExecutionCounter++;
0565      }
0566 
0567      static uint maxFailureAttempts();
0568 
0569      /**
0570       * @brief checkRepeatSequence Check if the entire job sequence might be repeated
0571       * @return true if the checkbox is set and the number of iterations is below the
0572       * configured threshold
0573       */
0574      bool checkRepeatSequence();
0575 
0576 signals:
0577     // ////////////////////////////////////////////////////////////////////
0578     // communication with the UI
0579     // ////////////////////////////////////////////////////////////////////
0580     // State change of EKOS
0581     void ekosStateChanged(EkosState state);
0582     // State change of INDI
0583     void indiStateChanged(INDIState state);
0584     // overall scheduler state changed
0585     void schedulerStateChanged(SchedulerState state);
0586     // startup state
0587     void startupStateChanged(StartupState state);
0588     // shutdown state
0589     void shutdownStateChanged(ShutdownState state);
0590     // parking state
0591     void parkWaitStateChanged(ParkWaitState state);
0592     // profiles updated
0593     void profilesChanged();
0594     // current profile changed
0595     void currentProfileChanged();
0596     // new log text for the module log window
0597     void newLog(const QString &text);
0598     // current position in the job list changed
0599     void currentPositionChanged(int pos);
0600     // job stage of the current job changed
0601     void jobStageChanged(SchedulerJobStage stage);
0602     // night time calculation updated
0603     void updateNightTime(SchedulerJob const * job = nullptr);
0604 
0605 
0606 private:
0607     // ////////////////////////////////////////////////////////////////////
0608     // Scheduler jobs
0609     // ////////////////////////////////////////////////////////////////////
0610     // List of all jobs as entered by the user or file
0611     QList<SchedulerJob *> m_jobs;
0612     // Active job
0613     SchedulerJob *m_activeJob { nullptr };
0614 
0615     // ////////////////////////////////////////////////////////////////////
0616     // state attributes
0617     // ////////////////////////////////////////////////////////////////////
0618     // coarse grained state describing the general execution state
0619     SchedulerState m_schedulerState { SCHEDULER_IDLE };
0620     // states of the scheduler startup
0621     StartupState m_startupState { STARTUP_IDLE };
0622     // Startup script URL
0623     QUrl m_startupScriptURL;
0624     // states of the scheduler shutdown
0625     ShutdownState m_shutdownState { SHUTDOWN_IDLE };
0626     // current position on the job list - necessary if there is no line selected in the
0627     // UI, for example after deleting a row.
0628     int m_currentPosition { -1 };
0629     // Shutdown script URL
0630     QUrl m_shutdownScriptURL;
0631     // states of parking
0632     ParkWaitState m_parkWaitState { PARKWAIT_IDLE };
0633     // current profile
0634     QString m_currentProfile;
0635     // all profiles
0636     QStringList m_profiles;
0637     // Was job modified and needs saving?
0638     bool m_dirty { false };
0639 
0640     // EKOS state describing whether EKOS is running (remember that the scheduler
0641     // does not need EKOS running).
0642     EkosState m_ekosState { EKOS_IDLE };
0643     // Execution state of INDI
0644     INDIState m_indiState { INDI_IDLE };
0645     // Last communication result with EKOS and INDI
0646     CommunicationStatus m_EkosCommunicationStatus { Ekos::Idle };
0647     CommunicationStatus m_INDICommunicationStatus { Ekos::Idle };
0648 
0649     // device readiness
0650     bool m_MountReady { false };
0651     bool m_CaptureReady { false };
0652     bool m_DomeReady { false };
0653     bool m_CapReady { false };
0654 
0655     // Restricts (the internal solver) to using the index and healpix
0656     // from the previous solve, if that solve was successful, when
0657     // doing the pointing check. -1 means no restriction.
0658     int m_IndexToUse { -1 };
0659     int m_HealpixToUse { -1 };
0660 
0661     // Check if initial autofocus is completed and do not run autofocus until
0662     // there is a change is telescope position/alignment.
0663     bool m_autofocusCompleted { false };
0664 
0665     // Keep watch of weather status
0666     ISD::Weather::Status m_weatherStatus { ISD::Weather::WEATHER_IDLE };
0667 
0668     // ////////////////////////////////////////////////////////////////////
0669     // counters
0670     // ////////////////////////////////////////////////////////////////////
0671     // count for job sequence iteration
0672     uint m_sequenceExecutionCounter { 1 };
0673     // Keep track of INDI connection failures
0674     uint8_t m_indiConnectFailureCount { 0 };
0675     // Keep track of Ekos connection failures
0676     uint8_t m_ekosConnectFailureCount { 0 };
0677     // failures parking dust cap
0678     uint8_t m_parkingCapFailureCount { 0 };
0679     // failures parking mount
0680     uint8_t m_parkingMountFailureCount { 0 };
0681     // failures parking dome
0682     uint8_t m_parkingDomeFailureCount { 0 };
0683     // How many repeated job batches did we complete thus far?
0684     uint16_t m_captureBatch { 0 };
0685     // Keep track of Ekos capture module failures
0686     uint8_t m_captureFailureCount { 0 };
0687     // Keep track of Ekos focus module failures
0688     uint8_t m_focusFailureCount { 0 };
0689     // Keep track of Ekos guide module failures
0690     uint8_t m_guideFailureCount { 0 };
0691     // Keep track of Ekos align module failures
0692     uint8_t m_alignFailureCount { 0 };
0693     // frames count for all signatures
0694     QMap<QString, uint16_t> m_CapturedFramesCount;
0695 
0696     // ////////////////////////////////////////////////////////////////////
0697     // Scheduler iterations
0698     // ////////////////////////////////////////////////////////////////////
0699 
0700     // The type of scheduler iteration that should be run next.
0701     SchedulerTimerState m_timerState { RUN_NOTHING };
0702     // Variable keeping the number of millisconds the scheduler should wait
0703     // after the current scheduler iteration.
0704     int m_timerInterval { -1 };
0705     // Whether the scheduler has been setup for the next iteration,
0706     // that is, whether timerInterval and timerState have been set this iteration.
0707     bool m_iterationSetup { false };
0708     // The timer used to wakeup the scheduler between iterations.
0709     QTimer m_iterationTimer;
0710     // Counter for how many scheduler iterations have been processed.
0711     int m_schedulerIteration { 0 };
0712     // The time when the scheduler first started running iterations.
0713     qint64 m_startMSecs { 0 };
0714     // This is the time between typical scheduler iterations.
0715     // The time can be modified for testing.
0716     int m_UpdatePeriodMs = 1000;
0717 
0718     // ////////////////////////////////////////////////////////////////////
0719     // time and timers
0720     // ////////////////////////////////////////////////////////////////////
0721     // constants for current dawn and dusk
0722     /// Store next dawn to calculate dark skies range
0723     static QDateTime m_Dawn;
0724     /// Store next dusk to calculate dark skies range
0725     static QDateTime m_Dusk;
0726     /// Pre-dawn is where we stop all jobs, it is a user-configurable value before Dawn.
0727     static QDateTime m_PreDawnDateTime;
0728     // Generic time to track timeout of current operation in progress.
0729     // Used by startCurrentOperationTimer() and getCurrentOperationMsec().
0730     KStarsDateTime currentOperationTime;
0731     bool currentOperationTimeStarted { false };
0732     // Delay for restarting the guider
0733     int m_restartGuidingInterval { -1 };
0734     KStarsDateTime m_restartGuidingTime;
0735     // Used in testing, instead of KStars::Instance() resources
0736     static KStarsDateTime *storedLocalTime;
0737     // The various preemptiveShutdown states are controlled by this one variable.
0738     QDateTime m_preemptiveShutdownWakeupTime;
0739 
0740     // These are used in testing, instead of KStars::Instance() resources
0741     static GeoLocation *storedGeo;
0742 
0743 };
0744 } // Ekos namespace