LCOV - code coverage report
Current view: top level - libMagAOX/app/dev - stdMotionStage.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 6.8 % 162 11
Test Date: 2026-01-03 21:03:39 Functions: 33.3 % 15 5

            Line data    Source code
       1              : /** \file stdMotionStage.hpp
       2              :   * \brief Standard motion stage interface
       3              :   *
       4              :   * \author Jared R. Males (jaredmales@gmail.com)
       5              :   *
       6              :   * \ingroup app_files
       7              :   */
       8              : 
       9              : #ifndef stdMotionStage_hpp
      10              : #define stdMotionStage_hpp
      11              : 
      12              : 
      13              : namespace MagAOX
      14              : {
      15              : namespace app
      16              : {
      17              : namespace dev
      18              : {
      19              : 
      20              : 
      21              : /// MagAO-X standard motion stage interface
      22              : /** Implements the standard interface to a MagAO-X motion stage.
      23              :   * This includes the mcbl filter wheels, the zaber stages.
      24              :   *
      25              :   * The required interface to be implemented in derivedT is
      26              :   * \code
      27              : 
      28              :     int stop(); //Note that the INDI mutex will not be locked on this call
      29              : 
      30              :     int startHoming(); //INDI mutex will be locked on this call.
      31              : 
      32              :     float presetNumber();
      33              : 
      34              :     int moveTo(float); //INDI mutex will be locked on this call.
      35              :     \endcode
      36              :   *
      37              :   * In addition the derived class is responsible for setting m_moving and m_preset. m_preset_target should also be set if the wheel
      38              :   * is moved via a low-level position command.
      39              :   *
      40              :   * The derived class `derivedT` must be a MagAOXApp\<true\>, and should declare this class a friend like so:
      41              :    \code
      42              :     friend class dev::stdMotionStage<derivedT>;
      43              :    \endcode
      44              :   *
      45              :   *
      46              :   *
      47              :   * Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic`, `appShutdown`
      48              :   * `onPowerOff`, and `whilePowerOff`,  must be placed in the derived class's functions of the same name.
      49              :   *
      50              :   * \ingroup appdev
      51              :   */
      52              : template<class derivedT>
      53              : class stdMotionStage
      54              : {
      55              : protected:
      56              : 
      57              :    /** \name Configurable Parameters
      58              :     * @{
      59              :     */
      60              : 
      61              :    bool m_powerOnHome {false}; ///< If true, then the motor is homed at startup (by this software or actual power on)
      62              : 
      63              :    int m_homePreset {-1}; ///< If >=0, this preset position is moved to after homing
      64              : 
      65              :    std::vector<std::string> m_presetNames; ///< The names of each position on the stage.
      66              : 
      67              :    std::vector<float> m_presetPositions; ///< The positions, in arbitrary units, of each preset.  If 0, then the integer position number (starting from 1) is used to calculate.
      68              : 
      69              :    ///@}
      70              : 
      71              :    std::string m_presetNotation {"preset"}; ///< Notation used to refer to a preset, should be singular, as in "preset" or "filter".
      72              : 
      73              :    bool m_fractionalPresets {true}; ///< Flag to set in constructor determining if fractional presets are allowed.  Used for INDI/GUIs.
      74              : 
      75              :    bool m_defaultPositions {true}; ///< Flag controlling whether the default preset positions (the vector index) are set in loadConfig.
      76              : 
      77              :    int8_t m_moving {0}; ///< Whether or not the stage is moving.  -2 means powered off, -1 means not homed, 0 means not moving, 1 means moving, 2 means homing.
      78              :    int8_t m_movingState {0}; ///< Used to track the type of command.  If > 1 this is a command to move to a preset.  If 0 then it is a move to an arbitrary position.
      79              : 
      80              :    float m_preset {0}; ///< The current numerical preset position [1.0 is index 0 in the preset name vector]
      81              :    float m_preset_target {0}; ///< The target numerical preset position [1.0 is index 0 in the preset name vector]
      82              : 
      83              : public:
      84              : 
      85              :    ///Destructor
      86              :    ~stdMotionStage() noexcept;
      87              : 
      88              :    /// Setup the configuration system
      89              :    /**
      90              :      * This should be called in `derivedT::setupConfig` as
      91              :      * \code
      92              :        stdMotionStage<derivedT>::setupConfig(config);
      93              :        \endcode
      94              :      * with appropriate error checking.
      95              :      */
      96              :    int setupConfig(mx::app::appConfigurator & config /**< [out] the derived classes configurator*/);
      97              : 
      98              :    /// load the configuration system results
      99              :    /**
     100              :      * This should be called in `derivedT::loadConfig` as
     101              :      * \code
     102              :        stdMotionStage<derivedT>::loadConfig(config);
     103              :        \endcode
     104              :      * with appropriate error checking.
     105              :      */
     106              :    int loadConfig(mx::app::appConfigurator & config /**< [in] the derived classes configurator*/);
     107              : 
     108              :    /// Startup function
     109              :    /**
     110              :      * This should be called in `derivedT::appStartup` as
     111              :      * \code
     112              :        stdMotionStage<derivedT>::appStartup();
     113              :        \endcode
     114              :      * with appropriate error checking.
     115              :      *
     116              :      * \returns 0 on success
     117              :      * \returns -1 on error, which is logged.
     118              :      */
     119              :    int appStartup();
     120              : 
     121              :    /// Application logic
     122              :    /** Checks the stdMotionStage thread
     123              :      *
     124              :      * This should be called from the derived's appLogic() as in
     125              :      * \code
     126              :        stdMotionStage<derivedT>::appLogic();
     127              :        \endcode
     128              :      * with appropriate error checking.
     129              :      *
     130              :      * \returns 0 on success
     131              :      * \returns -1 on error, which is logged.
     132              :      */
     133              :    int appLogic();
     134              : 
     135              :    /// Actions on power off
     136              :    /**
     137              :      * This should be called from the derived's onPowerOff() as in
     138              :      * \code
     139              :        stdMotionStage<derivedT>::onPowerOff();
     140              :        \endcode
     141              :      * with appropriate error checking.
     142              :      *
     143              :      * \returns 0 on success
     144              :      * \returns -1 on error, which is logged.
     145              :      */
     146              :    int onPowerOff();
     147              : 
     148              :    /// Actions while powered off
     149              :    /**
     150              :      * This should be called from the derived's whilePowerOff() as in
     151              :      * \code
     152              :        stdMotionStage<derivedT>::whilePowerOff();
     153              :        \endcode
     154              :      * with appropriate error checking.
     155              :      *
     156              :      * \returns 0 on success
     157              :      * \returns -1 on error, which is logged.
     158              :      */
     159              :    int whilePowerOff();
     160              : 
     161              :    /// Application the shutdown
     162              :    /** Shuts down the stdMotionStage thread
     163              :      *
     164              :      * \code
     165              :        stdMotionStage<derivedT>::appShutdown();
     166              :        \endcode
     167              :      * with appropriate error checking.
     168              :      *
     169              :      * \returns 0 on success
     170              :      * \returns -1 on error, which is logged.
     171              :      */
     172              :    int appShutdown();
     173              : 
     174              : protected:
     175              : 
     176              : 
     177              :     /** \name INDI
     178              :       *
     179              :       *@{
     180              :       */
     181              : protected:
     182              :    //declare our properties
     183              : 
     184              :    ///The position of the stage in presets
     185              :    pcf::IndiProperty m_indiP_preset;
     186              : 
     187              :    ///The name of the nearest preset for this position
     188              :    pcf::IndiProperty m_indiP_presetName;
     189              : 
     190              :    ///Command the stage to home. .
     191              :    pcf::IndiProperty m_indiP_home;
     192              : 
     193              :    ///Command the stage to halt.
     194              :    pcf::IndiProperty m_indiP_stop;
     195              : 
     196              : public:
     197              : 
     198              :    /// The static callback function to be registered for stdMotionStage properties
     199              :    /** Dispatches to the relevant handler
     200              :      *
     201              :      * \returns 0 on success.
     202              :      * \returns -1 on error.
     203              :      */
     204              :    static int st_newCallBack_stdMotionStage( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
     205              :                                              const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
     206              :                                            );
     207              : 
     208              :    /// Callback to process a NEW preset position request
     209              :    /**
     210              :      * \returns 0 on success.
     211              :      * \returns -1 on error.
     212              :      */
     213              :    int newCallBack_m_indiP_preset( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     214              : 
     215              :    /// Callback to process a NEW preset name request
     216              :    /**
     217              :      * \returns 0 on success.
     218              :      * \returns -1 on error.
     219              :      */
     220              :    int newCallBack_m_indiP_presetName( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     221              : 
     222              :    /// Callback to process a NEW home request switch toggle
     223              :    /**
     224              :      * \returns 0 on success.
     225              :      * \returns -1 on error.
     226              :      */
     227              :    int newCallBack_m_indiP_home( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     228              : 
     229              :    /// Callback to process a NEW stop request switch toggle
     230              :    /**
     231              :      * \returns 0 on success.
     232              :      * \returns -1 on error.
     233              :      */
     234              :    int newCallBack_m_indiP_stop( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     235              : 
     236              :    /// Update the INDI properties for this device controller
     237              :    /** You should call this once per main loop.
     238              :      * It is not called automatically.
     239              :      *
     240              :      * \returns 0 on success.
     241              :      * \returns -1 on error.
     242              :      */
     243              :    int updateINDI();
     244              : 
     245              :     ///@}
     246              : 
     247              :    /** \name Telemeter Interface
     248              :      * @{
     249              :      */
     250              : 
     251              :    int recordStage( bool force = false );
     252              : 
     253              :    ///@}
     254              : 
     255              : private:
     256            0 :    derivedT & derived()
     257              :    {
     258            0 :       return *static_cast<derivedT *>(this);
     259              :    }
     260              : };
     261              : 
     262              : template<class derivedT>
     263           48 : stdMotionStage<derivedT>::~stdMotionStage() noexcept
     264              : {
     265           48 :    return;
     266           48 : }
     267              : 
     268              : 
     269              : 
     270              : template<class derivedT>
     271            0 : int stdMotionStage<derivedT>::setupConfig(mx::app::appConfigurator & config)
     272              : {
     273              :    static_cast<void>(config);
     274              : 
     275            0 :    config.add("stage.powerOnHome", "", "stage.powerOnHome", argType::Required, "stage", "powerOnHome", false, "bool", "If true, home at startup/power-on.  Default=false.");
     276              : 
     277            0 :    config.add("stage.homePreset", "", "stage.homePreset", argType::Required, "stage", "homePreset", false, "int", "If >=0, this preset number is moved to after homing.");
     278              : 
     279            0 :    config.add(m_presetNotation + "s.names", "", m_presetNotation + "s.names",  argType::Required, m_presetNotation+"s", "names", false, "vector<string>", "The names of the " + m_presetNotation+ "s.");
     280            0 :    config.add(m_presetNotation + "s.positions", "", m_presetNotation + "s.positions",  argType::Required, m_presetNotation+"s", "positions", false, "vector<float>", "The positions of the " + m_presetNotation + "s.  If omitted or 0 then order is used.");
     281              : 
     282            0 :    return 0;
     283              : }
     284              : 
     285              : template<class derivedT>
     286            0 : int stdMotionStage<derivedT>::loadConfig(mx::app::appConfigurator & config)
     287              : {
     288            0 :    config(m_powerOnHome, "stage.powerOnHome");
     289            0 :    config(m_homePreset, "stage.homePreset");
     290              : 
     291            0 :    config(m_presetNames, m_presetNotation + "s.names");
     292              : 
     293            0 :    if(m_defaultPositions)
     294              :    {
     295            0 :       m_presetPositions.resize(m_presetNames.size(), 0);
     296            0 :       for(size_t n=0;n<m_presetPositions.size();++n) m_presetPositions[n] = n+1;
     297              :    }
     298              : 
     299            0 :    config(m_presetPositions, m_presetNotation + "s.positions");
     300              : 
     301            0 :    if(m_defaultPositions)
     302              :    {
     303            0 :       for(size_t n=0;n<m_presetPositions.size();++n) if(m_presetPositions[n] == 0) m_presetPositions[n] = n+1;
     304              :    }
     305              : 
     306            0 :    return 0;
     307              : }
     308              : 
     309              : 
     310              : 
     311              : template<class derivedT>
     312            0 : int stdMotionStage<derivedT>::appStartup()
     313              : {
     314            0 :    double step = 0.0;
     315            0 :    std::string format = "%.4f";
     316            0 :    if(!m_fractionalPresets)
     317              :    {
     318            0 :       step = 1.0;
     319            0 :       format = "%d";
     320              :    }
     321              : 
     322            0 :    derived().createStandardIndiNumber( m_indiP_preset, m_presetNotation, 1.0, (double) m_presetNames.size(), step, format);
     323            0 :    m_indiP_preset["current"].set(0);
     324            0 :    m_indiP_preset["target"].set(0);
     325            0 :    if( derived().registerIndiPropertyNew( m_indiP_preset, st_newCallBack_stdMotionStage) < 0)
     326              :    {
     327              :       #ifndef STDFILTERWHEEL_TEST_NOLOG
     328            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     329              :       #endif
     330            0 :       return -1;
     331              :    }
     332              : 
     333            0 :    if(derived().createStandardIndiSelectionSw( m_indiP_presetName, m_presetNotation + "Name", m_presetNames) < 0)
     334              :    {
     335            0 :       derivedT::template log<software_critical>({__FILE__, __LINE__});
     336            0 :       return -1;
     337              :    }
     338            0 :    if( derived().registerIndiPropertyNew( m_indiP_presetName, st_newCallBack_stdMotionStage) < 0)
     339              :    {
     340              :       #ifndef STDFILTERWHEEL_TEST_NOLOG
     341            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     342              :       #endif
     343            0 :       return -1;
     344              :    }
     345              : 
     346            0 :    derived().createStandardIndiRequestSw( m_indiP_home, "home");
     347            0 :    if( derived().registerIndiPropertyNew( m_indiP_home, st_newCallBack_stdMotionStage) < 0)
     348              :    {
     349              :       #ifndef STDFILTERWHEEL_TEST_NOLOG
     350            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     351              :       #endif
     352            0 :       return -1;
     353              :    }
     354              : 
     355            0 :    derived().createStandardIndiRequestSw( m_indiP_stop, "stop");
     356            0 :    if( derived().registerIndiPropertyNew( m_indiP_stop, st_newCallBack_stdMotionStage) < 0)
     357              :    {
     358              :       #ifndef STDFILTERWHEEL_TEST_NOLOG
     359            0 :       derivedT::template log<software_error>({__FILE__,__LINE__});
     360              :       #endif
     361            0 :       return -1;
     362              :    }
     363              : 
     364            0 :    return 0;
     365            0 : }
     366              : 
     367              : template<class derivedT>
     368            0 : int stdMotionStage<derivedT>::appLogic()
     369              : {
     370            0 :    return 0;
     371              : 
     372              : }
     373              : 
     374              : template<class derivedT>
     375            0 : int stdMotionStage<derivedT>::onPowerOff()
     376              : {
     377            0 :    m_moving = -2;
     378            0 :    m_preset = 0;
     379            0 :    m_preset_target = 0;
     380              : 
     381            0 :    if( !derived().m_indiDriver ) return 0;
     382              : 
     383            0 :    return 0;
     384              : }
     385              : 
     386              : template<class derivedT>
     387            0 : int stdMotionStage<derivedT>::whilePowerOff()
     388              : {
     389            0 :    if( !derived().m_indiDriver ) return 0;
     390              : 
     391            0 :    return 0;
     392              : }
     393              : 
     394              : template<class derivedT>
     395              : int stdMotionStage<derivedT>::appShutdown()
     396              : {
     397              :    return 0;
     398              : }
     399              : 
     400              : 
     401              : template<class derivedT>
     402            0 : int stdMotionStage<derivedT>::st_newCallBack_stdMotionStage( void * app,
     403              :                                                              const pcf::IndiProperty &ipRecv
     404              :                                                            )
     405              : {
     406            0 :    std::string name = ipRecv.getName();
     407            0 :    derivedT * _app = static_cast<derivedT *>(app);
     408              : 
     409            0 :    if(name == "stop") return _app->newCallBack_m_indiP_stop(ipRecv); //Check this first to make sure it
     410            0 :    if(name == "home") return _app->newCallBack_m_indiP_home(ipRecv);
     411            0 :    if(name == _app->m_presetNotation) return _app->newCallBack_m_indiP_preset(ipRecv);
     412            0 :    if(name == _app->m_presetNotation + "Name") return _app->newCallBack_m_indiP_presetName (ipRecv);
     413              : 
     414            0 :    return -1;
     415            0 : }
     416              : 
     417              : template<class derivedT>
     418            6 : int stdMotionStage<derivedT>::newCallBack_m_indiP_preset ( const pcf::IndiProperty &ipRecv )
     419              : {
     420            6 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_preset, ipRecv);
     421              : 
     422              :     float target;
     423              : 
     424            0 :     if( derived().indiTargetUpdate( m_indiP_preset, target, ipRecv, true) < 0)
     425              :     {
     426            0 :         derivedT::template log<software_error>({__FILE__,__LINE__});
     427            0 :         return -1;
     428              :     }
     429              : 
     430            0 :     m_preset_target = target;
     431              : 
     432            0 :     std::lock_guard<std::mutex> guard(derived().m_indiMutex);
     433            0 :     m_movingState = 0; //this is not a preset move
     434            0 :     return derived().moveTo(target);
     435              : 
     436            0 : }
     437              : 
     438              : template<class derivedT>
     439            6 : int stdMotionStage<derivedT>::newCallBack_m_indiP_presetName( const pcf::IndiProperty &ipRecv )
     440              : {
     441            6 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_presetName, ipRecv);
     442              : 
     443            0 :    std::string newName = "";
     444            0 :    int newn = -1;
     445              : 
     446              :    size_t i;
     447            0 :    for(i=0; i< m_presetNames.size(); ++i)
     448              :    {
     449            0 :       if(!ipRecv.find(m_presetNames[i])) continue;
     450              : 
     451            0 :       if(ipRecv[m_presetNames[i]].getSwitchState() == pcf::IndiElement::On)
     452              :       {
     453            0 :          if(newName != "")
     454              :          {
     455            0 :             derivedT::template log<text_log>("More than one " + m_presetNotation + " selected", logPrio::LOG_ERROR);
     456            0 :             return -1;
     457              :          }
     458              : 
     459            0 :          newName = m_presetNames[i];
     460            0 :          newn = i;
     461              :       }
     462              :    }
     463              : 
     464            0 :    if(newName == "" || newn < 0)
     465              :    {
     466            0 :       return 0; //This is just an reset of current probably
     467              :    }
     468              : 
     469            0 :    std::lock_guard<std::mutex> guard(derived().m_indiMutex);
     470              : 
     471            0 :    m_preset_target = m_presetPositions[newn];
     472            0 :    derived().updateIfChanged(m_indiP_preset, "target",  m_preset_target, INDI_BUSY);
     473              : 
     474            0 :    m_movingState = 1; //This is a preset move
     475            0 :    return derived().moveTo(m_preset_target);
     476              : 
     477            0 : }
     478              : 
     479              : template<class derivedT>
     480            6 : int stdMotionStage<derivedT>::newCallBack_m_indiP_home( const pcf::IndiProperty &ipRecv )
     481              : {
     482            6 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_home, ipRecv);
     483              : 
     484            0 :    if(!ipRecv.find("request")) return 0;
     485              : 
     486            0 :    if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
     487              :    {
     488            0 :       indi::updateSwitchIfChanged(m_indiP_home, "request", pcf::IndiElement::On, derived().m_indiDriver, INDI_BUSY);
     489              : 
     490            0 :       std::lock_guard<std::mutex> guard(derived().m_indiMutex);
     491            0 :       m_movingState = 0;
     492            0 :       return derived().startHoming();
     493            0 :    }
     494            0 :    return 0;
     495              : }
     496              : 
     497              : template<class derivedT>
     498            6 : int stdMotionStage<derivedT>::newCallBack_m_indiP_stop( const pcf::IndiProperty &ipRecv )
     499              : {
     500            6 :     INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_stop, ipRecv);
     501              : 
     502            0 :    if(ipRecv.getName() != m_indiP_stop.getName())
     503              :    {
     504            0 :       derivedT::template log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
     505            0 :       return -1;
     506              :    }
     507              : 
     508            0 :    if(!ipRecv.find("request")) return 0;
     509              : 
     510            0 :    if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
     511              :    {
     512            0 :       indi::updateSwitchIfChanged(m_indiP_stop, "request", pcf::IndiElement::On, derived().m_indiDriver, INDI_BUSY);
     513              : 
     514              :       //-->do not lock mutex!
     515            0 :       m_movingState = 0;
     516            0 :       return derived().stop();
     517              :    }
     518            0 :    return 0;
     519              : }
     520              : 
     521              : template<class derivedT>
     522            0 : int stdMotionStage<derivedT>::updateINDI()
     523              : {
     524            0 :    if( !derived().m_indiDriver ) return 0;
     525              : 
     526            0 :    int n = derived().presetNumber();
     527              : 
     528            0 :    size_t nn = n;
     529              : 
     530              :    //Check for changes and update the filterNames
     531            0 :    bool changed = false;
     532              : 
     533              :    static int8_t last_moving = -1; //Initialize so we always update first time through.
     534              : 
     535            0 :    if(last_moving != m_moving)
     536              :    {
     537            0 :       changed = true;
     538            0 :       last_moving = m_moving;
     539              :    }
     540              : 
     541            0 :    for(size_t i =0; i < m_presetNames.size(); ++i)
     542              :    {
     543            0 :       if( i == nn )
     544              :       {
     545            0 :          if(m_indiP_presetName[m_presetNames[i]] != pcf::IndiElement::On)
     546              :          {
     547            0 :             changed = true;
     548            0 :             m_indiP_presetName[m_presetNames[i]] = pcf::IndiElement::On;
     549              :          }
     550              :       }
     551              :       else
     552              :       {
     553            0 :          if(m_indiP_presetName[m_presetNames[i]] != pcf::IndiElement::Off)
     554              :          {
     555            0 :             changed = true;
     556            0 :             m_indiP_presetName[m_presetNames[i]] = pcf::IndiElement::Off;
     557              :          }
     558              :       }
     559              :    }
     560            0 :    if(changed)
     561              :    {
     562            0 :       if(m_moving > 0)
     563              :       {
     564            0 :          m_indiP_presetName.setState(INDI_BUSY);
     565              :       }
     566              :       else
     567              :       {
     568            0 :          m_indiP_presetName.setState(INDI_IDLE);
     569              :       }
     570              : 
     571            0 :       m_indiP_presetName.setTimeStamp(pcf::TimeStamp());
     572            0 :       derived().m_indiDriver->sendSetProperty(m_indiP_presetName);
     573              :    }
     574              : 
     575              : 
     576              : 
     577            0 :    if(m_moving && m_movingState < 1)
     578              :    {
     579            0 :       indi::updateIfChanged(m_indiP_preset, "current", m_preset, derived().m_indiDriver,INDI_BUSY);
     580            0 :       indi::updateIfChanged(m_indiP_preset, "target", m_preset_target, derived().m_indiDriver,INDI_BUSY);
     581              :    }
     582              :    else
     583              :    {
     584            0 :       indi::updateIfChanged(m_indiP_preset, "current", m_preset, derived().m_indiDriver,INDI_IDLE);
     585            0 :       indi::updateIfChanged(m_indiP_preset, "target", m_preset_target, derived().m_indiDriver,INDI_IDLE);
     586              :    }
     587              : 
     588            0 :    return recordStage();
     589              : }
     590              : 
     591              : template<class derivedT>
     592            0 : int stdMotionStage<derivedT>::recordStage(bool force)
     593              : {
     594            0 :    static int8_t last_moving = m_moving + 100; //guarantee first run
     595              :    static float last_preset;
     596            0 :    static std::string last_presetName;
     597              : 
     598            0 :    size_t n = derived().presetNumber();
     599              : 
     600            0 :    std::string presetName;
     601            0 :    if(n < m_presetNames.size()) presetName = m_presetNames[n];
     602              : 
     603            0 :    if( m_moving != last_moving || m_preset != last_preset || presetName != last_presetName || force)
     604              :    {
     605            0 :       derived().template telem<telem_stage>({m_moving, m_preset, presetName});
     606            0 :       last_moving = m_moving;
     607            0 :       last_preset = m_preset;
     608            0 :       last_presetName = presetName;
     609              : 
     610              :    }
     611              : 
     612              : 
     613            0 :    return 0;
     614            0 : }
     615              : 
     616              : 
     617              : } //namespace dev
     618              : } //namespace app
     619              : } //namespace MagAOX
     620              : 
     621              : #endif //stdMotionStage_hpp
        

Generated by: LCOV version 2.0-1