1 /* 2 SPDX-FileCopyrightText: 2015 Jasem Mutlaq <mutlaqja@ikarustech.com> 3 4 DBus calls from GSoC 2015 Ekos Scheduler project: 5 SPDX-FileCopyrightText: 2015 Daniel Leu <daniel_mihai.leu@cti.pub.ro> 6 7 SPDX-License-Identifier: GPL-2.0-or-later 8 */ 9 10 #pragma once 11 12 #include "ui_scheduler.h" 13 #include "ekos/align/align.h" 14 #include "indi/indiweather.h" 15 #include "schedulerjob.h" 16 17 #include <lilxml.h> 18 19 #include <QProcess> 20 #include <QTime> 21 #include <QTimer> 22 #include <QUrl> 23 #include <QtDBus> 24 25 #include <cstdint> 26 27 class QProgressIndicator; 28 29 class GeoLocation; 30 class SchedulerJob; 31 class SkyObject; 32 class KConfigDialog; 33 class TestSchedulerUnit; 34 35 class TestEkosSchedulerOps; 36 37 namespace Ekos 38 { 39 40 class SequenceJob; 41 42 /** 43 * @brief The Ekos scheduler is a simple scheduler class to orchestrate automated multi object observation jobs. 44 * @author Jasem Mutlaq 45 * @version 1.2 46 */ 47 class Scheduler : public QWidget, public Ui::Scheduler 48 { 49 Q_OBJECT 50 Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Scheduler") 51 Q_PROPERTY(Ekos::SchedulerState status READ status NOTIFY newStatus) 52 Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) 53 Q_PROPERTY(QString profile READ profile WRITE setProfile) 54 55 public: 56 typedef enum { EKOS_IDLE, EKOS_STARTING, EKOS_STOPPING, EKOS_READY } EkosState; 57 typedef enum { INDI_IDLE, INDI_CONNECTING, INDI_DISCONNECTING, INDI_PROPERTY_CHECK, INDI_READY } INDIState; 58 typedef enum 59 { 60 STARTUP_IDLE, 61 STARTUP_SCRIPT, 62 STARTUP_UNPARK_DOME, 63 STARTUP_UNPARKING_DOME, 64 STARTUP_UNPARK_MOUNT, 65 STARTUP_UNPARKING_MOUNT, 66 STARTUP_UNPARK_CAP, 67 STARTUP_UNPARKING_CAP, 68 STARTUP_ERROR, 69 STARTUP_COMPLETE 70 } StartupState; 71 typedef enum 72 { 73 SHUTDOWN_IDLE, 74 SHUTDOWN_PARK_CAP, 75 SHUTDOWN_PARKING_CAP, 76 SHUTDOWN_PARK_MOUNT, 77 SHUTDOWN_PARKING_MOUNT, 78 SHUTDOWN_PARK_DOME, 79 SHUTDOWN_PARKING_DOME, 80 SHUTDOWN_SCRIPT, 81 SHUTDOWN_SCRIPT_RUNNING, 82 SHUTDOWN_ERROR, 83 SHUTDOWN_COMPLETE 84 } ShutdownState; 85 typedef enum 86 { 87 PARKWAIT_IDLE, 88 PARKWAIT_PARK, 89 PARKWAIT_PARKING, 90 PARKWAIT_PARKED, 91 PARKWAIT_UNPARK, 92 PARKWAIT_UNPARKING, 93 PARKWAIT_UNPARKED, 94 PARKWAIT_ERROR 95 } ParkWaitStatus; 96 97 /** @brief options what should happen if an error or abort occurs */ 98 typedef enum 99 { 100 ERROR_DONT_RESTART, 101 ERROR_RESTART_AFTER_TERMINATION, 102 ERROR_RESTART_IMMEDIATELY 103 } ErrorHandlingStrategy; 104 105 /** @brief Columns, in the same order as UI. */ 106 typedef enum 107 { 108 SCHEDCOL_NAME = 0, 109 SCHEDCOL_STATUS, 110 SCHEDCOL_CAPTURES, 111 SCHEDCOL_ALTITUDE, 112 SCHEDCOL_SCORE, 113 SCHEDCOL_STARTTIME, 114 SCHEDCOL_ENDTIME, 115 SCHEDCOL_DURATION, 116 SCHEDCOL_LEADTIME, 117 SCHEDCOL_COUNT 118 } SchedulerColumns; 119 120 /** @brief IterationTypes, the different types of scheduler iterations that are run. */ 121 typedef enum 122 { 123 RUN_WAKEUP = 0, 124 RUN_SCHEDULER, 125 RUN_JOBCHECK, 126 RUN_SHUTDOWN, 127 RUN_NOTHING 128 } SchedulerTimerState; 129 130 /** @brief Constructor, the starndard scheduler constructor. */ 131 Scheduler(); 132 /** @brief DebugConstructor, a constructor used in testing with a mock ekos. */ 133 Scheduler(const QString &ekosPathStr, const QString &ekosInterfaceStr); 134 ~Scheduler() = default; 135 136 QString getCurrentJobName(); getCurrentJob()137 SchedulerJob *getCurrentJob() 138 { 139 return currentJob; 140 } 141 142 void appendLogText(const QString &); logText()143 QStringList logText() 144 { 145 return m_LogText; 146 } getLogText()147 QString getLogText() 148 { 149 return m_LogText.join("\n"); 150 } 151 void clearLog(); 152 void applyConfig(); 153 154 void addObject(SkyObject *object); 155 156 /** 157 * @brief startSlew DBus call for initiating slew 158 */ 159 void startSlew(); 160 161 /** 162 * @brief startFocusing DBus call for feeding ekos the specified settings and initiating focus operation 163 */ 164 void startFocusing(); 165 166 /** 167 * @brief startAstrometry initiation of the capture and solve operation. We change the job state 168 * after solver is started 169 */ 170 void startAstrometry(); 171 172 /** 173 * @brief startGuiding After ekos is fed the calibration options, we start the guiding process 174 * @param resetCalibration By default calibration is not reset until it is explicitly requested 175 */ 176 void startGuiding(bool resetCalibration = false); 177 178 /** 179 * @brief startCapture The current job file name is solved to an url which is fed to ekos. We then start the capture process 180 * @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 181 * is not loaded from disk again since that results in erasing all the history of the capture process. 182 */ 183 void startCapture(bool restart = false); 184 185 /** 186 * @brief getNextAction Checking for the next appropriate action regarding the current state of the scheduler and execute it 187 */ 188 void getNextAction(); 189 190 /** 191 * @brief disconnectINDI disconnect all INDI devices from server. 192 */ 193 void disconnectINDI(); 194 195 /** 196 * @brief stopEkos shutdown Ekos completely 197 */ 198 void stopEkos(); 199 200 /** 201 * @brief stopGuiding After guiding is done we need to stop the process 202 */ 203 void stopGuiding(); 204 205 /** 206 * @brief setSolverAction set the GOTO mode for the solver 207 * @param mode 0 For Sync, 1 for SlewToTarget, 2 for Nothing 208 */ 209 void setSolverAction(Align::GotoMode mode); 210 211 /** @defgroup SchedulerDBusInterface Ekos DBus Interface - Scheduler Module 212 * Ekos::Align interface provides primary functions to run and stop the scheduler. 213 */ 214 215 /*@{*/ 216 217 /** DBUS interface function. 218 * @brief Start the scheduler main loop and evaluate jobs and execute them accordingly. 219 */ 220 Q_SCRIPTABLE Q_NOREPLY void start(); 221 222 /** DBUS interface function. 223 * @brief Stop the scheduler. 224 */ 225 Q_SCRIPTABLE Q_NOREPLY void stop(); 226 227 /** DBUS interface function. 228 * @brief Remove all scheduler jobs 229 */ 230 Q_SCRIPTABLE Q_NOREPLY void removeAllJobs(); 231 232 /** DBUS interface function. 233 * @brief Loads the Ekos Scheduler List (.esl) file. 234 * @param fileURL path to a file 235 * @return true if loading file is successful, false otherwise. 236 */ 237 Q_SCRIPTABLE bool loadScheduler(const QString &fileURL); 238 239 /** DBUS interface function. 240 * @brief Set the file URL pointing to the capture sequence file 241 * @param sequenceFileURL URL of the capture sequence file 242 */ 243 Q_SCRIPTABLE void setSequence(const QString &sequenceFileURL); 244 245 /** DBUS interface function. 246 * @brief Resets all jobs to IDLE 247 */ 248 Q_SCRIPTABLE void resetAllJobs(); 249 250 /** DBUS interface function. 251 * @brief Resets all jobs to IDLE 252 */ 253 Q_SCRIPTABLE void sortJobsPerAltitude(); 254 status()255 Ekos::SchedulerState status() 256 { 257 return state; 258 } 259 setProfile(const QString & profile)260 void setProfile(const QString &profile) 261 { 262 schedulerProfileCombo->setCurrentText(profile); 263 } profile()264 QString profile() 265 { 266 return schedulerProfileCombo->currentText(); 267 } 268 269 /** 270 * @brief retrieve the error handling strategy from the UI 271 */ 272 ErrorHandlingStrategy getErrorHandlingStrategy(); 273 274 /** 275 * @brief select the error handling strategy (no restart, restart after all terminated, restart immediately) 276 */ 277 void setErrorHandlingStrategy (ErrorHandlingStrategy strategy); 278 279 /** @}*/ 280 281 // TODO: This section of static public and private methods should someday 282 // be moved from Scheduler and placed in a separate class, 283 // e.g. SchedulerPlanner or SchedulerJobEval 284 285 /** 286 * @brief setupJob Massive initialization of a SchedulerJob for testing and exectution 287 * @param job Target 288 */ 289 static void setupJob( 290 SchedulerJob &job, const QString &name, int priority, const dms &ra, 291 const dms &dec, double djd, double rotation, const QUrl &sequenceUrl, const QUrl &fitsUrl, 292 SchedulerJob::StartupCondition startup, const QDateTime &startupTime, int16_t startupOffset, 293 SchedulerJob::CompletionCondition completion, const QDateTime &completionTime, int completionRepeats, 294 double minimumAltitude, double minimumMoonSeparation, bool enforceWeather, bool enforceTwilight, 295 bool enforceArtificialHorizon, bool track, bool focus, bool align, bool guide); 296 297 /** 298 * @brief evaluateJobs Computes estimated start and end times for the SchedulerJobs passed in. Returns a proposed schedule. 299 * @param jobs The input list of SchedulerJobs to evaluate. 300 * @param state The current scheduler state. 301 * @param capturedFramesCount which parts of the schedulerJobs have already been completed. 302 * @param dawn next dawn, as a KStarsDateTime 303 * @param dusk next dusk, as a KStarsDateTime 304 * @param rescheduleErrors whether jobs that failed with errors should be rescheduled. 305 * @param restartJobs whether jobs that failed for one reason or another shoulc be rescheduled. 306 * @param possiblyDelay a return value indicating whether the timer should try scheduling again after a delay. 307 * @param scheduler instance of the scheduler used for logging. Can be nullptr. 308 * @return Total score 309 */ 310 static QList<SchedulerJob *> evaluateJobs(QList<SchedulerJob *> &jobs, SchedulerState state, 311 const QMap<QString, uint16_t> &capturedFramesCount, QDateTime const &dawn, QDateTime const &dusk, 312 bool rescheduleErrors, bool restartJobs, bool *possiblyDelay, Scheduler *scheduler); 313 /** 314 * @brief calculateJobScore Calculate job dark sky score, altitude score, and moon separation scores and returns the sum. 315 * @param job Target 316 * @param dawn next dawn, as a KStarsDateTime 317 * @param dusk next dusk, as a KStarsDateTime 318 * @param when date and time to evaluate constraints, now if omitted. 319 * @return Total score 320 */ 321 static int16_t calculateJobScore(SchedulerJob const *job, QDateTime const &dawn, QDateTime const &dusk, 322 QDateTime const &when = QDateTime()); 323 324 /** 325 * @brief estimateJobTime Estimates the time the job takes to complete based on the sequence file and what modules to utilize during the observation run. 326 * @param job target job 327 * @param capturedFramesCount a map of what's been captured already 328 * @param scheduler instance of the scheduler used for logging. Can be nullptr. 329 * @return Estimated time in seconds. 330 */ 331 static bool estimateJobTime(SchedulerJob *schedJob, const QMap<QString, uint16_t> &capturedFramesCount, 332 Scheduler *scheduler); 333 /** 334 * @brief loadSequenceQueue Loads what's necessary to estimate job completion time from a capture sequence queue file 335 * @param fileURL the filename 336 * @param schedJob the SchedulerJob is modified accoring to the contents of the sequence queue 337 * @param jobs the returned values read from the file 338 * @param hasAutoFocus a return value indicating whether autofocus can be triggered by the sequence. 339 * @param scheduler instance of the scheduler used for logging. Can be nullptr. 340 * @return Estimated time in seconds. 341 */ 342 343 static bool loadSequenceQueue(const QString &fileURL, SchedulerJob *schedJob, QList<SequenceJob *> &jobs, 344 bool &hasAutoFocus, Scheduler *scheduler); 345 346 /** 347 * @brief getDarkSkyScore Get the dark sky score of a date and time. The further from dawn the better. 348 * @param when date and time to check the dark sky score, now if omitted 349 * @return Dark sky score. Daylight get bad score, as well as pre-dawn to dawn. 350 */ 351 static int16_t getDarkSkyScore(QDateTime const &dawn, QDateTime const &dusk, QDateTime const &when = QDateTime()); 352 353 /** @brief Setter used in testing to fix the local time. Otherwise getter gets from KStars instance. */ 354 /** @{ */ 355 static KStarsDateTime getLocalTime(); setLocalTime(KStarsDateTime * time)356 static void setLocalTime(KStarsDateTime *time) 357 { 358 storedLocalTime = time; 359 } hasLocalTime()360 static bool hasLocalTime() 361 { 362 return storedLocalTime != nullptr; 363 } 364 /** @} */ 365 366 private: 367 /** 368 * @brief processJobInfo a utility used by loadSequenceQueue() to help it read a capture sequence file 369 * @param root the filename 370 * @param schedJob the SchedulerJob is modified accoring to the contents of the sequence queue 371 * @return a capture sequence 372 */ 373 static SequenceJob *processJobInfo(XMLEle *root, SchedulerJob *schedJob); 374 375 /** 376 * @brief timeHeuristics Estimates the number of seconds of overhead above and beyond imaging time, used by estimateJobTime. 377 * @param schedJob the scheduler job. 378 * @return seconds of overhead. 379 */ 380 static int timeHeuristics(const SchedulerJob *schedJob); 381 382 // Used in testing, instead of KStars::Instance() resources 383 static KStarsDateTime *storedLocalTime; 384 385 friend TestSchedulerUnit; 386 387 // TODO: See above TODO. End of static methods that might be moved to 388 // a separate Scheduler-related class. 389 390 /*@{*/ 391 392 private: 393 /** @internal Safeguard flag to avoid registering signals from widgets multiple times. 394 */ 395 bool jobChangesAreWatched { false }; 396 397 protected: 398 /** @internal Enables signal watch on SchedulerJob form values in order to apply changes to current job. 399 * @param enable is the toggle flag, true to watch for changes, false to ignore them. 400 */ 401 void watchJobChanges(bool enable); 402 403 /** @internal Marks the currently selected SchedulerJob as modified change. 404 * 405 * This triggers job re-evaluation. 406 * Next time save button is invoked, the complete content is written to disk. 407 */ 408 void setDirty(); 409 /** @} */ 410 411 protected: 412 /** @internal Associate job table cells on a row to the corresponding SchedulerJob. 413 * @param row is an integer indexing the row to associate cells from, and also the index of the job in the job list.. 414 */ 415 void setJobStatusCells(int row); 416 417 protected slots: 418 419 /** 420 * @brief registerNewModule Register an Ekos module as it arrives via DBus 421 * and create the appropriate DBus interface to communicate with it. 422 * @param name of module 423 */ 424 void registerNewModule(const QString &name); 425 426 /** 427 * @brief syncProperties Sync startup properties from the various device to enable/disable features in the scheduler 428 * like the ability to park/unpark..etc 429 */ 430 void syncProperties(); 431 432 /** 433 * @brief checkInterfaceReady Sometimes syncProperties() is not sufficient since the ready signal could have fired already 434 * and cannot be relied on to know once a module interface is ready. Therefore, we explicitly check if the module interface 435 * is ready. 436 * @param iface interface to test for readiness. 437 */ 438 void checkInterfaceReady(QDBusInterface *iface); 439 440 void setAlignStatus(Ekos::AlignState status); 441 void setGuideStatus(Ekos::GuideState status); 442 void setCaptureStatus(Ekos::CaptureState status); 443 void setFocusStatus(Ekos::FocusState status); 444 void setMountStatus(ISD::Telescope::Status status); 445 void setWeatherStatus(ISD::Weather::Status status); 446 447 /** 448 * @brief select object from KStars's find dialog. 449 */ 450 void selectObject(); 451 452 /** 453 * @brief Selects FITS file for solving. 454 */ 455 void selectFITS(); 456 457 /** 458 * @brief Selects sequence queue. 459 */ 460 void selectSequence(); 461 462 /** 463 * @brief Selects sequence queue. 464 */ 465 void selectStartupScript(); 466 467 /** 468 * @brief Selects sequence queue. 469 */ 470 void selectShutdownScript(); 471 472 /** 473 * @brief addToQueue Construct a SchedulerJob and add it to the queue or save job settings from current form values. 474 * jobUnderEdit determines whether to add or edit 475 */ 476 void saveJob(); 477 478 /** 479 * @brief addJob Add a new job from form values 480 */ 481 void addJob(); 482 483 /** 484 * @brief editJob Edit an observation job 485 * @param i index model in queue table 486 */ 487 void loadJob(QModelIndex i); 488 489 /** 490 * @brief removeJob Remove a job from the currently selected row. If no row is selected, it remove the last job in the queue. 491 */ 492 void removeJob(); 493 494 /** 495 * @brief setJobAddApply Set first button state to add new job or apply changes. 496 */ 497 void setJobAddApply(bool add_mode); 498 499 /** 500 * @brief setJobManipulation Enable or disable job manipulation buttons. 501 */ 502 void setJobManipulation(bool can_reorder, bool can_delete); 503 504 /** 505 * @brief set all GUI fields to the values of the given scheduler job 506 */ 507 void syncGUIToJob(SchedulerJob *job); 508 509 /** 510 * @brief jobSelectionChanged Update UI state when the job list is clicked once. 511 */ 512 void clickQueueTable(QModelIndex index); 513 514 /** 515 * @brief Update scheduler parameters to the currently selected scheduler job 516 * @param current table position 517 * @param previous table position 518 */ 519 void queueTableSelectionChanged(QModelIndex current, QModelIndex previous); 520 521 /** 522 * @brief reorderJobs Change the order of jobs in the UI based on a subset of its jobs. 523 */ 524 bool reorderJobs(QList<SchedulerJob*> reordered_sublist); 525 526 /** 527 * @brief moveJobUp Move the selected job up in the job list. 528 */ 529 void moveJobUp(); 530 531 /** 532 * @brief moveJobDown Move the selected job down in the list. 533 */ 534 void moveJobDown(); 535 536 /** 537 * @brief shouldSchedulerSleep Check if the scheduler needs to sleep until the job is ready 538 * @param currentJob Job to check 539 * @return True if we set the scheduler to sleep mode. False, if not required and we need to execute now 540 */ 541 bool shouldSchedulerSleep(SchedulerJob *currentJob); 542 543 bool completeShutdown(); 544 void toggleScheduler(); 545 void pause(); 546 void setPaused(); 547 void save(); 548 void saveAs(); 549 550 /** 551 * @brief load Open a file dialog to select an ESL file, and load its contents. 552 * @param clearQueue Clear the queue before loading, or append ESL contents to queue. 553 * @param filename If not empty, this file will be used instead of poping up a dialog. 554 */ 555 void load(bool clearQueue, const QString &filename = ""); 556 557 /** 558 * @brief appendEkosScheduleList Append the contents of an ESL file to the queue. 559 * @param fileURL File URL to load contents from. 560 * @return True if contents were loaded successfully, else false. 561 */ 562 bool appendEkosScheduleList(const QString &fileURL); 563 564 void resetJobEdit(); 565 566 /** 567 * @brief updateNightTime update the Twilight restriction with the argument job properties. 568 * @param job SchedulerJob for which to display the next dawn and dusk, or the job currently selected if null, or today's next dawn and dusk if no job is selected. 569 */ 570 void updateNightTime(SchedulerJob const * job = nullptr); 571 572 /** 573 * @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. 574 * @return False if this function needs to be called again later, true if situation is stable and operations may continue. 575 */ 576 bool checkStatus(); 577 578 /** 579 * @brief checkJobStage Check the progress of the job states and make DBUS call to start the next stage until the job is complete. 580 */ 581 void checkJobStage(); 582 583 /** 584 * @brief findNextJob Check if the job met the completion criteria, and if it did, then it search for next job candidate. If no jobs are found, it starts the shutdown stage. 585 */ 586 void findNextJob(); 587 588 /** 589 * @brief stopCurrentJobAction Stop whatever action taking place in the current job (eg. capture, guiding...etc). 590 */ 591 void stopCurrentJobAction(); 592 593 /** 594 * @brief manageConnectionLoss Mitigate loss of connection with the INDI server. 595 * @return true if connection to Ekos/INDI should be attempted again, false if not mitigation is available or needed. 596 */ 597 bool manageConnectionLoss(); 598 599 /** 600 * @brief readProcessOutput read running script process output and display it in Ekos 601 */ 602 void readProcessOutput(); 603 604 /** 605 * @brief checkProcessExit Check script process exist status. This is called when the process exists either normally or abnormally. 606 * @param exitCode exit code from the script process. Depending on the exist code, the status of startup/shutdown procedure is set accordingly. 607 */ 608 void checkProcessExit(int exitCode); 609 610 /** 611 * @brief resumeCheckStatus If the scheduler primary loop was suspended due to weather or sleep event, resume it again. 612 */ 613 void resumeCheckStatus(); 614 615 /** 616 * @brief checkWeather Check weather status and act accordingly depending on the current status of the scheduler and running jobs. 617 */ 618 //void checkWeather(); 619 620 /** 621 * @brief wakeUpScheduler Wake up scheduler from sleep state 622 */ 623 void wakeUpScheduler(); 624 625 /** 626 * @brief startJobEvaluation Start job evaluation only without starting the scheduler process itself. Display the result to the user. 627 */ 628 void startJobEvaluation(); 629 630 /** 631 * @brief startMosaicTool Start Mosaic tool and create jobs if necessary. 632 */ 633 void startMosaicTool(); 634 635 /** 636 * @brief displayTwilightWarning Display twilight warning to user if it is unchecked. 637 */ 638 void checkTwilightWarning(bool enabled); 639 640 void runStartupProcedure(); 641 void checkStartupProcedure(); 642 643 void runShutdownProcedure(); 644 void checkShutdownProcedure(); 645 646 void setINDICommunicationStatus(Ekos::CommunicationStatus status); 647 void setEkosCommunicationStatus(Ekos::CommunicationStatus status); 648 649 void simClockScaleChanged(float); 650 void simClockTimeChanged(); 651 652 signals: 653 void newLog(const QString &text); 654 void newStatus(Ekos::SchedulerState state); 655 void weatherChanged(ISD::Weather::Status state); 656 void newTarget(const QString &); 657 658 private: 659 /** 660 * @brief evaluateJobs evaluates the current state of each objects and gives each one a score based on the constraints. 661 * Given that score, the scheduler will decide which is the best job that needs to be executed. 662 */ 663 void evaluateJobs(bool evaluateOnly); 664 void processJobs(QList<SchedulerJob *> sortedJobs, bool jobEvaluationOnly); 665 666 /** 667 * @brief executeJob After the best job is selected, we call this in order to start the process that will execute the job. 668 * checkJobStatus slot will be connected in order to figure the exact state of the current job each second 669 * @param value 670 */ 671 void executeJob(SchedulerJob *job); 672 673 void executeScript(const QString &filename); 674 675 /** 676 * @brief getWeatherScore Get current weather condition score. 677 * @return If weather condition OK, return score 0, else bad score. 678 */ 679 int16_t getWeatherScore() const; 680 681 /** 682 * @brief calculateDawnDusk Get dawn and dusk times for today 683 */ 684 static void calculateDawnDusk(); 685 686 /** 687 * @brief checkEkosState Check ekos startup stages and take whatever action necessary to get Ekos up and running 688 * @return True if Ekos is running, false if Ekos start up is in progress. 689 */ 690 bool checkEkosState(); 691 692 /** 693 * @brief isINDIConnected Determines the status of the INDI connection. 694 * @return True if INDI connection is up and usable, else false. 695 */ 696 bool isINDIConnected(); 697 698 /** 699 * @brief checkINDIState Check INDI startup stages and take whatever action necessary to get INDI devices connected. 700 * @return True if INDI devices are connected, false if it is under progress. 701 */ 702 bool checkINDIState(); 703 704 /** 705 * @brief checkStartupState Check startup procedure stages and make sure all stages are complete. 706 * @return True if startup is complete, false otherwise. 707 */ 708 bool checkStartupState(); 709 710 /** 711 * @brief checkShutdownState Check shutdown procedure stages and make sure all stages are complete. 712 * @return 713 */ 714 bool checkShutdownState(); 715 716 /** 717 * @brief checkParkWaitState Check park wait state. 718 * @return If parking/unparking in progress, return false. If parking/unparking complete, return true. 719 */ 720 bool checkParkWaitState(); 721 722 /** 723 * @brief parkMount Park mount 724 */ 725 void parkMount(); 726 727 /** 728 * @brief unParkMount Unpark mount 729 */ 730 void unParkMount(); 731 732 /** 733 * @return True if mount is parked 734 */ 735 bool isMountParked(); 736 737 /** 738 * @brief parkDome Park dome 739 */ 740 void parkDome(); 741 742 /** 743 * @brief unParkDome Unpark dome 744 */ 745 void unParkDome(); 746 747 /** 748 * @return True if dome is parked 749 */ 750 bool isDomeParked(); 751 752 /** 753 * @brief parkCap Close dust cover 754 */ 755 void parkCap(); 756 757 /** 758 * @brief unCap Open dust cover 759 */ 760 void unParkCap(); 761 762 /** 763 * @brief checkMountParkingStatus check mount parking status and updating corresponding states accordingly. 764 */ 765 void checkMountParkingStatus(); 766 767 /** 768 * @brief checkDomeParkingStatus check dome parking status and updating corresponding states accordingly. 769 */ 770 void checkDomeParkingStatus(); 771 772 /** 773 * @brief checkDomeParkingStatus check dome parking status and updating corresponding states accordingly. 774 */ 775 void checkCapParkingStatus(); 776 777 /** 778 * @brief saveScheduler Save scheduler jobs to a file 779 * @param path path of a file 780 * @return true on success, false on failure. 781 */ 782 bool saveScheduler(const QUrl &fileURL); 783 784 /** 785 * @brief processJobInfo Process the job information from a scheduler file and populate jobs accordingly 786 * @param root XML root element of JOB 787 * @return true on success, false on failure. 788 */ 789 bool processJobInfo(XMLEle *root); 790 791 /** 792 * @brief createJobSequence Creates a job sequence for the mosaic tool given the prefix and output dir. The currently selected sequence file is modified 793 * and a new version given the supplied parameters are saved to the output directory 794 * @param prefix Prefix to set for the job sequence 795 * @param outputDir Output dir to set for the job sequence 796 * @return True if new file is saved, false otherwise 797 */ 798 bool createJobSequence(XMLEle *root, const QString &prefix, const QString &outputDir); 799 800 /** @internal Change the current job, updating associated widgets. 801 * @param job is an existing SchedulerJob to set as current, or nullptr. 802 */ 803 void setCurrentJob(SchedulerJob *job); 804 805 /** 806 * @brief processFITSSelection When a FITS file is selected, open it and try to guess 807 * the object name, and its J2000 RA/DE to fill the UI with such info automatically. 808 */ 809 void processFITSSelection(); 810 811 void loadProfiles(); 812 813 XMLEle *getSequenceJobRoot(); 814 815 /** 816 * @brief updateCompletedJobsCount For each scheduler job, examine sequence job storage and count captures. 817 * @param forced forces recounting captures unconditionally if true, else only IDLE, EVALUATION or new jobs are examined. 818 */ 819 void updateCompletedJobsCount(bool forced = false); 820 821 int getCompletedFiles(const QString &path, const QString &seqPrefix); 822 823 // retrieve the guiding status 824 GuideState getGuidingStatus(); 825 826 // Returns milliseconds since startCurrentOperationTImer() was called. 827 qint64 getCurrentOperationMsec(); 828 // Starts the above operation timer. 829 void startCurrentOperationTimer(); 830 831 // Controls for the guiding timer, which restarts guiding after failure. 832 void cancelGuidingTimer(); 833 bool isGuidingTimerActive(); 834 void startGuidingTimer(int milliseconds); 835 void processGuidingTimer(); 836 837 Ekos::Scheduler *ui { nullptr }; 838 //DBus interfaces 839 QPointer<QDBusInterface> focusInterface { nullptr }; 840 QPointer<QDBusInterface> ekosInterface { nullptr }; 841 QPointer<QDBusInterface> captureInterface { nullptr }; 842 QPointer<QDBusInterface> mountInterface { nullptr }; 843 QPointer<QDBusInterface> alignInterface { nullptr }; 844 QPointer<QDBusInterface> guideInterface { nullptr }; 845 QPointer<QDBusInterface> domeInterface { nullptr }; 846 QPointer<QDBusInterface> weatherInterface { nullptr }; 847 QPointer<QDBusInterface> capInterface { nullptr }; 848 849 // Interface strings for the dbus. Changeable for mocks when testing. Private so only tests can change. 850 QString focusInterfaceString { "org.kde.kstars.Ekos.Focus" }; setFocusInterfaceString(const QString & interface)851 void setFocusInterfaceString(const QString &interface) 852 { 853 focusInterfaceString = interface; 854 } 855 QString focusPathString { "/KStars/Ekos/Focus" }; setFocusPathString(const QString & interface)856 void setFocusPathString(const QString &interface) 857 { 858 focusPathString = interface; 859 } 860 861 // This is only used in the constructor 862 QString ekosInterfaceString { "org.kde.kstars.Ekos" }; 863 QString ekosPathString { "/KStars/Ekos" }; 864 865 QString mountInterfaceString { "org.kde.kstars.Ekos.Mount" }; setMountInterfaceString(const QString & interface)866 void setMountInterfaceString(const QString &interface) 867 { 868 mountInterfaceString = interface; 869 } 870 QString mountPathString { "/KStars/Ekos/Mount" }; setMountPathString(const QString & interface)871 void setMountPathString(const QString &interface) 872 { 873 mountPathString = interface; 874 } 875 876 QString captureInterfaceString { "org.kde.kstars.Ekos.Capture" }; setCaptureInterfaceString(const QString & interface)877 void setCaptureInterfaceString(const QString &interface) 878 { 879 captureInterfaceString = interface; 880 } 881 QString capturePathString { "/KStars/Ekos/Capture" }; setCapturePathString(const QString & interface)882 void setCapturePathString(const QString &interface) 883 { 884 capturePathString = interface; 885 } 886 887 QString alignInterfaceString { "org.kde.kstars.Ekos.Align" }; setAlignInterfaceString(const QString & interface)888 void setAlignInterfaceString(const QString &interface) 889 { 890 alignInterfaceString = interface; 891 } 892 QString alignPathString { "/KStars/Ekos/Align" }; setAlignPathString(const QString & interface)893 void setAlignPathString(const QString &interface) 894 { 895 alignPathString = interface; 896 } 897 898 QString guideInterfaceString { "org.kde.kstars.Ekos.Guide" }; setGuideInterfaceString(const QString & interface)899 void setGuideInterfaceString(const QString &interface) 900 { 901 guideInterfaceString = interface; 902 } 903 QString guidePathString { "/KStars/Ekos/Guide" }; setGuidePathString(const QString & interface)904 void setGuidePathString(const QString &interface) 905 { 906 guidePathString = interface; 907 } 908 909 QString domeInterfaceString { "org.kde.kstars.Ekos.Dome" }; setDomeInterfaceString(const QString & interface)910 void setDomeInterfaceString(const QString &interface) 911 { 912 domeInterfaceString = interface; 913 } 914 QString domePathString { "/KStars/Ekos/Dome" }; setDomePathString(const QString & interface)915 void setDomePathString(const QString &interface) 916 { 917 domePathString = interface; 918 } 919 920 QString weatherInterfaceString { "org.kde.kstars.Ekos.Weather" }; setWeatherInterfaceString(const QString & interface)921 void setWeatherInterfaceString(const QString &interface) 922 { 923 weatherInterfaceString = interface; 924 } 925 QString weatherPathString { "/KStars/Ekos/Weather" }; setWeatherPathString(const QString & interface)926 void setWeatherPathString(const QString &interface) 927 { 928 weatherPathString = interface; 929 } 930 931 QString dustCapInterfaceString { "org.kde.kstars.Ekos.DustCap" }; setDustCapInterfaceString(const QString & interface)932 void setDustCapInterfaceString(const QString &interface) 933 { 934 dustCapInterfaceString = interface; 935 } 936 QString dustCapPathString { "/KStars/Ekos/DustCap" }; setDustCapPathString(const QString & interface)937 void setDustCapPathString(const QString &interface) 938 { 939 dustCapPathString = interface; 940 } 941 942 // Scheduler and job state and stages 943 SchedulerState state { SCHEDULER_IDLE }; 944 EkosState ekosState { EKOS_IDLE }; 945 INDIState indiState { INDI_IDLE }; 946 StartupState startupState { STARTUP_IDLE }; 947 ShutdownState shutdownState { SHUTDOWN_IDLE }; 948 ParkWaitStatus parkWaitState { PARKWAIT_IDLE }; 949 Ekos::CommunicationStatus m_EkosCommunicationStatus { Ekos::Idle }; 950 Ekos::CommunicationStatus m_INDICommunicationStatus { Ekos::Idle }; 951 /// List of all jobs as entered by the user or file 952 QList<SchedulerJob *> jobs; 953 /// Active job 954 SchedulerJob *currentJob { nullptr }; 955 /// URL to store the scheduler file 956 QUrl schedulerURL; 957 /// URL for Ekos Sequence 958 QUrl sequenceURL; 959 /// FITS URL to solve 960 QUrl fitsURL; 961 /// Startup script URL 962 QUrl startupScriptURL; 963 /// Shutdown script URL 964 QUrl shutdownScriptURL; 965 /// Store all log strings 966 QStringList m_LogText; 967 /// Busy indicator widget 968 QProgressIndicator *pi { nullptr }; 969 /// Are we editing a job right now? Job row index 970 int jobUnderEdit { -1 }; 971 /// Pointer to Geographic location 972 GeoLocation *geo { nullptr }; 973 /// How many repeated job batches did we complete thus far? 974 uint16_t captureBatch { 0 }; 975 /// Startup and Shutdown scripts process 976 QProcess scriptProcess; 977 /// Store next dawn to calculate dark skies range 978 static QDateTime Dawn; 979 /// Store next dusk to calculate dark skies range 980 static QDateTime Dusk; 981 /// Pre-dawn is where we stop all jobs, it is a user-configurable value before Dawn. 982 static QDateTime preDawnDateTime; 983 /// Was job modified and needs saving? 984 bool mDirty { false }; 985 /// Keep watch of weather status 986 ISD::Weather::Status weatherStatus { ISD::Weather::WEATHER_IDLE }; 987 /// Keep track of how many times we didn't receive weather updates 988 uint8_t noWeatherCounter { 0 }; 989 990 // Utilities to control the preemptiveShutdown feature. 991 // Is the scheduler shutting down until later when it will resume a job? 992 void enablePreemptiveShutdown(const QDateTime &wakeupTime); 993 void disablePreemptiveShutdown(); 994 QDateTime getPreemptiveShutdownWakeupTime(); 995 bool preemptiveShutdown(); 996 // The various preemptiveShutdown states are controlled by this one variable. 997 QDateTime preemptiveShutdownWakeupTime; 998 999 /// Keep track of Load & Slew operation 1000 bool loadAndSlewProgress { false }; 1001 /// Check if initial autofocus is completed and do not run autofocus until there is a change is telescope position/alignment. 1002 bool autofocusCompleted { false }; 1003 /// Keep track of INDI connection failures 1004 uint8_t indiConnectFailureCount { 0 }; 1005 /// Keep track of Ekos connection failures 1006 uint8_t ekosConnectFailureCount { 0 }; 1007 /// Keep track of Ekos focus module failures 1008 uint8_t focusFailureCount { 0 }; 1009 /// Keep track of Ekos guide module failures 1010 uint8_t guideFailureCount { 0 }; 1011 /// Keep track of Ekos align module failures 1012 uint8_t alignFailureCount { 0 }; 1013 /// Keep track of Ekos capture module failures 1014 uint8_t captureFailureCount { 0 }; 1015 /// Counter to keep debug logging in check 1016 uint8_t checkJobStageCounter { 0 }; 1017 /// Call checkWeather when weatherTimer time expires. It is equal to the UpdatePeriod time in INDI::Weather device. 1018 //QTimer weatherTimer; 1019 1020 /// Delay for restarting the guider 1021 /// used by cancelGuidingTimer(), isGuidingTimerActive(), startGuidingTimer 1022 /// and processGuidingTimer. 1023 int restartGuidingInterval { -1 }; 1024 KStarsDateTime restartGuidingTime; 1025 1026 /// Generic time to track timeout of current operation in progress. 1027 /// Used by startCurrentOperationTimer() and getCurrentOperationMsec(). 1028 KStarsDateTime currentOperationTime; 1029 bool currentOperationTimeStarted { false }; 1030 1031 QUrl dirPath; 1032 1033 QMap<QString, uint16_t> m_CapturedFramesCount; 1034 1035 bool m_MountReady { false }; 1036 bool m_CaptureReady { false }; 1037 bool m_DomeReady { false }; 1038 bool m_CapReady { false }; 1039 1040 // When a module is commanded to perform an action, wait this many milliseconds 1041 // before check its state again. If State is still IDLE, then it either didn't received the command 1042 // or there is another problem. 1043 static const uint32_t ALIGN_INACTIVITY_TIMEOUT = 120000; 1044 static const uint32_t FOCUS_INACTIVITY_TIMEOUT = 120000; 1045 static const uint32_t CAPTURE_INACTIVITY_TIMEOUT = 120000; 1046 static const uint16_t GUIDE_INACTIVITY_TIMEOUT = 60000; 1047 1048 // Methods & variables that control the scheduler's iterations. 1049 1050 // Initializes the scheduler, then calls iterate(). 1051 void run(); 1052 // Repeatedly runs a scheduler iteration and then sleeps timerInterval millisconds 1053 // and run the next iteration. This continues until the sleep time is negative. 1054 void iterate(); 1055 // Initialize the scheduler. 1056 void init(); 1057 // Run a single scheduler iteration. 1058 int runSchedulerIteration(); 1059 1060 // This is the time between typical scheduler iterations. 1061 // The time can be modified for testing. 1062 int m_UpdatePeriodMs = 1000; setUpdateInterval(int ms)1063 void setUpdateInterval(int ms) 1064 { 1065 m_UpdatePeriodMs = ms; 1066 } 1067 // Setup the parameters for the next scheduler iteration. 1068 // When milliseconds is not passed in, it uses m_UpdatePeriodMs. 1069 void setupNextIteration(SchedulerTimerState nextState); 1070 void setupNextIteration(SchedulerTimerState nextState, int milliseconds); 1071 // True if the scheduler is between iterations and delaying longer than the typical update period. 1072 bool currentlySleeping(); 1073 // Used by the constructor in testing mainly so a mock ekos could be used. 1074 void setupScheduler(const QString &ekosPathStr, const QString &ekosInterfaceStr); 1075 // Prints all the relative state variables set during an iteration. For debugging. 1076 void printStates(const QString &label); 1077 1078 1079 // The type of scheduler iteration that should be run next. 1080 SchedulerTimerState timerState { RUN_NOTHING }; 1081 // Variable keeping the number of millisconds the scheduler should wait 1082 // after the current scheduler iteration. 1083 int timerInterval { -1 }; 1084 // Whether the scheduler has been setup for the next iteration, 1085 // that is, whether timerInterval and timerState have been set this iteration. 1086 bool iterationSetup { false }; 1087 // The timer used to wakeup the scheduler between iterations. 1088 QTimer iterationTimer; 1089 // Counter for how many scheduler iterations have been processed. 1090 int schedulerIteration { 0 }; 1091 // The time when the scheduler first started running iterations. 1092 qint64 startMSecs { 0 }; 1093 1094 friend TestEkosSchedulerOps; 1095 }; 1096 } 1097