LCOV - code coverage report
Current view: top level - libMagAOX/app - indiUtils.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 24.4 % 127 31
Test Date: 2026-01-03 21:03:39 Functions: 25.0 % 8 2

            Line data    Source code
       1              : /** \file indiUtils.hpp
       2              :  * \brief MagAO-X INDI Utilities
       3              :  * \author Jared R. Males (jaredmales@gmail.com)
       4              :  *
       5              :  * History:
       6              :  * - 2019-01-03 created by JRM
       7              :  *
       8              :  * \ingroup app_files
       9              :  */
      10              : 
      11              : #ifndef app_indiUtils_hpp
      12              : #define app_indiUtils_hpp
      13              : 
      14              : #include <iostream>
      15              : #include <limits>
      16              : 
      17              : #include "../../INDI/libcommon/IndiProperty.hpp"
      18              : #include "../../INDI/libcommon/IndiElement.hpp"
      19              : 
      20              : namespace MagAOX
      21              : {
      22              : namespace app
      23              : {
      24              : namespace indi
      25              : {
      26              : 
      27              : #define INDI_IDLE ( pcf::IndiProperty::Idle )
      28              : #define INDI_OK ( pcf::IndiProperty::Ok )
      29              : #define INDI_BUSY ( pcf::IndiProperty::Busy )
      30              : #define INDI_ALERT ( pcf::IndiProperty::Alert )
      31              : 
      32              : /// Add a standard INDI Text element
      33              : /**
      34              :  * \returns 0 on success
      35              :  * \returns -1 on error
      36              :  */
      37            0 : inline int addTextElement( pcf::IndiProperty &prop,      ///< [out] the property to which to add the elemtn
      38              :                            const std::string &name,      ///< [in] the name of the element
      39              :                            const std::string &label = "" ///< [in] [optional] the GUI label suggestion for this property
      40              : )
      41              : {
      42            0 :     prop.add( pcf::IndiElement( name, "" ) );
      43              : 
      44              :     // Don't set "" just in case libcommon does something with defaults
      45            0 :     if( label != "" )
      46              :     {
      47            0 :         prop[name].setLabel( label );
      48              :     }
      49              : 
      50            0 :     return 0;
      51              : }
      52              : 
      53              : /// Add a standard INDI Number element
      54              : /**
      55              :  * \returns 0 on success
      56              :  * \returns -1 on error
      57              :  */
      58              : template <typename T>
      59            0 : int addNumberElement( pcf::IndiProperty &prop,      ///< [out] the property to which to add the elemtn
      60              :                       const std::string &name,      ///< [in] the name of the element
      61              :                       const T           &min,       ///< [in] the minimum value for the element
      62              :                       const T           &max,       ///< [in] the minimum value for the element
      63              :                       const T           &step,      ///< [in] the step size of the lement
      64              :                       const std::string &format,    ///< [in] the _ value for the elements, applied to both target and
      65              :                                                     ///< current.  Set to "" to use the MagAO-X standard for type.
      66              :                       const std::string &label = "" ///< [in] [optional] the GUI label suggestion for this property
      67              : )
      68              : {
      69            0 :     prop.add( pcf::IndiElement( name, 0 ) );
      70            0 :     prop[name].setMin( min );
      71            0 :     prop[name].setMax( max );
      72            0 :     prop[name].setStep( step );
      73            0 :     prop[name].setFormat( format );
      74              : 
      75              :     // Don't set "" just in case libcommon does something with defaults
      76            0 :     if( label != "" )
      77              :     {
      78            0 :         prop[name].setLabel( label );
      79              :     }
      80              : 
      81            0 :     return 0;
      82              : }
      83              : 
      84              : /// Update the value of the INDI element, but only if it has changed.
      85              : /** Only sends the set property message if the new value is different.
      86              :  * For properties with more than one element that may have changed, you should use the vector version below.
      87              :  *
      88              :  * \todo this needs a const char specialization to std::string
      89              :  *
      90              :  */
      91              : template <typename T, class indiDriverT>
      92           16 : void updateIfChanged( pcf::IndiProperty &p,      ///< [in/out] The property containing the element to possibly update
      93              :                       const std::string &el,     ///< [in] The element name
      94              :                       const T           &newVal, ///< [in] the new value
      95              :                       indiDriverT       *indiDriver, ///< [in] the MagAOX INDI driver to use
      96              :                       pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
      97              : {
      98           16 :     if( !indiDriver )
      99            0 :         return;
     100              : 
     101              :     try
     102              :     {
     103              :         // This is same code in IndiElement
     104           16 :         std::stringstream ssValue;
     105           16 :         ssValue.precision( 15 );
     106           16 :         ssValue << std::boolalpha << newVal;
     107              : 
     108           16 :         pcf::IndiProperty::PropertyStateType oldState = p.getState();
     109              : 
     110              :         // Do comparison in string space, not raw value
     111           16 :         if( p[el].getValue() != ssValue.str() || oldState != newState )
     112              :         {
     113            9 :             p[el].set( newVal );
     114            9 :             p.setTimeStamp( pcf::TimeStamp() );
     115            9 :             p.setState( newState );
     116            9 :             indiDriver->sendSetProperty( p );
     117              :         }
     118           16 :     }
     119            0 :     catch( std::exception &e )
     120              :     {
     121            0 :         std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
     122            0 :         std::cerr << "from " << p.getName() << "." << el << ": ";
     123            0 :         std::cerr << e.what() << "\n";
     124              :     }
     125            0 :     catch( ... )
     126              :     {
     127            0 :         std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
     128            0 :         std::cerr << "from " << p.getName() << "." << el << "\n";
     129              :     }
     130              : }
     131              : 
     132              : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
     133              : /** Only sends the set property message if at least one of the new values is different, or if the state has changed.
     134              :  *
     135              :  *
     136              :  */
     137              : template <typename T, class indiDriverT>
     138            0 : void updateIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
     139              :                       const std::vector<std::string>      &els,        ///< [in] The element names
     140              :                       const std::vector<T>                &newVals,    ///< [in] the new values
     141              :                       indiDriverT                         *indiDriver, ///< [in] the MagAOX INDI driver to use
     142              :                       pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
     143              : {
     144            0 :     if( !indiDriver )
     145            0 :         return;
     146              : 
     147            0 :     size_t n = 0; // loop index outside so we can use it for error reporting.
     148              : 
     149              :     try
     150              :     {
     151              :         // First we look for any changes
     152            0 :         bool                                 changed  = false;
     153            0 :         pcf::IndiProperty::PropertyStateType oldState = p.getState();
     154              : 
     155            0 :         if( oldState != newState )
     156            0 :             changed = true;
     157              : 
     158            0 :         for( n = 0; n < els.size() && changed != true; ++n )
     159              :         {
     160              :             // This is same code in IndiElement
     161            0 :             std::stringstream ssValue;
     162            0 :             ssValue.precision( 15 );
     163            0 :             ssValue << std::boolalpha << newVals[n];
     164              : 
     165              :             // compare in string space
     166            0 :             if( p[els[n]].getValue() != ssValue.str() )
     167            0 :                 changed = true;
     168              :         }
     169              : 
     170              :         // and if there are changes, we send an update
     171            0 :         if( changed )
     172              :         {
     173            0 :             for( n = 0; n < els.size(); ++n )
     174              :             {
     175            0 :                 p[els[n]].set( newVals[n] );
     176              :             }
     177            0 :             p.setTimeStamp( pcf::TimeStamp() );
     178            0 :             p.setState( newState );
     179            0 :             indiDriver->sendSetProperty( p );
     180              :         }
     181              :     }
     182            0 :     catch( std::exception &e )
     183              :     {
     184            0 :         std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
     185            0 :         if( n < els.size() )
     186              :         {
     187            0 :             std::cerr << "from " << p.getName() << "." << els[n] << ": ";
     188              :         }
     189            0 :         std::cerr << e.what() << "\n";
     190              :     }
     191            0 :     catch( ... )
     192              :     {
     193            0 :         std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
     194            0 :         if( n < els.size() )
     195              :         {
     196            0 :             std::cerr << "from " << p.getName() << "." << els[n] << "\n";
     197              :         }
     198              :     }
     199              : }
     200              : 
     201              : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
     202              : /** Only sends the set property message if at least one of the new values is different, or if the state has changed.
     203              :  *
     204              :  * \tparam T the type of the values
     205              :  * \tparam elVecT the type of the element names, should be either std::string or const char *
     206              :  * \tparam indiDriverT the type of the INDI driver class
     207              :  */
     208              : template <typename T, typename elVecT, class indiDriverT>
     209            0 : void updatesIfChanged( pcf::IndiProperty    &p,   ///< [in/out] The property containing the element to possibly update
     210              :                        const elVecT         &els, ///< [in] The element names
     211              :                        const std::vector<T> &newVals,    ///< [in] the new values
     212              :                        indiDriverT          *indiDriver, ///< [in] the MagAOX INDI driver to use
     213              :                        pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
     214              : {
     215            0 :     if( !indiDriver )
     216            0 :         return;
     217              : 
     218            0 :     size_t n = 0; // loop index outside so we can use it for error reporting.
     219              : 
     220              :     try
     221              :     {
     222              :         // First we look for any changes
     223            0 :         bool                                 changed  = false;
     224            0 :         pcf::IndiProperty::PropertyStateType oldState = p.getState();
     225              : 
     226            0 :         if( oldState != newState )
     227            0 :             changed = true;
     228              : 
     229            0 :         for( n = 0; n < els.size() && changed != true; ++n )
     230              :         {
     231              :             // This is same code in IndiElement
     232            0 :             std::stringstream ssValue;
     233            0 :             ssValue.precision( 15 );
     234            0 :             ssValue << std::boolalpha << newVals[n];
     235              : 
     236              :             // compare in string space
     237            0 :             if( p[els[n]].getValue() != ssValue.str() )
     238            0 :                 changed = true;
     239              :         }
     240              : 
     241              :         // and if there are changes, we send an update
     242            0 :         if( changed )
     243              :         {
     244            0 :             for( n = 0; n < els.size(); ++n )
     245              :             {
     246            0 :                 p[els[n]].set( newVals[n] );
     247              :             }
     248            0 :             p.setTimeStamp( pcf::TimeStamp() );
     249            0 :             p.setState( newState );
     250            0 :             indiDriver->sendSetProperty( p );
     251              :         }
     252              :     }
     253            0 :     catch( std::exception &e )
     254              :     {
     255            0 :         std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
     256            0 :         if( n < els.size() )
     257              :         {
     258            0 :             std::cerr << "from " << p.getName() << "." << els[n] << ": ";
     259              :         }
     260            0 :         std::cerr << e.what() << "\n";
     261              :     }
     262            0 :     catch( ... )
     263              :     {
     264            0 :         std::cerr << "Exception caught at " << __FILE__ << " " << __LINE__ << " ";
     265            0 :         if( n < els.size() )
     266              :         {
     267            0 :             std::cerr << "from " << p.getName() << "." << els[n] << "\n";
     268              :         }
     269              :     }
     270              : }
     271              : 
     272              : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
     273              : /** Specialization for const char * element names.
     274              :  *
     275              :  * Only sends the set property message if at least one of the new values is different, or if the state has changed.
     276              :  *
     277              :  * \tparam T the type of the values
     278              :  * \tparam indiDriverT the type of the INDI driver class
     279              :  *
     280              :  */
     281              : template <typename T, class indiDriverT>
     282            0 : void updatesIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
     283              :                        const std::vector<const char *>     &els,        ///< [in] The element names
     284              :                        const std::vector<T>                &newVals,    ///< [in] the new values
     285              :                        indiDriverT                         *indiDriver, ///< [in] the MagAOX INDI driver to use
     286              :                        pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
     287              : {
     288            0 :     updatesIfChanged<T, std::vector<const char *>, indiDriverT>( p, els, newVals, indiDriver, newState );
     289            0 : }
     290              : 
     291              : /// Update the elements of an INDI propery, but only if there has been a change in at least one.
     292              : /** Specialization for std::string element names.
     293              :  *
     294              :  * Only sends the set property message if at least one of the new values is different, or if the state has changed.
     295              :  *
     296              :  * \tparam T the type of the values
     297              :  * \tparam indiDriverT the type of the INDI driver class
     298              :  *
     299              :  */
     300              : template <typename T, class indiDriverT>
     301              : void updatesIfChanged( pcf::IndiProperty &p, ///< [in/out] The property containing the element to possibly update
     302              :                        const std::vector<std::string>      &els,        ///< [in] The element names
     303              :                        const std::vector<T>                &newVals,    ///< [in] the new values
     304              :                        indiDriverT                         *indiDriver, ///< [in] the MagAOX INDI driver to use
     305              :                        pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
     306              : {
     307              :     updatesIfChanged<T, std::vector<std::string>, indiDriverT>( p, els, newVals, indiDriver, newState );
     308              : }
     309              : 
     310              : /// Update the value of the INDI element, but only if it has changed.
     311              : /** Only sends the set property message if the new value is different.
     312              :  *
     313              :  * \todo investigate how this handles floating point values and string conversions.
     314              :  * \todo this needs a const char specialization to std::string
     315              :  *
     316              :  */
     317              : template <class indiDriverT>
     318            0 : void updateSwitchIfChanged( pcf::IndiProperty &p,  ///< [in/out] The property containing the element to possibly update
     319              :                             const std::string &el, ///< [in] The element name
     320              :                             const pcf::IndiElement::SwitchStateType &newVal,     ///< [in] the new value
     321              :                             indiDriverT                             *indiDriver, ///< [in] the MagAOX INDI driver to use
     322              :                             pcf::IndiProperty::PropertyStateType     newState = pcf::IndiProperty::Ok )
     323              : {
     324            0 :     if( !indiDriver )
     325              :     {
     326            0 :         return;
     327              :     }
     328              : 
     329              :     try
     330              :     {
     331            0 :         pcf::IndiElement::SwitchStateType oldVal = p[el].getSwitchState();
     332              : 
     333            0 :         pcf::IndiProperty::PropertyStateType oldState = p.getState();
     334              : 
     335            0 :         if( oldVal != newVal || oldState != newState )
     336              :         {
     337            0 :             p[el].setSwitchState( newVal );
     338            0 :             p.setTimeStamp( pcf::TimeStamp() );
     339            0 :             p.setState( newState );
     340            0 :             indiDriver->sendSetProperty( p );
     341              :         }
     342              :     }
     343            0 :     catch( ... )
     344              :     {
     345            0 :         std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
     346            0 :         std::cerr << "from " << p.getName() << "." << el << "\n";
     347              :     }
     348              : }
     349              : 
     350              : /// Update the values of a one-of-many INDI switch vector, but only if it has changed.
     351              : /** Only sends the set property message if the new settings are different.
     352              :  *
     353              :  *
     354              :  */
     355              : template <class indiDriverT>
     356              : void updateSelectionSwitchIfChanged(
     357              :     pcf::IndiProperty                   &p,  ///< [in/out] The property containing the element to possibly update
     358              :     const std::string                   &el, ///< [in] The element name which is now on
     359              :     indiDriverT                         *indiDriver, ///< [in] the MagAOX INDI driver to use
     360              :     pcf::IndiProperty::PropertyStateType newState = pcf::IndiProperty::Ok )
     361              : {
     362              :     if( !indiDriver )
     363              :         return;
     364              : 
     365              :     if( !p.find( el ) )
     366              :     {
     367              :         std::cerr << "INDI error at " << __FILE__ << " " << __LINE__ << "\n";
     368              :         std::cerr << p.getName() << " does not have " << el << "\n";
     369              :         return;
     370              :     }
     371              : 
     372              :     try
     373              :     {
     374              : 
     375              :         bool changed = false;
     376              :         for( auto elit = p.getElements().begin(); elit != p.getElements().end(); ++elit )
     377              :         {
     378              :             if( elit->first == el )
     379              :             {
     380              :                 if( elit->second.getSwitchState() != pcf::IndiElement::On )
     381              :                 {
     382              :                     p[elit->first].setSwitchState( pcf::IndiElement::On );
     383              :                     changed = true;
     384              :                 }
     385              :             }
     386              :             else
     387              :             {
     388              :                 if( elit->second.getSwitchState() != pcf::IndiElement::Off )
     389              :                 {
     390              :                     p[elit->first].setSwitchState( pcf::IndiElement::Off );
     391              :                     changed = true;
     392              :                 }
     393              :             }
     394              :         }
     395              : 
     396              :         pcf::IndiProperty::PropertyStateType oldState = p.getState();
     397              : 
     398              :         if( changed || oldState != newState )
     399              :         {
     400              :             p.setState( newState );
     401              :             p.setTimeStamp( pcf::TimeStamp() );
     402              :             indiDriver->sendSetProperty( p );
     403              :         }
     404              :     }
     405              :     catch( ... )
     406              :     {
     407              :         std::cerr << "INDI Exception at " << __FILE__ << " " << __LINE__ << "\n";
     408              :         std::cerr << "from " << p.getName() << "." << el << "\n";
     409              :     }
     410              : }
     411              : 
     412              : /// Parse an INDI key into the device and property names
     413              : /** We often represent an INDI property as a unique key in the form
     414              :  * `deviceName.propName`.  This function parses such a key into its
     415              :  * parts.
     416              :  *
     417              :  * \returns 0  on success
     418              :  * \returns -1 if the provided key is not at least 3 characters long
     419              :  * \returns -2 if no '.' is found
     420              :  * \returns -3 if '.' is the first character
     421              :  * \returns -4 if '.' is the last character
     422              :  */
     423            6 : inline int parseIndiKey( std::string       &devName,  ///< [out] the device name
     424              :                          std::string       &propName, ///< [out] the property name
     425              :                          const std::string &key       ///< [in] the key to parse
     426              : )
     427              : {
     428            6 :     if( key.size() < 3 )
     429              :     {
     430            2 :         return -1;
     431              :     }
     432              : 
     433            4 :     size_t p = key.find( '.' );
     434              : 
     435            4 :     if( p == std::string::npos )
     436              :     {
     437            1 :         devName  = "";
     438            1 :         propName = "";
     439            1 :         return -2;
     440              :     }
     441              : 
     442            3 :     if( p == 0 )
     443              :     {
     444            1 :         devName  = "";
     445            1 :         propName = "";
     446            1 :         return -3;
     447              :     }
     448              : 
     449            2 :     if( p == key.size() - 1 )
     450              :     {
     451            1 :         devName  = "";
     452            1 :         propName = "";
     453            1 :         return -4;
     454              :     }
     455              : 
     456            1 :     devName  = key.substr( 0, p );
     457            1 :     propName = key.substr( p + 1 );
     458              : 
     459            1 :     return 0;
     460              : }
     461              : 
     462              : } // namespace indi
     463              : } // namespace app
     464              : } // namespace MagAOX
     465              : 
     466              : #endif // app_magAOXIndiDriver_hpp
        

Generated by: LCOV version 2.0-1