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 setScopeParked(false);
508 return IPS_BUSY;
509 case ISD::PARK_UNKNOWN:
510 // retrieve the mount park state
511 emit readCurrentMountParkState();
512 return IPS_BUSY;
513 case ISD::PARK_UNPARKED:
514 wpScopeStatus = WP_UNPARKED;
515 break;
516 }
517 return IPS_BUSY;
518 }
519 else if (wpScopeStatus < WP_SLEWING)
520 {
521 wallCoord.HorizontalToEquatorial(KStarsData::Instance()->lst(),
522 KStarsData::Instance()->geo()->lat());
523 wpScopeStatus = WP_SLEWING;
524 emit slewTelescope(wallCoord);
525 emit newLog(i18n("Mount slewing to wall position (az =%1 alt =%2)",
526 wallCoord.az().toDMSString(), wallCoord.alt().toDMSString()));
527 return IPS_BUSY;
528 }
529 // wait until actions completed
530 else if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
531 return IPS_BUSY;
532 // Check if slewing is complete
533 else if (wpScopeStatus == WP_SLEW_COMPLETED)
534 {
535 wpScopeStatus = WP_TRACKING_BUSY;
536 emit setScopeTracking(false);
537 emit newLog(i18n("Slew to wall position complete, stop tracking."));
538 return IPS_BUSY;
539 }
540 else if (wpScopeStatus == WP_TRACKING_OFF)
541 emit newLog(i18n("Slew to wall position complete, tracking stopped."));
542
543 // wall position reached, check if we have a light box to turn on for flats and off otherwise
544 bool captureFlats = (frametype == FRAME_FLAT);
545 LightState targetLightState = (captureFlats ? CAP_LIGHT_ON :
546 CAP_LIGHT_OFF);
547
548 if (m_CameraState->hasLightBox == true)
549 {
550 if (m_CameraState->getLightBoxLightState() != targetLightState)
551 {
552 m_CameraState->setLightBoxLightState(CAP_LIGHT_BUSY);
553 emit setLightBoxLight(captureFlats);
554 emit newLog(captureFlats ? i18n("Turn light box light on...") : i18n("Turn light box light off..."));
555 return IPS_BUSY;
556 }
557 }
558 }
559 // everything ready
560 return IPS_OK;
561}
562
563IPState SequenceJobState::checkPreMountParkReady()
564{
565 if (m_CameraState->hasTelescope)
566 {
567 switch (m_CameraState->getScopeParkState())
568 {
569 case ISD::PARK_PARKED:
570 break;
571 case ISD::PARK_ERROR:
572 emit newLog(i18n("Parking mount failed, aborting..."));
573 emit abortCapture();
574 return IPS_ALERT;
575 case ISD::PARK_PARKING:
576 return IPS_BUSY;
577 case ISD::PARK_UNPARKED:
578 case ISD::PARK_UNPARKING:
579 // park the scope
580 emit setScopeParked(true);
581 emit newLog(i18n("Parking mount prior to calibration frames capture..."));
582 return IPS_BUSY;
583 case ISD::PARK_UNKNOWN:
584 // retrieve the mount park state
585 emit readCurrentMountParkState();
586 return IPS_BUSY;
587 }
588 }
589 // everything ready
590 return IPS_OK;
591
592}
593
594IPState SequenceJobState::checkPreDomeParkReady()
595{
596 if (m_CameraState->hasDome)
597 {
598 if (m_CameraState->getDomeState() == ISD::Dome::DOME_ERROR)
599 {
600 emit newLog(i18n("Parking dome failed, aborting..."));
601 emit abortCapture();
602 return IPS_ALERT;
603 }
604 else if (m_CameraState->getDomeState() == ISD::Dome::DOME_PARKING)
605 return IPS_BUSY;
606 else if (m_CameraState->getDomeState() != ISD::Dome::DOME_PARKED)
607 {
608 m_CameraState->setDomeState(ISD::Dome::DOME_PARKING);
609 emit setDomeParked(true);
610 emit newLog(i18n("Parking dome prior to calibration frames capture..."));
611 return IPS_BUSY;
612 }
613 }
614 // everything ready
615 return IPS_OK;
616}
617
618IPState SequenceJobState::checkFlatSyncFocus()
619{
620 // check already running?
621 if (flatSyncStatus == FS_BUSY)
622 {
623 m_FlatSyncCheck.start();
624 return IPS_BUSY;
625 }
626
627 if (m_frameType == FRAME_FLAT && Options::flatSyncFocus() && flatSyncStatus != FS_COMPLETED)
628 {
629 flatSyncStatus = FS_BUSY;
630 emit flatSyncFocus(targetFilterID);
631 return IPS_BUSY;
632 }
633 // everything ready
634 return IPS_OK;
635}
636
637IPState SequenceJobState::checkHasShutter()
638{
639 if (m_CameraState->shutterStatus == SHUTTER_BUSY)
640 return IPS_BUSY;
641 if (m_CameraState->shutterStatus != SHUTTER_UNKNOWN)
642 return IPS_OK;
643 // query the status
644 m_CameraState->shutterStatus = SHUTTER_BUSY;
645 emit queryHasShutter();
646 return IPS_BUSY;
647}
648
649IPState SequenceJobState::checkLightFrameScopeCoverOpen()
650{
651 // Account for light box only (no dust cap)
652 if (m_CameraState->hasLightBox && m_CameraState->getLightBoxLightState() != CAP_LIGHT_OFF)
653 {
654 if (m_CameraState->getLightBoxLightState() != CAP_LIGHT_BUSY)
655 {
656 m_CameraState->setLightBoxLightState(CAP_LIGHT_BUSY);
657 emit setLightBoxLight(false);
658 emit newLog(i18n("Turn light box light off..."));
659 }
660 return IPS_BUSY;
661 }
662
663 // If we have a dust cap, then we must unpark
664 if (m_CameraState->hasDustCap)
665 {
666 if (m_CameraState->getDustCapState() != CAP_IDLE)
667 {
668 if (m_CameraState->getDustCapState() != CAP_UNPARKING)
669 {
670 m_CameraState->setDustCapState(CAP_UNPARKING);
671 emit parkDustCap(false);
672 emit newLog(i18n("Unparking dust cap..."));
673 }
674 return IPS_BUSY;
675 }
676
677 return IPS_OK;
678 }
679
680 // If telescopes were MANUALLY covered before
681 // we need to manually uncover them.
682 if (m_CameraState->m_ManualCoverState != CameraState::MANAUL_COVER_OPEN)
683 {
684 // If we already asked for confirmation and waiting for it
685 // let us see if the confirmation is fulfilled
686 // otherwise we return.
687 if (coverQueryState == CAL_CHECK_CONFIRMATION)
688 return IPS_BUSY;
689
690 emit askManualScopeOpen(m_CameraState->m_ManualCoverState == CameraState::MANUAL_COVER_CLOSED_LIGHT);
691
692 return IPS_BUSY;
693 }
694
695 // scope cover open (or no scope cover)
696 return IPS_OK;
697}
698
699bool SequenceJobState::isInitialized(CaptureWorkflowActionType action)
700{
701 return m_CameraState.data()->isInitialized[action];
702}
703
704void SequenceJobState::setInitialized(CaptureWorkflowActionType action, bool init)
705{
706 m_CameraState.data()->isInitialized[action] = init;
707}
708
709void SequenceJobState::setCurrentFilterID(int value)
710{
711 // ignore events if preparation is already completed
712 if (preparationCompleted())
713 return;
714
715 m_CameraState->currentFilterID = value;
716 if (isInitialized(CAPTURE_ACTION_FILTER) == false && value != targetFilterID)
717 {
718 // mark filter preparation action
719 // TODO introduce settle time
720 prepareActions[CAPTURE_ACTION_FILTER] = false;
721
722 emit changeFilterPosition(targetFilterID, m_filterPolicy);
723 emit prepareState(CAPTURE_CHANGING_FILTER);
724 }
725 setInitialized(CAPTURE_ACTION_FILTER, true);
726
727 if (value == targetFilterID)
728 prepareActions[CAPTURE_ACTION_FILTER] = true;
729 else if (value < 0)
730 {
731 m_PreparationState = PREP_NONE;
732 emit prepareComplete(false);
733 return;
734 }
735
736 checkAllActionsReady();
737}
738
739void SequenceJobState::setCurrentCCDTemperature(double currentTemperature)
740{
741 // ignore events if preparation is already completed
742 if (preparationCompleted())
743 return;
744
745 // skip if next value should be ignored
746 if (ignoreNextValue[CAPTURE_ACTION_TEMPERATURE])
747 {
748 ignoreNextValue[CAPTURE_ACTION_TEMPERATURE] = false;
749 return;
750 }
751
752 if (isInitialized(CAPTURE_ACTION_TEMPERATURE))
753 {
754 if (m_enforceTemperature == false
755 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
756 prepareActions[CAPTURE_ACTION_TEMPERATURE] = true;
757
758 checkAllActionsReady();
759 }
760 else
761 {
762 setInitialized(CAPTURE_ACTION_TEMPERATURE, true);
763 if (m_enforceTemperature == false
764 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
765 {
766 prepareActions[CAPTURE_ACTION_TEMPERATURE] = true;
767 checkAllActionsReady();
768 }
769 else
770 {
771 prepareTemperatureCheck(m_enforceTemperature);
772 }
773 }
774}
775
776void SequenceJobState::setCurrentRotatorPositionAngle(double rotatorAngle, IPState state)
777{
778 // ignore events if preparation is already completed
779 if (preparationCompleted())
780 return;
781
782 double currentPositionAngle = RotatorUtils::Instance()->calcCameraAngle(rotatorAngle, false);
783
784 if (isInitialized(CAPTURE_ACTION_ROTATOR))
785 {
786 // TODO introduce settle time
787 // TODO make sure rotator has fully stopped -> see 'align.cpp' captureAndSolve()
788 if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
789 && state != IPS_BUSY)
790 prepareActions[CAPTURE_ACTION_ROTATOR] = true;
791
792 checkAllActionsReady();
793 }
794 else
795 {
796 setInitialized(CAPTURE_ACTION_ROTATOR, true);
797 if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
798 && state != IPS_BUSY)
799 {
800 prepareActions[CAPTURE_ACTION_ROTATOR] = true;
801 checkAllActionsReady();
802 }
803 else
804 {
805 prepareRotatorCheck();
806 }
807 }
808}
809
810void SequenceJobState::setFocusStatus(FocusState state)
811{
812 // ignore events if preparation is already completed
813 if (preparationCompleted())
814 return;
815
816 switch (state)
817 {
818 case FOCUS_COMPLETE:
819 // did we wait for a successful autofocus run?
820 if (prepareActions[CAPTURE_ACTION_AUTOFOCUS] == false)
821 {
822 prepareActions[CAPTURE_ACTION_AUTOFOCUS] = true;
823 checkAllActionsReady();
824 }
825 break;
826 case FOCUS_ABORTED:
827 case FOCUS_FAILED:
828 // finish preparation with failure
829 emit prepareComplete(false);
830 break;
831 default:
832 // in all other cases do nothing
833 break;
834 }
835}
836
837void SequenceJobState::updateManualScopeCover(bool closed, bool success, bool light)
838{
839 // ignore events if preparation is already completed
840 if (preparationCompleted())
841 return;
842
843 // covering confirmed
844 if (success == true)
845 {
846 if (closed)
847 m_CameraState->m_ManualCoverState = light ? CameraState::MANUAL_COVER_CLOSED_LIGHT :
848 CameraState::MANUAL_COVER_CLOSED_DARK;
849 else
850 m_CameraState->m_ManualCoverState = CameraState::MANAUL_COVER_OPEN;
851 coverQueryState = CAL_CHECK_TASK;
852 // re-run checks
853 checkAllActionsReady();
854 }
855 // cancelled
856 else
857 {
858 m_CameraState->shutterStatus = SHUTTER_UNKNOWN;
859 coverQueryState = CAL_CHECK_TASK;
860 // abort, no further checks
861 emit abortCapture();
862 }
863}
864
865void SequenceJobState::lightBoxLight(bool on)
866{
867 // ignore events if preparation is already completed
868 if (preparationCompleted())
869 return;
870
871 m_CameraState->setLightBoxLightState(on ? CAP_LIGHT_ON : CAP_LIGHT_OFF);
872 emit newLog(i18n(on ? "Light box on." : "Light box off."));
873 // re-run checks
874 checkAllActionsReady();
875}
876
877void SequenceJobState::dustCapStateChanged(ISD::DustCap::Status status)
878{
879 // ignore events if preparation is already completed
880 if (preparationCompleted())
881 return;
882
883 switch (status)
884 {
885 case ISD::DustCap::CAP_ERROR:
886 emit abortCapture();
887 break;
888 default:
889 // do nothing
890 break;
891 }
892
893 // re-run checks
894 checkAllActionsReady();
895}
896
897void SequenceJobState::scopeStatusChanged(ISD::Mount::Status status)
898{
899 // ignore events if preparation is already completed
900 if (preparationCompleted())
901 return;
902
903 // handle wall position
904 switch (status)
905 {
906 case ISD::Mount::MOUNT_TRACKING:
907 if (wpScopeStatus == WP_SLEWING)
908 wpScopeStatus = WP_SLEW_COMPLETED;
909 break;
910 case ISD::Mount::MOUNT_IDLE:
911 if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
912 wpScopeStatus = WP_TRACKING_OFF;
913 break;
914 case ISD::Mount::MOUNT_PARKING:
915 // Ensure the parking state to avoid double park calls
916 m_CameraState->setScopeParkState(ISD::PARK_PARKING);
917 break;
918 default:
919 // do nothing
920 break;
921 }
922 // re-run checks
923 checkAllActionsReady();
924}
925
926void SequenceJobState::scopeParkStatusChanged(ISD::ParkStatus)
927{
928 // re-run checks
929 checkAllActionsReady();
930}
931
932void SequenceJobState::domeStatusChanged(ISD::Dome::Status)
933{
934 // re-run checks
935 checkAllActionsReady();
936}
937
938void SequenceJobState::flatSyncFocusChanged(bool completed)
939{
940 // ignore events if preparation is already completed
941 if (preparationCompleted())
942 return;
943
944 flatSyncStatus = (completed ? FS_COMPLETED : FS_BUSY);
945 // re-run checks
946 checkAllActionsReady();
947}
948
949void SequenceJobState::hasShutter(bool present)
950{
951 // ignore events if preparation is already completed
952 if (preparationCompleted())
953 return;
954
955 if (present == true)
956 m_CameraState->shutterStatus = SHUTTER_YES;
957 else
958 m_CameraState->shutterStatus = SHUTTER_NO;
959
960 // re-run checks
961 checkAllActionsReady();
962}
963
964SequenceJobState::PreparationState SequenceJobState::getPreparationState() const
965{
966 return m_PreparationState;
967}
968
969void SequenceJobState::setFilterStatus(FilterState filterState)
970{
971 // ignore events if preparation is already completed
972 if (preparationCompleted())
973 return;
974
975 switch (filterState)
976 {
977 case FILTER_AUTOFOCUS:
978 // we need to wait until focusing has completed
979 prepareActions[CAPTURE_ACTION_AUTOFOCUS] = false;
980 emit prepareState(CAPTURE_FOCUSING);
981 break;
982
983 // nothing to do in all other cases
984 case FILTER_IDLE:
985 case FILTER_OFFSET:
986 case FILTER_CHANGE:
987 break;
988 }
989
990}
991} // 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
GeoCoordinates geo(const QVariant &location)
QCA_EXPORT void init()
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 7 2025 11:47:10 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.