LCOV - code coverage report
Current view: top level - apps/xInstGraph/xigNodes - fsmNode.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 56.4 % 172 97
Test Date: 2026-01-03 21:03:39 Functions: 88.9 % 18 16

            Line data    Source code
       1              : /** \file fsmNode.hpp
       2              :  * \brief The MagAO-X Instrument Graph fsmNode header file
       3              :  *
       4              :  * \ingroup instGraph_files
       5              :  */
       6              : 
       7              : #ifndef fsmNode_hpp
       8              : #define fsmNode_hpp
       9              : 
      10              : #include "xigNode.hpp"
      11              : 
      12              : enum class fsmNodeActionT
      13              : {
      14              :     passive,   /**< Only monitor and report FSM state, don't change puts*/
      15              :     threshOff, /**< If state is not in one of the specified states, turn puts off*/
      16              :     active,    /**< If state is in one of the specified states, turn puts on. Turn them off otherwise.*/
      17              :     unknown    /**< Unknown action, generally an error. */
      18              : };
      19              : 
      20           15 : std::string fsmNodeActionT2String( fsmNodeActionT action )
      21              : {
      22           15 :     if( action == fsmNodeActionT::passive )
      23              :     {
      24           30 :         return "passive";
      25              :     }
      26            0 :     else if( action == fsmNodeActionT::threshOff )
      27              :     {
      28            0 :         return "threshOff";
      29              :     }
      30            0 :     else if( action == fsmNodeActionT::active )
      31              :     {
      32            0 :         return "active";
      33              :     }
      34              :     else
      35              :     {
      36            0 :         return "";
      37              :     }
      38              : }
      39              : 
      40           15 : fsmNodeActionT fsmNodeActionTFromString( const std::string &action )
      41              : {
      42           15 :     if( action == "passive" )
      43              :     {
      44           13 :         return fsmNodeActionT::passive;
      45              :     }
      46            2 :     else if( action == "threshOff" )
      47              :     {
      48            1 :         return fsmNodeActionT::threshOff;
      49              :     }
      50            1 :     else if( action == "active" )
      51              :     {
      52            1 :         return fsmNodeActionT::active;
      53              :     }
      54              :     else
      55              :     {
      56            0 :         return fsmNodeActionT::unknown;
      57              :     }
      58              : }
      59              : 
      60              : /// Implementation of an instGraph node interface for a MagAO-X Finite State Machine (FSM)
      61              : /** This class is interraces to a standard FSM.  It tracks the FSM state INDI property
      62              :  * and keeps its internal state updated.
      63              :  *
      64              :  * Whether it impacts ioput status depends on the `action` specified.
      65              :  *
      66              :  */
      67              : 
      68              : class fsmNode : public xigNode
      69              : {
      70              : 
      71              :     typedef MagAOX::app::stateCodes::stateCodeT stateCodeT;
      72              : 
      73              :   protected:
      74              :     std::string m_device;               ///< The INDI device name. Defaults to the node name set on construction.
      75              :     std::string m_fsmPropName{ "fsm" }; ///< The INDI property name for the FSM, normally "fsm".
      76              :     std::string m_fsmElName{ "state" }; ///< The INDI property element name for the FSM, normally "state".
      77              : 
      78              :     std::string m_fsmKey; ///< The unique INDI key, `<device>.<fsmPropName>`, for the FSM state INDI property.
      79              : 
      80              :     fsmNodeActionT m_fsmAction{ fsmNodeActionT::passive };
      81              : 
      82              :     std::vector<stateCodeT> m_targetStates;
      83              : 
      84              :     stateCodeT  m_state{ -999 }; ///< The numerical code of the current state.
      85              :     std::string m_stateStr;      ///< The string name of the current state.
      86              : 
      87              :     bool m_stateOnTarget{ false }; ///< Flag indicating if the current state matches any of the target states.
      88              : 
      89              :   public:
      90              :     /// Constructor.
      91              :     /**
      92              :      * Default c'tor is deleted in base classs.  Must supply both node name and a parentGraph with a node with the same
      93              :      * name in it.
      94              :      */
      95              :     fsmNode( const std::string  &name,       /**< [in] the name of the node */
      96              :              ingr::instGraphXML *parentGraph /**< [in] the parent instGraph */
      97              :     );
      98              : 
      99              :     /// Set the device name
     100              :     /**
     101              :      * Derived classes may implement this to add extra logic.  The device name defaults
     102              :      * to the node name on construction.
     103              :      */
     104              :     virtual void device( const std::string &dev /**< [in] the new device name */ );
     105              : 
     106              :     /// Get the device name
     107              :     /**
     108              :      * \return the current value of m_device
     109              :      */
     110              :     const std::string &device() const;
     111              : 
     112              :     /// Set the fsm property name
     113              :     /**
     114              :      * Derived classes may implement this to add extra logic.  The fsm property name defaults
     115              :      * to "fsm"
     116              :      *
     117              :      * This can only be called before device is set
     118              :      */
     119              :     virtual void fsmPropName( const std::string &pn /**< [in] the new property name */ );
     120              : 
     121              :     /// Get the fsm property name
     122              :     /**
     123              :      * \return the current value of m_fsmPropName
     124              :      */
     125              :     const std::string &fsmPropName() const;
     126              : 
     127              :     /// Set the fsm element name
     128              :     /**
     129              :      * Derived classes may implement this to add extra logic.  The fsm element name defaults
     130              :      * to "state"
     131              :      *
     132              :      * This can be called at any time
     133              :      */
     134              :     virtual void fsmElName( const std::string &en /**< [in] the new element name */ );
     135              : 
     136              :     /// Get the fsm element name
     137              :     /**
     138              :      * \return the current value of m_fsmElName
     139              :      */
     140              :     const std::string &fsmElName() const;
     141              : 
     142              :     /// Get the FSM unique key
     143              :     /**
     144              :      * \return the current value of m_fsmKey
     145              :      */
     146              :     const std::string &fsmKey() const;
     147              : 
     148              :     /// Get the action
     149              :     /**
     150              :      * \return the current value of m_fsmAction
     151              :      */
     152              :     fsmNodeActionT fsmAction() const;
     153              : 
     154              :     /// Set the action
     155              :     void fsmAction( fsmNodeActionT act );
     156              : 
     157              :     /// Get the target states
     158              :     /**
     159              :      * \return the current value of m_targetStates
     160              :      */
     161              :     const std::vector<stateCodeT> &targetStates() const;
     162              : 
     163              :     /// Load this specific node's settings from an application configuration
     164              :     /**
     165              :      * Verifies that the named node is an fsmNode.
     166              :      *
     167              :      * \throws std::runtime_error if m_parentGraph is nullptr or the config is not for an fsmNode.
     168              :      */
     169              :     void loadConfig( mx::app::appConfigurator &config /**< [in] the application configurator
     170              :                                                                 loaded with this node's options*/ );
     171              : 
     172              :   protected:
     173              :     /// Load this specific node's settings from an application configuration of a derived class
     174              :     /**
     175              :      * Does not cerifies that the named node is an fsmNode.
     176              :      *
     177              :      */
     178              :     void loadConfigDerived( mx::app::appConfigurator &config /**< [in] the application configurator
     179              :                                                                 loaded with this node's options*/ );
     180              : 
     181              :   public:
     182              :     /// INDI SetProperty callback
     183              :     virtual int handleSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the received INDI property to handle*/ );
     184              : 
     185              :     /// INDI SetProperty callback with indication if action was taken
     186              :     /** The possible actions are determined by m_fsmAction.  If the action was taken then the caller
     187              :      *  should return without further processing.
     188              :      *
     189              :      */
     190              :     virtual int handleSetProperty( bool &actionTaken, /** < [out] indicates if action taken (true). */
     191              :                                    const pcf::IndiProperty &ipRecv /**< [in] the received INDI property to handle*/ );
     192              : 
     193              :   public:
     194              :     virtual void updateGUI();
     195              : };
     196              : 
     197          173 : inline fsmNode::fsmNode( const std::string &name, ingr::instGraphXML *parentGraph ) : xigNode( name, parentGraph )
     198              : {
     199           34 : }
     200              : 
     201           22 : inline void fsmNode::device( const std::string &dev )
     202              : {
     203           22 :     if( m_device != "" && dev != m_device )
     204              :     {
     205            1 :         std::string msg = "fsmNode::device attempt to change device name from " + m_device + " to " + dev;
     206            1 :         msg += " in " + name();
     207            1 :         msg += " at ";
     208            1 :         msg += __FILE__;
     209            1 :         msg += " " + std::to_string( __LINE__ );
     210            1 :         throw std::runtime_error( msg );
     211            1 :     }
     212              : 
     213           21 :     if( dev == "" )
     214              :     {
     215            0 :         std::string msg = "fsmNode::device attempt to set empty device name in " + name();
     216            0 :         msg += " at ";
     217            0 :         msg += __FILE__;
     218            0 :         msg += " " + std::to_string( __LINE__ );
     219            0 :         throw std::runtime_error( msg );
     220            0 :     }
     221              : 
     222           21 :     m_device = dev;
     223           21 :     m_fsmKey = m_device + '.' + m_fsmPropName;
     224              : 
     225           21 :     key( m_fsmKey );
     226           21 : }
     227              : 
     228            5 : inline const std::string &fsmNode::device() const
     229              : {
     230            5 :     return m_device;
     231              : }
     232              : 
     233           15 : inline void fsmNode::fsmPropName( const std::string &pn )
     234              : {
     235           15 :     if( m_fsmPropName != "" && m_device != "" )
     236              :     {
     237            0 :         std::string msg = "fsmNode::fsmPropName attempt to change fsmPropName name from " + m_fsmPropName + " to " + pn;
     238            0 :         msg += " in " + name();
     239            0 :         msg += " at ";
     240            0 :         msg += __FILE__;
     241            0 :         msg += " " + std::to_string( __LINE__ );
     242            0 :         throw std::runtime_error( msg );
     243            0 :     }
     244              : 
     245           15 :     if( m_device != "" )
     246              :     {
     247            0 :         std::string msg = "fsmNode::fsmPropName attempt to set propName after device already set " + name();
     248            0 :         msg += " at ";
     249            0 :         msg += __FILE__;
     250            0 :         msg += " " + std::to_string( __LINE__ );
     251            0 :         throw std::runtime_error( msg );
     252            0 :     }
     253              : 
     254           15 :     if( pn == "" )
     255              :     {
     256            0 :         std::string msg = "fsmNode::fsmPropName attempt to set propName to empty " + name();
     257            0 :         msg += " at ";
     258            0 :         msg += __FILE__;
     259            0 :         msg += " " + std::to_string( __LINE__ );
     260            0 :         throw std::runtime_error( msg );
     261            0 :     }
     262              : 
     263           15 :     m_fsmPropName = pn;
     264           15 : }
     265              : 
     266           15 : inline const std::string &fsmNode::fsmPropName() const
     267              : {
     268           15 :     return m_fsmPropName;
     269              : }
     270              : 
     271           15 : inline void fsmNode::fsmElName( const std::string &en )
     272              : {
     273           15 :     if( en == "" )
     274              :     {
     275            0 :         std::string msg = "fsmNode::fsmElName attempt to set elName to empty " + name();
     276            0 :         msg += " at ";
     277            0 :         msg += __FILE__;
     278            0 :         msg += " " + std::to_string( __LINE__ );
     279            0 :         throw std::runtime_error( msg );
     280            0 :     }
     281              : 
     282           15 :     m_fsmElName = en;
     283           15 : }
     284              : 
     285           15 : inline const std::string &fsmNode::fsmElName() const
     286              : {
     287           15 :     return m_fsmElName;
     288              : }
     289              : 
     290            3 : const std::string &fsmNode::fsmKey() const
     291              : {
     292            3 :     return m_fsmKey;
     293              : }
     294              : 
     295            3 : fsmNodeActionT fsmNode::fsmAction() const
     296              : {
     297            3 :     return m_fsmAction;
     298              : }
     299              : 
     300            0 : void fsmNode::fsmAction( fsmNodeActionT act )
     301              : {
     302            0 :     m_fsmAction = act;
     303            0 : }
     304              : 
     305            6 : const std::vector<fsmNode::stateCodeT> &fsmNode::targetStates() const
     306              : {
     307            6 :     return m_targetStates;
     308              : }
     309              : 
     310            4 : inline void fsmNode::loadConfig( mx::app::appConfigurator &config )
     311              : {
     312            4 :     if( !m_parentGraph )
     313              :     {
     314            0 :         std::string msg = "fsmNode::loadConfig: parent graph is null";
     315            0 :         msg += " at ";
     316            0 :         msg += __FILE__;
     317            0 :         msg += " " + std::to_string( __LINE__ );
     318            0 :         throw std::runtime_error( msg );
     319            0 :     }
     320              : 
     321            4 :     std::string type;
     322            4 :     config.configUnused( type, mx::app::iniFile::makeKey( name(), "type" ) );
     323              : 
     324            4 :     if( type != "fsm" )
     325              :     {
     326            0 :         std::string msg = "fsmNode::loadConfig: node type is not fsmNode";
     327            0 :         msg += " at ";
     328            0 :         msg += __FILE__;
     329            0 :         msg += " " + std::to_string( __LINE__ );
     330            0 :         throw std::runtime_error( msg );
     331            0 :     }
     332              : 
     333            4 :     loadConfigDerived( config );
     334            4 : }
     335              : 
     336           15 : inline void fsmNode::loadConfigDerived( mx::app::appConfigurator &config )
     337              : {
     338              :     //This must be first
     339           15 :     std::string propName = fsmPropName();
     340           15 :     config.configUnused( propName, mx::app::iniFile::makeKey( name(), "fsmPropName" ) );
     341           15 :     fsmPropName( propName );
     342              : 
     343           15 :     std::string dev = name();
     344           15 :     config.configUnused( dev, mx::app::iniFile::makeKey( name(), "device" ) );
     345           15 :     device( dev );
     346              : 
     347           15 :     std::string elName = fsmElName();
     348           15 :     config.configUnused( elName, mx::app::iniFile::makeKey( name(), "fsmElName" ) );
     349           15 :     fsmElName( elName );
     350              : 
     351           15 :     std::string action = fsmNodeActionT2String( m_fsmAction );
     352           15 :     config.configUnused( action, mx::app::iniFile::makeKey( name(), "fsmAction" ) );
     353           15 :     m_fsmAction = fsmNodeActionTFromString( action );
     354              : 
     355           15 :     if( m_fsmAction == fsmNodeActionT::unknown )
     356              :     {
     357            0 :         std::string msg = XIGN_EXCEPTION( "fsmNode::loadConfig", "fsmAction is unknown" );
     358            0 :         throw std::runtime_error( msg );
     359            0 :     }
     360              : 
     361           15 :     std::vector<std::string> targetStates;
     362           15 :     config.configUnused( targetStates, mx::app::iniFile::makeKey( name(), "targetStates" ) );
     363           15 :     m_targetStates.resize( targetStates.size() );
     364           18 :     for( size_t n = 0; n < targetStates.size(); ++n )
     365              :     {
     366            3 :         m_targetStates[n] = MagAOX::app::stateCodes::str2Code( targetStates[n] );
     367              :     }
     368              : 
     369           15 :     if( m_parentGraph && m_node )
     370              :     {
     371           75 :         m_parentGraph->valueExtra( m_node->name(), "state", "" );
     372              :     }
     373           15 : }
     374              : 
     375           14 : inline int fsmNode::handleSetProperty( const pcf::IndiProperty &ipRecv )
     376              : {
     377              :     bool actionTaken;
     378           28 :     return handleSetProperty( actionTaken, ipRecv );
     379              : }
     380              : 
     381           22 : inline int fsmNode::handleSetProperty( bool &actionTaken, const pcf::IndiProperty &ipRecv )
     382              : {
     383           22 :     if( ipRecv.createUniqueKey() != m_fsmKey )
     384              :     {
     385           19 :         actionTaken = false;
     386           19 :         return 0;
     387              :     }
     388              : 
     389            3 :     if( !ipRecv.find( m_fsmElName ) )
     390              :     {
     391            0 :         actionTaken = false;
     392            0 :         return 0;
     393              :     }
     394              : 
     395            3 :     m_stateStr = ipRecv[m_fsmElName].get<std::string>();
     396              : 
     397            3 :     MagAOX::app::stateCodes::stateCodeT state = MagAOX::app::stateCodes::str2CodeFast( m_stateStr );
     398              : 
     399            3 :     if( state != m_state )
     400              :     {
     401            3 :         ++m_changes;
     402              :     }
     403              : 
     404            3 :     m_state = state;
     405              : 
     406            6 :     m_parentGraph->valueExtra( m_node->name(), "fsmstate", m_stateStr );
     407              : 
     408            3 :     bool stateOnTarget = false;
     409              : 
     410            3 :     for( auto state : m_targetStates )
     411              :     {
     412            0 :         if( m_state == state )
     413              :         {
     414            0 :             stateOnTarget = true;
     415            0 :             break;
     416              :         }
     417              :     }
     418            3 :     m_stateOnTarget = stateOnTarget;
     419              : 
     420            3 :     if( m_fsmAction == fsmNodeActionT::threshOff )
     421              :     {
     422            0 :         if( m_stateOnTarget )
     423              :         {
     424            0 :             actionTaken = false;
     425            0 :             return 0;
     426              :         }
     427              :         else
     428              :         {
     429            0 :             togglePutsOff();
     430            0 :             actionTaken = true;
     431            0 :             return 0;
     432              :         }
     433              :     }
     434            3 :     else if( m_fsmAction == fsmNodeActionT::active )
     435              :     {
     436            0 :         if( m_stateOnTarget )
     437              :         {
     438            0 :             togglePutsOn();
     439            0 :             actionTaken = true;
     440            0 :             return 0;
     441              :         }
     442              :         else
     443              :         {
     444            0 :             togglePutsOff();
     445            0 :             actionTaken = true;
     446            0 :             return 0;
     447              :         }
     448              :     }
     449              :     else // m_fsmAction == fsmNodeActionT::passive
     450              :     {
     451            3 :         actionTaken = false;
     452            3 :         return 0;
     453              :     }
     454              : }
     455              : 
     456            0 : inline void fsmNode::updateGUI()
     457              : {
     458            0 : }
     459              : 
     460              : #endif // fsmNode_hpp
        

Generated by: LCOV version 2.0-1