LCOV - code coverage report
Current view: top level - apps/stateRuleEngine - stateRuleEngine.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 0.0 % 115 0
Test Date: 2026-01-03 21:03:39 Functions: 0.0 % 10 0

            Line data    Source code
       1              : /** \file stateRuleEngine.hpp
       2              :   * \brief The MagAO-X stateRuleEngine application header file
       3              :   *
       4              :   * \ingroup stateRuleEngine_files
       5              :   */
       6              : 
       7              : #ifndef stateRuleEngine_hpp
       8              : #define stateRuleEngine_hpp
       9              : 
      10              : 
      11              : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
      12              : #include "../../magaox_git_version.h"
      13              : 
      14              : #include "indiCompRuleConfig.hpp"
      15              : 
      16              : /** \defgroup stateRuleEngine
      17              :   * \brief The MagAO-X stateRuleEngine application
      18              :   *
      19              :   * <a href="../handbook/operating/software/apps/stateRuleEngine.html">Application Documentation</a>
      20              :   *
      21              :   * \ingroup apps
      22              :   *
      23              :   */
      24              : 
      25              : /** \defgroup stateRuleEngine_files
      26              :   * \ingroup stateRuleEngine
      27              :   */
      28              : 
      29              : namespace MagAOX
      30              : {
      31              : namespace app
      32              : {
      33              : 
      34              : /// The MagAO-X stateRuleEngine
      35              : /**
      36              :   * \ingroup stateRuleEngine
      37              :   */
      38              : class stateRuleEngine : public MagAOXApp<true>
      39              : {
      40              : 
      41              :     //Give the test harness access.
      42              :     friend class stateRuleEngine_test;
      43              : 
      44              : protected:
      45              : 
      46              :     /** \name Configurable Parameters
      47              :       *@{
      48              :       */
      49              : 
      50              :      std::string m_ruleDir; /**< Directory containing config files containing rules to load. Relative to config directory.  If this is
      51              :                                  set, then rules in the device config file are ignored*/
      52              : 
      53              :     indiRuleMaps m_ruleMaps;
      54              : 
      55              :     ///@}
      56              : 
      57              : public:
      58              :     /// Default c'tor.
      59              :     stateRuleEngine();
      60              : 
      61              :     /// D'tor, declared and defined for noexcept.
      62            0 :     ~stateRuleEngine() noexcept
      63            0 :     {}
      64              : 
      65              :     virtual void setupConfig();
      66              : 
      67              :     /// Implementation of loadConfig logic, separated for testing.
      68              :     /** This is called by loadConfig().
      69              :       */
      70              :     int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
      71              : 
      72              :     virtual void loadConfig();
      73              : 
      74              :     /// Startup function
      75              :     /**
      76              :       *
      77              :       */
      78              :     virtual int appStartup();
      79              : 
      80              :     /// Implementation of the FSM for stateRuleEngine.
      81              :     /**
      82              :       * \returns 0 on no critical error
      83              :       * \returns -1 on an error requiring shutdown
      84              :       */
      85              :     virtual int appLogic();
      86              : 
      87              :     /// Shutdown the app.
      88              :     /**
      89              :       *
      90              :       */
      91              :     virtual int appShutdown();
      92              : 
      93              : 
      94              :     /// The static callback function to be registered for rule properties
      95              :     /**
      96              :       *
      97              :       * \returns 0 on success.
      98              :       * \returns -1 on error.
      99              :       */
     100              :     static int st_newCallBack_ruleProp( void * app,                     ///< [in] a pointer to this, will be static_cast-ed to derivedT.
     101              :                                         const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
     102              :                                       );
     103              : 
     104              :     /// Callback to process a NEW preset position request
     105              :     /**
     106              :       * \returns 0 on success.
     107              :       * \returns -1 on error.
     108              :       */
     109              :     int newCallBack_ruleProp( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
     110              : 
     111              : 
     112              :     pcf::IndiProperty m_indiP_info;
     113              :     pcf::IndiProperty m_indiP_caution;
     114              :     pcf::IndiProperty m_indiP_warning;
     115              :     pcf::IndiProperty m_indiP_alert;
     116              : 
     117              : };
     118              : 
     119            0 : stateRuleEngine::stateRuleEngine() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
     120              : {
     121            0 :     return;
     122            0 : }
     123              : 
     124            0 : void stateRuleEngine::setupConfig()
     125              : {
     126            0 :     config.add( "rules.dir",
     127              :                 "",
     128              :                 "rules.dir",
     129              :                 argType::Required,
     130              :                 "rules",
     131              :                 "dir",
     132              :                 false,
     133              :                 "string",
     134              :                 "Directory containing config files containing rules to load. Relative to config directory.  If this is "
     135              :                 "set, then rules in the device config file are ignored" );
     136            0 : }
     137              : 
     138            0 : int stateRuleEngine::loadConfigImpl( mx::app::appConfigurator & _config )
     139              : {
     140            0 :     _config(m_ruleDir, "rules.dir");
     141              : 
     142            0 :     std::map<std::string, ruleRuleKeys> rrkMap;
     143              : 
     144            0 :     if(m_ruleDir == "")
     145              :     {
     146              :         try
     147              :         {
     148            0 :             loadRuleConfig(m_ruleMaps, rrkMap, _config);
     149            0 :             finalizeRuleValRules(m_ruleMaps, rrkMap);
     150              :         }
     151            0 :         catch(mx::err::mxException & e)
     152              :         {
     153            0 :             return log<software_critical,-1>({__FILE__,__LINE__, std::string("Rule config exception caught:\n") + e.what()});
     154            0 :         }
     155              :     }
     156              :     else
     157              :     {
     158            0 :         std::vector<std::string> conffiles;
     159            0 :         if(mx::ioutils::getFileNames(conffiles, m_configDir + "/" + m_ruleDir, "", "", ".conf") != mx::error_t::noerror)
     160              :         {
     161            0 :             return log<software_critical,-1>({__FILE__,__LINE__, "Error reading rules"});
     162              :         }
     163              : 
     164            0 :         for(auto & cnf : conffiles)
     165              :         {
     166              :             //Create a configurator and set it up to log
     167            0 :             mx::app::appConfigurator fcfg;
     168              : 
     169            0 :             fcfg.m_sources = true;
     170            0 :             fcfg.configLog = configLog;
     171              : 
     172              :             //now process the config file
     173            0 :             if( fcfg.readConfig(cnf) < 0 )
     174              :             {
     175            0 :                 return log<software_critical,-1>({__FILE__,__LINE__, "error reading rule config file: " + cnf});
     176              :             }
     177              : 
     178              :             try
     179              :             {
     180              :                 //and finally add to our rule map
     181            0 :                 loadRuleConfig(m_ruleMaps, rrkMap, fcfg);
     182              :             }
     183            0 :             catch(mx::err::mxException & e)
     184              :             {
     185            0 :                 return log<software_critical,-1>({__FILE__,__LINE__, std::string("Rule config exception caught from ") + cnf + ":\n" + e.what()});
     186            0 :             }
     187            0 :         }
     188              : 
     189              :         try
     190              :         {
     191            0 :             finalizeRuleValRules(m_ruleMaps, rrkMap);
     192              :         }
     193            0 :         catch(const std::exception& e)
     194              :         {
     195            0 :             return log<software_critical,-1>({__FILE__,__LINE__, std::string("Error finalizing rules:\n") + e.what()});
     196            0 :         }
     197              : 
     198            0 :     }
     199              : 
     200            0 :     return 0;
     201            0 : }
     202              : 
     203            0 : void stateRuleEngine::loadConfig()
     204              : {
     205            0 :     if(loadConfigImpl(config) < 0)
     206              :     {
     207            0 :         log<software_critical>({__FILE__,__LINE__,"error in configuration"});
     208            0 :         m_shutdown = true;
     209              :     }
     210            0 : }
     211              : 
     212            0 : int stateRuleEngine::appStartup()
     213              : {
     214            0 :     for(auto it = m_ruleMaps.rules.begin(); it != m_ruleMaps.rules.end(); ++it)
     215              :     {
     216            0 :         if(it->second->priority() == rulePriority::info)
     217              :         {
     218            0 :             if(m_indiP_info.getDevice() != m_configName)
     219              :             {
     220            0 :                 if(registerIndiPropertyNew( m_indiP_info, "info", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
     221            0 :                                                                 pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
     222              :                 {
     223            0 :                     return log<software_critical,-1>({__FILE__, __LINE__});
     224              :                 }
     225              :             }
     226              : 
     227            0 :             m_indiP_info.add(pcf::IndiElement(it->first, pcf::IndiElement::Off));
     228            0 :             m_indiP_info[it->first].setLabel(it->second->message());
     229              :         }
     230              : 
     231            0 :         if(it->second->priority() == rulePriority::caution)
     232              :         {
     233            0 :             if(m_indiP_caution.getDevice() != m_configName)
     234              :             {
     235            0 :                 if(registerIndiPropertyNew( m_indiP_caution, "caution", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
     236            0 :                                                                 pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
     237              :                 {
     238            0 :                     return log<software_critical,-1>({__FILE__, __LINE__});
     239              :                 }
     240              :             }
     241              : 
     242            0 :             m_indiP_caution.add(pcf::IndiElement(it->first, pcf::IndiElement::Off));
     243            0 :             m_indiP_caution[it->first].setLabel(it->second->message());
     244              :         }
     245              : 
     246            0 :         if(it->second->priority() == rulePriority::warning)
     247              :         {
     248            0 :             if(m_indiP_warning.getDevice() != m_configName)
     249              :             {
     250            0 :                 if(registerIndiPropertyNew( m_indiP_warning, "warning", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
     251            0 :                                                                 pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
     252              :                 {
     253            0 :                     return log<software_critical,-1>({__FILE__, __LINE__});
     254              :                 }
     255              :             }
     256              : 
     257            0 :             m_indiP_warning.add(pcf::IndiElement(it->first, pcf::IndiElement::Off));
     258            0 :             m_indiP_warning[it->first].setLabel(it->second->message());
     259              :         }
     260              : 
     261            0 :         if(it->second->priority() == rulePriority::alert)
     262              :         {
     263            0 :             if(m_indiP_alert.getDevice() != m_configName)
     264              :             {
     265            0 :                 if(registerIndiPropertyNew( m_indiP_alert, "alert", pcf::IndiProperty::Switch, pcf::IndiProperty::ReadOnly,
     266            0 :                                                                 pcf::IndiProperty::Idle, pcf::IndiProperty::AnyOfMany, nullptr) < 0)
     267              :                 {
     268            0 :                     return log<software_critical,-1>({__FILE__, __LINE__});
     269              :                 }
     270              :             }
     271              : 
     272            0 :             m_indiP_alert.add(pcf::IndiElement(it->first, pcf::IndiElement::Off));
     273            0 :             m_indiP_alert[it->first].setLabel(it->second->message());
     274              : 
     275              :         }
     276              :     }
     277              : 
     278            0 :     for(auto it = m_ruleMaps.props.begin(); it != m_ruleMaps.props.end(); ++it)
     279              :     {
     280            0 :         if(it->second == nullptr) continue;
     281              : 
     282            0 :         std::string devName, propName;
     283              : 
     284            0 :         int rv = indi::parseIndiKey(devName, propName, it->first);
     285            0 :         if(rv != 0)
     286              :         {
     287            0 :             log<software_error>({__FILE__, __LINE__, 0, rv, "error parsing INDI key: " + it->first});
     288            0 :             return -1;
     289              :         }
     290              : 
     291            0 :         registerIndiPropertySet( *it->second, devName, propName, st_newCallBack_ruleProp);
     292            0 :     }
     293              : 
     294            0 :     state(stateCodes::READY);
     295              : 
     296            0 :     return 0;
     297              : }
     298              : 
     299            0 : int stateRuleEngine::appLogic()
     300              : {
     301            0 :     for(auto it = m_ruleMaps.rules.begin(); it != m_ruleMaps.rules.end(); ++it)
     302              :     {
     303              :         #if 0
     304              :         try
     305              :         {
     306              :             bool val = it->second->value();
     307              :             std::cerr << it->first << " " << val << "\n";
     308              :         }
     309              :         catch(...){}
     310              :         #endif
     311              : 
     312            0 :         if(it->second->priority() != rulePriority::none)
     313              :         {
     314              :             try
     315              :             {
     316            0 :                 bool val = it->second->value();
     317              : 
     318            0 :                 pcf::IndiElement::SwitchStateType onoff = pcf::IndiElement::Off;
     319            0 :                 if(val) onoff = pcf::IndiElement::On;
     320              : 
     321            0 :                 if(it->second->priority() == rulePriority::info)
     322              :                 {
     323            0 :                     updateSwitchIfChanged(m_indiP_info, it->first, onoff);
     324              :                 }
     325            0 :                 else if(it->second->priority() == rulePriority::caution)
     326              :                 {
     327            0 :                     updateSwitchIfChanged(m_indiP_caution, it->first, onoff);
     328              :                 }
     329            0 :                 else if(it->second->priority() == rulePriority::warning)
     330              :                 {
     331            0 :                     updateSwitchIfChanged(m_indiP_warning, it->first, onoff);
     332              :                 }
     333              :                 else
     334              :                 {
     335            0 :                     updateSwitchIfChanged(m_indiP_alert, it->first, onoff);
     336              :                 }
     337              : 
     338              :             }
     339            0 :             catch(const std::exception & e)
     340              :             {
     341              :                 ///\todo how to handle startup vs misconfiguration
     342              : 
     343              :                 /*
     344              :                 if(it->second->priority() == rulePriority::none)
     345              :                 {
     346              :                     updateSwitchIfChanged(m_indiP_info, it->first, pcf::IndiElement::Off);
     347              :                 }*/
     348            0 :             }
     349              :         }
     350              :     }
     351              : 
     352              : 
     353            0 :     return 0;
     354              : }
     355              : 
     356            0 : int stateRuleEngine::appShutdown()
     357              : {
     358            0 :     return 0;
     359              : }
     360              : 
     361            0 : int stateRuleEngine::st_newCallBack_ruleProp( void * app,
     362              :                                               const pcf::IndiProperty &ipRecv
     363              :                                             )
     364              : {
     365            0 :     stateRuleEngine * sre = static_cast<stateRuleEngine *>(app);
     366              : 
     367            0 :     sre->newCallBack_ruleProp(ipRecv);
     368              : 
     369            0 :     return 0;
     370              : }
     371              : 
     372            0 : int stateRuleEngine::newCallBack_ruleProp( const pcf::IndiProperty &ipRecv)
     373              : {
     374            0 :     std::string key = ipRecv.createUniqueKey();
     375              : 
     376            0 :     if(m_ruleMaps.props.count(key) == 0)
     377              :     {
     378            0 :         return 0;
     379              :     }
     380              : 
     381            0 :     if(m_ruleMaps.props[key] == nullptr) //
     382              :     {
     383            0 :         return 0;
     384              :     }
     385              : 
     386            0 :     *m_ruleMaps.props[key] = ipRecv;
     387              : 
     388            0 :     return 0;
     389            0 : }
     390              : 
     391              : } //namespace app
     392              : } //namespace MagAOX
     393              : 
     394              : #endif //stateRuleEngine_hpp
        

Generated by: LCOV version 2.0-1