LCOV - code coverage report
Current view: top level - libMagAOX/app - indiUtils.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 29.1 % 151 44
Test Date: 2026-04-15 19:34:29 Functions: 44.4 % 9 4

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

Generated by: LCOV version 2.0-1