File indexing completed on 2024-04-28 03:43:19

0001 /*  Ekos state machine for a single capture job sequence
0002     SPDX-FileCopyrightText: Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "sequencejobstate.h"
0008 
0009 #include "Options.h"
0010 #include "kstarsdata.h"
0011 #include "indicom.h"
0012 #include "ekos/auxiliary/rotatorutils.h"
0013 
0014 namespace Ekos
0015 {
0016 SequenceJobState::SequenceJobState(const QSharedPointer<CaptureModuleState> &sharedState)
0017 {
0018     m_CaptureModuleState = sharedState;
0019 }
0020 
0021 void SequenceJobState::setFrameType(CCDFrameType frameType)
0022 {
0023     // set the frame type
0024     m_frameType = frameType;
0025     // reset the preparation state
0026     m_PreparationState = PREP_NONE;
0027 }
0028 
0029 void SequenceJobState::initPreparation(bool isPreview)
0030 {
0031     m_status      = JOB_BUSY;
0032     m_isPreview   = isPreview;
0033     wpScopeStatus = WP_NONE;
0034 }
0035 
0036 void SequenceJobState::prepareLightFrameCapture(bool enforceCCDTemp, bool isPreview)
0037 {
0038     // precondition: do not start while already being busy and conditions haven't changed
0039     if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
0040         return;
0041 
0042     // initialize the states
0043     initPreparation(isPreview);
0044 
0045     // Reset all prepare actions
0046     setAllActionsReady();
0047 
0048     // disable batch mode for previews
0049     emit setCCDBatchMode(!isPreview);
0050 
0051     // Check if we need to update temperature (only skip if the value is initialized and within the limits)
0052     prepareTemperatureCheck(enforceCCDTemp);
0053 
0054     // Check if we need to update rotator (only skip if the value is initialized and within the limits)
0055     prepareRotatorCheck();
0056 
0057     // Hint: Filter changes are actually done in SequenceJob::capture();
0058 
0059     // preparation started
0060     m_PreparationState = PREP_BUSY;
0061     // check if the preparations are already completed
0062     checkAllActionsReady();
0063 }
0064 
0065 void SequenceJobState::prepareFlatFrameCapture(bool enforceCCDTemp, bool isPreview)
0066 {
0067     // precondition: do not start while already being busy and conditions haven't changed
0068     if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
0069         return;
0070 
0071     // initialize the states
0072     initPreparation(isPreview);
0073 
0074     // Reset all prepare actions
0075     setAllActionsReady();
0076 
0077     // disable batch mode for previews
0078     emit setCCDBatchMode(!isPreview);
0079 
0080     // Check if we need to update temperature (only skip if the value is initialized and within the limits)
0081     prepareTemperatureCheck(enforceCCDTemp);
0082 
0083     // preparation started
0084     m_PreparationState = PREP_BUSY;
0085     // check if the preparations are already completed
0086     checkAllActionsReady();
0087 }
0088 
0089 void SequenceJobState::prepareDarkFrameCapture(bool enforceCCDTemp, bool isPreview)
0090 {
0091     // precondition: do not start while already being busy and conditions haven't changed
0092     if (m_status == JOB_BUSY && enforceCCDTemp == m_enforceTemperature)
0093         return;
0094 
0095     // initialize the states
0096     initPreparation(isPreview);
0097 
0098     // Reset all prepare actions
0099     setAllActionsReady();
0100 
0101     // disable batch mode for previews
0102     emit setCCDBatchMode(!isPreview);
0103 
0104     // Check if we need to update temperature (only skip if the value is initialized and within the limits)
0105     prepareTemperatureCheck(enforceCCDTemp);
0106 
0107     // preparation started
0108     m_PreparationState = PREP_BUSY;
0109     // check if the preparations are already completed
0110     checkAllActionsReady();
0111 }
0112 
0113 void SequenceJobState::prepareBiasFrameCapture(bool enforceCCDTemp, bool isPreview)
0114 {
0115     prepareDarkFrameCapture(enforceCCDTemp, isPreview);
0116 }
0117 
0118 bool SequenceJobState::initCapture(CCDFrameType frameType, bool isPreview, bool isAutofocusReady, FITSMode mode)
0119 {
0120     m_PreparationState = PREP_INIT_CAPTURE;
0121     autoFocusReady = isAutofocusReady;
0122     m_fitsMode = mode;
0123 
0124     //check for setting the target filter
0125     prepareTargetFilter(frameType, isPreview);
0126     checkAllActionsReady();
0127 
0128     return areActionsReady();
0129 }
0130 
0131 bool SequenceJobState::areActionsReady()
0132 {
0133     for (bool &ready : prepareActions.values())
0134     {
0135         if (ready == false)
0136             return false;
0137     }
0138 
0139     return true;
0140 }
0141 
0142 void SequenceJobState::checkAllActionsReady()
0143 {
0144     switch (m_PreparationState)
0145     {
0146         // capture preparation
0147         case PREP_BUSY:
0148             switch (m_frameType)
0149             {
0150                 case FRAME_LIGHT:
0151                     if (areActionsReady())
0152                     {
0153                         // as last step ensure that the scope is uncovered
0154                         if (checkLightFrameScopeCoverOpen() != IPS_OK)
0155                             return;
0156 
0157                         m_PreparationState = PREP_COMPLETED;
0158                         emit prepareComplete();
0159                     }
0160                     break;
0161                 case FRAME_FLAT:
0162                     if (!areActionsReady())
0163                         return;
0164 
0165                     // 1. Check if the selected flats light source is ready
0166                     if (checkFlatsCoverReady() != IPS_OK)
0167                         return;
0168 
0169                     // 2. If we used AUTOFOCUS before for a specific frame (e.g. Lum)
0170                     //    then the absolute focus position for Lum is recorded in the filter manager
0171                     //    when we take flats again, we always go back to the same focus position as the light frames to ensure
0172                     //    near identical focus for both frames.
0173                     if (checkFlatSyncFocus() != IPS_OK)
0174                         return;
0175 
0176                     // all preparations ready, avoid doubled events
0177                     if (m_PreparationState == PREP_BUSY)
0178                     {
0179                         m_PreparationState = PREP_COMPLETED;
0180                         emit prepareComplete();
0181                     }
0182                     break;
0183                 // darks and bias frames are handled in the same way
0184                 case FRAME_DARK:
0185                 case FRAME_BIAS:
0186                     if (!areActionsReady())
0187                         return;
0188 
0189                     // 1. check if the scope is covered appropriately
0190                     if (checkDarksCoverReady() != IPS_OK)
0191                         return;
0192 
0193                     // 2. avoid doubled events
0194                     if (m_PreparationState == PREP_BUSY)
0195                     {
0196                         m_PreparationState = PREP_COMPLETED;
0197                         emit prepareComplete();
0198                     }
0199                     break;
0200                 default:
0201                     // all other cases not refactored yet, preparation immediately completed
0202                     emit prepareComplete();
0203                     break;
0204             }
0205             break;
0206 
0207         // capture initialization (final preparation steps before starting frame capturing)
0208         case PREP_INIT_CAPTURE:
0209             if (areActionsReady())
0210             {
0211                 // reset the state to avoid double events
0212                 m_PreparationState = PREP_NONE;
0213                 emit initCaptureComplete(m_fitsMode);
0214             }
0215             break;
0216 
0217         case PREP_NONE:
0218         case PREP_COMPLETED:
0219             // in all other cases do nothing
0220             break;
0221     }
0222 }
0223 
0224 void SequenceJobState::setAllActionsReady()
0225 {
0226     QMutableMapIterator<CaptureModuleState::PrepareActions, bool> it(prepareActions);
0227 
0228     while (it.hasNext())
0229     {
0230         it.next();
0231         it.setValue(true);
0232     }
0233     // reset the initialization state
0234     for (CaptureModuleState::PrepareActions action :
0235             {
0236                 CaptureModuleState::ACTION_FILTER, CaptureModuleState::ACTION_ROTATOR, CaptureModuleState::ACTION_TEMPERATURE
0237             })
0238         setInitialized(action, false);
0239 }
0240 
0241 void SequenceJobState::prepareTargetFilter(CCDFrameType frameType, bool isPreview)
0242 {
0243     if (targetFilterID != INVALID_VALUE)
0244     {
0245         if (isInitialized(CaptureModuleState::ACTION_FILTER) == false)
0246         {
0247             prepareActions[CaptureModuleState::ACTION_FILTER] = false;
0248 
0249             // Don't perform autofocus on preview or calibration frames or if Autofocus is not ready yet.
0250             if (isPreview || frameType != FRAME_LIGHT || autoFocusReady == false)
0251                 m_filterPolicy = static_cast<FilterManager::FilterPolicy>(m_filterPolicy & ~FilterManager::AUTOFOCUS_POLICY);
0252 
0253             emit readFilterPosition();
0254         }
0255         else if (targetFilterID != m_CaptureModuleState->currentFilterID)
0256         {
0257             // mark filter preparation action
0258             prepareActions[CaptureModuleState::ACTION_FILTER] = false;
0259 
0260             // determine policy
0261             m_filterPolicy = FilterManager::ALL_POLICIES;
0262 
0263             // Don't perform autofocus on preview or calibration frames or if Autofocus is not ready yet.
0264             if (isPreview || frameType != FRAME_LIGHT || autoFocusReady == false)
0265                 m_filterPolicy = static_cast<FilterManager::FilterPolicy>(m_filterPolicy & ~FilterManager::AUTOFOCUS_POLICY);
0266 
0267             emit changeFilterPosition(targetFilterID, m_filterPolicy);
0268             emit prepareState(CAPTURE_CHANGING_FILTER);
0269         }
0270     }
0271 }
0272 
0273 void SequenceJobState::prepareTemperatureCheck(bool enforceCCDTemp)
0274 {
0275     // turn on CCD temperature enforcing if required
0276     m_enforceTemperature = enforceCCDTemp;
0277 
0278     if (m_enforceTemperature)
0279     {
0280         prepareActions[CaptureModuleState::ACTION_TEMPERATURE] = false;
0281         if (isInitialized(CaptureModuleState::ACTION_TEMPERATURE))
0282         {
0283             // ignore the next value since after setting temperature the next received value will be
0284             // exactly this value no matter what the CCD temperature
0285             ignoreNextValue[CaptureModuleState::ACTION_TEMPERATURE] = true;
0286             // request setting temperature
0287             emit setCCDTemperature(targetTemperature);
0288             emit prepareState(CAPTURE_SETTING_TEMPERATURE);
0289         }
0290         // trigger setting current value first if not initialized
0291         else
0292             emit readCurrentState(CAPTURE_SETTING_TEMPERATURE);
0293 
0294     }
0295 }
0296 
0297 void SequenceJobState::prepareRotatorCheck()
0298 {
0299     if (targetPositionAngle > Ekos::INVALID_VALUE)
0300     {
0301         if (isInitialized(CaptureModuleState::ACTION_ROTATOR))
0302         {
0303             prepareActions[CaptureModuleState::ACTION_ROTATOR] = false;
0304             double rawAngle = RotatorUtils::Instance()->calcRotatorAngle(targetPositionAngle);
0305             emit prepareState(CAPTURE_SETTING_ROTATOR);
0306             emit setRotatorAngle(rawAngle);
0307         }
0308         // trigger setting current value first if not initialized
0309         else
0310             emit readCurrentState(CAPTURE_SETTING_ROTATOR);
0311     }
0312 }
0313 
0314 IPState SequenceJobState::checkCalibrationPreActionsReady()
0315 {
0316     IPState result = IPS_OK;
0317 
0318     if (m_CalibrationPreAction & ACTION_WALL)
0319         result = checkWallPositionReady(FRAME_FLAT);
0320 
0321     if (result != IPS_OK)
0322         return result;
0323 
0324     if (m_CalibrationPreAction & ACTION_PARK_MOUNT)
0325         result = checkPreMountParkReady();
0326 
0327     if (result != IPS_OK)
0328         return result;
0329 
0330     if (m_CalibrationPreAction & ACTION_PARK_DOME)
0331         result = checkPreDomeParkReady();
0332 
0333     return result;
0334 }
0335 
0336 IPState SequenceJobState::checkFlatsCoverReady()
0337 {
0338     auto result = checkCalibrationPreActionsReady();
0339     if (result == IPS_OK)
0340     {
0341         if (m_CaptureModuleState->hasDustCap && m_CaptureModuleState->hasLightBox)
0342             return checkDustCapReady(FRAME_FLAT);
0343         // In case we have a wall action then we are facing a flat light source and we can immediately continue to next step
0344         else if (m_CalibrationPreAction & ACTION_WALL)
0345             return IPS_OK;
0346         else
0347         {
0348             // In case we ONLY have a lightbox then we need to ensure it's toggled correctly first
0349             if (m_CaptureModuleState->hasLightBox)
0350                 return checkDustCapReady(FRAME_FLAT);
0351 
0352             return checkManualCoverReady(true);
0353         }
0354     }
0355 
0356     return result;
0357 }
0358 
0359 IPState SequenceJobState::checkDarksCoverReady()
0360 {
0361     IPState result = checkCalibrationPreActionsReady();;
0362 
0363     if (result == IPS_OK)
0364     {
0365         // 1. check if the CCD has a shutter
0366         result = checkHasShutter();
0367         if (result != IPS_OK)
0368             return result;
0369 
0370         if (m_CaptureModuleState->hasDustCap)
0371             return checkDustCapReady(FRAME_DARK);
0372         // In case we have a wall action then we are facing a designated location and we can immediately continue to next step
0373         else if (m_CalibrationPreAction & ACTION_WALL)
0374             return IPS_OK;
0375         else
0376             return checkManualCoverReady(false);
0377     }
0378     return result;
0379 }
0380 
0381 IPState SequenceJobState::checkManualCoverReady(bool lightSourceRequired)
0382 {
0383     // Manual mode we need to cover mount with evenly illuminated field.
0384     if (lightSourceRequired && m_CaptureModuleState->m_ManualCoverState != CaptureModuleState::MANUAL_COVER_CLOSED_LIGHT)
0385     {
0386         if (coverQueryState == CAL_CHECK_CONFIRMATION)
0387             return IPS_BUSY;
0388 
0389         // request asking the user to cover the scope manually with a light source
0390         emit askManualScopeCover(i18n("Cover the telescope with an evenly illuminated light source."),
0391                                  i18n("Flat Frame"), true);
0392         coverQueryState = CAL_CHECK_CONFIRMATION;
0393 
0394         return IPS_BUSY;
0395     }
0396     else if (!lightSourceRequired && m_CaptureModuleState->m_ManualCoverState != CaptureModuleState::MANUAL_COVER_CLOSED_DARK &&
0397              m_CaptureModuleState->shutterStatus == CaptureModuleState::SHUTTER_NO)
0398     {
0399         if (coverQueryState == CAL_CHECK_CONFIRMATION)
0400             return IPS_BUSY;
0401 
0402         emit askManualScopeCover(i18n("Cover the telescope in order to take a dark exposure."),
0403                                  i18n("Dark Exposure"), false);
0404 
0405         coverQueryState = CAL_CHECK_CONFIRMATION;
0406 
0407         return IPS_BUSY;
0408     }
0409     return IPS_OK;
0410 }
0411 
0412 IPState SequenceJobState::checkDustCapReady(CCDFrameType frameType)
0413 {
0414     // turning on flat light running
0415     if (m_CaptureModuleState->getLightBoxLightState() == CaptureModuleState::CAP_LIGHT_BUSY  ||
0416             m_CaptureModuleState->getDustCapState() == CaptureModuleState::CAP_PARKING ||
0417             m_CaptureModuleState->getDustCapState() == CaptureModuleState::CAP_UNPARKING)
0418         return IPS_BUSY;
0419     // error occured
0420     if (m_CaptureModuleState->getDustCapState() == CaptureModuleState::CAP_ERROR)
0421         return IPS_ALERT;
0422 
0423     auto captureLights = (frameType == FRAME_LIGHT);
0424 
0425     // for flats open the cap and close it otherwise
0426     CaptureModuleState::CapState targetCapState = captureLights ? CaptureModuleState::CAP_IDLE : CaptureModuleState::CAP_PARKED;
0427     // If cap is parked, unpark it since dark cap uses external light source.
0428     if (m_CaptureModuleState->hasDustCap && m_CaptureModuleState->getDustCapState() != targetCapState)
0429     {
0430         m_CaptureModuleState->setDustCapState(captureLights ? CaptureModuleState::CAP_UNPARKING : CaptureModuleState::CAP_PARKING);
0431         emit parkDustCap(!captureLights);
0432         emit newLog(captureLights ? i18n("Unparking dust cap...") : i18n("Parking dust cap..."));
0433         return IPS_BUSY;
0434     }
0435 
0436     auto captureFlats = (frameType == FRAME_FLAT);
0437     CaptureModuleState::LightState targetLightBoxStatus = captureFlats ? CaptureModuleState::CAP_LIGHT_ON :
0438             CaptureModuleState::CAP_LIGHT_OFF;
0439 
0440     if (m_CaptureModuleState->hasLightBox && m_CaptureModuleState->getLightBoxLightState() != targetLightBoxStatus)
0441     {
0442         m_CaptureModuleState->setLightBoxLightState(CaptureModuleState::CAP_LIGHT_BUSY);
0443         emit setLightBoxLight(captureFlats);
0444         emit newLog(captureFlats ? i18n("Turn light box light on...") : i18n("Turn light box light off..."));
0445         return IPS_BUSY;
0446     }
0447 
0448     // nothing more to do
0449     return IPS_OK;
0450 }
0451 
0452 IPState SequenceJobState::checkWallPositionReady(CCDFrameType frametype)
0453 {
0454     if (m_CaptureModuleState->hasTelescope)
0455     {
0456         if (wpScopeStatus < WP_SLEWING)
0457         {
0458             wallCoord.HorizontalToEquatorial(KStarsData::Instance()->lst(),
0459                                              KStarsData::Instance()->geo()->lat());
0460             wpScopeStatus = WP_SLEWING;
0461             emit slewTelescope(wallCoord);
0462             emit newLog(i18n("Mount slewing to wall position (az =%1 alt =%2)",
0463                              wallCoord.alt().toDMSString(), wallCoord.az().toDMSString()));
0464             return IPS_BUSY;
0465         }
0466         // wait until actions completed
0467         else if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
0468             return IPS_BUSY;
0469         // Check if slewing is complete
0470         else if (wpScopeStatus == WP_SLEW_COMPLETED)
0471         {
0472             wpScopeStatus = WP_TRACKING_BUSY;
0473             emit setScopeTracking(false);
0474             emit newLog(i18n("Slew to wall position complete, stop tracking."));
0475             return IPS_BUSY;
0476         }
0477         else if (wpScopeStatus == WP_TRACKING_OFF)
0478             emit newLog(i18n("Slew to wall position complete, tracking stopped."));
0479 
0480         // wall position reached, check if we have a light box to turn on for flats and off otherwise
0481         bool captureFlats = (frametype == FRAME_FLAT);
0482         CaptureModuleState::LightState targetLightState = (captureFlats ? CaptureModuleState::CAP_LIGHT_ON :
0483                 CaptureModuleState::CAP_LIGHT_OFF);
0484 
0485         if (m_CaptureModuleState->hasLightBox == true)
0486         {
0487             if (m_CaptureModuleState->getLightBoxLightState() != targetLightState)
0488             {
0489                 m_CaptureModuleState->setLightBoxLightState(CaptureModuleState::CAP_LIGHT_BUSY);
0490                 emit setLightBoxLight(captureFlats);
0491                 emit newLog(captureFlats ? i18n("Turn light box light on...") : i18n("Turn light box light off..."));
0492                 return IPS_BUSY;
0493             }
0494         }
0495     }
0496     // everything ready
0497     return IPS_OK;
0498 }
0499 
0500 IPState SequenceJobState::checkPreMountParkReady()
0501 {
0502     if (m_CaptureModuleState->hasTelescope)
0503     {
0504         if (m_CaptureModuleState->getScopeParkState() == ISD::PARK_ERROR)
0505         {
0506             emit newLog(i18n("Parking mount failed, aborting..."));
0507             emit abortCapture();
0508             return IPS_ALERT;
0509         }
0510         else if (m_CaptureModuleState->getScopeParkState() == ISD::PARK_PARKING)
0511             return IPS_BUSY;
0512         else if (m_CaptureModuleState->getScopeParkState() != ISD::PARK_PARKED)
0513         {
0514             m_CaptureModuleState->setScopeParkState(ISD::PARK_PARKING);
0515             emit setScopeParked(true);
0516             emit newLog(i18n("Parking mount prior to calibration frames capture..."));
0517             return IPS_BUSY;
0518         }
0519     }
0520     // everything ready
0521     return IPS_OK;
0522 }
0523 
0524 IPState SequenceJobState::checkPreDomeParkReady()
0525 {
0526     if (m_CaptureModuleState->hasDome)
0527     {
0528         if (m_CaptureModuleState->getDomeState() == ISD::Dome::DOME_ERROR)
0529         {
0530             emit newLog(i18n("Parking dome failed, aborting..."));
0531             emit abortCapture();
0532             return IPS_ALERT;
0533         }
0534         else if (m_CaptureModuleState->getDomeState() == ISD::Dome::DOME_PARKING)
0535             return IPS_BUSY;
0536         else if (m_CaptureModuleState->getDomeState() != ISD::Dome::DOME_PARKED)
0537         {
0538             m_CaptureModuleState->setDomeState(ISD::Dome::DOME_PARKING);
0539             emit setDomeParked(true);
0540             emit newLog(i18n("Parking dome prior to calibration frames capture..."));
0541             return IPS_BUSY;
0542         }
0543     }
0544     // everything ready
0545     return IPS_OK;
0546 }
0547 
0548 IPState SequenceJobState::checkFlatSyncFocus()
0549 {
0550     // check already running?
0551     if (flatSyncStatus == FS_BUSY)
0552     {
0553         QTimer::singleShot(1000, this, [this]
0554         {
0555             // wait for one second and repeat the request again
0556             emit flatSyncFocus(targetFilterID);
0557         });
0558         return IPS_BUSY;
0559     }
0560 
0561     if (m_frameType == FRAME_FLAT && Options::flatSyncFocus() && flatSyncStatus != FS_COMPLETED)
0562     {
0563         flatSyncStatus = FS_BUSY;
0564         emit flatSyncFocus(targetFilterID);
0565         return IPS_BUSY;
0566     }
0567     // everything ready
0568     return IPS_OK;
0569 }
0570 
0571 IPState SequenceJobState::checkHasShutter()
0572 {
0573     if (m_CaptureModuleState->shutterStatus == CaptureModuleState::SHUTTER_BUSY)
0574         return IPS_BUSY;
0575     if (m_CaptureModuleState->shutterStatus != CaptureModuleState::SHUTTER_UNKNOWN)
0576         return IPS_OK;
0577     // query the status
0578     m_CaptureModuleState->shutterStatus = CaptureModuleState::SHUTTER_BUSY;
0579     emit queryHasShutter();
0580     return IPS_BUSY;
0581 }
0582 
0583 IPState SequenceJobState::checkLightFrameScopeCoverOpen()
0584 {
0585     // Account for light box only (no dust cap)
0586     if (m_CaptureModuleState->hasLightBox && m_CaptureModuleState->getLightBoxLightState() != CaptureModuleState::CAP_LIGHT_OFF)
0587     {
0588         if (m_CaptureModuleState->getLightBoxLightState() != CaptureModuleState::CAP_LIGHT_BUSY)
0589         {
0590             m_CaptureModuleState->setLightBoxLightState(CaptureModuleState::CAP_LIGHT_BUSY);
0591             emit setLightBoxLight(false);
0592             emit newLog(i18n("Turn light box light off..."));
0593         }
0594         return IPS_BUSY;
0595     }
0596 
0597     // If we have a dust cap, then we must unpark
0598     if (m_CaptureModuleState->hasDustCap)
0599     {
0600         if (m_CaptureModuleState->getDustCapState() != CaptureModuleState::CAP_IDLE)
0601         {
0602             if (m_CaptureModuleState->getDustCapState() != CaptureModuleState::CAP_UNPARKING)
0603             {
0604                 m_CaptureModuleState->setDustCapState(CaptureModuleState::CAP_UNPARKING);
0605                 emit parkDustCap(false);
0606                 emit newLog(i18n("Unparking dust cap..."));
0607             }
0608             return IPS_BUSY;
0609         }
0610 
0611         return IPS_OK;
0612     }
0613 
0614     // If telescopes were MANUALLY covered before
0615     // we need to manually uncover them.
0616     if (m_CaptureModuleState->m_ManualCoverState != CaptureModuleState::MANAUL_COVER_OPEN)
0617     {
0618         // If we already asked for confirmation and waiting for it
0619         // let us see if the confirmation is fulfilled
0620         // otherwise we return.
0621         if (coverQueryState == CAL_CHECK_CONFIRMATION)
0622             return IPS_BUSY;
0623 
0624         emit askManualScopeOpen(m_CaptureModuleState->m_ManualCoverState == CaptureModuleState::MANUAL_COVER_CLOSED_LIGHT);
0625 
0626         return IPS_BUSY;
0627     }
0628 
0629     // scope cover open (or no scope cover)
0630     return IPS_OK;
0631 }
0632 
0633 bool SequenceJobState::isInitialized(CaptureModuleState::PrepareActions action)
0634 {
0635     return m_CaptureModuleState.data()->isInitialized[action];
0636 }
0637 
0638 void SequenceJobState::setInitialized(CaptureModuleState::PrepareActions action, bool init)
0639 {
0640     m_CaptureModuleState.data()->isInitialized[action] = init;
0641 }
0642 
0643 void SequenceJobState::setCurrentFilterID(int value)
0644 {
0645     m_CaptureModuleState->currentFilterID = value;
0646     if (isInitialized(CaptureModuleState::ACTION_FILTER) == false && value != targetFilterID)
0647     {
0648         // mark filter preparation action
0649         // TODO introduce settle time
0650         prepareActions[CaptureModuleState::ACTION_FILTER] = false;
0651 
0652         emit changeFilterPosition(targetFilterID, m_filterPolicy);
0653         emit prepareState(CAPTURE_CHANGING_FILTER);
0654     }
0655     setInitialized(CaptureModuleState::ACTION_FILTER, true);
0656 
0657     if (value == targetFilterID)
0658         prepareActions[CaptureModuleState::ACTION_FILTER] = true;
0659 
0660     checkAllActionsReady();
0661 }
0662 
0663 void SequenceJobState::setCurrentCCDTemperature(double currentTemperature)
0664 {
0665     // skip if next value should be ignored
0666     if (ignoreNextValue[CaptureModuleState::ACTION_TEMPERATURE])
0667     {
0668         ignoreNextValue[CaptureModuleState::ACTION_TEMPERATURE] = false;
0669         return;
0670     }
0671 
0672     if (isInitialized(CaptureModuleState::ACTION_TEMPERATURE))
0673     {
0674         if (m_enforceTemperature == false
0675                 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
0676             prepareActions[CaptureModuleState::ACTION_TEMPERATURE] = true;
0677 
0678         checkAllActionsReady();
0679     }
0680     else
0681     {
0682         setInitialized(CaptureModuleState::ACTION_TEMPERATURE, true);
0683         if (m_enforceTemperature == false
0684                 || fabs(targetTemperature - currentTemperature) <= Options::maxTemperatureDiff())
0685         {
0686             prepareActions[CaptureModuleState::ACTION_TEMPERATURE] = true;
0687             checkAllActionsReady();
0688         }
0689         else
0690         {
0691             prepareTemperatureCheck(m_enforceTemperature);
0692         }
0693     }
0694 }
0695 
0696 void SequenceJobState::setCurrentRotatorPositionAngle(double rotatorAngle, IPState state)
0697 {
0698     double currentPositionAngle = RotatorUtils::Instance()->calcCameraAngle(rotatorAngle, false);
0699 
0700     if (isInitialized(CaptureModuleState::ACTION_ROTATOR))
0701     {
0702         // TODO introduce settle time
0703         // TODO make sure rotator has fully stopped -> see 'align.cpp' captureAndSolve()
0704         if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
0705                 && state != IPS_BUSY)
0706             prepareActions[CaptureModuleState::ACTION_ROTATOR] = true;
0707 
0708         checkAllActionsReady();
0709     }
0710     else
0711     {
0712         setInitialized(CaptureModuleState::ACTION_ROTATOR, true);
0713         if (fabs(currentPositionAngle - targetPositionAngle) * 60 <= Options::astrometryRotatorThreshold()
0714                 && state != IPS_BUSY)
0715         {
0716             prepareActions[CaptureModuleState::ACTION_ROTATOR] = true;
0717             checkAllActionsReady();
0718         }
0719         else
0720         {
0721             prepareRotatorCheck();
0722         }
0723     }
0724 }
0725 
0726 void SequenceJobState::setFocusStatus(FocusState state)
0727 {
0728     switch (state)
0729     {
0730         case FOCUS_COMPLETE:
0731             // did we wait for a successful autofocus run?
0732             if (prepareActions[CaptureModuleState::ACTION_AUTOFOCUS] == false)
0733             {
0734                 prepareActions[CaptureModuleState::ACTION_AUTOFOCUS] = true;
0735                 checkAllActionsReady();
0736             }
0737             break;
0738         case FOCUS_ABORTED:
0739         case FOCUS_FAILED:
0740             // finish preparation with failure
0741             emit prepareComplete(false);
0742             break;
0743         default:
0744             // in all other cases do nothing
0745             break;
0746     }
0747 }
0748 
0749 void SequenceJobState::updateManualScopeCover(bool closed, bool success, bool light)
0750 {
0751     // covering confirmed
0752     if (success == true)
0753     {
0754         if (closed)
0755             m_CaptureModuleState->m_ManualCoverState = light ? CaptureModuleState::MANUAL_COVER_CLOSED_LIGHT :
0756                     CaptureModuleState::MANUAL_COVER_CLOSED_DARK;
0757         else
0758             m_CaptureModuleState->m_ManualCoverState = CaptureModuleState::MANAUL_COVER_OPEN;
0759         coverQueryState = CAL_CHECK_TASK;
0760         // re-run checks
0761         checkAllActionsReady();
0762     }
0763     // cancelled
0764     else
0765     {
0766         m_CaptureModuleState->shutterStatus = CaptureModuleState::SHUTTER_UNKNOWN;
0767         coverQueryState = CAL_CHECK_TASK;
0768         // abort, no further checks
0769         emit abortCapture();
0770     }
0771 }
0772 
0773 void SequenceJobState::lightBoxLight(bool on)
0774 {
0775     m_CaptureModuleState->setLightBoxLightState(on ? CaptureModuleState::CAP_LIGHT_ON : CaptureModuleState::CAP_LIGHT_OFF);
0776     emit newLog(i18n(on ? "Light box on." : "Light box off."));
0777     // re-run checks
0778     checkAllActionsReady();
0779 }
0780 
0781 void SequenceJobState::dustCapStateChanged(ISD::DustCap::Status status)
0782 {
0783     switch (status)
0784     {
0785         case ISD::DustCap::CAP_ERROR:
0786             m_CaptureModuleState->setDustCapState(CaptureModuleState::CAP_ERROR);
0787             emit abortCapture();
0788             break;
0789         case ISD::DustCap::CAP_PARKED:
0790             m_CaptureModuleState->setDustCapState(CaptureModuleState::CAP_PARKED);
0791             emit newLog(i18n("Dust cap parked."));
0792             break;
0793         case ISD::DustCap::CAP_IDLE:
0794             m_CaptureModuleState->setDustCapState(CaptureModuleState::CAP_IDLE);
0795             emit newLog(i18n("Dust cap unparked."));
0796             break;
0797         case ISD::DustCap::CAP_UNPARKING:
0798             m_CaptureModuleState->setDustCapState(CaptureModuleState::CAP_UNPARKING);
0799             break;
0800         case ISD::DustCap::CAP_PARKING:
0801             m_CaptureModuleState->setDustCapState(CaptureModuleState::CAP_PARKING);
0802             break;
0803     }
0804 
0805     // re-run checks
0806     checkAllActionsReady();
0807 }
0808 
0809 void SequenceJobState::scopeStatusChanged(ISD::Mount::Status status)
0810 {
0811     // handle wall position
0812     switch (status)
0813     {
0814         case ISD::Mount::MOUNT_TRACKING:
0815             if (wpScopeStatus == WP_SLEWING)
0816                 wpScopeStatus = WP_SLEW_COMPLETED;
0817             break;
0818         case ISD::Mount::MOUNT_IDLE:
0819             if (wpScopeStatus == WP_SLEWING || wpScopeStatus == WP_TRACKING_BUSY)
0820                 wpScopeStatus = WP_TRACKING_OFF;
0821             break;
0822         default:
0823             // do nothing
0824             break;
0825     }
0826     m_CaptureModuleState->setScopeState(status);
0827     // re-run checks
0828     checkAllActionsReady();
0829 }
0830 
0831 void SequenceJobState::scopeParkStatusChanged(ISD::ParkStatus status)
0832 {
0833     m_CaptureModuleState->setScopeParkState(status);
0834     // re-run checks
0835     checkAllActionsReady();
0836 }
0837 
0838 void SequenceJobState::domeStatusChanged(ISD::Dome::Status status)
0839 {
0840     m_CaptureModuleState->setDomeState(status);
0841     // re-run checks
0842     checkAllActionsReady();
0843 }
0844 
0845 void SequenceJobState::flatSyncFocusChanged(bool completed)
0846 {
0847     flatSyncStatus = (completed ? FS_COMPLETED : FS_BUSY);
0848     // re-run checks
0849     checkAllActionsReady();
0850 }
0851 
0852 void SequenceJobState::hasShutter(bool present)
0853 {
0854     if (present == true)
0855         m_CaptureModuleState->shutterStatus = CaptureModuleState::SHUTTER_YES;
0856     else
0857         m_CaptureModuleState->shutterStatus = CaptureModuleState::SHUTTER_NO;
0858 
0859     // re-run checks
0860     checkAllActionsReady();
0861 }
0862 
0863 SequenceJobState::PreparationState SequenceJobState::getPreparationState() const
0864 {
0865     return m_PreparationState;
0866 }
0867 
0868 void SequenceJobState::setFilterStatus(FilterState filterState)
0869 {
0870     switch (filterState)
0871     {
0872         case FILTER_AUTOFOCUS:
0873             // we need to wait until focusing has completed
0874             prepareActions[CaptureModuleState::ACTION_AUTOFOCUS] = false;
0875             emit prepareState(CAPTURE_FOCUSING);
0876             break;
0877 
0878         // nothing to do in all other cases
0879         case FILTER_IDLE:
0880         case FILTER_OFFSET:
0881         case FILTER_CHANGE:
0882             break;
0883     }
0884 
0885 }
0886 } // namespace