LCOV - code coverage report
Current view: top level - apps/xInstGraph/xigNodes - indiPropNode.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 87.0 % 154 134
Test Date: 2026-01-03 21:03:39 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /** \file indiPropNode.hpp
       2              :  * \brief The MagAO-X Instrument Graph indiPropNode header file
       3              :  *
       4              :  * \ingroup instGraph_files
       5              :  */
       6              : 
       7              : #ifndef indiPropNode_hpp
       8              : #define indiPropNode_hpp
       9              : 
      10              : #include "fsmNode.hpp"
      11              : 
      12              : /// An instGraph node which tracks a specific INDI property and element of that property.
      13              : /** When the element matches the target value all puts are turned on.  All puts are off
      14              :  *  otherwise.
      15              :  */
      16              : class indiPropNode : public fsmNode
      17              : {
      18              : 
      19              :   protected:
      20              :     std::string m_propKey; ///< unique key, device.name, of the property tp track
      21              :     std::string m_propEl;  ///< the element of the property to track
      22              : 
      23              :     std::string m_propValStr; ///< the target value of the element. This is always set.
      24              : 
      25              :     double m_propValNum{ std::numeric_limits<double>::lowest() }; /**< the numeric target value, set from
      26              :                                                                        m_propValStr if the property is a number.*/
      27              : 
      28              :     /** The switch target value, set from m_propValStr if the property is a switch.  In this case
      29              :      *  m_propValStr can have values `On` or `Off`.  The comparison is made insensitive to case (ON and off
      30              :      *  are valid).
      31              :      */
      32              :     pcf::IndiElement::SwitchStateType m_propValSw{ pcf::IndiElement::SwitchStateType::UnknownSwitchState };
      33              : 
      34              :     /// The property type.  Discovered introspectively on first call to \ref handleSetProperty.
      35              :     pcf::IndiProperty::Type m_type{ pcf::IndiProperty::Unknown };
      36              : 
      37              :     double m_tol{ 1e-7 }; ///< The tolerance for floating point comparison.  Default is 1e-7.
      38              : 
      39              :     bool m_state{ false }; ///< The current state of the comparison.
      40              : 
      41              :     bool m_first{ true }; ///< Flag indicating if it's the first call to \ref handleSetProperty
      42              : 
      43              :     std::string m_onStr {"ON"};
      44              :     std::string m_offStr {"OFF"};
      45              : 
      46              :   public:
      47              :     /// Only c'tor.  Must be constructed with node name and a parent graph.
      48              :     indiPropNode( const std::string  &name,       /** [in] the name of this node*/
      49              :                   ingr::instGraphXML *parentGraph /** [in] the graph which this node belongs to*/
      50              :     );
      51              : 
      52              :     /// Set the unique key of the INDI property to track
      53              :     void propKey( const std::string &pk /** [in] */ );
      54              : 
      55              :     /// Get the unique key of the INDI property to track
      56              :     /**
      57              :      * \returns the value of m_propKey
      58              :      */
      59              :     const std::string &propKey() const;
      60              : 
      61              :     /// Set the element of the INDI property to track
      62              :     void propEl( const std::string &pe /** [in] */ );
      63              : 
      64              :     /// Get the element of the INDI property to track
      65              :     /**
      66              :      * \returns the value of m_propEl
      67              :      */
      68              :     const std::string &propEl() const;
      69              : 
      70              :     /// Set the target value of the INDI element.
      71              :     /** Always set in its string form and converted as needed
      72              :      */
      73              :     void propValStr( const std::string &pv /** [in] */ );
      74              : 
      75              :     /// Get the target value of the INDI element.
      76              :     /**
      77              :      * \returns the value of m_propValStr
      78              :      */
      79              :     const std::string &propValStr() const;
      80              : 
      81              :     /// Get the target value of the INDI element if it's a number.
      82              :     /**
      83              :      * \returns the value of m_propValNum
      84              :      */
      85              :     const double &propValNum() const;
      86              : 
      87              :     /// Get the target value of the INDI element if it's a switch.
      88              :     /**
      89              :      * \returns the value of m_propValSw
      90              :      */
      91              :     const pcf::IndiElement::SwitchStateType &propValSw();
      92              : 
      93              :     /// Get the type of the INDI property being tracked
      94              :     /**
      95              :      * \returns the value of m_type
      96              :      */
      97              :     const pcf::IndiProperty::Type &type() const;
      98              : 
      99              :     /// Get the tolerance used for numeric comparison
     100              :     /**
     101              :      * \returns the value of m_tol
     102              :      */
     103              :     const double &tol() const;
     104              : 
     105              :     /// Get the current value of the comparison
     106              :     /**
     107              :      * \returns the value of m_state
     108              :      */
     109              :     const bool &state() const;
     110              : 
     111              :   protected:
     112              :     /// On first call to handleSetProperty we find the property type and convert the target value
     113              :     virtual int firstSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the received INDI property*/ );
     114              : 
     115              :   public:
     116              :     /// INDI SetProperty callback
     117              :     virtual int handleSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the received INDI property to handle*/ );
     118              : 
     119              :     /// Toggle all puts on
     120              :     virtual void toggleOn();
     121              : 
     122              :     /// Toggle all puts off
     123              :     virtual void toggleOff();
     124              : 
     125              :     /// Configure this node form an appConfigurator.
     126              :     void loadConfig( mx::app::appConfigurator &config /**< [in] the loaded configuration */ );
     127              : };
     128              : 
     129           50 : indiPropNode::indiPropNode( const std::string &name, ingr::instGraphXML *parentGraph ) : fsmNode( name, parentGraph )
     130              : {
     131           12 :     if( m_parentGraph )
     132              :     {
     133           60 :         m_parentGraph->valueExtra( m_node->name(), "fsmstate", "" );
     134              :     }
     135           12 : }
     136              : 
     137            8 : inline void indiPropNode::propKey( const std::string &pk )
     138              : {
     139            8 :     m_propKey = pk;
     140              : 
     141            8 :     key( m_propKey );
     142            8 : }
     143              : 
     144            2 : const std::string &indiPropNode::propKey() const
     145              : {
     146            2 :     return m_propKey;
     147              : }
     148              : 
     149              : inline void indiPropNode::propEl( const std::string &pe )
     150              : {
     151              :     m_propEl = pe;
     152              : }
     153              : 
     154            2 : const std::string &indiPropNode::propEl() const
     155              : {
     156            2 :     return m_propEl;
     157              : }
     158              : 
     159              : inline void indiPropNode::propValStr( const std::string &pv )
     160              : {
     161              :     m_propValStr = pv;
     162              : }
     163              : 
     164            2 : const std::string &indiPropNode::propValStr() const
     165              : {
     166            2 :     return m_propValStr;
     167              : }
     168              : 
     169            3 : const double &indiPropNode::propValNum() const
     170              : {
     171            3 :     return m_propValNum;
     172              : }
     173              : 
     174            3 : const pcf::IndiElement::SwitchStateType &indiPropNode::propValSw()
     175              : {
     176            3 :     return m_propValSw;
     177              : }
     178              : 
     179            5 : const pcf::IndiProperty::Type &indiPropNode::type() const
     180              : {
     181            5 :     return m_type;
     182              : }
     183              : 
     184            2 : const double &indiPropNode::tol() const
     185              : {
     186            2 :     return m_tol;
     187              : }
     188              : 
     189            8 : const bool &indiPropNode::state() const
     190              : {
     191            8 :     return m_state;
     192              : }
     193              : 
     194            5 : inline int indiPropNode::firstSetProperty( const pcf::IndiProperty &ipRecv )
     195              : {
     196              :     // On first call we figure what type it is and convert the value
     197              : 
     198            5 :     if( ipRecv.getType() == pcf::IndiProperty::Type::Number )
     199              :     {
     200            2 :         m_type = pcf::IndiProperty::Type::Number;
     201              :         try
     202              :         {
     203            2 :             m_propValNum = std::stod( m_propValStr );
     204              :         }
     205            1 :         catch( const std::exception &e )
     206              :         {
     207            5 :             std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "exception caught" );
     208            1 :             msg += ": ";
     209            1 :             msg += e.what();
     210              : 
     211            1 :             throw std::runtime_error( msg );
     212            2 :         }
     213              :     }
     214            3 :     else if( ipRecv.getType() == pcf::IndiProperty::Type::Switch )
     215              :     {
     216            2 :         m_type = pcf::IndiProperty::Type::Switch;
     217              :         try
     218              :         {
     219            2 :             std::string ustr = m_propValStr;
     220            2 :             std::transform( m_propValStr.begin(), m_propValStr.end(), ustr.begin(), ::toupper );
     221              : 
     222            2 :             if( ustr == "ON" )
     223              :             {
     224            1 :                 m_propValSw = pcf::IndiElement::SwitchStateType::On;
     225              :             }
     226            1 :             else if( ustr == "OFF" )
     227              :             {
     228            0 :                 m_propValSw = pcf::IndiElement::SwitchStateType::Off;
     229              :             }
     230              :             else
     231              :             {
     232            5 :                 std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "invalid switch state" );
     233            1 :                 throw std::invalid_argument( msg );
     234            1 :             }
     235            2 :         }
     236            1 :         catch( const std::exception &e )
     237              :         {
     238            5 :             std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "exception caught" );
     239            1 :             msg += ": ";
     240            1 :             msg += e.what();
     241              : 
     242            1 :             throw std::runtime_error( msg );
     243            2 :         }
     244              :     }
     245            1 :     else if( ipRecv.getType() == pcf::IndiProperty::Type::Text )
     246              :     {
     247            1 :         m_type = pcf::IndiProperty::Type::Text;
     248              :     }
     249              :     else
     250              :     {
     251            0 :         std::string msg = XIGN_EXCEPTION( "indiPropNode::firstSetProperty", "INDI property of type not implemented" );
     252            0 :         throw std::runtime_error( msg );
     253            0 :     }
     254              : 
     255            3 :     return 0;
     256              : }
     257              : 
     258            8 : inline int indiPropNode::handleSetProperty( const pcf::IndiProperty &ipRecv )
     259              : {
     260            8 :     bool actionTaken = false;
     261              : 
     262            8 :     int rv = fsmNode::handleSetProperty( actionTaken, ipRecv );
     263            8 :     if( rv < 0 )
     264              :     {
     265            0 :         std::cerr << "Error from fsmNode::handleSetProperty\n";
     266            0 :         return rv;
     267              :     }
     268              : 
     269            8 :     if( actionTaken )
     270              :     {
     271            0 :         return 0;
     272              :     }
     273              : 
     274            8 :     if( ipRecv.createUniqueKey() != m_propKey )
     275              :     {
     276            0 :         return 0;
     277              :     }
     278              : 
     279            8 :     if( !ipRecv.find( m_propEl ) )
     280              :     {
     281            0 :         std::cerr << "!ipRecv.find( m_propEl )\n";
     282            0 :         return -1;
     283              :     }
     284              : 
     285            8 :     if( m_first )
     286              :     {
     287              :         try
     288              :         {
     289            5 :             int rv = firstSetProperty( ipRecv );
     290            3 :             if(rv < 0)
     291              :             {
     292            0 :                 std::cerr << "Error from firstSetProperty\n";
     293            0 :                 return rv;
     294              :             }
     295              :         }
     296            2 :         catch( const std::exception &e )
     297              :         {
     298           10 :             std::string msg = XIGN_EXCEPTION( "indiPropNode::handleSetProperty", "exception caught" );
     299            2 :             msg += ": ";
     300            2 :             msg += e.what();
     301              : 
     302            2 :             throw std::runtime_error( msg );
     303            4 :         }
     304              :     }
     305              : 
     306            6 :     bool on = false;
     307              : 
     308            6 :     if( m_type == pcf::IndiProperty::Type::Number )
     309              :     {
     310            2 :         if( fabs( ipRecv[m_propEl].get<double>() - m_propValNum ) <= m_tol )
     311              :         {
     312            1 :             on = true;
     313              :         }
     314              :     }
     315            4 :     else if( m_type == pcf::IndiProperty::Type::Switch )
     316              :     {
     317            2 :         if( ipRecv[m_propEl].getSwitchState() == m_propValSw )
     318              :         {
     319            1 :             on = true;
     320              :         }
     321              :     }
     322            2 :     else if( m_type == pcf::IndiProperty::Type::Text )
     323              :     {
     324            2 :         if( ipRecv[m_propEl].get() == m_propValStr )
     325              :         {
     326            1 :             on = true;
     327              :         }
     328              :     }
     329              :     else
     330              :     {
     331            0 :         std::string msg = XIGN_EXCEPTION( "indiPropNode::handleSetProperty", "type not implemented" );
     332            0 :         throw std::runtime_error( msg );
     333            0 :     }
     334              : 
     335            6 :     if( m_first )
     336              :     {
     337              :         // trigger the change on the first run.
     338            3 :         if( on )
     339              :         {
     340            3 :             m_state = false;
     341              :         }
     342              :         else
     343              :         {
     344            0 :             m_state = true;
     345              :         }
     346              : 
     347              :         // made this it's own branch so we don't do this every time:
     348            3 :         m_first = false;
     349              :     }
     350              : 
     351            6 :     if( on != m_state )
     352              :     {
     353            6 :         ++m_changes;
     354            6 :         m_state = on;
     355            6 :         if( on )
     356              :         {
     357            3 :             toggleOn();
     358            6 :             m_parentGraph->valueExtra( m_node->name(), "state", m_onStr );
     359            3 :             return 0;
     360              :         }
     361              :         else
     362              :         {
     363            3 :             toggleOff();
     364            6 :             m_parentGraph->valueExtra( m_node->name(), "state", m_offStr );
     365            3 :             return 0;
     366              :         }
     367              :     }
     368              : 
     369            0 :     return 0;
     370              : }
     371              : 
     372            3 : inline void indiPropNode::toggleOn()
     373              : {
     374            3 :     togglePutsOn();
     375            3 : }
     376              : 
     377            3 : inline void indiPropNode::toggleOff()
     378              : {
     379            3 :     togglePutsOff();
     380            3 : }
     381              : 
     382           12 : inline void indiPropNode::loadConfig( mx::app::appConfigurator &config )
     383              : {
     384           12 :     if( !m_parentGraph )
     385              :     {
     386            0 :         std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "parent graph is null" );
     387            0 :         throw std::runtime_error( msg );
     388            0 :     }
     389              : 
     390           12 :     std::string type;
     391           12 :     config.configUnused( type, mx::app::iniFile::makeKey( name(), "type" ) );
     392              : 
     393           12 :     if( type != "indiProp" )
     394              :     {
     395            5 :         std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "node type is not indiProp" );
     396            1 :         throw std::runtime_error( msg );
     397            1 :     }
     398              : 
     399           11 :     fsmNode::loadConfigDerived( config );
     400              : 
     401           11 :     std::string pk;
     402           11 :     config.configUnused( pk, mx::app::iniFile::makeKey( name(), "propKey" ) );
     403              : 
     404           11 :     if( pk == "" )
     405              :     {
     406            5 :         std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "propKey can not be empty" );
     407            1 :         throw std::runtime_error( msg );
     408            1 :     }
     409              : 
     410           10 :     std::string pe;
     411           10 :     config.configUnused( pe, mx::app::iniFile::makeKey( name(), "propEl" ) );
     412              : 
     413           10 :     if( pe == "" )
     414              :     {
     415            5 :         std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "propEl can not be empty" );
     416            1 :         throw std::runtime_error( msg );
     417            1 :     }
     418              : 
     419            9 :     std::string pv;
     420            9 :     config.configUnused( pv, mx::app::iniFile::makeKey( name(), "propVal" ) );
     421              : 
     422            9 :     if( pv == "" )
     423              :     {
     424            5 :         std::string msg = XIGN_EXCEPTION( "indiPropNode::loadConfig", "propVal can not be empty" );
     425            1 :         throw std::runtime_error( msg );
     426            1 :     }
     427              : 
     428            8 :     config.configUnused( m_tol, mx::app::iniFile::makeKey( name(), "tol" ) );
     429              : 
     430              :     // Add propEl and propVal
     431            8 :     propKey( pk );
     432            8 :     m_propEl     = pe;
     433            8 :     m_propValStr = pv;
     434              : 
     435           16 :     config.configUnused( m_onStr, mx::app::iniFile::makeKey( name(), "onStr" ) );
     436            8 :     config.configUnused( m_offStr, mx::app::iniFile::makeKey( name(), "offStr" ) );
     437           18 : }
     438              : 
     439              : #endif // indiPropNode_hpp
        

Generated by: LCOV version 2.0-1