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 if (isInitialized(CAPTURE_ACTION_FILTER) == false && value != targetFilterID)
721 {
722 // mark filter preparation action
723 // TODO introduce settle time
724 prepareActions[CAPTURE_ACTION_FILTER] = false;
725
726 emit changeFilterPosition(targetFilterID, m_filterPolicy);
727 emit prepareState(CAPTURE_CHANGING_FILTER);
728 }
729 setInitialized(CAPTURE_ACTION_FILTER, true);
730
731 if (value == targetFilterID)
732 prepareActions[CAPTURE_ACTION_FILTER] = true;
733 else if (value < 0)
734 {
735 m_PreparationState = PREP_NONE;
736 emit prepareComplete(false);
737 return;
738 }
739
740 checkAllActionsReady();
741}
742
743void SequenceJobState::setCurrentCCDTemperature(double currentTemperature)
744{
745 // ignore events if preparation is already completed
746 if (preparationCompleted())
747 return;
748
749 // skip if next value should be ignored
750 if (ignoreNextValue[CAPTURE_ACTION_TEMPERATURE])
751 {
752 ignoreNextValue[CAPTURE_ACTION_TEMPERATURE] = false;
753 return;
754 }
755
756 if (isInitialized(CAPTURE_ACTION_TEMPERATURE))
757 {
758 if (m_enforceTemperature == false
759 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
760 prepareActions[CAPTURE_ACTION_TEMPERATURE] = true;
761
762 checkAllActionsReady();
763 }
764 else
765 {
766 setInitialized(CAPTURE_ACTION_TEMPERATURE, true);
767 if (m_enforceTemperature == false
768 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
769 {
770 prepareActions[CAPTURE_ACTION_TEMPERATURE] = true;
771 checkAllActionsReady();
772 }
773 else
774 {
775 prepareTemperatureCheck(m_enforceTemperature);
776 }
777 }
778}
779
780void SequenceJobState::setCurrentRotatorPositionAngle(double rotatorAngle, IPState state)
781{
782 // ignore events if preparation is already completed
783 if (preparationCompleted())
784 return;
785
786 double currentPositionAngle = RotatorUtils::Instance()->calcCameraAngle(rotatorAngle, false);
787
788 if (isInitialized(CAPTURE_ACTION_ROTATOR))
789 {
790 // TODO introduce settle time
791 // TODO make sure rotator has fully stopped -> see 'align.cpp' captureAndSolve()
792 if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
793 && state != IPS_BUSY)
794 prepareActions[CAPTURE_ACTION_ROTATOR] = true;
795
796 checkAllActionsReady();
797 }
798 else
799 {
800 setInitialized(CAPTURE_ACTION_ROTATOR, true);
801 if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
802 && state != IPS_BUSY)
803 {
804 prepareActions[CAPTURE_ACTION_ROTATOR] = true;
805 checkAllActionsReady();
806 }
807 else
808 {
809 prepareRotatorCheck();
810 }
811 }
812}
813
814void SequenceJobState::setFocusStatus(FocusState state)
815{
816 // ignore events if preparation is already completed
817 if (preparationCompleted())
818 return;
819
820 switch (state)
821 {
822 case FOCUS_COMPLETE:
823 // did we wait for a successful autofocus run?
824 if (prepareActions[CAPTURE_ACTION_AUTOFOCUS] == false)
825 {
826 prepareActions[CAPTURE_ACTION_AUTOFOCUS] = true;
827 checkAllActionsReady();
828 }
829 break;
830 case FOCUS_ABORTED:
831 case FOCUS_FAILED:
832 // finish preparation with failure
833 emit prepareComplete(false);
834 break;
835 default:
836 // in all other cases do nothing
837 break;
838 }
839}
840
841void SequenceJobState::updateManualScopeCover(bool closed, bool success, bool light)
842{
843 // ignore events if preparation is already completed
844 if (preparationCompleted())
845 return;
846
847 // covering confirmed
848 if (success == true)
849 {
850 if (closed)
851 m_CameraState->m_ManualCoverState = light ? CameraState::MANUAL_COVER_CLOSED_LIGHT :
852 CameraState::MANUAL_COVER_CLOSED_DARK;
853 else
854 m_CameraState->m_ManualCoverState = CameraState::MANAUL_COVER_OPEN;
855 coverQueryState = CAL_CHECK_TASK;
856 // re-run checks
857 checkAllActionsReady();
858 }
859 // cancelled
860 else
861 {
862 m_CameraState->shutterStatus = SHUTTER_UNKNOWN;
863 coverQueryState = CAL_CHECK_TASK;
864 // abort, no further checks
865 emit abortCapture();
866 }
867}
868
869void SequenceJobState::lightBoxLight(bool on)
870{
871 // ignore events if preparation is already completed
872 if (preparationCompleted())
873 return;
874
875 m_CameraState->setLightBoxLightState(on ? CAP_LIGHT_ON : CAP_LIGHT_OFF);
876 emit newLog(i18n(on ? "Light box on." : "Light box off."));
877 // re-run checks
878 checkAllActionsReady();
879}
880
881void SequenceJobState::dustCapStateChanged(ISD::DustCap::Status status)
882{
883 // ignore events if preparation is already completed
884 if (preparationCompleted())
885 return;
886
887 switch (status)
888 {
889 case ISD::DustCap::CAP_ERROR:
890 emit abortCapture();
891 break;
892 default:
893 // do nothing
894 break;
895 }
896
897 // re-run checks
898 checkAllActionsReady();
899}
900
901void SequenceJobState::scopeStatusChanged(ISD::Mount::Status status)
902{
903 // ignore events if preparation is already completed
904 if (preparationCompleted())
905 return;
906
907 // handle wall position
908 switch (status)
909 {
910 case ISD::Mount::MOUNT_TRACKING:
911 if (wpScopeStatus == WP_SLEWING)
912 wpScopeStatus = WP_SLEW_COMPLETED;
913 break;
914 case ISD::Mount::MOUNT_IDLE:
915 if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
916 wpScopeStatus = WP_TRACKING_OFF;
917 break;
918 case ISD::Mount::MOUNT_PARKING:
919 // Ensure the parking state to avoid double park calls
920 m_CameraState->setScopeParkState(ISD::PARK_PARKING);
921 break;
922 default:
923 // do nothing
924 break;
925 }
926 // re-run checks
927 checkAllActionsReady();
928}
929
930void SequenceJobState::scopeParkStatusChanged(ISD::ParkStatus)
931{
932 // re-run checks
933 checkAllActionsReady();
934}
935
936void SequenceJobState::domeStatusChanged(ISD::Dome::Status)
937{
938 // re-run checks
939 checkAllActionsReady();
940}
941
942void SequenceJobState::flatSyncFocusChanged(bool completed)
943{
944 // ignore events if preparation is already completed
945 if (preparationCompleted())
946 return;
947
948 flatSyncStatus = (completed ? FS_COMPLETED : FS_BUSY);
949 // re-run checks
950 checkAllActionsReady();
951}
952
953void SequenceJobState::hasShutter(bool present)
954{
955 // ignore events if preparation is already completed
956 if (preparationCompleted())
957 return;
958
959 if (present == true)
960 m_CameraState->shutterStatus = SHUTTER_YES;
961 else
962 m_CameraState->shutterStatus = SHUTTER_NO;
963
964 // re-run checks
965 checkAllActionsReady();
966}
967
968SequenceJobState::PreparationState SequenceJobState::getPreparationState() const
969{
970 return m_PreparationState;
971}
972
973void SequenceJobState::setFilterStatus(FilterState filterState)
974{
975 // ignore events if preparation is already completed
976 if (preparationCompleted())
977 return;
978
979 switch (filterState)
980 {
981 case FILTER_AUTOFOCUS:
982 // we need to wait until focusing has completed
983 prepareActions[CAPTURE_ACTION_AUTOFOCUS] = false;
984 emit prepareState(CAPTURE_FOCUSING);
985 break;
986
987 // nothing to do in all other cases
988 case FILTER_IDLE:
989 case FILTER_OFFSET:
990 case FILTER_CHANGE:
991 break;
992 }
993
994}
995} // 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 Feb 21 2025 11:54:27 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.