Kstars

sequencejobstate.cpp
1/* Ekos state machine for a single capture job sequence
2 SPDX-FileCopyrightText: Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "sequencejobstate.h"
8
9#include "Options.h"
10#include "kstarsdata.h"
11#include "indicom.h"
12#include "ekos/auxiliary/rotatorutils.h"
13
14namespace Ekos
15{
16SequenceJobState::SequenceJobState(const QSharedPointer<CameraState> &sharedState)
17{
18 m_CameraState = sharedState;
19 m_FlatSyncCheck.setSingleShot(true);
20 m_FlatSyncCheck.setInterval(1000);
21 m_FlatSyncCheck.callOnTimeout(this, [this]()
22 {
23 emit flatSyncFocus(targetFilterID);
24 });
25
26 // Monitor capture operations timeout
27 m_CaptureOperationsTimer.setSingleShot(false);
28 m_CaptureOperationsTimer.setInterval(Options::captureOperationsTimeout() * 1000);
29 connect(&m_CaptureOperationsTimer, &QTimer::timeout, this, [this]()
30 {
31 // If camera is paused, stop timer completely.
32 if (m_CameraState->isCapturePausing() || m_PreparationState == PREP_COMPLETED || m_PreparationState == PREP_NONE)
33 {
34 m_CaptureOperationsTimer.stop();
35 return;
36 }
37
38 // If preparation is not complete by now, let should abort.
39 if (!preparationCompleted())
40 {
41 emit newLog(i18n("Capture operations timed out after %1 seconds.", Options::captureOperationsTimeout()));
42 m_CaptureOperationsTimer.stop();
43 emit prepareComplete(false);
44 }
45 });
46}
47
48void SequenceJobState::setFrameType(CCDFrameType frameType)
49{
50 // set the frame type
51 m_frameType = frameType;
52 // reset the preparation state
53 m_PreparationState = PREP_NONE;
54}
55
56void SequenceJobState::initPreparation(bool isPreview)
57{
58 m_status = JOB_BUSY;
59 m_isPreview = isPreview;
60 wpScopeStatus = WP_NONE;
61 m_CaptureOperationsTimer.start();
62}
63
64void SequenceJobState::prepareLightFrameCapture(bool enforceCCDTemp, bool isPreview)
65{
66 // precondition: do not start while already being busy and conditions haven't changed
67 if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
68 return;
69
70 // initialize the states
71 initPreparation(isPreview);
72
73 // Reset all prepare actions
74 setAllActionsReady();
75
76 // disable batch mode for previews
77 emit setCCDBatchMode(!isPreview);
78
79 // Check if we need to update temperature (only skip if the value is initialized and within the limits)
80 prepareTemperatureCheck(enforceCCDTemp);
81
82 // Check if we need to update rotator (only skip if the value is initialized and within the limits)
83 prepareRotatorCheck();
84
85 // Hint: Filter changes are actually done in SequenceJob::capture();
86
87 // preparation started
88 m_PreparationState = PREP_BUSY;
89 // check if the preparations are already completed
90 checkAllActionsReady();
91}
92
93void SequenceJobState::prepareFlatFrameCapture(bool enforceCCDTemp, bool isPreview)
94{
95 // precondition: do not start while already being busy and conditions haven't changed
96 if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
97 return;
98
99 // initialize the states
100 initPreparation(isPreview);
101
102 // Reset all prepare actions
103 setAllActionsReady();
104
105 // disable batch mode for previews
106 emit setCCDBatchMode(!isPreview);
107
108 // Check if we need to update temperature (only skip if the value is initialized and within the limits)
109 prepareTemperatureCheck(enforceCCDTemp);
110
111 // preparation started
112 m_PreparationState = PREP_BUSY;
113 // check if the preparations are already completed
114 checkAllActionsReady();
115}
116
117void SequenceJobState::prepareDarkFrameCapture(bool enforceCCDTemp, bool isPreview)
118{
119 // precondition: do not start while already being busy and conditions haven't changed
120 if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
121 return;
122
123 // initialize the states
124 initPreparation(isPreview);
125
126 // Reset all prepare actions
127 setAllActionsReady();
128
129 // disable batch mode for previews
130 emit setCCDBatchMode(!isPreview);
131
132 // Check if we need to update temperature (only skip if the value is initialized and within the limits)
133 prepareTemperatureCheck(enforceCCDTemp);
134
135 // preparation started
136 m_PreparationState = PREP_BUSY;
137 // check if the preparations are already completed
138 checkAllActionsReady();
139}
140
141void SequenceJobState::prepareBiasFrameCapture(bool enforceCCDTemp, bool isPreview)
142{
143 prepareDarkFrameCapture(enforceCCDTemp, isPreview);
144}
145
146bool SequenceJobState::initCapture(CCDFrameType frameType, bool isPreview, bool isAutofocusReady, FITSMode mode)
147{
148 m_PreparationState = PREP_INIT_CAPTURE;
149 autoFocusReady = isAutofocusReady;
150 m_fitsMode = mode;
151 m_CaptureOperationsTimer.start();
152
153 //check for setting the target filter
154 prepareTargetFilter(frameType, isPreview);
155 checkAllActionsReady();
156
157 return areActionsReady();
158}
159
160bool SequenceJobState::areActionsReady()
161{
162 for (bool &ready : prepareActions.values())
163 {
164 if (ready == false)
165 return false;
166 }
167
168 return true;
169}
170
171void SequenceJobState::checkAllActionsReady()
172{
173 // ignore events if preparation is already completed
174 if (preparationCompleted())
175 {
176 m_CaptureOperationsTimer.stop();
177 return;
178 }
179
180 switch (m_PreparationState)
181 {
182 // capture preparation
183 case PREP_BUSY:
184 switch (m_frameType)
185 {
186 case FRAME_LIGHT:
187 if (areActionsReady())
188 {
189 // as last step ensure that the scope is uncovered
190 if (checkLightFrameScopeCoverOpen() != IPS_OK)
191 return;
192
193 m_PreparationState = PREP_COMPLETED;
194 emit prepareComplete();
195 }
196 break;
197 case FRAME_FLAT:
198 if (!areActionsReady())
199 return;
200
201 // 1. Check if the selected flats light source is ready
202 if (checkFlatsCoverReady() != IPS_OK)
203 return;
204
205 // 2. If we used AUTOFOCUS before for a specific frame (e.g. Lum)
206 // then the absolute focus position for Lum is recorded in the filter manager
207 // when we take flats again, we always go back to the same focus position as the light frames to ensure
208 // near identical focus for both frames.
209 if (checkFlatSyncFocus() != IPS_OK)
210 return;
211
212 // all preparations ready, avoid doubled events
213 if (m_PreparationState == PREP_BUSY)
214 {
215 m_PreparationState = PREP_COMPLETED;
216 emit prepareComplete();
217 }
218 break;
219 // darks and bias frames are handled in the same way
220 case FRAME_DARK:
221 case FRAME_BIAS:
222 if (!areActionsReady())
223 return;
224
225 // 1. check if the scope is covered appropriately
226 if (checkDarksCoverReady() != IPS_OK)
227 return;
228
229 // 2. avoid doubled events
230 if (m_PreparationState == PREP_BUSY)
231 {
232 m_PreparationState = PREP_COMPLETED;
233 emit prepareComplete();
234 }
235 break;
236 default:
237 // all other cases not refactored yet, preparation immediately completed
238 emit prepareComplete();
239 break;
240 }
241 break;
242
243 // capture initialization (final preparation steps before starting frame capturing)
244 case PREP_INIT_CAPTURE:
245 if (areActionsReady())
246 {
247 // reset the state to avoid double events
248 m_PreparationState = PREP_NONE;
249 emit initCaptureComplete(m_fitsMode);
250 }
251 break;
252
253 case PREP_NONE:
254 case PREP_COMPLETED:
255 // in all other cases do nothing
256 break;
257 }
258}
259
260void SequenceJobState::setAllActionsReady()
261{
262 QMutableMapIterator<CaptureWorkflowActionType, bool> it(prepareActions);
263
264 while (it.hasNext())
265 {
266 it.next();
267 it.setValue(true);
268 }
269 // reset the initialization state
270 for (CaptureWorkflowActionType action :
271 {
272 CAPTURE_ACTION_FILTER, CAPTURE_ACTION_ROTATOR, CAPTURE_ACTION_TEMPERATURE
273 })
274 setInitialized(action, false);
275}
276
277void SequenceJobState::prepareTargetFilter(CCDFrameType frameType, bool isPreview)
278{
279 if (targetFilterID != INVALID_VALUE)
280 {
281 if (isInitialized(CAPTURE_ACTION_FILTER) == false)
282 {
283 prepareActions[CAPTURE_ACTION_FILTER] = false;
284
285 // Don't perform autofocus on preview or calibration frames or if Autofocus is not ready yet.
286 if (isPreview || frameType != FRAME_LIGHT || autoFocusReady == false)
287 m_filterPolicy = static_cast<FilterManager::FilterPolicy>(m_filterPolicy & ~FilterManager::AUTOFOCUS_POLICY);
288
289 emit readFilterPosition();
290 }
291 else if (targetFilterID != m_CameraState->currentFilterID)
292 {
293 // mark filter preparation action
294 prepareActions[CAPTURE_ACTION_FILTER] = false;
295
296 // determine policy
297 m_filterPolicy = FilterManager::ALL_POLICIES;
298
299 // Don't perform autofocus on preview or calibration frames or if Autofocus is not ready yet.
300 if (isPreview || frameType != FRAME_LIGHT || autoFocusReady == false)
301 m_filterPolicy = static_cast<FilterManager::FilterPolicy>(m_filterPolicy & ~FilterManager::AUTOFOCUS_POLICY);
302
303 emit changeFilterPosition(targetFilterID, m_filterPolicy);
304 emit prepareState(CAPTURE_CHANGING_FILTER);
305 }
306 }
307}
308
309void SequenceJobState::prepareTemperatureCheck(bool enforceCCDTemp)
310{
311 // turn on CCD temperature enforcing if required
312 m_enforceTemperature = enforceCCDTemp;
313
314 if (m_enforceTemperature)
315 {
316 prepareActions[CAPTURE_ACTION_TEMPERATURE] = false;
317 if (isInitialized(CAPTURE_ACTION_TEMPERATURE))
318 {
319 // ignore the next value since after setting temperature the next received value will be
320 // exactly this value no matter what the CCD temperature
321 ignoreNextValue[CAPTURE_ACTION_TEMPERATURE] = true;
322 // request setting temperature
323 emit setCCDTemperature(targetTemperature);
324 emit prepareState(CAPTURE_SETTING_TEMPERATURE);
325 }
326 // trigger setting current value first if not initialized
327 else
328 emit readCurrentState(CAPTURE_SETTING_TEMPERATURE);
329
330 }
331}
332
333void SequenceJobState::prepareRotatorCheck()
334{
335 if (targetPositionAngle > Ekos::INVALID_VALUE)
336 {
337 if (isInitialized(CAPTURE_ACTION_ROTATOR))
338 {
339 prepareActions[CAPTURE_ACTION_ROTATOR] = false;
340 double rawAngle = RotatorUtils::Instance()->calcRotatorAngle(targetPositionAngle);
341 emit prepareState(CAPTURE_SETTING_ROTATOR);
342 emit setRotatorAngle(rawAngle);
343 }
344 // trigger setting current value first if not initialized
345 else
346 emit readCurrentState(CAPTURE_SETTING_ROTATOR);
347 }
348}
349
350IPState SequenceJobState::checkCalibrationPreActionsReady()
351{
352 IPState result = IPS_OK;
353
354 if (m_CalibrationPreAction & CAPTURE_PREACTION_WALL)
355 result = checkWallPositionReady(FRAME_FLAT);
356
357 if (result != IPS_OK)
358 return result;
359
360 if (m_CalibrationPreAction & CAPTURE_PREACTION_PARK_MOUNT)
361 result = checkPreMountParkReady();
362
363 if (result != IPS_OK)
364 return result;
365
366 if (m_CalibrationPreAction & CAPTURE_PREACTION_PARK_DOME)
367 result = checkPreDomeParkReady();
368
369 return result;
370}
371
372IPState SequenceJobState::checkFlatsCoverReady()
373{
374 auto result = checkCalibrationPreActionsReady();
375 if (result == IPS_OK)
376 {
377 if (m_CameraState->hasDustCap && m_CameraState->hasLightBox)
378 return checkDustCapReady(FRAME_FLAT);
379 // In case we have a wall action then we are facing a flat light source and we can immediately continue to next step
380 else if (m_CalibrationPreAction & CAPTURE_PREACTION_WALL)
381 return IPS_OK;
382 else
383 {
384 // In case we ONLY have a lightbox then we need to ensure it's toggled correctly first
385 if (m_CameraState->hasLightBox)
386 return checkDustCapReady(FRAME_FLAT);
387
388 return checkManualCoverReady(true);
389 }
390 }
391
392 return result;
393}
394
395IPState SequenceJobState::checkDarksCoverReady()
396{
397 IPState result = checkCalibrationPreActionsReady();;
398
399 if (result == IPS_OK)
400 {
401 // 1. check if the CCD has a shutter
402 result = checkHasShutter();
403 if (result != IPS_OK)
404 return result;
405
406 if (m_CameraState->hasDustCap)
407 return checkDustCapReady(FRAME_DARK);
408 // In case we have a wall action then we are facing a designated location and we can immediately continue to next step
409 else if (m_CalibrationPreAction & CAPTURE_PREACTION_WALL)
410 return IPS_OK;
411 else
412 return checkManualCoverReady(false);
413 }
414 return result;
415}
416
417IPState SequenceJobState::checkManualCoverReady(bool lightSourceRequired)
418{
419 // Manual mode we need to cover mount with evenly illuminated field.
420 if (lightSourceRequired && m_CameraState->m_ManualCoverState != CameraState::MANUAL_COVER_CLOSED_LIGHT)
421 {
422 if (coverQueryState == CAL_CHECK_CONFIRMATION)
423 return IPS_BUSY;
424
425 // request asking the user to cover the scope manually with a light source
426 emit askManualScopeCover(i18n("Cover the telescope with an evenly illuminated light source."),
427 i18n("Flat Frame"), true);
428 coverQueryState = CAL_CHECK_CONFIRMATION;
429
430 return IPS_BUSY;
431 }
432 else if (!lightSourceRequired && m_CameraState->m_ManualCoverState != CameraState::MANUAL_COVER_CLOSED_DARK &&
433 m_CameraState->shutterStatus == SHUTTER_NO)
434 {
435 if (coverQueryState == CAL_CHECK_CONFIRMATION)
436 return IPS_BUSY;
437
438 emit askManualScopeCover(i18n("Cover the telescope in order to take a dark exposure."),
439 i18n("Dark Exposure"), false);
440
441 coverQueryState = CAL_CHECK_CONFIRMATION;
442
443 return IPS_BUSY;
444 }
445 return IPS_OK;
446}
447
448IPState SequenceJobState::checkDustCapReady(CCDFrameType frameType)
449{
450 // turning on flat light running
451 if (m_CameraState->getLightBoxLightState() == CAP_LIGHT_BUSY ||
452 m_CameraState->getDustCapState() == CAP_PARKING ||
453 m_CameraState->getDustCapState() == CAP_UNPARKING)
454 return IPS_BUSY;
455 // error occured
456 if (m_CameraState->getDustCapState() == CAP_ERROR)
457 return IPS_ALERT;
458
459 auto captureLights = (frameType == FRAME_LIGHT);
460
461 // for flats open the cap and close it otherwise
462 CapState targetCapState = captureLights ? CAP_IDLE : CAP_PARKED;
463 // If cap is parked, unpark it since dark cap uses external light source.
464 if (m_CameraState->hasDustCap && m_CameraState->getDustCapState() != targetCapState)
465 {
466 m_CameraState->setDustCapState(captureLights ? CAP_UNPARKING : CAP_PARKING);
467 emit parkDustCap(!captureLights);
468 emit newLog(captureLights ? i18n("Unparking dust cap...") : i18n("Parking dust cap..."));
469 return IPS_BUSY;
470 }
471
472 auto captureFlats = (frameType == FRAME_FLAT);
473 LightState targetLightBoxStatus = captureFlats ? CAP_LIGHT_ON :
474 CAP_LIGHT_OFF;
475
476 if (m_CameraState->hasLightBox && m_CameraState->getLightBoxLightState() != targetLightBoxStatus)
477 {
478 m_CameraState->setLightBoxLightState(CAP_LIGHT_BUSY);
479 emit setLightBoxLight(captureFlats);
480 emit newLog(captureFlats ? i18n("Turn light box light on...") : i18n("Turn light box light off..."));
481 return IPS_BUSY;
482 }
483
484 // nothing more to do
485 return IPS_OK;
486}
487
488IPState SequenceJobState::checkWallPositionReady(CCDFrameType frametype)
489{
490 if (m_CameraState->hasTelescope)
491 {
492 // check if mount is unparked and unpark if necessary
493 if (wpScopeStatus < WP_UNPARKED)
494 {
495 switch (m_CameraState->getScopeParkState())
496 {
497 case ISD::PARK_ERROR:
498 emit newLog(i18n("Parking mount failed, aborting..."));
499 emit abortCapture();
500 return IPS_ALERT;
501 case ISD::PARK_UNPARKING:
502 case ISD::PARK_PARKING:
503 return IPS_BUSY;
504 case ISD::PARK_PARKED:
505 // unpark the scope
506 wpScopeStatus = WP_UNPARKING;
507 emit newLog(i18n("Unparking mount..."));
508 emit setScopeParked(false);
509 return IPS_BUSY;
510 case ISD::PARK_UNKNOWN:
511 // retrieve the mount park state
512 emit readCurrentMountParkState();
513 return IPS_BUSY;
514 case ISD::PARK_UNPARKED:
515 wpScopeStatus = WP_UNPARKED;
516 emit newLog(i18n("Mount unparked."));
517 // check what to do next
518 checkWallPositionReady(frametype);
519 break;
520 }
521 return IPS_BUSY;
522 }
523 else if (wpScopeStatus < WP_SLEWING)
524 {
525 wallCoord.HorizontalToEquatorial(KStarsData::Instance()->lst(),
526 KStarsData::Instance()->geo()->lat());
527 wpScopeStatus = WP_SLEWING;
528 emit slewTelescope(wallCoord);
529 emit newLog(i18n("Mount slewing to wall position (az =%1 alt =%2)",
530 wallCoord.az().toDMSString(), wallCoord.alt().toDMSString()));
531 return IPS_BUSY;
532 }
533 // wait until actions completed
534 else if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
535 return IPS_BUSY;
536 // Check if slewing is complete
537 else if (wpScopeStatus == WP_SLEW_COMPLETED)
538 {
539 wpScopeStatus = WP_TRACKING_BUSY;
540 emit setScopeTracking(false);
541 emit newLog(i18n("Slew to wall position complete, stop tracking."));
542 return IPS_BUSY;
543 }
544 else if (wpScopeStatus == WP_TRACKING_OFF)
545 emit newLog(i18n("Slew to wall position complete, tracking stopped."));
546
547 // wall position reached, check if we have a light box to turn on for flats and off otherwise
548 bool captureFlats = (frametype == FRAME_FLAT);
549 LightState targetLightState = (captureFlats ? CAP_LIGHT_ON :
550 CAP_LIGHT_OFF);
551
552 if (m_CameraState->hasLightBox == true)
553 {
554 if (m_CameraState->getLightBoxLightState() != targetLightState)
555 {
556 m_CameraState->setLightBoxLightState(CAP_LIGHT_BUSY);
557 emit setLightBoxLight(captureFlats);
558 emit newLog(captureFlats ? i18n("Turn light box light on...") : i18n("Turn light box light off..."));
559 return IPS_BUSY;
560 }
561 }
562 }
563 // everything ready
564 return IPS_OK;
565}
566
567IPState SequenceJobState::checkPreMountParkReady()
568{
569 if (m_CameraState->hasTelescope)
570 {
571 switch (m_CameraState->getScopeParkState())
572 {
573 case ISD::PARK_PARKED:
574 break;
575 case ISD::PARK_ERROR:
576 emit newLog(i18n("Parking mount failed, aborting..."));
577 emit abortCapture();
578 return IPS_ALERT;
579 case ISD::PARK_PARKING:
580 return IPS_BUSY;
581 case ISD::PARK_UNPARKED:
582 case ISD::PARK_UNPARKING:
583 // park the scope
584 emit setScopeParked(true);
585 emit newLog(i18n("Parking mount prior to calibration frames capture..."));
586 return IPS_BUSY;
587 case ISD::PARK_UNKNOWN:
588 // retrieve the mount park state
589 emit readCurrentMountParkState();
590 return IPS_BUSY;
591 }
592 }
593 // everything ready
594 return IPS_OK;
595
596}
597
598IPState SequenceJobState::checkPreDomeParkReady()
599{
600 if (m_CameraState->hasDome)
601 {
602 if (m_CameraState->getDomeState() == ISD::Dome::DOME_ERROR)
603 {
604 emit newLog(i18n("Parking dome failed, aborting..."));
605 emit abortCapture();
606 return IPS_ALERT;
607 }
608 else if (m_CameraState->getDomeState() == ISD::Dome::DOME_PARKING)
609 return IPS_BUSY;
610 else if (m_CameraState->getDomeState() != ISD::Dome::DOME_PARKED)
611 {
612 m_CameraState->setDomeState(ISD::Dome::DOME_PARKING);
613 emit setDomeParked(true);
614 emit newLog(i18n("Parking dome prior to calibration frames capture..."));
615 return IPS_BUSY;
616 }
617 }
618 // everything ready
619 return IPS_OK;
620}
621
622IPState SequenceJobState::checkFlatSyncFocus()
623{
624 // check already running?
625 if (flatSyncStatus == FS_BUSY)
626 {
627 m_FlatSyncCheck.start();
628 return IPS_BUSY;
629 }
630
631 if (m_frameType == FRAME_FLAT && Options::flatSyncFocus() && flatSyncStatus != FS_COMPLETED)
632 {
633 flatSyncStatus = FS_BUSY;
634 emit flatSyncFocus(targetFilterID);
635 return IPS_BUSY;
636 }
637 // everything ready
638 return IPS_OK;
639}
640
641IPState SequenceJobState::checkHasShutter()
642{
643 if (m_CameraState->shutterStatus == SHUTTER_BUSY)
644 return IPS_BUSY;
645 if (m_CameraState->shutterStatus != SHUTTER_UNKNOWN)
646 return IPS_OK;
647 // query the status
648 m_CameraState->shutterStatus = SHUTTER_BUSY;
649 emit queryHasShutter();
650 return IPS_BUSY;
651}
652
653IPState SequenceJobState::checkLightFrameScopeCoverOpen()
654{
655 // Account for light box only (no dust cap)
656 if (m_CameraState->hasLightBox && m_CameraState->getLightBoxLightState() != CAP_LIGHT_OFF)
657 {
658 if (m_CameraState->getLightBoxLightState() != CAP_LIGHT_BUSY)
659 {
660 m_CameraState->setLightBoxLightState(CAP_LIGHT_BUSY);
661 emit setLightBoxLight(false);
662 emit newLog(i18n("Turn light box light off..."));
663 }
664 return IPS_BUSY;
665 }
666
667 // If we have a dust cap, then we must unpark
668 if (m_CameraState->hasDustCap)
669 {
670 if (m_CameraState->getDustCapState() != CAP_IDLE)
671 {
672 if (m_CameraState->getDustCapState() != CAP_UNPARKING)
673 {
674 m_CameraState->setDustCapState(CAP_UNPARKING);
675 emit parkDustCap(false);
676 emit newLog(i18n("Unparking dust cap..."));
677 }
678 return IPS_BUSY;
679 }
680
681 return IPS_OK;
682 }
683
684 // If telescopes were MANUALLY covered before
685 // we need to manually uncover them.
686 if (m_CameraState->m_ManualCoverState != CameraState::MANAUL_COVER_OPEN)
687 {
688 // If we already asked for confirmation and waiting for it
689 // let us see if the confirmation is fulfilled
690 // otherwise we return.
691 if (coverQueryState == CAL_CHECK_CONFIRMATION)
692 return IPS_BUSY;
693
694 emit askManualScopeOpen(m_CameraState->m_ManualCoverState == CameraState::MANUAL_COVER_CLOSED_LIGHT);
695
696 return IPS_BUSY;
697 }
698
699 // scope cover open (or no scope cover)
700 return IPS_OK;
701}
702
703bool SequenceJobState::isInitialized(CaptureWorkflowActionType action)
704{
705 return m_CameraState.data()->isInitialized[action];
706}
707
708void SequenceJobState::setInitialized(CaptureWorkflowActionType action, bool init)
709{
710 m_CameraState.data()->isInitialized[action] = init;
711}
712
713void SequenceJobState::setCurrentFilterID(int value)
714{
715 // ignore events if preparation is already completed
716 if (preparationCompleted())
717 return;
718
719 m_CameraState->currentFilterID = value;
720 // Signal the FilterManager if we need a new targetFilterID or a new autofocus on that filter
721 if (isInitialized(CAPTURE_ACTION_FILTER) == false &&
722 (value != targetFilterID || m_filterPolicy & FilterManager::AUTOFOCUS_POLICY))
723 {
724 // mark filter preparation action
725 // TODO introduce settle time
726 prepareActions[CAPTURE_ACTION_FILTER] = false;
727
728 emit changeFilterPosition(targetFilterID, m_filterPolicy);
729 emit prepareState(CAPTURE_CHANGING_FILTER);
730 }
731 setInitialized(CAPTURE_ACTION_FILTER, true);
732
733 if (value == targetFilterID)
734 prepareActions[CAPTURE_ACTION_FILTER] = true;
735 else if (value < 0)
736 {
737 m_PreparationState = PREP_NONE;
738 emit prepareComplete(false);
739 return;
740 }
741
742 checkAllActionsReady();
743}
744
745void SequenceJobState::setCurrentCCDTemperature(double currentTemperature)
746{
747 // ignore events if preparation is already completed
748 if (preparationCompleted())
749 return;
750
751 // skip if next value should be ignored
752 if (ignoreNextValue[CAPTURE_ACTION_TEMPERATURE])
753 {
754 ignoreNextValue[CAPTURE_ACTION_TEMPERATURE] = false;
755 return;
756 }
757
758 if (isInitialized(CAPTURE_ACTION_TEMPERATURE))
759 {
760 if (m_enforceTemperature == false
761 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
762 prepareActions[CAPTURE_ACTION_TEMPERATURE] = true;
763
764 checkAllActionsReady();
765 }
766 else
767 {
768 setInitialized(CAPTURE_ACTION_TEMPERATURE, true);
769 if (m_enforceTemperature == false
770 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
771 {
772 prepareActions[CAPTURE_ACTION_TEMPERATURE] = true;
773 checkAllActionsReady();
774 }
775 else
776 {
777 prepareTemperatureCheck(m_enforceTemperature);
778 }
779 }
780}
781
782void SequenceJobState::setCurrentRotatorPositionAngle(double rotatorAngle, IPState state)
783{
784 // ignore events if preparation is already completed
785 if (preparationCompleted())
786 return;
787
788 double currentPositionAngle = RotatorUtils::Instance()->calcCameraAngle(rotatorAngle, false);
789
790 if (isInitialized(CAPTURE_ACTION_ROTATOR))
791 {
792 // TODO introduce settle time
793 // TODO make sure rotator has fully stopped -> see 'align.cpp' captureAndSolve()
794 if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
795 && state != IPS_BUSY)
796 prepareActions[CAPTURE_ACTION_ROTATOR] = true;
797
798 checkAllActionsReady();
799 }
800 else
801 {
802 setInitialized(CAPTURE_ACTION_ROTATOR, true);
803 if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
804 && state != IPS_BUSY)
805 {
806 prepareActions[CAPTURE_ACTION_ROTATOR] = true;
807 checkAllActionsReady();
808 }
809 else
810 {
811 prepareRotatorCheck();
812 }
813 }
814}
815
816void SequenceJobState::setFocusStatus(FocusState state)
817{
818 // ignore events if preparation is already completed
819 if (preparationCompleted())
820 return;
821
822 switch (state)
823 {
824 case FOCUS_COMPLETE:
825 // did we wait for a successful autofocus run?
826 if (prepareActions[CAPTURE_ACTION_AUTOFOCUS] == false)
827 {
828 prepareActions[CAPTURE_ACTION_AUTOFOCUS] = true;
829 checkAllActionsReady();
830 }
831 break;
832 case FOCUS_ABORTED:
833 case FOCUS_FAILED:
834 // finish preparation with failure
835 emit prepareComplete(false);
836 break;
837 default:
838 // in all other cases do nothing
839 break;
840 }
841}
842
843void SequenceJobState::updateManualScopeCover(bool closed, bool success, bool light)
844{
845 // ignore events if preparation is already completed
846 if (preparationCompleted())
847 return;
848
849 // covering confirmed
850 if (success == true)
851 {
852 if (closed)
853 m_CameraState->m_ManualCoverState = light ? CameraState::MANUAL_COVER_CLOSED_LIGHT :
854 CameraState::MANUAL_COVER_CLOSED_DARK;
855 else
856 m_CameraState->m_ManualCoverState = CameraState::MANAUL_COVER_OPEN;
857 coverQueryState = CAL_CHECK_TASK;
858 // re-run checks
859 checkAllActionsReady();
860 }
861 // cancelled
862 else
863 {
864 m_CameraState->shutterStatus = SHUTTER_UNKNOWN;
865 coverQueryState = CAL_CHECK_TASK;
866 // abort, no further checks
867 emit abortCapture();
868 }
869}
870
871void SequenceJobState::lightBoxLight(bool on)
872{
873 // ignore events if preparation is already completed
874 if (preparationCompleted())
875 return;
876
877 m_CameraState->setLightBoxLightState(on ? CAP_LIGHT_ON : CAP_LIGHT_OFF);
878 emit newLog(i18n(on ? "Light box on." : "Light box off."));
879 // re-run checks
880 checkAllActionsReady();
881}
882
883void SequenceJobState::dustCapStateChanged(ISD::DustCap::Status status)
884{
885 // ignore events if preparation is already completed
886 if (preparationCompleted())
887 return;
888
889 switch (status)
890 {
891 case ISD::DustCap::CAP_ERROR:
892 emit newLog(i18n("Dust cap error occurred."));
893 emit abortCapture();
894 break;
895 case ISD::DustCap::CAP_IDLE:
896 emit newLog(i18n("Dust cap is idle."));
897 break;
898 case ISD::DustCap::CAP_PARKED:
899 emit newLog(i18n("Dust cap is parked."));
900 break;
901 case ISD::DustCap::CAP_PARKING:
902 emit newLog(i18n("Dust cap is parking..."));
903 break;
904 case ISD::DustCap::CAP_UNPARKING:
905 emit newLog(i18n("Dust cap is unparking..."));
906 break;
907 default:
908 emit newLog(i18n("Dust cap status unknown."));
909 break;
910 }
911
912 // re-run checks
913 checkAllActionsReady();
914}
915
916void SequenceJobState::scopeStatusChanged(ISD::Mount::Status status)
917{
918 // ignore events if preparation is already completed
919 if (preparationCompleted())
920 return;
921
922 // handle wall position
923 switch (status)
924 {
925 case ISD::Mount::MOUNT_TRACKING:
926 if (wpScopeStatus == WP_SLEWING)
927 wpScopeStatus = WP_SLEW_COMPLETED;
928 break;
929 case ISD::Mount::MOUNT_IDLE:
930 if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
931 wpScopeStatus = WP_TRACKING_OFF;
932 break;
933 case ISD::Mount::MOUNT_PARKING:
934 // Ensure the parking state to avoid double park calls
935 m_CameraState->setScopeParkState(ISD::PARK_PARKING);
936 break;
937 default:
938 // do nothing
939 break;
940 }
941 // re-run checks
942 checkAllActionsReady();
943}
944
945void SequenceJobState::scopeParkStatusChanged(ISD::ParkStatus)
946{
947 // re-run checks
948 checkAllActionsReady();
949}
950
951void SequenceJobState::domeStatusChanged(ISD::Dome::Status)
952{
953 // re-run checks
954 checkAllActionsReady();
955}
956
957void SequenceJobState::flatSyncFocusChanged(bool completed)
958{
959 // ignore events if preparation is already completed
960 if (preparationCompleted())
961 return;
962
963 flatSyncStatus = (completed ? FS_COMPLETED : FS_BUSY);
964 // re-run checks
965 checkAllActionsReady();
966}
967
968void SequenceJobState::hasShutter(bool present)
969{
970 // ignore events if preparation is already completed
971 if (preparationCompleted())
972 return;
973
974 if (present == true)
975 m_CameraState->shutterStatus = SHUTTER_YES;
976 else
977 m_CameraState->shutterStatus = SHUTTER_NO;
978
979 // re-run checks
980 checkAllActionsReady();
981}
982
983SequenceJobState::PreparationState SequenceJobState::getPreparationState() const
984{
985 return m_PreparationState;
986}
987
988void SequenceJobState::setFilterStatus(FilterState filterState)
989{
990 // ignore events if preparation is already completed
991 if (preparationCompleted())
992 return;
993
994 switch (filterState)
995 {
996 case FILTER_AUTOFOCUS:
997 // we need to wait until focusing has completed
998 prepareActions[CAPTURE_ACTION_AUTOFOCUS] = false;
999 emit prepareState(CAPTURE_FOCUSING);
1000 break;
1001
1002 // nothing to do in all other cases
1003 case FILTER_IDLE:
1004 case FILTER_OFFSET:
1005 case FILTER_CHANGE:
1006 break;
1007 }
1008
1009}
1010} // namespace
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:83
@ CAPTURE_SETTING_ROTATOR
Definition ekos.h:108
@ CAPTURE_FOCUSING
Definition ekos.h:103
@ CAPTURE_CHANGING_FILTER
Definition ekos.h:105
@ CAPTURE_SETTING_TEMPERATURE
Definition ekos.h:107
void init(KXmlGuiWindow *window, KGameDifficulty *difficulty=nullptr)
GeoCoordinates geo(const QVariant &location)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 28 2025 11:57:24 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.