LCOV - code coverage report
Current view: top level - libMagAOX/app/dev - stdMotionStage.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 19.1 % 204 39
Test Date: 2026-04-15 19:34:29 Functions: 60.0 % 20 12

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

Generated by: LCOV version 2.0-1