File indexing completed on 2024-05-05 11:59:42
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 "schedulertypes.h" 0010 #include "ekos/auxiliary/modulelogger.h" 0011 #include "ekos/align/align.h" 0012 #include "indi/indiweather.h" 0013 #include "dms.h" 0014 0015 #include <QObject> 0016 #include <QPointer> 0017 #include <QDBusInterface> 0018 #include <QProcess> 0019 0020 namespace Ekos 0021 { 0022 0023 class SchedulerJob; 0024 class GreedyScheduler; 0025 class SchedulerModuleState; 0026 0027 /** 0028 * @class SchedulerProcess 0029 * @brief The SchedulerProcess class holds the entire business logic for controlling the 0030 * execution of the EKOS scheduler. 0031 */ 0032 class SchedulerProcess : public QObject, public ModuleLogger 0033 { 0034 Q_OBJECT 0035 0036 public: 0037 SchedulerProcess(QSharedPointer<SchedulerModuleState> state); 0038 0039 // //////////////////////////////////////////////////////////////////// 0040 // process steps 0041 // //////////////////////////////////////////////////////////////////// 0042 0043 /** 0044 * @brief execute Execute the schedule, start if idle or paused. 0045 */ 0046 void execute(); 0047 0048 /** 0049 * @brief findNextJob Check if the job met the completion criteria, and if it did, then it search for next job candidate. 0050 * If no jobs are found, it starts the shutdown stage. 0051 */ 0052 void findNextJob(); 0053 0054 /** 0055 * @brief stopCurrentJobAction Stop whatever action taking place in the current job (eg. capture, guiding...etc). 0056 */ 0057 void stopCurrentJobAction(); 0058 0059 /** 0060 * @brief executeJob After the best job is selected, we call this in order to start the process that will execute the job. 0061 * checkJobStatus slot will be connected in order to figure the exact state of the current job each second 0062 * @return True if job is accepted and can be executed, false otherwise. 0063 */ 0064 bool executeJob(SchedulerJob *job); 0065 0066 /** 0067 * @brief wakeUpScheduler Wake up scheduler from sleep state 0068 */ 0069 void wakeUpScheduler(); 0070 0071 /** 0072 * @brief Setup the main loop and start. 0073 */ 0074 void startScheduler(); 0075 0076 /** 0077 * @brief stopScheduler Stop the scheduler execution. If stopping succeeded, 0078 * a {@see #schedulerStopped()} signal is emitted 0079 */ 0080 void stopScheduler(); 0081 0082 /** 0083 * @brief shouldSchedulerSleep Check if the scheduler needs to sleep until the job is ready 0084 * @param job Job to check 0085 * @return True if we set the scheduler to sleep mode. False, if not required and we need to execute now 0086 */ 0087 bool shouldSchedulerSleep(SchedulerJob *job); 0088 0089 /** 0090 * @brief startSlew DBus call for initiating slew 0091 */ 0092 void startSlew(); 0093 0094 /** 0095 * @brief startFocusing DBus call for feeding ekos the specified settings and initiating focus operation 0096 */ 0097 void startFocusing(); 0098 0099 /** 0100 * @brief startAstrometry initiation of the capture and solve operation. We change the job state 0101 * after solver is started 0102 */ 0103 void startAstrometry(); 0104 0105 /** 0106 * @brief startGuiding After ekos is fed the calibration options, we start the guiding process 0107 * @param resetCalibration By default calibration is not reset until it is explicitly requested 0108 */ 0109 void startGuiding(bool resetCalibration = false); 0110 0111 /** 0112 * @brief stopGuiding After guiding is done we need to stop the process 0113 */ 0114 void stopGuiding(); 0115 0116 /** 0117 * @brief processGuidingTimer Check the guiding timer, and possibly restart guiding. 0118 */ 0119 void processGuidingTimer(); 0120 0121 /** 0122 * @brief startCapture The current job file name is solved to an url which is fed to ekos. We then start the capture process 0123 * @param restart Set to true if the goal to restart an existing sequence. The only difference is that when a sequence is restarted, sequence file 0124 * is not loaded from disk again since that results in erasing all the history of the capture process. 0125 */ 0126 void startCapture(bool restart = false); 0127 0128 /** 0129 * @brief updateCompletedJobsCount For each scheduler job, examine sequence job storage and count captures. 0130 * @param forced forces recounting captures unconditionally if true, else only IDLE, EVALUATION or new jobs are examined. 0131 */ 0132 void updateCompletedJobsCount(bool forced = false); 0133 0134 /** 0135 * @brief setSolverAction set the GOTO mode for the solver 0136 * @param mode 0 For Sync, 1 for SlewToTarget, 2 for Nothing 0137 */ 0138 void setSolverAction(Align::GotoMode mode); 0139 0140 /** 0141 * @brief loadProfiles Load the existing EKOS profiles 0142 */ 0143 void loadProfiles(); 0144 0145 /** 0146 * @brief checkEkosState Check ekos startup stages and take whatever action necessary to get Ekos up and running 0147 * @return True if Ekos is running, false if Ekos start up is in progress. 0148 */ 0149 bool checkEkosState(); 0150 0151 /** 0152 * @brief checkINDIState Check INDI startup stages and take whatever action necessary to get INDI devices connected. 0153 * @return True if INDI devices are connected, false if it is under progress. 0154 */ 0155 bool checkINDIState(); 0156 0157 /** 0158 * @brief completeShutdown Try to complete the scheduler shutdown 0159 * @return false iff some further action is required 0160 */ 0161 bool completeShutdown(); 0162 0163 /** 0164 * @brief disconnectINDI disconnect all INDI devices from server. 0165 */ 0166 void disconnectINDI(); 0167 0168 /** 0169 * @brief manageConnectionLoss Mitigate loss of connection with the INDI server. 0170 * @return true if connection to Ekos/INDI should be attempted again, false if not mitigation is available or needed. 0171 */ 0172 bool manageConnectionLoss(); 0173 0174 /** 0175 * @brief checkDomeParkingStatus check dome parking status and updating corresponding states accordingly. 0176 */ 0177 void checkCapParkingStatus(); 0178 0179 /** 0180 * @brief checkStartupState Check startup procedure stages and make sure all stages are complete. 0181 * @return True if startup is complete, false otherwise. 0182 */ 0183 bool checkStartupState(); 0184 /** 0185 * @brief checkShutdownState Check shutdown procedure stages and make sure all stages are complete. 0186 * @return 0187 */ 0188 bool checkShutdownState(); 0189 0190 /** 0191 * @brief checkParkWaitState Check park wait state. 0192 * @return If parking/unparking in progress, return false. If parking/unparking complete, return true. 0193 */ 0194 bool checkParkWaitState(); 0195 0196 /** 0197 * @brief runStartupProcedure Execute the startup of the scheduler itself to be prepared 0198 * for running scheduler jobs. 0199 */ 0200 void runStartupProcedure(); 0201 0202 /** 0203 * @brief runShutdownProcedure Shutdown the scheduler itself and EKOS (if configured to do so). 0204 */ 0205 void runShutdownProcedure(); 0206 0207 /** 0208 * @brief setPaused pausing the scheduler 0209 */ 0210 void setPaused(); 0211 0212 /** 0213 * @brief resetJobs Reset all jobs counters 0214 */ 0215 void resetJobs(); 0216 0217 /** 0218 * @brief selectActiveJob Select the job that should be executed 0219 */ 0220 void selectActiveJob(const QList<SchedulerJob *> &jobs); 0221 0222 /** 0223 * @brief evaluateJobs evaluates the current state of each objects and gives each one a score based on the constraints. 0224 * Given that score, the scheduler will decide which is the best job that needs to be executed. 0225 */ 0226 void evaluateJobs(bool evaluateOnly); 0227 0228 /** 0229 * @brief checkJobStatus Check the overall state of the scheduler, Ekos, and INDI. When all is OK, it calls evaluateJobs() when no job is current or executeJob() if a job is selected. 0230 * @return False if this function needs to be called again later, true if situation is stable and operations may continue. 0231 */ 0232 bool checkStatus(); 0233 0234 /** 0235 * @brief getNextAction Checking for the next appropriate action regarding the current state of the scheduler and execute it 0236 */ 0237 void getNextAction(); 0238 0239 /** 0240 * @brief Repeatedly runs a scheduler iteration and then sleeps timerInterval millisconds 0241 * and run the next iteration. This continues until the sleep time is negative. 0242 */ 0243 void iterate(); 0244 0245 /** 0246 * @brief Run a single scheduler iteration. 0247 */ 0248 int runSchedulerIteration(); 0249 0250 /** 0251 * @brief checkJobStage Check the progress of the job states and make DBUS calls to start the next stage until the job is complete. 0252 */ 0253 void checkJobStage(); 0254 void checkJobStageEpilogue(); 0255 0256 0257 /** 0258 * @brief saveScheduler Save scheduler jobs to a file 0259 * @param path path of a file 0260 * @return true on success, false on failure. 0261 */ 0262 bool saveScheduler(const QUrl &fileURL); 0263 0264 /** 0265 * @brief appendEkosScheduleList Append the contents of an ESL file to the queue. 0266 * @param fileURL File URL to load contents from. 0267 * @return True if contents were loaded successfully, else false. 0268 */ 0269 bool appendEkosScheduleList(const QString &fileURL); 0270 0271 /** 0272 * @brief appendLogText Append a new line to the logging. 0273 */ 0274 void appendLogText(const QString &logentry) override 0275 { 0276 emit newLog(logentry); 0277 } 0278 0279 // //////////////////////////////////////////////////////////////////// 0280 // device handling 0281 // //////////////////////////////////////////////////////////////////// 0282 void setAlignStatus(Ekos::AlignState status); 0283 void setGuideStatus(Ekos::GuideState status); 0284 void setCaptureStatus(Ekos::CaptureState status); 0285 void setFocusStatus(Ekos::FocusState status); 0286 void setMountStatus(ISD::Mount::Status status); 0287 void setWeatherStatus(ISD::Weather::Status status); 0288 0289 /** 0290 * @return True if mount is parked 0291 */ 0292 bool isMountParked(); 0293 /** 0294 * @return True if dome is parked 0295 */ 0296 bool isDomeParked(); 0297 0298 // //////////////////////////////////////////////////////////////////// 0299 // state machine and scheduler 0300 // //////////////////////////////////////////////////////////////////// 0301 QSharedPointer<SchedulerModuleState> m_moduleState; 0302 QSharedPointer<SchedulerModuleState> moduleState() const 0303 { 0304 return m_moduleState; 0305 } 0306 0307 QPointer<Ekos::GreedyScheduler> m_GreedyScheduler; 0308 QPointer<GreedyScheduler> &getGreedyScheduler() 0309 { 0310 return m_GreedyScheduler; 0311 } 0312 0313 // //////////////////////////////////////////////////////////////////// 0314 // DBUS interfaces to devices 0315 // //////////////////////////////////////////////////////////////////// 0316 QPointer<QDBusInterface> ekosInterface() const 0317 { 0318 return m_ekosInterface; 0319 } 0320 void setEkosInterface(QPointer<QDBusInterface> newInterface) 0321 { 0322 m_ekosInterface = newInterface; 0323 } 0324 QPointer<QDBusInterface> indiInterface() const 0325 { 0326 return m_indiInterface; 0327 } 0328 void setIndiInterface(QPointer<QDBusInterface> newInterface) 0329 { 0330 m_indiInterface = newInterface; 0331 } 0332 QPointer<QDBusInterface> focusInterface() const 0333 { 0334 return m_focusInterface; 0335 } 0336 void setFocusInterface(QPointer<QDBusInterface> newInterface) 0337 { 0338 m_focusInterface = newInterface; 0339 } 0340 QPointer<QDBusInterface> captureInterface() const 0341 { 0342 return m_captureInterface; 0343 } 0344 void setCaptureInterface(QPointer<QDBusInterface> newInterface) 0345 { 0346 m_captureInterface = newInterface; 0347 } 0348 QPointer<QDBusInterface> mountInterface() const 0349 { 0350 return m_mountInterface; 0351 } 0352 void setMountInterface(QPointer<QDBusInterface> newInterface) 0353 { 0354 m_mountInterface = newInterface; 0355 } 0356 QPointer<QDBusInterface> alignInterface() const 0357 { 0358 return m_alignInterface; 0359 } 0360 void setAlignInterface(QPointer<QDBusInterface> newInterface) 0361 { 0362 m_alignInterface = newInterface; 0363 } 0364 QPointer<QDBusInterface> guideInterface() const 0365 { 0366 return m_guideInterface; 0367 } 0368 void setGuideInterface(QPointer<QDBusInterface> newInterface) 0369 { 0370 m_guideInterface = newInterface; 0371 } 0372 QPointer<QDBusInterface> domeInterface() const 0373 { 0374 return m_domeInterface; 0375 } 0376 void setDomeInterface(QPointer<QDBusInterface> newInterface) 0377 { 0378 m_domeInterface = newInterface; 0379 } 0380 QPointer<QDBusInterface> weatherInterface() const 0381 { 0382 return m_weatherInterface; 0383 } 0384 void setWeatherInterface(QPointer<QDBusInterface> newInterface) 0385 { 0386 m_weatherInterface = newInterface; 0387 } 0388 QPointer<QDBusInterface> capInterface() const 0389 { 0390 return m_capInterface; 0391 } 0392 void setCapInterface(QPointer<QDBusInterface> newInterface) 0393 { 0394 m_capInterface = newInterface; 0395 } 0396 0397 /** 0398 * @brief createJobSequence Creates a job sequence for the mosaic tool given the prefix and output dir. The currently selected sequence file is modified 0399 * and a new version given the supplied parameters are saved to the output directory 0400 * @param prefix Prefix to set for the job sequence 0401 * @param outputDir Output dir to set for the job sequence 0402 * @return True if new file is saved, false otherwise 0403 */ 0404 bool createJobSequence(XMLEle *root, const QString &prefix, const QString &outputDir); 0405 0406 /** 0407 * @brief getSequenceJobRoot Read XML data from capture sequence job 0408 * @param filename 0409 * @return 0410 */ 0411 XMLEle *getSequenceJobRoot(const QString &filename) const; 0412 0413 /** 0414 * @brief getGuidingStatus Retrieve the guiding status. 0415 */ 0416 GuideState getGuidingStatus(); 0417 0418 QProcess &scriptProcess() 0419 { 0420 return m_scriptProcess; 0421 } 0422 0423 signals: 0424 // new log text for the module log window 0425 void newLog(const QString &text); 0426 // status updates 0427 void schedulerStopped(); 0428 void shutdownStarted(); 0429 void schedulerSleeping(bool shutdown, bool sleep); 0430 void schedulerPaused(); 0431 void changeSleepLabel(QString text, bool show = true); 0432 // state changes 0433 void jobsUpdated(QJsonArray jobsList); 0434 void updateJobTable(SchedulerJob *job = nullptr); 0435 // loading jobs 0436 void addJob(SchedulerJob *job); 0437 void syncGreedyParams(); 0438 void syncGUIToGeneralSettings(); 0439 void updateSchedulerURL(const QString &fileURL); 0440 // required for Analyze timeline 0441 void jobStarted(const QString &jobName); 0442 void jobEnded(const QString &jobName, const QString &endReason); 0443 0444 0445 private: 0446 // DBus interfaces to devices 0447 QPointer<QDBusInterface> m_ekosInterface { nullptr }; 0448 QPointer<QDBusInterface> m_indiInterface { nullptr }; 0449 QPointer<QDBusInterface> m_focusInterface { nullptr }; 0450 QPointer<QDBusInterface> m_captureInterface { nullptr }; 0451 QPointer<QDBusInterface> m_mountInterface { nullptr }; 0452 QPointer<QDBusInterface> m_alignInterface { nullptr }; 0453 QPointer<QDBusInterface> m_guideInterface { nullptr }; 0454 QPointer<QDBusInterface> m_domeInterface { nullptr }; 0455 QPointer<QDBusInterface> m_weatherInterface { nullptr }; 0456 QPointer<QDBusInterface> m_capInterface { nullptr }; 0457 0458 // When a module is commanded to perform an action, wait this many milliseconds 0459 // before check its state again. If State is still IDLE, then it either didn't received the command 0460 // or there is another problem. 0461 static const uint32_t ALIGN_INACTIVITY_TIMEOUT = 120000; 0462 static const uint32_t FOCUS_INACTIVITY_TIMEOUT = 120000; 0463 static const uint32_t CAPTURE_INACTIVITY_TIMEOUT = 120000; 0464 static const uint16_t GUIDE_INACTIVITY_TIMEOUT = 60000; 0465 /// Counter to keep debug logging in check 0466 uint8_t checkJobStageCounter { 0 }; 0467 0468 // Startup and Shutdown scripts process 0469 QProcess m_scriptProcess; 0470 // //////////////////////////////////////////////////////////////////// 0471 // process steps 0472 // //////////////////////////////////////////////////////////////////// 0473 0474 /** 0475 * @brief executeScript Execute pre- or post job script 0476 */ 0477 void executeScript(const QString &filename); 0478 0479 /** 0480 * @brief stopEkos shutdown Ekos completely 0481 */ 0482 void stopEkos(); 0483 0484 /** 0485 * @brief checkMountParkingStatus check mount parking status and updating corresponding states accordingly. 0486 */ 0487 void checkMountParkingStatus(); 0488 0489 /** 0490 * @brief checkDomeParkingStatus check dome parking status and updating corresponding states accordingly. 0491 */ 0492 void checkDomeParkingStatus(); 0493 0494 // //////////////////////////////////////////////////////////////////// 0495 // device handling 0496 // //////////////////////////////////////////////////////////////////// 0497 /** 0498 * @brief parkCap Close dust cover 0499 */ 0500 void parkCap(); 0501 0502 /** 0503 * @brief unCap Open dust cover 0504 */ 0505 void unParkCap(); 0506 0507 /** 0508 * @brief parkMount Park mount 0509 */ 0510 void parkMount(); 0511 0512 /** 0513 * @brief unParkMount Unpark mount 0514 */ 0515 void unParkMount(); 0516 0517 /** 0518 * @brief parkDome Park dome 0519 */ 0520 void parkDome(); 0521 0522 /** 0523 * @brief unParkDome Unpark dome 0524 */ 0525 void unParkDome(); 0526 0527 // //////////////////////////////////////////////////////////////////// 0528 // helper functions 0529 // //////////////////////////////////////////////////////////////////// 0530 0531 /** 0532 * @brief checkStartupProcedure restart regularly {@see #checkStartupState()} until completed 0533 */ 0534 void checkStartupProcedure(); 0535 0536 /** 0537 * @brief checkShutdownProcedure Check regularly if the shutdown has completed (see 0538 * {@see #checkShutdownState()}) and stop EKOS if the corresponding configuration flag is set. 0539 */ 0540 void checkShutdownProcedure(); 0541 0542 /** 0543 * @brief checkProcessExit Check script process exist status. This is called when the process exists either normally or abnormally. 0544 * @param exitCode exit code from the script process. Depending on the exist code, the status of startup/shutdown procedure is set accordingly. 0545 */ 0546 void checkProcessExit(int exitCode); 0547 0548 /** 0549 * @brief readProcessOutput read running script process output and display it in Ekos 0550 */ 0551 void readProcessOutput(); 0552 0553 /** 0554 * @brief Returns true if the job is storing its captures on the same machine as the scheduler. 0555 */ 0556 bool canCountCaptures(const SchedulerJob &job); 0557 0558 /** 0559 * @brief activeJob Shortcut to the active job held in the state machine 0560 */ 0561 SchedulerJob *activeJob(); 0562 0563 // Prints all the relative state variables set during an iteration. For debugging. 0564 void printStates(const QString &label); 0565 0566 }; 0567 } // Ekos namespace