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

            Line data    Source code
       1              : /** \file indiCompRuleConfig.hpp
       2              :   * \brief Configuration of rules for the MagAO-X stateRuleEngine
       3              :   *
       4              :   * \ingroup stateRuleEngine_files
       5              :   */
       6              : 
       7              : #ifndef stateRuleEngine_indiCompRuleConfig_hpp
       8              : #define stateRuleEngine_indiCompRuleConfig_hpp
       9              : 
      10              : #include <map>
      11              : 
      12              : #include "indiCompRules.hpp"
      13              : 
      14              : /// Structure to provide management of the rule and property maps
      15              : /** This owns all pointers in the rule engine, and `delete`s them on destruction.
      16              :   */
      17              : struct indiRuleMaps
      18              : {
      19              :     typedef std::map<std::string, indiCompRule*> ruleMapT;
      20              :     typedef std::map<std::string, pcf::IndiProperty*> propMapT;
      21              : 
      22              :     ruleMapT rules;
      23              :     propMapT props;
      24              : 
      25            0 :     ~indiRuleMaps()
      26              :     {
      27            0 :         auto rit = rules.begin();
      28            0 :         while(rit != rules.end())
      29              :         {
      30            0 :             delete rit->second;
      31            0 :             ++rit;
      32              :         }
      33              : 
      34            0 :         auto pit = props.begin();
      35            0 :         while(pit != props.end())
      36              :         {
      37            0 :             delete pit->second;
      38            0 :             ++pit;
      39              :         }
      40            0 :     }
      41              : };
      42              : 
      43              : /* Structure used to hold ruleVal rule keys aside for final processing
      44              :    ruleVal rules can be created before the rules they link exist, so
      45              :    we hold the keys aside and set the pointers after all rules are created.
      46              : */
      47              : struct ruleRuleKeys
      48              : {
      49              :     std::string rule1;
      50              :     std::string rule2;
      51              : };
      52              : 
      53              : /// Extract a property from a rule configuration
      54              : /** Reads the property and element, adding the property to the property map if necessary.
      55              :   *
      56              :   * \throws mx::err::invalidconfig if the property is already in the map but of a different type
      57              :   */
      58            0 : void extractRuleProp( pcf::IndiProperty ** prop,            ///< [out] pointer to the property, newly created or existing, which is in the map.
      59              :                       std::string & element,                ///< [out] the element name from the configuration
      60              :                       indiRuleMaps & maps,                  ///< [in] contains the property map to which the property is added
      61              :                       const std::string & section,          ///< [in] name of the section for this rule
      62              :                       const std::string & propkey,          ///< [in] the key for the property name
      63              :                       const std::string & elkey,            ///< [in] the key for the element name
      64              :                       const pcf::IndiProperty::Type & type, ///< [in] the type of the property
      65              :                       mx::app::appConfigurator & config     ///< [in] the application configuration structure
      66              :                     )
      67              : {
      68            0 :     std::string property;
      69            0 :     config.configUnused(property, mx::app::iniFile::makeKey(section, propkey ));
      70              : 
      71            0 :     if(maps.props.count(property) > 0)
      72              :     {
      73              :         //If the property already exists we just check if it's the right type
      74            0 :         if(maps.props[property]->getType() != type)
      75              :         {
      76            0 :             mxThrowException(mx::err::invalidconfig, "extracPropRule", "property " + property + " exists but is not correct type");
      77              :         }
      78              : 
      79            0 :         *prop = maps.props[property];
      80              :     }
      81              :     else
      82              :     {
      83              :         //Otherwise we create it
      84            0 :         *prop = new pcf::IndiProperty(type);
      85            0 :         maps.props.insert(std::pair<std::string, pcf::IndiProperty*>({property, *prop}));
      86              : 
      87              :         ///\todo have to split device and propertyName
      88              :     }
      89              : 
      90            0 :     config.configUnused(element, mx::app::iniFile::makeKey(section, elkey));
      91              : 
      92            0 : }
      93              : 
      94              : /// \cond
      95              : // strip leading and trailing whitespace and then opening and closing "".  leaves spaces between "".
      96            0 : inline void stripQuotesWS( std::string &str )
      97              : {
      98            0 :     if( str.size() == 0 )
      99              :     {
     100            0 :         return;
     101              :     }
     102              : 
     103            0 :     if( str[0] != '\"' && str[0] != ' ' && str.back() != ' ' ) // get out fast if we can
     104              :     {
     105            0 :         return;
     106              :     }
     107              : 
     108              :     // strip white space at front
     109            0 :     size_t ns = str.find_first_not_of( " \t\r\n" );
     110            0 :     if( ns != std::string::npos && ns != 0 )
     111              :     {
     112            0 :         str.erase( 0, ns );
     113              : 
     114            0 :         if( str.size() == 0 )
     115              :         {
     116            0 :             return;
     117              :         }
     118              :     }
     119            0 :     else if( ns == std::string::npos ) // the rare all spaces
     120              :     {
     121            0 :         str = "";
     122            0 :         return;
     123              :     }
     124              : 
     125              :     // strip white space at back
     126            0 :     ns = str.find_last_not_of( " \t\r\n" );
     127            0 :     if( ns != std::string::npos && ns != str.size() - 1 )
     128              :     {
     129            0 :         str.erase( ns + 1 );
     130              : 
     131            0 :         if( str.size() == 0 )
     132              :         {
     133            0 :             return;
     134              :         }
     135              :     }
     136              : 
     137            0 :     if( str[0] == '\"' && str.back() == '\"' )
     138              :     {
     139            0 :         if( str.size() == 1 || str.size() == 2 )
     140              :         {
     141            0 :             str = "";
     142            0 :             return;
     143              :         }
     144            0 :         str.erase( str.size() - 1, 1 );
     145            0 :         str.erase( 0, 1 );
     146              :     }
     147            0 :     else if( str[0] == '\"')
     148              :     {
     149            0 :         if( str.size() == 1 )
     150              :         {
     151            0 :             str = "";
     152            0 :             return;
     153              :         }
     154            0 :         str.erase( 0, 1 );
     155              :     }
     156            0 :     else if( str.back() == '\"' )
     157              :     {
     158            0 :         if( str.size() == 1 || str.size() == 2 )
     159              :         {
     160            0 :             str = "";
     161            0 :             return;
     162              :         }
     163            0 :         str.erase( str.size() - 1, 1 );
     164              :     }
     165              : }
     166              : /// \endcond
     167              : 
     168              : 
     169              : /// Load the rule and properties maps for a rule engine from a configuration file
     170              : /** ///\todo check for insertion failure
     171              :   * ///\todo add a constructor that has priority, message, and comparison, to reduce duplication
     172              :   */
     173            0 : void loadRuleConfig( indiRuleMaps & maps,              ///< [out] contains the rule and property maps in which to place the items found in config
     174              :                      std::map<std::string, ruleRuleKeys> & rrkMap, ///< [out] Holds the ruleVal rule keys aside for later post-processing
     175              :                      mx::app::appConfigurator & config ///< [in] the application configuration structure
     176              :                    )
     177              : {
     178            0 :     std::vector<std::string> sections;
     179              : 
     180            0 :     config.unusedSections(sections);
     181              : 
     182            0 :     if( sections.size() == 0 )
     183              :     {
     184            0 :         mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "no rules found in config");
     185              :     }
     186              : 
     187              : 
     188            0 :     for(size_t i=0; i< sections.size(); ++i)
     189              :     {
     190            0 :         bool ruleTypeSet = config.isSetUnused(mx::app::iniFile::makeKey(sections[i], "ruleType" ));
     191              : 
     192              :         //If there is no ruleType then this isn't a rule
     193            0 :         if( !ruleTypeSet ) continue;
     194              : 
     195              :         //If the rule already exists this is an error
     196            0 :         if(maps.rules.count(sections[i]) != 0)
     197              :         {
     198            0 :             mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "duplicate rule: " + sections[i]);
     199              :         }
     200              : 
     201            0 :         std::string ruleType;
     202            0 :         config.configUnused(ruleType, mx::app::iniFile::makeKey(sections[i], "ruleType" ));
     203              : 
     204            0 :         std::string priostr="none";
     205            0 :         config.configUnused(priostr, mx::app::iniFile::makeKey(sections[i], "priority" ));
     206            0 :         rulePriority priority = string2priority(priostr);
     207              : 
     208            0 :         std::string message;
     209            0 :         config.configUnused(message, mx::app::iniFile::makeKey(sections[i], "message" ));
     210            0 :         stripQuotesWS(message); //strips "" and any leading/trailing whitespace
     211              : 
     212              : 
     213              : 
     214            0 :         std::string compstr="Eq";
     215            0 :         config.configUnused(compstr, mx::app::iniFile::makeKey(sections[i], "comp" ));
     216            0 :         ruleComparison comparison = string2comp(compstr);
     217              : 
     218            0 :         if(ruleType == numValRule::name)
     219              :         {
     220            0 :             numValRule * nvr = new numValRule;
     221            0 :             maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], nvr}));
     222              : 
     223            0 :             nvr->priority(priority);
     224            0 :             nvr->message(message);
     225            0 :             nvr->comparison(comparison);
     226              : 
     227            0 :             pcf::IndiProperty * prop = nullptr;
     228            0 :             std::string element;
     229              : 
     230            0 :             extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Number, config );
     231            0 :             nvr->property(prop);
     232            0 :             nvr->element(element);
     233              : 
     234            0 :             double target = nvr->target();
     235            0 :             config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
     236            0 :             nvr->target(target);
     237              : 
     238            0 :             double tol = nvr->tol();
     239            0 :             config.configUnused(tol, mx::app::iniFile::makeKey(sections[i], "tol" ));
     240            0 :             nvr->tol(tol);
     241            0 :         }
     242            0 :         else if(ruleType == txtValRule::name)
     243              :         {
     244            0 :             txtValRule * tvr = new txtValRule;
     245            0 :             maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], tvr}));
     246              : 
     247            0 :             tvr->priority(priority);
     248            0 :             tvr->message(message);
     249            0 :             tvr->comparison(comparison);
     250              : 
     251            0 :             pcf::IndiProperty * prop = nullptr;
     252            0 :             std::string element;
     253              : 
     254            0 :             extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Text, config );
     255            0 :             tvr->property(prop);
     256            0 :             tvr->element(element);
     257              : 
     258              : 
     259            0 :             std::string target = tvr->target();
     260            0 :             config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
     261            0 :             tvr->target(target);
     262              : 
     263            0 :         }
     264            0 :         else if(ruleType == swValRule::name)
     265              :         {
     266            0 :             swValRule * svr = new swValRule;
     267            0 :             maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], svr}));
     268              : 
     269            0 :             svr->priority(priority);
     270            0 :             svr->message(message);
     271            0 :             svr->comparison(comparison);
     272              : 
     273            0 :             pcf::IndiProperty * prop  = nullptr;
     274            0 :             std::string element;
     275              : 
     276            0 :             extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Switch, config );
     277            0 :             svr->property(prop);
     278            0 :             svr->element(element);
     279              : 
     280            0 :             std::string target = "On";
     281            0 :             config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
     282            0 :             svr->target(target);
     283            0 :         }
     284            0 :         else if(ruleType == elCompNumRule::name)
     285              :         {
     286            0 :             elCompNumRule * nvr = new elCompNumRule;
     287            0 :             maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], nvr}));
     288              : 
     289            0 :             nvr->priority(priority);
     290            0 :             nvr->message(message);
     291            0 :             nvr->comparison(comparison);
     292              : 
     293              :             pcf::IndiProperty * prop1;
     294            0 :             std::string element1;
     295              : 
     296            0 :             extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Number, config );
     297            0 :             nvr->property1(prop1);
     298            0 :             nvr->element1(element1);
     299              : 
     300              :             pcf::IndiProperty * prop2;
     301            0 :             std::string element2;
     302              : 
     303            0 :             extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Number, config );
     304            0 :             nvr->property2(prop2);
     305            0 :             nvr->element2(element2);
     306            0 :         }
     307            0 :         else if(ruleType == elCompTxtRule::name)
     308              :         {
     309            0 :             elCompTxtRule * tvr = new elCompTxtRule;
     310            0 :             maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], tvr}));
     311              : 
     312            0 :             tvr->priority(priority);
     313            0 :             tvr->message(message);
     314            0 :             tvr->comparison(comparison);
     315              : 
     316              :             pcf::IndiProperty * prop1;
     317            0 :             std::string element1;
     318              : 
     319            0 :             extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Text, config );
     320            0 :             tvr->property1(prop1);
     321            0 :             tvr->element1(element1);
     322              : 
     323              :             pcf::IndiProperty * prop2;
     324            0 :             std::string element2;
     325              : 
     326            0 :             extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Text, config );
     327            0 :             tvr->property2(prop2);
     328            0 :             tvr->element2(element2);
     329            0 :         }
     330            0 :         else if(ruleType == elCompSwRule::name)
     331              :         {
     332            0 :             elCompSwRule * svr = new elCompSwRule;
     333            0 :             maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], svr}));
     334              : 
     335            0 :             svr->priority(priority);
     336            0 :             svr->message(message);
     337            0 :             svr->comparison(comparison);
     338              : 
     339              :             pcf::IndiProperty * prop1;
     340            0 :             std::string element1;
     341              : 
     342            0 :             extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Switch, config );
     343            0 :             svr->property1(prop1);
     344            0 :             svr->element1(element1);
     345              : 
     346              :             pcf::IndiProperty * prop2;
     347            0 :             std::string element2;
     348              : 
     349            0 :             extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Switch, config );
     350            0 :             svr->property2(prop2);
     351            0 :             svr->element2(element2);
     352            0 :         }
     353            0 :         else if(ruleType == ruleCompRule::name)
     354              :         {
     355              :             //Here we have to hold the ruleVal keys separately for later processing after all the rules are created.
     356              : 
     357            0 :             if(rrkMap.count(sections[i]) > 0)
     358              :             {
     359              :                 //This probably should be impossible, since we already checked maps.rules above...
     360            0 :                 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "duplicate ruleRule: " + sections[i]);
     361              :             }
     362              : 
     363            0 :             ruleCompRule * rcr = new ruleCompRule;
     364            0 :             maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], rcr}));
     365              : 
     366            0 :             rcr->priority(priority);
     367            0 :             rcr->comparison(comparison);
     368              : 
     369            0 :             ruleRuleKeys rrk;
     370              : 
     371            0 :             config.configUnused(rrk.rule1, mx::app::iniFile::makeKey(sections[i], "rule1" ));
     372            0 :             if(rrk.rule1 == "")
     373              :             {
     374            0 :                 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 for ruleVal rule " + sections[i] + " not found");
     375              :             }
     376            0 :             if(rrk.rule1 == sections[i])
     377              :             {
     378            0 :                 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 for ruleVal rule " + sections[i] + " can't equal rule name");
     379              :             }
     380              : 
     381            0 :             config.configUnused(rrk.rule2, mx::app::iniFile::makeKey(sections[i], "rule2" ));
     382            0 :             if(rrk.rule2 == "")
     383              :             {
     384            0 :                 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 for ruleVal rule " + sections[i] + " not found");
     385              :             }
     386            0 :             if(rrk.rule2 == sections[i])
     387              :             {
     388            0 :                 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 for ruleVal rule " + sections[i] + " can't equal rule name");
     389              :             }
     390              : 
     391            0 :             rrkMap.insert(std::pair<std::string, ruleRuleKeys>(sections[i], rrk));
     392            0 :         }
     393              :         else
     394              :         {
     395            0 :             mxThrowException(mx::err::notimpl, "loadRuleConfig", "unknown rule type " + ruleType + " in " + sections[i]);
     396              :         }
     397            0 :     }
     398            0 : }
     399              : 
     400              : /// Finalize ruleVal rules
     401              : /** ///\todo check for insertion failure
     402              :   * ///\todo add a constructor that has priority, message, and comparison, to reduce duplication
     403              :   */
     404            0 : void finalizeRuleValRules( indiRuleMaps & maps,    /**< [in/out] contains the rule and property maps with rules ot finalize */
     405              :                            std::map<std::string, ruleRuleKeys> & rrkMap ///< [out] Holds the ruleVal rule keys aside for later post-processing
     406              :                          )
     407              : {
     408              :     //Now set the rule pointers for any ruleVal rules
     409            0 :     auto it=rrkMap.begin();
     410            0 :     while(it != rrkMap.end())
     411              :     {
     412            0 :         if( maps.rules.count(it->first) == 0 )
     413              :         {
     414            0 :             mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule parsing error for " + it->first);
     415              :         }
     416              : 
     417            0 :         if( maps.rules.count(it->second.rule1) == 0 )
     418              :         {
     419            0 :             mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 " + it->second.rule1 + " not found for ruleVal rule " + it->first );
     420              :         }
     421              : 
     422            0 :         if( maps.rules.count(it->second.rule2) == 0 )
     423              :         {
     424            0 :             mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 " + it->second.rule2 + " not found for ruleVal rule " + it->first );
     425              :         }
     426              : 
     427            0 :         ruleCompRule * rcr = nullptr;
     428              : 
     429              :         try
     430              :         {
     431            0 :             rcr = dynamic_cast<ruleCompRule *>(maps.rules[it->first]);
     432              :         }
     433            0 :         catch(const std::exception & e)
     434              :         {
     435            0 :             mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "error casting " + it->first + ": " + e.what() );
     436            0 :         }
     437              : 
     438            0 :         if(rcr == nullptr)
     439              :         {
     440            0 :             mxThrowException(mx::err::invalidconfig, "loadRuleConfig", it->first + " is not a ruleVal rule but has rules" );
     441              :         }
     442              : 
     443            0 :         rcr->rule1(maps.rules[it->second.rule1]);
     444            0 :         rcr->rule2(maps.rules[it->second.rule2]);
     445              : 
     446            0 :         ++it;
     447              :     }
     448            0 : }
     449              : 
     450              : #endif //stateRuleEngine_indiCompRuleConfig_hpp
        

Generated by: LCOV version 2.0-1