1 /* Ekos Scheduler Job 2 SPDX-FileCopyrightText: Jasem Mutlaq <mutlaqja@ikarustech.com> 3 4 SPDX-License-Identifier: GPL-2.0-or-later 5 */ 6 7 #pragma once 8 9 #include "skypoint.h" 10 11 #include <QUrl> 12 #include <QMap> 13 #include "ksmoon.h" 14 #include "kstarsdatetime.h" 15 16 class ArtificialHorizon; 17 class QTableWidgetItem; 18 class QLabel; 19 class KSMoon; 20 class TestSchedulerUnit; 21 class TestEkosSchedulerOps; 22 class dms; 23 24 class SchedulerJob 25 { 26 public: 27 SchedulerJob(); 28 29 /** @brief States of a SchedulerJob. */ 30 typedef enum 31 { 32 JOB_IDLE, /**< Job was just created, and is not evaluated yet */ 33 JOB_EVALUATION, /**< Job is being evaluated */ 34 JOB_SCHEDULED, /**< Job was evaluated, and has a schedule */ 35 JOB_BUSY, /**< Job is being processed */ 36 JOB_ERROR, /**< Job encountered a fatal issue while processing, and must be reset manually */ 37 JOB_ABORTED, /**< Job encountered a transitory issue while processing, and will be rescheduled */ 38 JOB_INVALID, /**< Job has an incorrect configuration, and cannot proceed */ 39 JOB_COMPLETE /**< Job finished all required captures */ 40 } JOBStatus; 41 42 /** @brief Running stages of a SchedulerJob. */ 43 typedef enum 44 { 45 STAGE_IDLE, 46 STAGE_SLEWING, 47 STAGE_SLEW_COMPLETE, 48 STAGE_FOCUSING, 49 STAGE_FOCUS_COMPLETE, 50 STAGE_ALIGNING, 51 STAGE_ALIGN_COMPLETE, 52 STAGE_RESLEWING, 53 STAGE_RESLEWING_COMPLETE, 54 STAGE_POSTALIGN_FOCUSING, 55 STAGE_POSTALIGN_FOCUSING_COMPLETE, 56 STAGE_GUIDING, 57 STAGE_GUIDING_COMPLETE, 58 STAGE_CAPTURING, 59 STAGE_COMPLETE 60 } JOBStage; 61 62 /** @brief Conditions under which a SchedulerJob may start. */ 63 typedef enum 64 { 65 START_ASAP, 66 START_CULMINATION, 67 START_AT 68 } StartupCondition; 69 70 /** @brief Conditions under which a SchedulerJob may complete. */ 71 typedef enum 72 { 73 FINISH_SEQUENCE, 74 FINISH_REPEAT, 75 FINISH_LOOP, 76 FINISH_AT 77 } CompletionCondition; 78 79 /** @brief Actions that may be processed when running a SchedulerJob. 80 * FIXME: StepPipeLine is actually a mask, change this into a bitfield. 81 */ 82 typedef enum 83 { 84 USE_NONE = 0, 85 USE_TRACK = 1 << 0, 86 USE_FOCUS = 1 << 1, 87 USE_ALIGN = 1 << 2, 88 USE_GUIDE = 1 << 3 89 } StepPipeline; 90 91 /** @brief Coordinates of the target of this job. */ 92 /** @{ */ getTargetCoords()93 SkyPoint const &getTargetCoords() const 94 { 95 return targetCoords; 96 } 97 void setTargetCoords(const dms &ra, const dms &dec, double djd); 98 /** @} */ 99 getRotation()100 double getRotation() 101 { 102 return rotation; 103 } 104 void setRotation(double rotation); 105 106 /** @brief Capture sequence this job uses while running. */ 107 /** @{ */ getSequenceFile()108 QUrl getSequenceFile() const 109 { 110 return sequenceFile; 111 } 112 void setSequenceFile(const QUrl &value); 113 /** @} */ 114 115 /** @brief FITS file whose plate solve produces target coordinates. */ 116 /** @{ */ getFITSFile()117 QUrl getFITSFile() const 118 { 119 return fitsFile; 120 } 121 void setFITSFile(const QUrl &value); 122 /** @} */ 123 124 /** @brief Minimal target altitude to process this job */ 125 /** @{ */ getMinAltitude()126 double getMinAltitude() const 127 { 128 return minAltitude; 129 } 130 void setMinAltitude(const double &value); 131 /** @} */ 132 133 /** @brief Does this job have a min-altitude parameter. */ 134 /** @{ */ hasMinAltitude()135 bool hasMinAltitude() const 136 { 137 return UNDEFINED_ALTITUDE < minAltitude; 138 } 139 static constexpr int UNDEFINED_ALTITUDE = -90; 140 /** @} */ 141 142 /** @brief Does this job have any altitude constraints. */ 143 /** @{ */ 144 bool hasAltitudeConstraint() const; 145 /** @} */ 146 147 /** @brief Minimal Moon separation to process this job. */ 148 /** @{ */ getMinMoonSeparation()149 double getMinMoonSeparation() const 150 { 151 return minMoonSeparation; 152 } 153 void setMinMoonSeparation(const double &value); 154 /** @} */ 155 156 /** @brief Whether to restrict this job to good weather. */ 157 /** @{ */ getEnforceWeather()158 bool getEnforceWeather() const 159 { 160 return enforceWeather; 161 } 162 void setEnforceWeather(bool value); 163 /** @} */ 164 165 /** @brief Mask of actions to process for this job. */ 166 /** @{ */ getStepPipeline()167 StepPipeline getStepPipeline() const 168 { 169 return stepPipeline; 170 } 171 void setStepPipeline(const StepPipeline &value); 172 /** @} */ 173 174 /** @brief Condition under which this job starts. */ 175 /** @{ */ getStartupCondition()176 StartupCondition getStartupCondition() const 177 { 178 return startupCondition; 179 } 180 void setStartupCondition(const StartupCondition &value); 181 /** @} */ 182 183 /** @brief Condition under which this job completes. */ 184 /** @{ */ getCompletionCondition()185 CompletionCondition getCompletionCondition() const 186 { 187 return completionCondition; 188 } 189 void setCompletionCondition(const CompletionCondition &value); 190 /** @} */ 191 192 /** @brief Target culmination proximity under which this job starts. */ 193 /** @{ */ getCulminationOffset()194 int16_t getCulminationOffset() const 195 { 196 return culminationOffset; 197 } 198 void setCulminationOffset(const int16_t &value); 199 /** @} */ 200 201 /** @brief Timestamp format to use when displaying information about this job. */ 202 /** @{ */ getDateTimeDisplayFormat()203 QString const &getDateTimeDisplayFormat() const 204 { 205 return dateTimeDisplayFormat; 206 } 207 void setDateTimeDisplayFormat(const QString &value); 208 /** @} */ 209 210 /** @brief Original startup condition, as entered by the user. */ 211 /** @{ */ getFileStartupCondition()212 StartupCondition getFileStartupCondition() const 213 { 214 return fileStartupCondition; 215 } 216 void setFileStartupCondition(const StartupCondition &value); 217 /** @} */ 218 219 /** @brief Original time at which the job must start, as entered by the user. */ 220 /** @{ */ getFileStartupTime()221 QDateTime getFileStartupTime() const 222 { 223 return fileStartupTime; 224 } 225 void setFileStartupTime(const QDateTime &value); 226 /** @} */ 227 228 /** @brief Whether this job requires re-focus while running its capture sequence. */ 229 /** @{ */ getInSequenceFocus()230 bool getInSequenceFocus() const 231 { 232 return inSequenceFocus; 233 } 234 void setInSequenceFocus(bool value); 235 /** @} */ 236 237 /** @brief Job priority, low priority value means most prioritary. */ 238 /** @{ */ getPriority()239 uint8_t getPriority() const 240 { 241 return priority; 242 } 243 void setPriority(const uint8_t &value); 244 /** @} */ 245 246 /** @brief Whether to restrict job to night time. */ 247 /** @{ */ getEnforceTwilight()248 bool getEnforceTwilight() const 249 { 250 return enforceTwilight; 251 } 252 void setEnforceTwilight(bool value); 253 /** @} */ 254 255 /** @brief Whether to restrict job to night time. */ 256 /** @{ */ getEnforceArtificialHorizon()257 bool getEnforceArtificialHorizon() const 258 { 259 return enforceArtificialHorizon; 260 } 261 void setEnforceArtificialHorizon(bool value); 262 /** @} */ 263 264 /** @brief Current name of the scheduler job. */ 265 /** @{ */ getName()266 QString getName() const 267 { 268 return name; 269 } 270 void setName(const QString &value); 271 /** @} */ 272 273 /** @brief Shortcut to widget cell for job name in the job queue table. */ 274 /** @{ */ getNameCell()275 QTableWidgetItem *getNameCell() const 276 { 277 return nameCell; 278 } 279 void setNameCell(QTableWidgetItem *cell); 280 /** @} */ 281 282 /** @brief Current state of the scheduler job. 283 * Setting state to JOB_ABORTED automatically resets the startup characteristics. 284 * Setting state to JOB_INVALID automatically resets the startup characteristics and the duration estimation. 285 * @see SchedulerJob::setStartupCondition, SchedulerJob::setFileStartupCondition, SchedulerJob::setStartupTime 286 * and SchedulerJob::setFileStartupTime. 287 */ 288 /** @{ */ getState()289 JOBStatus getState() const 290 { 291 return state; 292 } 293 void setState(const JOBStatus &value); 294 /** @} */ 295 296 /** @brief Shortcut to widget cell for job state in the job queue table. */ 297 /** @{ */ getStatusCell()298 QTableWidgetItem *getStatusCell() const 299 { 300 return statusCell; 301 } 302 void setStatusCell(QTableWidgetItem *cell); 303 /** @} */ 304 305 /** @brief Current stage of the scheduler job. */ 306 /** @{ */ getStage()307 JOBStage getStage() const 308 { 309 return stage; 310 } 311 void setStage(const JOBStage &value); 312 /** @} */ 313 314 /** @brief Shortcut to widget cell for job stage in the job queue table. */ 315 /** @{ */ getStageCell()316 QTableWidgetItem *getStageCell() const 317 { 318 return stageCell; 319 } 320 void setStageCell(QTableWidgetItem *cell); getStageLabel()321 QLabel *getStageLabel() const 322 { 323 return stageLabel; 324 } 325 void setStageLabel(QLabel *label); 326 /** @} */ 327 328 /** @brief Number of captures required in the associated sequence. */ 329 /** @{ */ getSequenceCount()330 int getSequenceCount() const 331 { 332 return sequenceCount; 333 } 334 void setSequenceCount(const int count); 335 /** @} */ 336 337 /** @brief Number of captures completed in the associated sequence. */ 338 /** @{ */ getCompletedCount()339 int getCompletedCount() const 340 { 341 return completedCount; 342 } 343 void setCompletedCount(const int count); 344 /** @} */ 345 346 /** @brief Shortcut to widget cell for captures in the job queue table. */ 347 /** @{ */ getCaptureCountCell()348 QTableWidgetItem *getCaptureCountCell() const 349 { 350 return captureCountCell; 351 } 352 void setCaptureCountCell(QTableWidgetItem *value); 353 /** @} */ 354 355 /** @brief Time at which the job must start. */ 356 /** @{ */ getStartupTime()357 QDateTime getStartupTime() const 358 { 359 return startupTime; 360 } 361 void setStartupTime(const QDateTime &value); 362 /** @} */ 363 364 /** @brief Shortcut to widget cell for startup time in the job queue table. */ 365 /** @{ */ getStartupCell()366 QTableWidgetItem *getStartupCell() const 367 { 368 return startupCell; 369 } 370 void setStartupCell(QTableWidgetItem *value); 371 /** @} */ 372 373 /** @brief Shortcut to widget cell for altitude in the job queue table. */ 374 /** @{ */ getAltitudeCell()375 QTableWidgetItem *getAltitudeCell() const 376 { 377 return altitudeCell; 378 } 379 void setAltitudeCell(QTableWidgetItem *value); 380 /** @} */ 381 382 /** @brief Time after which the job is considered complete. */ 383 /** @{ */ getCompletionTime()384 QDateTime getCompletionTime() const 385 { 386 return completionTime; 387 } 388 void setCompletionTime(const QDateTime &value); 389 /** @} */ 390 391 /** @brief Shortcut to widget cell for completion time in the job queue table. */ 392 /** @{ */ getCompletionCell()393 QTableWidgetItem *getCompletionCell() const 394 { 395 return completionCell; 396 } 397 void setCompletionCell(QTableWidgetItem *value); 398 /** @} */ 399 400 /** @brief Estimation of the time the job will take to process. */ 401 /** @{ */ getEstimatedTime()402 int64_t getEstimatedTime() const 403 { 404 return estimatedTime; 405 } 406 void setEstimatedTime(const int64_t &value); 407 /** @} */ 408 409 /** @brief Shortcut to widget cell for estimated time in the job queue table. */ 410 /** @{ */ getEstimatedTimeCell()411 QTableWidgetItem *getEstimatedTimeCell() const 412 { 413 return estimatedTimeCell; 414 } 415 void setEstimatedTimeCell(QTableWidgetItem *value); 416 /** @} */ 417 418 /** @brief Estimation of the lead time the job will have to process. */ 419 /** @{ */ getLeadTime()420 int64_t getLeadTime() const 421 { 422 return leadTime; 423 } 424 void setLeadTime(const int64_t &value); 425 /** @} */ 426 427 /** @brief Shortcut to widget cell for estimated time in the job queue table. */ 428 /** @{ */ getLeadTimeCell()429 QTableWidgetItem *getLeadTimeCell() const 430 { 431 return leadTimeCell; 432 } 433 void setLeadTimeCell(QTableWidgetItem *value); 434 /** @} */ 435 436 /** @brief Current score of the scheduler job. */ 437 /** @{ */ getScore()438 int getScore() const 439 { 440 return score; 441 } 442 void setScore(int value); 443 /** @} */ 444 445 /** @brief Shortcut to widget cell for job score in the job queue table. */ 446 /** @{ */ getScoreCell()447 QTableWidgetItem *getScoreCell() const 448 { 449 return scoreCell; 450 } 451 void setScoreCell(QTableWidgetItem *value); 452 /** @} */ 453 454 /** @brief Whether this job requires light frames, or only calibration frames. */ 455 /** @{ */ getLightFramesRequired()456 bool getLightFramesRequired() const 457 { 458 return lightFramesRequired; 459 } 460 void setLightFramesRequired(bool value); 461 /** @} */ 462 463 /** @brief Number of times this job must be repeated (in terms of capture count). */ 464 /** @{ */ getRepeatsRequired()465 uint16_t getRepeatsRequired() const 466 { 467 return repeatsRequired; 468 } 469 void setRepeatsRequired(const uint16_t &value); 470 /** @} */ 471 472 /** @brief Number of times this job still has to be repeated (in terms of capture count). */ 473 /** @{ */ getRepeatsRemaining()474 uint16_t getRepeatsRemaining() const 475 { 476 return repeatsRemaining; 477 } 478 void setRepeatsRemaining(const uint16_t &value); 479 /** @} */ 480 481 /** @brief The map of capture counts for this job, keyed by its capture storage signatures. */ 482 /** @{ */ 483 typedef QMap<QString, uint16_t> CapturedFramesMap; getCapturedFramesMap()484 const CapturedFramesMap &getCapturedFramesMap() const 485 { 486 return capturedFramesMap; 487 } 488 void setCapturedFramesMap(const CapturedFramesMap &value); 489 /** @} */ 490 491 /** @brief Refresh all cells connected to this SchedulerJob. */ 492 void updateJobCells(); 493 494 /** @brief Resetting a job to original values: 495 * - idle state and stage 496 * - original startup, none if asap, else user original setting 497 * - duration not estimated 498 * - full repeat count 499 */ 500 void reset(); 501 502 /** @brief Determining whether a SchedulerJob is a duplicate of another. 503 * @param a_job is the other SchedulerJob to test duplication against. 504 * @return True if objects are different, but name and sequence file are identical, else false. 505 * @warning This is a weak comparison, but that's what the scheduler looks at to decide completion. 506 */ isDuplicateOf(SchedulerJob const * a_job)507 bool isDuplicateOf(SchedulerJob const *a_job) const 508 { 509 return this != a_job && name == a_job->name && sequenceFile == a_job->sequenceFile; 510 } 511 512 /** @brief Compare ::SchedulerJob instances based on score. 513 * @todo This is a qSort predicate, deprecated in QT5. 514 * @arg a, b are ::SchedulerJob instances to compare. 515 * @return true if the score of b is lower than the score of a. 516 * @return false if the score of b is higher than or equal to the score of a. 517 */ 518 static bool decreasingScoreOrder(SchedulerJob const *a, SchedulerJob const *b); 519 520 /** @brief Compare ::SchedulerJob instances based on priority. 521 * @todo This is a qSort predicate, deprecated in QT5. 522 * @arg a, b are ::SchedulerJob instances to compare. 523 * @return true if the priority of a is lower than the priority of b. 524 * @return false if the priority of a is higher than or equal to the priority of b. 525 */ 526 static bool increasingPriorityOrder(SchedulerJob const *a, SchedulerJob const *b); 527 528 /** @brief Compare ::SchedulerJob instances based on altitude and movement in sky at startup time. 529 * @todo This is a qSort predicate, deprecated in QT5. 530 * @arg a, b are ::SchedulerJob instances to compare. 531 * @arg when is the date/time to use to calculate the altitude to sort with, defaulting to a's startup time. 532 * @note To obtain proper sort between several SchedulerJobs, all should have the same startup time. 533 * @note Use std:bind to bind a specific date/time to this predicate for altitude calculation. 534 * @return true is a is setting but not b. 535 * @return false if b is setting but not a. 536 * @return true otherwise, if the altitude of b is lower than the altitude of a. 537 * @return false otherwise, if the altitude of b is higher than or equal to the altitude of a. 538 */ 539 static bool decreasingAltitudeOrder(SchedulerJob const *a, SchedulerJob const *b, QDateTime const &when = QDateTime()); 540 541 /** @brief Compare ::SchedulerJob instances based on startup time. 542 * @todo This is a qSort predicate, deprecated in QT5. 543 * @arg a, b are ::SchedulerJob instances to compare. 544 * @return true if the startup time of a is sooner than the priority of b. 545 * @return false if the startup time of a is later than or equal to the priority of b. 546 */ 547 static bool increasingStartupTimeOrder(SchedulerJob const *a, SchedulerJob const *b); 548 549 /** 550 * @brief getAltitudeScore Get the altitude score of an object. The higher the better 551 * @param when date and time to check the target altitude, now if omitted. 552 * @param altPtr returns the altitude in degrees if not a nullptr. 553 * @return Altitude score. Target altitude below minimum altitude required by job or setting target under 3 degrees below minimum altitude get bad score. 554 */ 555 int16_t getAltitudeScore(QDateTime const &when = QDateTime(), double *altPtr = nullptr) const; 556 557 /** 558 * @brief getMoonSeparationScore Get moon separation score. The further apart, the better, up a maximum score of 20. 559 * @param when date and time to check the moon separation, now if omitted. 560 * @return Moon separation score 561 */ 562 int16_t getMoonSeparationScore(QDateTime const &when = QDateTime()) const; 563 564 /** 565 * @brief getCurrentMoonSeparation Get current moon separation in degrees at current time for the given job 566 * @param job scheduler job 567 * @return Separation in degrees 568 */ 569 double getCurrentMoonSeparation() const; 570 571 /** 572 * @brief calculateAltitudeTime calculate the altitude time given the minimum altitude given. 573 * @param when date and time to start searching from, now if omitted. 574 * @return The date and time the target is at or above the argument altitude, valid if found, invalid if not achievable (always under altitude). 575 */ 576 QDateTime calculateAltitudeTime(QDateTime const &when = QDateTime()) const; 577 578 /** 579 * @brief calculateCulmination find culmination time adjust for the job offset 580 * @param when date and time to start searching from, now if omitted 581 * @return The date and time the target is in entering the culmination interval, valid if found, invalid if not achievable (currently always valid). 582 */ 583 QDateTime calculateCulmination(QDateTime const &when = QDateTime()) const; 584 585 /** 586 * @brief calculateDawnDusk find the next astronomical dawn and dusk after the current date and time of observation 587 */ 588 static void calculateDawnDusk(QDateTime const &when, QDateTime &dawn, QDateTime &dusk); 589 590 /** 591 * @brief getNextAstronomicalTwilightDawn 592 * @return a local time QDateTime locating the first astronomical dawn after this observation. 593 * @note The dawn time takes into account the Ekos dawn offset. 594 */ getDawnAstronomicalTwilight()595 QDateTime getDawnAstronomicalTwilight() const 596 { 597 return nextDawn; 598 }; 599 600 /** 601 * @brief getDuskAstronomicalTwilight 602 * @return a local-time QDateTime locating the first astronomical dusk after this observation. 603 * @note The dusk time takes into account the Ekos dusk offset. 604 */ getDuskAstronomicalTwilight()605 QDateTime getDuskAstronomicalTwilight() const 606 { 607 return nextDusk; 608 }; 609 610 /** 611 * @brief runsDuringAstronomicalNightTime 612 * @return true if the next dawn/dusk event after this observation is the astronomical dawn, else false. 613 * @note This function relies on the guarantee that dawn and dusk are calculated to be the first events after this observation. 614 */ 615 bool runsDuringAstronomicalNightTime() const; 616 617 /** 618 * @brief findAltitude Find altitude given a specific time 619 * @param target Target 620 * @param when date time to find altitude 621 * @param is_setting whether target is setting at the argument time (optional). 622 * @param debug outputs calculation to log file (optional). 623 * @return Altitude of the target at the specific date and time given. 624 * @warning This function uses the current KStars geolocation. 625 */ 626 static double findAltitude(const SkyPoint &target, const QDateTime &when, bool *is_setting = nullptr, bool debug = false); 627 628 /** 629 * @brief getMinAltitudeConstraint Find minimum allowed altitude for this job at the given azimuth. 630 * @param azimuth Azimuth 631 * @return Minimum allowed altitude of the target at the specific azimuth. 632 */ 633 double getMinAltitudeConstraint(double azimuth) const; 634 635 // Convenience debugging methods. 636 static QString jobStatusString(JOBStatus status); 637 static QString jobStageString(JOBStage stage); 638 639 private: 640 // Private constructor for unit testing. 641 SchedulerJob(KSMoon *moonPtr); 642 friend TestSchedulerUnit; 643 friend TestEkosSchedulerOps; 644 645 /** @brief Setter used in the unit test to fix the local time. Otherwise getter gets from KStars instance. */ 646 /** @{ */ 647 static KStarsDateTime getLocalTime(); setLocalTime(KStarsDateTime * time)648 static void setLocalTime(KStarsDateTime *time) 649 { 650 storedLocalTime = time; 651 } hasLocalTime()652 static bool hasLocalTime() 653 { 654 return storedLocalTime != nullptr; 655 } 656 /** @} */ 657 658 /** @brief Setter used in testing to fix the geo location. Otherwise getter gets from KStars instance. */ 659 /** @{ */ 660 static const GeoLocation *getGeo(); setGeo(GeoLocation * geo)661 static void setGeo(GeoLocation *geo) 662 { 663 storedGeo = geo; 664 } hasGeo()665 static bool hasGeo() 666 { 667 return storedGeo != nullptr; 668 } 669 /** @} */ 670 671 /** @brief Setter used in testing to fix the artificial horizon. Otherwise getter gets from KStars instance. */ 672 /** @{ */ 673 static const ArtificialHorizon *getHorizon(); setHorizon(ArtificialHorizon * horizon)674 static void setHorizon(ArtificialHorizon *horizon) 675 { 676 storedHorizon = horizon; 677 } hasHorizon()678 static bool hasHorizon() 679 { 680 return storedHorizon != nullptr; 681 } 682 683 /** @} */ 684 685 QString name; 686 SkyPoint targetCoords; 687 double rotation { -1 }; 688 JOBStatus state { JOB_IDLE }; 689 JOBStage stage { STAGE_IDLE }; 690 691 StartupCondition fileStartupCondition { START_ASAP }; 692 StartupCondition startupCondition { START_ASAP }; 693 CompletionCondition completionCondition { FINISH_SEQUENCE }; 694 695 int sequenceCount { 0 }; 696 int completedCount { 0 }; 697 698 QDateTime fileStartupTime; 699 QDateTime startupTime; 700 QDateTime completionTime; 701 702 /* @internal Caches to optimize cell rendering. */ 703 /* @{ */ 704 double altitudeAtStartup { 0 }; 705 double altitudeAtCompletion { 0 }; 706 bool isSettingAtStartup { false }; 707 bool isSettingAtCompletion { false }; 708 /* @} */ 709 710 QUrl sequenceFile; 711 QUrl fitsFile; 712 713 double minAltitude { UNDEFINED_ALTITUDE }; 714 double minMoonSeparation { -1 }; 715 716 bool enforceWeather { false }; 717 bool enforceTwilight { false }; 718 bool enforceArtificialHorizon { false }; 719 720 QDateTime nextDawn; 721 QDateTime nextDusk; 722 723 StepPipeline stepPipeline { USE_NONE }; 724 725 /** @internal Widget cell/label shortcuts. */ 726 /** @{ */ 727 QTableWidgetItem *nameCell { nullptr }; 728 QLabel *nameLabel { nullptr }; 729 QTableWidgetItem *statusCell { nullptr }; 730 QTableWidgetItem *stageCell { nullptr }; 731 QLabel *stageLabel { nullptr }; 732 QTableWidgetItem *altitudeCell { nullptr }; 733 QTableWidgetItem *startupCell { nullptr }; 734 QTableWidgetItem *completionCell { nullptr }; 735 QTableWidgetItem *estimatedTimeCell { nullptr }; 736 QTableWidgetItem *captureCountCell { nullptr }; 737 QTableWidgetItem *scoreCell { nullptr }; 738 QTableWidgetItem *leadTimeCell { nullptr }; 739 /** @} */ 740 741 int score { 0 }; 742 int16_t culminationOffset { 0 }; 743 uint8_t priority { 10 }; 744 int64_t estimatedTime { -1 }; 745 int64_t leadTime { 0 }; 746 uint16_t repeatsRequired { 1 }; 747 uint16_t repeatsRemaining { 1 }; 748 bool inSequenceFocus { false }; 749 750 QString dateTimeDisplayFormat; 751 752 bool lightFramesRequired { false }; 753 754 QMap<QString, uint16_t> capturedFramesMap; 755 756 /// Pointer to Moon object 757 KSMoon *moon { nullptr }; 758 759 // These are used in testing, instead of KStars::Instance() resources 760 static KStarsDateTime *storedLocalTime; 761 static GeoLocation *storedGeo; 762 static ArtificialHorizon *storedHorizon; 763 }; 764