Changeset View
Changeset View
Standalone View
Standalone View
kstars/ekos/scheduler/schedulerjob.cpp
1 | /* Ekos Scheduler Job | 1 | /* Ekos Scheduler Job | ||
---|---|---|---|---|---|
2 | Copyright (C) Jasem Mutlaq <mutlaqja@ikarustech.com> | 2 | Copyright (C) Jasem Mutlaq <mutlaqja@ikarustech.com> | ||
3 | 3 | | |||
4 | This application is free software; you can redistribute it and/or | 4 | This application is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public | 5 | modify it under the terms of the GNU General Public | ||
6 | License as published by the Free Software Foundation; either | 6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | 7 | version 2 of the License, or (at your option) any later version. | ||
8 | */ | 8 | */ | ||
9 | 9 | | |||
10 | #include "schedulerjob.h" | 10 | #include "schedulerjob.h" | ||
11 | 11 | | |||
12 | #include "dms.h" | 12 | #include "dms.h" | ||
13 | #include "kstarsdata.h" | 13 | #include "kstarsdata.h" | ||
14 | #include "Options.h" | ||||
14 | #include "scheduler.h" | 15 | #include "scheduler.h" | ||
15 | 16 | | |||
16 | #include <knotification.h> | 17 | #include <knotification.h> | ||
17 | 18 | | |||
18 | #include <QTableWidgetItem> | 19 | #include <QTableWidgetItem> | ||
19 | 20 | | |||
20 | void SchedulerJob::setName(const QString &value) | 21 | void SchedulerJob::setName(const QString &value) | ||
21 | { | 22 | { | ||
22 | name = value; | 23 | name = value; | ||
23 | updateJobCell(); | 24 | updateJobCells(); | ||
24 | } | 25 | } | ||
25 | 26 | | |||
26 | void SchedulerJob::setStartupCondition(const StartupCondition &value) | 27 | void SchedulerJob::setStartupCondition(const StartupCondition &value) | ||
27 | { | 28 | { | ||
28 | startupCondition = value; | 29 | startupCondition = value; | ||
30 | | ||||
31 | /* Keep startup time and condition valid */ | ||||
29 | if (value == START_ASAP) | 32 | if (value == START_ASAP) | ||
30 | startupTime = QDateTime(); | 33 | startupTime = QDateTime(); | ||
31 | updateJobCell(); | 34 | | ||
35 | /* Refresh estimated time - which update job cells */ | ||||
36 | setEstimatedTime(estimatedTime); | ||||
32 | } | 37 | } | ||
33 | 38 | | |||
34 | void SchedulerJob::setStartupTime(const QDateTime &value) | 39 | void SchedulerJob::setStartupTime(const QDateTime &value) | ||
35 | { | 40 | { | ||
36 | startupTime = value; | 41 | startupTime = value; | ||
37 | 42 | | |||
43 | /* Keep startup time and condition valid */ | ||||
38 | if (value.isValid()) | 44 | if (value.isValid()) | ||
39 | startupCondition = START_AT; | 45 | startupCondition = START_AT; | ||
46 | else | ||||
47 | startupCondition = fileStartupCondition; | ||||
48 | | ||||
49 | // Refresh altitude - invalid date/time is taken care of when rendering | ||||
50 | altitudeAtStartup = Ekos::Scheduler::findAltitude(targetCoords, startupTime, &isSettingAtStartup); | ||||
40 | 51 | | |||
41 | /* Refresh estimated time - which update job cells */ | 52 | /* Refresh estimated time - which update job cells */ | ||
42 | setEstimatedTime(estimatedTime); | 53 | setEstimatedTime(estimatedTime); | ||
43 | } | 54 | } | ||
44 | 55 | | |||
45 | void SchedulerJob::setSequenceFile(const QUrl &value) | 56 | void SchedulerJob::setSequenceFile(const QUrl &value) | ||
46 | { | 57 | { | ||
47 | sequenceFile = value; | 58 | sequenceFile = value; | ||
Show All 16 Lines | |||||
64 | 75 | | |||
65 | void SchedulerJob::setEnforceWeather(bool value) | 76 | void SchedulerJob::setEnforceWeather(bool value) | ||
66 | { | 77 | { | ||
67 | enforceWeather = value; | 78 | enforceWeather = value; | ||
68 | } | 79 | } | ||
69 | 80 | | |||
70 | void SchedulerJob::setCompletionTime(const QDateTime &value) | 81 | void SchedulerJob::setCompletionTime(const QDateTime &value) | ||
71 | { | 82 | { | ||
72 | /* If argument completion time is valid, automatically switch condition to FINISH_AT */ | 83 | /* If completion time is valid, automatically switch condition to FINISH_AT */ | ||
73 | if (value.isValid()) | 84 | if (value.isValid()) | ||
74 | { | 85 | { | ||
75 | setCompletionCondition(FINISH_AT); | 86 | setCompletionCondition(FINISH_AT); | ||
76 | completionTime = value; | 87 | completionTime = value; | ||
88 | altitudeAtCompletion = Ekos::Scheduler::findAltitude(targetCoords, completionTime, &isSettingAtCompletion); | ||||
89 | setEstimatedTime(-1); | ||||
90 | } | ||||
91 | /* If completion time is invalid, and job is looping, keep completion time undefined */ | ||||
92 | else if (FINISH_LOOP == completionCondition) | ||||
93 | { | ||||
94 | completionTime = QDateTime(); | ||||
95 | altitudeAtCompletion = Ekos::Scheduler::findAltitude(targetCoords, completionTime, &isSettingAtCompletion); | ||||
96 | setEstimatedTime(-1); | ||||
77 | } | 97 | } | ||
78 | /* If completion time is not valid, but startup time is, deduce completion from startup and duration */ | 98 | /* If completion time is invalid, deduce completion from startup and duration */ | ||
79 | else if (startupTime.isValid()) | 99 | else if (startupTime.isValid()) | ||
80 | { | 100 | { | ||
81 | completionTime = startupTime.addSecs(estimatedTime); | 101 | completionTime = startupTime.addSecs(estimatedTime); | ||
102 | altitudeAtCompletion = Ekos::Scheduler::findAltitude(targetCoords, completionTime, &isSettingAtCompletion); | ||||
103 | updateJobCells(); | ||||
82 | } | 104 | } | ||
105 | /* Else just refresh estimated time - which update job cells */ | ||||
106 | else setEstimatedTime(estimatedTime); | ||||
83 | 107 | | |||
84 | /* Refresh estimated time - which update job cells */ | 108 | | ||
85 | setEstimatedTime(estimatedTime); | 109 | /* Invariants */ | ||
110 | Q_ASSERT_X(completionTime.isValid() ? | ||||
111 | (FINISH_AT == completionCondition || FINISH_REPEAT == completionCondition || FINISH_SEQUENCE == completionCondition) : | ||||
112 | FINISH_LOOP == completionCondition, | ||||
113 | __FUNCTION__, "Valid completion time implies job is FINISH_AT/REPEAT/SEQUENCE, else job is FINISH_LOOP."); | ||||
86 | } | 114 | } | ||
87 | 115 | | |||
88 | void SchedulerJob::setCompletionCondition(const CompletionCondition &value) | 116 | void SchedulerJob::setCompletionCondition(const CompletionCondition &value) | ||
89 | { | 117 | { | ||
90 | completionCondition = value; | 118 | completionCondition = value; | ||
91 | 119 | | |||
92 | // Update repeats requirement, looping jobs have none | 120 | // Update repeats requirement, looping jobs have none | ||
93 | switch (completionCondition) | 121 | switch (completionCondition) | ||
94 | { | 122 | { | ||
95 | case FINISH_AT: | | |||
96 | case FINISH_LOOP: | 123 | case FINISH_LOOP: | ||
124 | setCompletionTime(QDateTime()); | ||||
125 | /* Fall through */ | ||||
126 | case FINISH_AT: | ||||
97 | if (0 < getRepeatsRequired()) | 127 | if (0 < getRepeatsRequired()) | ||
98 | setRepeatsRequired(0); | 128 | setRepeatsRequired(0); | ||
99 | break; | 129 | break; | ||
100 | 130 | | |||
101 | case FINISH_SEQUENCE: | 131 | case FINISH_SEQUENCE: | ||
102 | if (1 != getRepeatsRequired()) | 132 | if (1 != getRepeatsRequired()) | ||
103 | setRepeatsRequired(1); | 133 | setRepeatsRequired(1); | ||
104 | break; | 134 | break; | ||
105 | 135 | | |||
106 | case FINISH_REPEAT: | 136 | case FINISH_REPEAT: | ||
107 | if (0 == getRepeatsRequired()) | 137 | if (0 == getRepeatsRequired()) | ||
108 | setRepeatsRequired(1); | 138 | setRepeatsRequired(1); | ||
109 | break; | 139 | break; | ||
110 | 140 | | |||
111 | default: break; | 141 | default: break; | ||
112 | } | 142 | } | ||
113 | 143 | | |||
114 | updateJobCell(); | 144 | updateJobCells(); | ||
115 | } | 145 | } | ||
116 | 146 | | |||
117 | void SchedulerJob::setStepPipeline(const StepPipeline &value) | 147 | void SchedulerJob::setStepPipeline(const StepPipeline &value) | ||
118 | { | 148 | { | ||
119 | stepPipeline = value; | 149 | stepPipeline = value; | ||
120 | } | 150 | } | ||
121 | 151 | | |||
122 | void SchedulerJob::setState(const JOBStatus &value) | 152 | void SchedulerJob::setState(const JOBStatus &value) | ||
Show All 14 Lines | 153 | { | |||
137 | 167 | | |||
138 | /* If job is aborted, automatically reset its startup characteristics */ | 168 | /* If job is aborted, automatically reset its startup characteristics */ | ||
139 | if (JOB_ABORTED == value) | 169 | if (JOB_ABORTED == value) | ||
140 | { | 170 | { | ||
141 | setStartupCondition(fileStartupCondition); | 171 | setStartupCondition(fileStartupCondition); | ||
142 | /* setStartupTime(fileStartupTime); */ | 172 | /* setStartupTime(fileStartupTime); */ | ||
143 | } | 173 | } | ||
144 | 174 | | |||
145 | updateJobCell(); | 175 | updateJobCells(); | ||
176 | } | ||||
177 | | ||||
178 | void SchedulerJob::setLeadTime(const int64_t &value) | ||||
179 | { | ||||
180 | leadTime = value; | ||||
181 | updateJobCells(); | ||||
146 | } | 182 | } | ||
147 | 183 | | |||
148 | void SchedulerJob::setScore(int value) | 184 | void SchedulerJob::setScore(int value) | ||
149 | { | 185 | { | ||
150 | score = value; | 186 | score = value; | ||
151 | updateJobCell(); | 187 | updateJobCells(); | ||
152 | } | 188 | } | ||
153 | 189 | | |||
154 | void SchedulerJob::setCulminationOffset(const int16_t &value) | 190 | void SchedulerJob::setCulminationOffset(const int16_t &value) | ||
155 | { | 191 | { | ||
156 | culminationOffset = value; | 192 | culminationOffset = value; | ||
157 | } | 193 | } | ||
158 | 194 | | |||
159 | void SchedulerJob::setSequenceCount(const int count) | 195 | void SchedulerJob::setSequenceCount(const int count) | ||
160 | { | 196 | { | ||
161 | sequenceCount = count; | 197 | sequenceCount = count; | ||
162 | updateJobCell(); | 198 | updateJobCells(); | ||
163 | } | 199 | } | ||
164 | 200 | | |||
165 | void SchedulerJob::setNameCell(QTableWidgetItem *value) | 201 | void SchedulerJob::setNameCell(QTableWidgetItem *value) | ||
166 | { | 202 | { | ||
167 | nameCell = value; | 203 | nameCell = value; | ||
168 | updateJobCell(); | | |||
169 | } | 204 | } | ||
170 | 205 | | |||
171 | void SchedulerJob::setCompletedCount(const int count) | 206 | void SchedulerJob::setCompletedCount(const int count) | ||
172 | { | 207 | { | ||
173 | completedCount = count; | 208 | completedCount = count; | ||
174 | updateJobCell(); | 209 | updateJobCells(); | ||
175 | } | 210 | } | ||
176 | 211 | | |||
177 | void SchedulerJob::setStatusCell(QTableWidgetItem *value) | 212 | void SchedulerJob::setStatusCell(QTableWidgetItem *value) | ||
178 | { | 213 | { | ||
179 | statusCell = value; | 214 | statusCell = value; | ||
180 | updateJobCell(); | 215 | if (nullptr != statusCell) | ||
181 | if (statusCell) | | |||
182 | statusCell->setToolTip(i18n("Current status of job '%1', managed by the Scheduler.\n" | 216 | statusCell->setToolTip(i18n("Current status of job '%1', managed by the Scheduler.\n" | ||
183 | "If invalid, the Scheduler was not able to find a proper observation time for the target.\n" | 217 | "If invalid, the Scheduler was not able to find a proper observation time for the target.\n" | ||
184 | "If aborted, the Scheduler missed the scheduled time or encountered transitory issues and will reschedule the job.\n" | 218 | "If aborted, the Scheduler missed the scheduled time or encountered transitory issues and will reschedule the job.\n" | ||
185 | "If complete, the Scheduler verified that all sequence captures requested were stored, including repeats.", | 219 | "If complete, the Scheduler verified that all sequence captures requested were stored, including repeats.", | ||
186 | name)); | 220 | name)); | ||
187 | } | 221 | } | ||
188 | 222 | | |||
223 | void SchedulerJob::setAltitudeCell(QTableWidgetItem *value) | ||||
224 | { | ||||
225 | altitudeCell = value; | ||||
226 | if (nullptr != altitudeCell) | ||||
227 | altitudeCell->setToolTip(i18n("Current altitude of the target of job '%1'.\n" | ||||
228 | "The altitude at startup, if available, is displayed between parentheses.\n" | ||||
229 | "A rising target is indicated with an arrow going up.\n" | ||||
230 | "A setting target is indicated with an arrow going down.", | ||||
231 | name)); | ||||
232 | } | ||||
233 | | ||||
189 | void SchedulerJob::setStartupCell(QTableWidgetItem *value) | 234 | void SchedulerJob::setStartupCell(QTableWidgetItem *value) | ||
190 | { | 235 | { | ||
191 | startupCell = value; | 236 | startupCell = value; | ||
192 | updateJobCell(); | 237 | if (nullptr != startupCell) | ||
193 | if (startupCell) | | |||
194 | startupCell->setToolTip(i18n("Startup time for job '%1', as estimated by the Scheduler.\n" | 238 | startupCell->setToolTip(i18n("Startup time for job '%1', as estimated by the Scheduler.\n" | ||
195 | "Fixed time from user or culmination time is marked with a chronometer symbol. ", | 239 | "Fixed time from user or culmination time is marked with a chronometer symbol. ", | ||
196 | name)); | 240 | name)); | ||
197 | } | 241 | } | ||
198 | 242 | | |||
199 | void SchedulerJob::setCompletionCell(QTableWidgetItem *value) | 243 | void SchedulerJob::setCompletionCell(QTableWidgetItem *value) | ||
200 | { | 244 | { | ||
201 | completionCell = value; | 245 | completionCell = value; | ||
202 | updateJobCell(); | 246 | if (nullptr != completionCell) | ||
203 | if (completionCell) | | |||
204 | completionCell->setToolTip(i18n("Completion time for job '%1', as estimated by the Scheduler.\n" | 247 | completionCell->setToolTip(i18n("Completion time for job '%1', as estimated by the Scheduler.\n" | ||
205 | "Can be specified by the user to limit duration of looping jobs.\n" | 248 | "You may specify a fixed time to limit duration of looping jobs." | ||
206 | "Fixed time from user is marked with a chronometer symbol. ", | 249 | "A warning symbol indicates the altitude at completion may cause the job to abort before completion.\n", | ||
207 | name)); | 250 | name)); | ||
208 | } | 251 | } | ||
209 | 252 | | |||
210 | void SchedulerJob::setCaptureCountCell(QTableWidgetItem *value) | 253 | void SchedulerJob::setCaptureCountCell(QTableWidgetItem *value) | ||
211 | { | 254 | { | ||
212 | captureCountCell = value; | 255 | captureCountCell = value; | ||
213 | updateJobCell(); | 256 | if (nullptr != captureCountCell) | ||
214 | if (captureCountCell) | | |||
215 | captureCountCell->setToolTip(i18n("Count of captures stored for job '%1', based on its sequence job.\n" | 257 | captureCountCell->setToolTip(i18n("Count of captures stored for job '%1', based on its sequence job.\n" | ||
216 | "This is a summary, additional specific frame types may be required to complete the job.", | 258 | "This is a summary, additional specific frame types may be required to complete the job.", | ||
217 | name)); | 259 | name)); | ||
218 | } | 260 | } | ||
219 | 261 | | |||
220 | void SchedulerJob::setScoreCell(QTableWidgetItem *value) | 262 | void SchedulerJob::setScoreCell(QTableWidgetItem *value) | ||
221 | { | 263 | { | ||
222 | scoreCell = value; | 264 | scoreCell = value; | ||
223 | updateJobCell(); | 265 | if (nullptr != scoreCell) | ||
224 | if (scoreCell) | | |||
225 | scoreCell->setToolTip(i18n("Current score for job '%1', from its altitude, moon separation and sky darkness.\n" | 266 | scoreCell->setToolTip(i18n("Current score for job '%1', from its altitude, moon separation and sky darkness.\n" | ||
226 | "Negative if adequate altitude is not achieved yet or if there is no proper observation time today.\n" | 267 | "Negative if adequate altitude is not achieved yet or if there is no proper observation time today.\n" | ||
227 | "The Scheduler will refresh scores when picking a new candidate job.", | 268 | "The Scheduler will refresh scores when picking a new candidate job.", | ||
228 | name)); | 269 | name)); | ||
229 | } | 270 | } | ||
230 | 271 | | |||
272 | void SchedulerJob::setLeadTimeCell(QTableWidgetItem *value) | ||||
273 | { | ||||
274 | leadTimeCell = value; | ||||
275 | if (nullptr != leadTimeCell) | ||||
276 | leadTimeCell->setToolTip(i18n("Time interval from the job which precedes job '%1'.\n" | ||||
277 | "Adjust the Lead Time in Ekos options to increase that duration and leave time for jobs to complete.\n" | ||||
278 | "Rearrange jobs to minimize that duration and optimize your imaging time.", | ||||
279 | name)); | ||||
280 | } | ||||
281 | | ||||
231 | void SchedulerJob::setDateTimeDisplayFormat(const QString &value) | 282 | void SchedulerJob::setDateTimeDisplayFormat(const QString &value) | ||
232 | { | 283 | { | ||
233 | dateTimeDisplayFormat = value; | 284 | dateTimeDisplayFormat = value; | ||
234 | updateJobCell(); | 285 | updateJobCells(); | ||
235 | } | 286 | } | ||
236 | 287 | | |||
237 | void SchedulerJob::setStage(const JOBStage &value) | 288 | void SchedulerJob::setStage(const JOBStage &value) | ||
238 | { | 289 | { | ||
239 | stage = value; | 290 | stage = value; | ||
240 | updateJobCell(); | 291 | updateJobCells(); | ||
241 | } | 292 | } | ||
242 | 293 | | |||
243 | void SchedulerJob::setStageCell(QTableWidgetItem *cell) | 294 | void SchedulerJob::setStageCell(QTableWidgetItem *cell) | ||
244 | { | 295 | { | ||
245 | stageCell = cell; | 296 | stageCell = cell; | ||
246 | updateJobCell(); | 297 | // FIXME: Add a tool tip if cell is used | ||
247 | } | 298 | } | ||
248 | 299 | | |||
249 | void SchedulerJob::setStageLabel(QLabel *label) | 300 | void SchedulerJob::setStageLabel(QLabel *label) | ||
250 | { | 301 | { | ||
251 | stageLabel = label; | 302 | stageLabel = label; | ||
252 | updateJobCell(); | | |||
253 | } | 303 | } | ||
254 | 304 | | |||
255 | void SchedulerJob::setFileStartupCondition(const StartupCondition &value) | 305 | void SchedulerJob::setFileStartupCondition(const StartupCondition &value) | ||
256 | { | 306 | { | ||
257 | fileStartupCondition = value; | 307 | fileStartupCondition = value; | ||
258 | } | 308 | } | ||
259 | 309 | | |||
260 | void SchedulerJob::setFileStartupTime(const QDateTime &value) | 310 | void SchedulerJob::setFileStartupTime(const QDateTime &value) | ||
261 | { | 311 | { | ||
262 | fileStartupTime = value; | 312 | fileStartupTime = value; | ||
263 | } | 313 | } | ||
264 | 314 | | |||
265 | void SchedulerJob::setEstimatedTime(const int64_t &value) | 315 | void SchedulerJob::setEstimatedTime(const int64_t &value) | ||
266 | { | 316 | { | ||
267 | /* If startup and completion times are fixed, estimated time cannot change */ | 317 | /* Estimated time is generally the difference between startup and completion times: | ||
268 | if (START_AT == startupCondition && FINISH_AT == completionCondition) | 318 | * - It is fixed when startup and completion times are fixed, that is, we disregard the argument | ||
319 | * - Else mostly it pushes completion time from startup time | ||||
320 | * | ||||
321 | * However it cannot advance startup time when completion time is fixed because of the way jobs are scheduled. | ||||
322 | * This situation requires a warning in the user interface when there is not enough time for the job to process. | ||||
323 | */ | ||||
324 | | ||||
325 | /* If startup and completion times are fixed, estimated time cannot change - disregard the argument */ | ||||
326 | if (START_ASAP != fileStartupCondition && FINISH_AT == completionCondition) | ||||
269 | { | 327 | { | ||
270 | estimatedTime = startupTime.secsTo(completionTime); | 328 | estimatedTime = startupTime.secsTo(completionTime); | ||
271 | } | 329 | } | ||
272 | /* If startup time is fixed but not completion time, estimated time adjusts completion time */ | 330 | /* If completion time isn't fixed, estimated time adjusts completion time */ | ||
273 | else if (START_AT == startupCondition) | 331 | else if (FINISH_AT != completionCondition && FINISH_LOOP != completionCondition) | ||
274 | { | 332 | { | ||
275 | estimatedTime = value; | 333 | estimatedTime = value; | ||
276 | completionTime = startupTime.addSecs(value); | 334 | completionTime = startupTime.addSecs(value); | ||
335 | altitudeAtCompletion = Ekos::Scheduler::findAltitude(targetCoords, completionTime, &isSettingAtCompletion); | ||||
277 | } | 336 | } | ||
278 | /* If completion time is fixed but not startup time, estimated time adjusts startup time */ | 337 | /* Else estimated time is simply stored as is - covers FINISH_LOOP from setCompletionTime */ | ||
279 | /* FIXME: adjusting startup time will probably not work, because jobs are scheduled from first available altitude */ | | |||
280 | else if (FINISH_AT == completionCondition) | | |||
281 | { | | |||
282 | estimatedTime = value; | | |||
283 | startupTime = completionTime.addSecs(-value); | | |||
284 | } | | |||
285 | /* Else estimated time is simply stored as is */ | | |||
286 | else estimatedTime = value; | 338 | else estimatedTime = value; | ||
287 | 339 | | |||
288 | updateJobCell(); | 340 | updateJobCells(); | ||
289 | } | 341 | } | ||
290 | 342 | | |||
291 | void SchedulerJob::setInSequenceFocus(bool value) | 343 | void SchedulerJob::setInSequenceFocus(bool value) | ||
292 | { | 344 | { | ||
293 | inSequenceFocus = value; | 345 | inSequenceFocus = value; | ||
294 | } | 346 | } | ||
295 | 347 | | |||
296 | void SchedulerJob::setPriority(const uint8_t &value) | 348 | void SchedulerJob::setPriority(const uint8_t &value) | ||
297 | { | 349 | { | ||
298 | priority = value; | 350 | priority = value; | ||
299 | } | 351 | } | ||
300 | 352 | | |||
301 | void SchedulerJob::setEnforceTwilight(bool value) | 353 | void SchedulerJob::setEnforceTwilight(bool value) | ||
302 | { | 354 | { | ||
303 | enforceTwilight = value; | 355 | enforceTwilight = value; | ||
304 | } | 356 | } | ||
305 | 357 | | |||
306 | void SchedulerJob::setEstimatedTimeCell(QTableWidgetItem *value) | 358 | void SchedulerJob::setEstimatedTimeCell(QTableWidgetItem *value) | ||
307 | { | 359 | { | ||
308 | estimatedTimeCell = value; | 360 | estimatedTimeCell = value; | ||
309 | updateJobCell(); | | |||
310 | if (estimatedTimeCell) | 361 | if (estimatedTimeCell) | ||
311 | estimatedTimeCell->setToolTip(i18n("Duration job '%1' will take to complete when started, as estimated by the Scheduler.\n" | 362 | estimatedTimeCell->setToolTip(i18n("Duration job '%1' will take to complete when started, as estimated by the Scheduler.\n" | ||
312 | "Depends on the actions to be run, and the sequence job to be processed.", | 363 | "Depends on the actions to be run, and the sequence job to be processed.", | ||
313 | name)); | 364 | name)); | ||
314 | } | 365 | } | ||
315 | 366 | | |||
316 | void SchedulerJob::setLightFramesRequired(bool value) | 367 | void SchedulerJob::setLightFramesRequired(bool value) | ||
317 | { | 368 | { | ||
Show All 16 Lines | 384 | if (FINISH_SEQUENCE != completionCondition) | |||
334 | setCompletionCondition(FINISH_SEQUENCE); | 385 | setCompletionCondition(FINISH_SEQUENCE); | ||
335 | } | 386 | } | ||
336 | else | 387 | else | ||
337 | { | 388 | { | ||
338 | if (FINISH_LOOP != completionCondition) | 389 | if (FINISH_LOOP != completionCondition) | ||
339 | setCompletionCondition(FINISH_LOOP); | 390 | setCompletionCondition(FINISH_LOOP); | ||
340 | } | 391 | } | ||
341 | 392 | | |||
342 | updateJobCell(); | 393 | updateJobCells(); | ||
343 | } | 394 | } | ||
344 | 395 | | |||
345 | void SchedulerJob::setRepeatsRemaining(const uint16_t &value) | 396 | void SchedulerJob::setRepeatsRemaining(const uint16_t &value) | ||
346 | { | 397 | { | ||
347 | repeatsRemaining = value; | 398 | repeatsRemaining = value; | ||
348 | updateJobCell(); | 399 | updateJobCells(); | ||
349 | } | 400 | } | ||
350 | 401 | | |||
351 | void SchedulerJob::setCapturedFramesMap(const CapturedFramesMap &value) | 402 | void SchedulerJob::setCapturedFramesMap(const CapturedFramesMap &value) | ||
352 | { | 403 | { | ||
353 | capturedFramesMap = value; | 404 | capturedFramesMap = value; | ||
354 | } | 405 | } | ||
355 | 406 | | |||
356 | void SchedulerJob::setTargetCoords(dms& ra, dms& dec) | 407 | void SchedulerJob::setTargetCoords(dms& ra, dms& dec) | ||
357 | { | 408 | { | ||
358 | targetCoords.setRA0(ra); | 409 | targetCoords.setRA0(ra); | ||
359 | targetCoords.setDec0(dec); | 410 | targetCoords.setDec0(dec); | ||
360 | 411 | | |||
361 | targetCoords.apparentCoord(static_cast<long double>(J2000), KStarsData::Instance()->ut().djd()); | 412 | targetCoords.apparentCoord(static_cast<long double>(J2000), KStarsData::Instance()->ut().djd()); | ||
362 | } | 413 | } | ||
363 | 414 | | |||
364 | void SchedulerJob::updateJobCell() | 415 | void SchedulerJob::updateJobCells() | ||
365 | { | 416 | { | ||
366 | if (nameCell) | 417 | if (nullptr != nameCell) | ||
367 | { | 418 | { | ||
368 | nameCell->setText(name); | 419 | nameCell->setText(name); | ||
420 | if (nullptr != nameCell) | ||||
369 | nameCell->tableWidget()->resizeColumnToContents(nameCell->column()); | 421 | nameCell->tableWidget()->resizeColumnToContents(nameCell->column()); | ||
370 | } | 422 | } | ||
371 | 423 | | |||
372 | if (nameLabel) | 424 | if (nullptr != nameLabel) | ||
373 | { | 425 | { | ||
374 | nameLabel->setText(name + QString(":")); | 426 | nameLabel->setText(name + QString(":")); | ||
375 | } | 427 | } | ||
376 | 428 | | |||
377 | if (statusCell) | 429 | if (nullptr != statusCell) | ||
378 | { | 430 | { | ||
379 | static QMap<JOBStatus, QString> stateStrings; | 431 | static QMap<JOBStatus, QString> stateStrings; | ||
380 | static QString stateStringUnknown; | 432 | static QString stateStringUnknown; | ||
381 | if (stateStrings.isEmpty()) | 433 | if (stateStrings.isEmpty()) | ||
382 | { | 434 | { | ||
383 | stateStrings[JOB_IDLE] = i18n("Idle"); | 435 | stateStrings[JOB_IDLE] = i18n("Idle"); | ||
384 | stateStrings[JOB_EVALUATION] = i18n("Evaluating"); | 436 | stateStrings[JOB_EVALUATION] = i18n("Evaluating"); | ||
385 | stateStrings[JOB_SCHEDULED] = i18n("Scheduled"); | 437 | stateStrings[JOB_SCHEDULED] = i18n("Scheduled"); | ||
386 | stateStrings[JOB_BUSY] = i18n("Running"); | 438 | stateStrings[JOB_BUSY] = i18n("Running"); | ||
387 | stateStrings[JOB_INVALID] = i18n("Invalid"); | 439 | stateStrings[JOB_INVALID] = i18n("Invalid"); | ||
388 | stateStrings[JOB_COMPLETE] = i18n("Complete"); | 440 | stateStrings[JOB_COMPLETE] = i18n("Complete"); | ||
389 | stateStrings[JOB_ABORTED] = i18n("Aborted"); | 441 | stateStrings[JOB_ABORTED] = i18n("Aborted"); | ||
390 | stateStrings[JOB_ERROR] = i18n("Error"); | 442 | stateStrings[JOB_ERROR] = i18n("Error"); | ||
391 | stateStringUnknown = i18n("Unknown"); | 443 | stateStringUnknown = i18n("Unknown"); | ||
392 | } | 444 | } | ||
393 | statusCell->setText(stateStrings.value(state, stateStringUnknown)); | 445 | statusCell->setText(stateStrings.value(state, stateStringUnknown)); | ||
446 | | ||||
447 | if (nullptr != statusCell->tableWidget()) | ||||
394 | statusCell->tableWidget()->resizeColumnToContents(statusCell->column()); | 448 | statusCell->tableWidget()->resizeColumnToContents(statusCell->column()); | ||
395 | } | 449 | } | ||
396 | 450 | | |||
397 | if (stageCell || stageLabel) | 451 | if (nullptr != stageCell || nullptr != stageLabel) | ||
398 | { | 452 | { | ||
399 | /* Translated string cache - overkill, probably, and doesn't warn about missing enums like switch/case should ; also, not thread-safe */ | 453 | /* Translated string cache - overkill, probably, and doesn't warn about missing enums like switch/case should ; also, not thread-safe */ | ||
400 | /* FIXME: this should work with a static initializer in C++11, but QT versions are touchy on this, and perhaps i18n can't be used? */ | 454 | /* FIXME: this should work with a static initializer in C++11, but QT versions are touchy on this, and perhaps i18n can't be used? */ | ||
401 | static QMap<JOBStage, QString> stageStrings; | 455 | static QMap<JOBStage, QString> stageStrings; | ||
402 | static QString stageStringUnknown; | 456 | static QString stageStringUnknown; | ||
403 | if (stageStrings.isEmpty()) | 457 | if (stageStrings.isEmpty()) | ||
404 | { | 458 | { | ||
405 | stageStrings[STAGE_IDLE] = i18n("Idle"); | 459 | stageStrings[STAGE_IDLE] = i18n("Idle"); | ||
406 | stageStrings[STAGE_SLEWING] = i18n("Slewing"); | 460 | stageStrings[STAGE_SLEWING] = i18n("Slewing"); | ||
407 | stageStrings[STAGE_SLEW_COMPLETE] = i18n("Slew complete"); | 461 | stageStrings[STAGE_SLEW_COMPLETE] = i18n("Slew complete"); | ||
408 | stageStrings[STAGE_FOCUSING] = | 462 | stageStrings[STAGE_FOCUSING] = | ||
409 | stageStrings[STAGE_POSTALIGN_FOCUSING] = i18n("Focusing"); | 463 | stageStrings[STAGE_POSTALIGN_FOCUSING] = i18n("Focusing"); | ||
410 | stageStrings[STAGE_FOCUS_COMPLETE] = | 464 | stageStrings[STAGE_FOCUS_COMPLETE] = | ||
411 | stageStrings[STAGE_POSTALIGN_FOCUSING_COMPLETE ] = i18n("Focus complete"); | 465 | stageStrings[STAGE_POSTALIGN_FOCUSING_COMPLETE ] = i18n("Focus complete"); | ||
412 | stageStrings[STAGE_ALIGNING] = i18n("Aligning"); | 466 | stageStrings[STAGE_ALIGNING] = i18n("Aligning"); | ||
413 | stageStrings[STAGE_ALIGN_COMPLETE] = i18n("Align complete"); | 467 | stageStrings[STAGE_ALIGN_COMPLETE] = i18n("Align complete"); | ||
414 | stageStrings[STAGE_RESLEWING] = i18n("Repositioning"); | 468 | stageStrings[STAGE_RESLEWING] = i18n("Repositioning"); | ||
415 | stageStrings[STAGE_RESLEWING_COMPLETE] = i18n("Repositioning complete"); | 469 | stageStrings[STAGE_RESLEWING_COMPLETE] = i18n("Repositioning complete"); | ||
416 | /*stageStrings[STAGE_CALIBRATING] = i18n("Calibrating");*/ | 470 | /*stageStrings[STAGE_CALIBRATING] = i18n("Calibrating");*/ | ||
417 | stageStrings[STAGE_GUIDING] = i18n("Guiding"); | 471 | stageStrings[STAGE_GUIDING] = i18n("Guiding"); | ||
418 | stageStrings[STAGE_GUIDING_COMPLETE] = i18n("Guiding complete"); | 472 | stageStrings[STAGE_GUIDING_COMPLETE] = i18n("Guiding complete"); | ||
419 | stageStrings[STAGE_CAPTURING] = i18n("Capturing"); | 473 | stageStrings[STAGE_CAPTURING] = i18n("Capturing"); | ||
420 | stageStringUnknown = i18n("Unknown"); | 474 | stageStringUnknown = i18n("Unknown"); | ||
421 | } | 475 | } | ||
422 | if (stageCell) | 476 | if (nullptr != stageCell) | ||
423 | { | 477 | { | ||
424 | stageCell->setText(stageStrings.value(stage, stageStringUnknown)); | 478 | stageCell->setText(stageStrings.value(stage, stageStringUnknown)); | ||
479 | if (nullptr != stageCell->tableWidget()) | ||||
425 | stageCell->tableWidget()->resizeColumnToContents(stageCell->column()); | 480 | stageCell->tableWidget()->resizeColumnToContents(stageCell->column()); | ||
426 | } | 481 | } | ||
427 | if (stageLabel) | 482 | if (nullptr != stageLabel) | ||
428 | { | 483 | { | ||
429 | stageLabel->setText(QString("%1: %2").arg(name, stageStrings.value(stage, stageStringUnknown))); | 484 | stageLabel->setText(QString("%1: %2").arg(name, stageStrings.value(stage, stageStringUnknown))); | ||
430 | } | 485 | } | ||
431 | } | 486 | } | ||
432 | 487 | | |||
433 | if (startupCell && startupCell->tableWidget()) | 488 | if (nullptr != startupCell) | ||
434 | { | 489 | { | ||
435 | /* Display a startup time if job is running, scheduled to run or about to be re-scheduled */ | 490 | /* Display startup time if it is valid */ | ||
436 | if (JOB_SCHEDULED == state || JOB_BUSY == state || JOB_ABORTED == state) switch (fileStartupCondition) | 491 | if (startupTime.isValid()) | ||
492 | { | ||||
493 | startupCell->setText(QString("%1%2%L3° %4") | ||||
494 | .arg(altitudeAtStartup < minAltitude ? QString(QChar(0x26A0)) : "") | ||||
495 | .arg(QChar(isSettingAtStartup ? 0x2193 : 0x2191)) | ||||
496 | .arg(altitudeAtStartup, 0, 'f', 1) | ||||
497 | .arg(startupTime.toString(dateTimeDisplayFormat))); | ||||
498 | | ||||
499 | switch (fileStartupCondition) | ||||
437 | { | 500 | { | ||
438 | /* If the original condition is START_AT/START_CULMINATION, startup time is fixed */ | 501 | /* If the original condition is START_AT/START_CULMINATION, startup time is fixed */ | ||
439 | case START_AT: | 502 | case START_AT: | ||
440 | case START_CULMINATION: | 503 | case START_CULMINATION: | ||
441 | startupCell->setText(startupTime.toString(dateTimeDisplayFormat)); | | |||
442 | startupCell->setIcon(QIcon::fromTheme("chronometer")); | 504 | startupCell->setIcon(QIcon::fromTheme("chronometer")); | ||
443 | break; | 505 | break; | ||
444 | 506 | | |||
445 | /* If the original condition is START_ASAP, startup time is informational */ | 507 | /* If the original condition is START_ASAP, startup time is informational */ | ||
446 | case START_ASAP: | 508 | case START_ASAP: | ||
447 | startupCell->setText(startupTime.toString(dateTimeDisplayFormat)); | | |||
448 | startupCell->setIcon(QIcon()); | 509 | startupCell->setIcon(QIcon()); | ||
449 | break; | 510 | break; | ||
450 | 511 | | |||
451 | /* Else do not display any startup time */ | 512 | default: break; | ||
452 | default: | | |||
453 | startupCell->setText(QString()); | | |||
454 | startupCell->setIcon(QIcon()); | | |||
455 | break; | | |||
456 | } | 513 | } | ||
457 | /* Display a missed startup time if job is invalid */ | | |||
458 | else if (JOB_INVALID == state && START_AT == fileStartupCondition) | | |||
459 | { | | |||
460 | startupCell->setText(startupTime.toString(dateTimeDisplayFormat)); | | |||
461 | startupCell->setIcon(QIcon::fromTheme("chronometer")); | | |||
462 | } | 514 | } | ||
463 | /* Else do not display any startup time */ | 515 | /* Else do not display any startup time */ | ||
464 | else | 516 | else | ||
465 | { | 517 | { | ||
466 | startupCell->setText(QString()); | 518 | startupCell->setText("-"); | ||
467 | startupCell->setIcon(QIcon()); | 519 | startupCell->setIcon(QIcon()); | ||
468 | } | 520 | } | ||
469 | 521 | | |||
522 | if (nullptr != startupCell->tableWidget()) | ||||
470 | startupCell->tableWidget()->resizeColumnToContents(startupCell->column()); | 523 | startupCell->tableWidget()->resizeColumnToContents(startupCell->column()); | ||
471 | } | 524 | } | ||
472 | 525 | | |||
473 | if (completionCell && completionCell->tableWidget()) | 526 | if (nullptr != altitudeCell) | ||
527 | { | ||||
528 | // FIXME: Cache altitude calculations | ||||
529 | bool is_setting = false; | ||||
530 | double const alt = Ekos::Scheduler::findAltitude(targetCoords, QDateTime(), &is_setting); | ||||
531 | | ||||
532 | altitudeCell->setText(QString("%1%L2°") | ||||
533 | .arg(QChar(is_setting ? 0x2193 : 0x2191)) | ||||
534 | .arg(alt, 0, 'f', 1)); | ||||
535 | | ||||
536 | if (nullptr != altitudeCell->tableWidget()) | ||||
537 | altitudeCell->tableWidget()->resizeColumnToContents(altitudeCell->column()); | ||||
538 | } | ||||
539 | | ||||
540 | if (nullptr != completionCell) | ||||
474 | { | 541 | { | ||
475 | /* Display a completion time if job is running, scheduled to run or about to be re-scheduled */ | 542 | /* Display completion time if it is valid and job is not looping */ | ||
476 | if (JOB_SCHEDULED == state || JOB_BUSY == state || JOB_ABORTED == state) switch (completionCondition) | 543 | if (FINISH_LOOP != completionCondition && completionTime.isValid()) | ||
477 | { | 544 | { | ||
478 | case FINISH_LOOP: | 545 | completionCell->setText(QString("%1%2%L3° %4") | ||
479 | completionCell->setText(QString("-")); | 546 | .arg(altitudeAtCompletion < minAltitude ? QString(QChar(0x26A0)) : "") | ||
480 | completionCell->setIcon(QIcon()); | 547 | .arg(QChar(isSettingAtCompletion ? 0x2193 : 0x2191)) | ||
481 | break; | 548 | .arg(altitudeAtCompletion, 0, 'f', 1) | ||
549 | .arg(completionTime.toString(dateTimeDisplayFormat))); | ||||
482 | 550 | | |||
551 | switch (completionCondition) | ||||
552 | { | ||||
483 | case FINISH_AT: | 553 | case FINISH_AT: | ||
484 | completionCell->setText(completionTime.toString(dateTimeDisplayFormat)); | | |||
485 | completionCell->setIcon(QIcon::fromTheme("chronometer")); | 554 | completionCell->setIcon(QIcon::fromTheme("chronometer")); | ||
486 | break; | 555 | break; | ||
487 | 556 | | |||
488 | case FINISH_SEQUENCE: | 557 | case FINISH_SEQUENCE: | ||
489 | case FINISH_REPEAT: | 558 | case FINISH_REPEAT: | ||
490 | default: | 559 | default: | ||
491 | completionCell->setText(completionTime.toString(dateTimeDisplayFormat)); | | |||
492 | completionCell->setIcon(QIcon()); | 560 | completionCell->setIcon(QIcon()); | ||
493 | break; | 561 | break; | ||
494 | } | 562 | } | ||
563 | } | ||||
495 | /* Else do not display any completion time */ | 564 | /* Else do not display any completion time */ | ||
496 | else | 565 | else | ||
497 | { | 566 | { | ||
498 | completionCell->setText(QString()); | 567 | completionCell->setText("-"); | ||
499 | completionCell->setIcon(QIcon()); | 568 | completionCell->setIcon(QIcon()); | ||
500 | } | 569 | } | ||
501 | 570 | | |||
571 | if (nullptr != completionCell->tableWidget()) | ||||
502 | completionCell->tableWidget()->resizeColumnToContents(completionCell->column()); | 572 | completionCell->tableWidget()->resizeColumnToContents(completionCell->column()); | ||
503 | } | 573 | } | ||
504 | 574 | | |||
505 | if (estimatedTimeCell && estimatedTimeCell->tableWidget()) | 575 | if (nullptr != estimatedTimeCell) | ||
506 | { | 576 | { | ||
507 | if (0 < estimatedTime) | 577 | if (0 < estimatedTime) | ||
508 | /* Seconds to ms - this doesn't follow dateTimeDisplayFormat, which renders YMD too */ | 578 | /* Seconds to ms - this doesn't follow dateTimeDisplayFormat, which renders YMD too */ | ||
509 | estimatedTimeCell->setText(QTime::fromMSecsSinceStartOfDay(estimatedTime*1000).toString("HH:mm:ss")); | 579 | estimatedTimeCell->setText(QTime::fromMSecsSinceStartOfDay(estimatedTime*1000).toString("HH:mm:ss")); | ||
580 | #if 0 | ||||
510 | else if(0 == estimatedTime) | 581 | else if(0 == estimatedTime) | ||
511 | /* FIXME: this special case could be merged with the previous, kept for future to indicate actual duration */ | 582 | /* FIXME: this special case could be merged with the previous, kept for future to indicate actual duration */ | ||
512 | estimatedTimeCell->setText("00:00:00"); | 583 | estimatedTimeCell->setText("00:00:00"); | ||
584 | #endif | ||||
513 | else | 585 | else | ||
514 | /* Invalid marker */ | 586 | /* Invalid marker */ | ||
515 | estimatedTimeCell->setText("-"); | 587 | estimatedTimeCell->setText("-"); | ||
516 | 588 | | |||
589 | /* Warn the end-user if estimated time doesn't fit in the startup/completion interval */ | ||||
590 | if (estimatedTime < startupTime.secsTo(completionTime)) | ||||
591 | estimatedTimeCell->setIcon(QIcon::fromTheme("document-find")); | ||||
592 | else | ||||
593 | estimatedTimeCell->setIcon(QIcon()); | ||||
594 | | ||||
595 | if (nullptr != estimatedTimeCell->tableWidget()) | ||||
517 | estimatedTimeCell->tableWidget()->resizeColumnToContents(estimatedTimeCell->column()); | 596 | estimatedTimeCell->tableWidget()->resizeColumnToContents(estimatedTimeCell->column()); | ||
518 | } | 597 | } | ||
519 | 598 | | |||
520 | if (captureCountCell && captureCountCell->tableWidget()) | 599 | if (nullptr != captureCountCell) | ||
521 | { | 600 | { | ||
522 | switch (completionCondition) | 601 | switch (completionCondition) | ||
523 | { | 602 | { | ||
524 | case FINISH_LOOP: | | |||
525 | case FINISH_AT: | 603 | case FINISH_AT: | ||
604 | // FIXME: Attempt to calculate the number of frames until end - requires detailed imaging time | ||||
605 | | ||||
606 | case FINISH_LOOP: | ||||
526 | // If looping, display the count of completed frames | 607 | // If looping, display the count of completed frames | ||
527 | captureCountCell->setText(QString("%L1").arg(completedCount)); | 608 | captureCountCell->setText(QString("%L1/-").arg(completedCount)); | ||
528 | break; | 609 | break; | ||
529 | 610 | | |||
530 | case FINISH_SEQUENCE: | 611 | case FINISH_SEQUENCE: | ||
531 | case FINISH_REPEAT: | 612 | case FINISH_REPEAT: | ||
532 | default: | 613 | default: | ||
533 | // If repeating, display the count of completed frames to the count of requested frames | 614 | // If repeating, display the count of completed frames to the count of requested frames | ||
534 | captureCountCell->setText(QString("%L1/%L2").arg(completedCount).arg(sequenceCount)); | 615 | captureCountCell->setText(QString("%L1/%L2").arg(completedCount).arg(sequenceCount)); | ||
535 | break; | 616 | break; | ||
536 | } | 617 | } | ||
618 | | ||||
619 | if (nullptr != captureCountCell->tableWidget()) | ||||
537 | captureCountCell->tableWidget()->resizeColumnToContents(captureCountCell->column()); | 620 | captureCountCell->tableWidget()->resizeColumnToContents(captureCountCell->column()); | ||
538 | } | 621 | } | ||
539 | 622 | | |||
540 | if (scoreCell && scoreCell->tableWidget()) | 623 | if (nullptr != scoreCell) | ||
541 | { | 624 | { | ||
542 | if (0 <= score) | 625 | if (0 <= score) | ||
543 | scoreCell->setText(QString("%L1").arg(score)); | 626 | scoreCell->setText(QString("%L1").arg(score)); | ||
544 | else | 627 | else | ||
545 | /* FIXME: negative scores are just weird for the end-user */ | 628 | /* FIXME: negative scores are just weird for the end-user */ | ||
546 | scoreCell->setText(QString("<0")); | 629 | scoreCell->setText("<0"); | ||
547 | 630 | | |||
631 | if (nullptr != scoreCell->tableWidget()) | ||||
548 | scoreCell->tableWidget()->resizeColumnToContents(scoreCell->column()); | 632 | scoreCell->tableWidget()->resizeColumnToContents(scoreCell->column()); | ||
549 | } | 633 | } | ||
634 | | ||||
635 | if (nullptr != leadTimeCell) | ||||
636 | { | ||||
637 | // Display lead time, plus a warning if lead time is more than twice the lead time of the Ekos options | ||||
638 | switch (state) | ||||
639 | { | ||||
640 | case JOB_INVALID: | ||||
641 | case JOB_ERROR: | ||||
642 | case JOB_COMPLETE: | ||||
643 | leadTimeCell->setText("-"); | ||||
644 | break; | ||||
645 | | ||||
646 | default: | ||||
647 | leadTimeCell->setText(QString("%1%2") | ||||
648 | .arg(Options::leadTime() * 60 * 2 < leadTime ? QString(QChar(0x26A0)) : "") | ||||
649 | .arg(QTime::fromMSecsSinceStartOfDay(leadTime*1000).toString("HH:mm:ss"))); | ||||
650 | break; | ||||
651 | } | ||||
652 | | ||||
653 | if (nullptr != leadTimeCell->tableWidget()) | ||||
654 | leadTimeCell->tableWidget()->resizeColumnToContents(leadTimeCell->column()); | ||||
655 | } | ||||
550 | } | 656 | } | ||
551 | 657 | | |||
552 | void SchedulerJob::reset() | 658 | void SchedulerJob::reset() | ||
553 | { | 659 | { | ||
554 | state = JOB_IDLE; | 660 | state = JOB_IDLE; | ||
555 | stage = STAGE_IDLE; | 661 | stage = STAGE_IDLE; | ||
556 | estimatedTime = -1; | 662 | estimatedTime = -1; | ||
663 | leadTime = 0; | ||||
557 | startupCondition = fileStartupCondition; | 664 | startupCondition = fileStartupCondition; | ||
558 | startupTime = fileStartupCondition == START_AT ? fileStartupTime : QDateTime(); | 665 | startupTime = fileStartupCondition == START_AT ? fileStartupTime : QDateTime(); | ||
559 | /* No change to culmination offset */ | 666 | /* No change to culmination offset */ | ||
560 | repeatsRemaining = repeatsRequired; | 667 | repeatsRemaining = repeatsRequired; | ||
668 | updateJobCells(); | ||||
561 | } | 669 | } | ||
562 | 670 | | |||
563 | bool SchedulerJob::decreasingScoreOrder(SchedulerJob const *job1, SchedulerJob const *job2) | 671 | bool SchedulerJob::decreasingScoreOrder(SchedulerJob const *job1, SchedulerJob const *job2) | ||
564 | { | 672 | { | ||
565 | return job1->getScore() > job2->getScore(); | 673 | return job1->getScore() > job2->getScore(); | ||
566 | } | 674 | } | ||
567 | 675 | | |||
568 | bool SchedulerJob::increasingPriorityOrder(SchedulerJob const *job1, SchedulerJob const *job2) | 676 | bool SchedulerJob::increasingPriorityOrder(SchedulerJob const *job1, SchedulerJob const *job2) | ||
569 | { | 677 | { | ||
570 | return job1->getPriority() < job2->getPriority(); | 678 | return job1->getPriority() < job2->getPriority(); | ||
571 | } | 679 | } | ||
572 | 680 | | |||
573 | bool SchedulerJob::decreasingAltitudeOrder(SchedulerJob const *job1, SchedulerJob const *job2) | 681 | bool SchedulerJob::decreasingAltitudeOrder(SchedulerJob const *job1, SchedulerJob const *job2, QDateTime const &when) | ||
574 | { | 682 | { | ||
575 | return Ekos::Scheduler::findAltitude(job1->getTargetCoords(), job1->getStartupTime()) > | 683 | bool A_is_setting = job1->isSettingAtStartup; | ||
576 | Ekos::Scheduler::findAltitude(job2->getTargetCoords(), job2->getStartupTime()); | 684 | double const altA = when.isValid() ? | ||
685 | Ekos::Scheduler::findAltitude(job1->getTargetCoords(), when, &A_is_setting) : | ||||
686 | job1->altitudeAtStartup; | ||||
687 | | ||||
688 | bool B_is_setting = job2->isSettingAtStartup; | ||||
689 | double const altB = when.isValid() ? | ||||
690 | Ekos::Scheduler::findAltitude(job2->getTargetCoords(), when, &B_is_setting) : | ||||
691 | job2->altitudeAtStartup; | ||||
692 | | ||||
693 | // Sort with the setting target first | ||||
694 | if (A_is_setting && !B_is_setting) | ||||
695 | return true; | ||||
696 | else if (!A_is_setting && B_is_setting) | ||||
697 | return false; | ||||
698 | | ||||
699 | // If both targets rise or set, sort by decreasing altitude, considering a setting target is prioritary | ||||
700 | return (A_is_setting && B_is_setting) ? altA < altB : altB < altA; | ||||
577 | } | 701 | } | ||
578 | 702 | | |||
579 | bool SchedulerJob::increasingStartupTimeOrder(SchedulerJob const *job1, SchedulerJob const *job2) | 703 | bool SchedulerJob::increasingStartupTimeOrder(SchedulerJob const *job1, SchedulerJob const *job2) | ||
580 | { | 704 | { | ||
581 | return job1->getStartupTime() < job2->getStartupTime(); | 705 | return job1->getStartupTime() < job2->getStartupTime(); | ||
582 | } | 706 | } |