API
indiCompRuleConfig.hpp
Go to the documentation of this file.
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  */
18 {
19  typedef std::map<std::string, indiCompRule*> ruleMapT;
20  typedef std::map<std::string, pcf::IndiProperty*> propMapT;
21 
24 
26  {
27  auto rit = rules.begin();
28  while(rit != rules.end())
29  {
30  delete rit->second;
31  ++rit;
32  }
33 
34  auto pit = props.begin();
35  while(pit != props.end())
36  {
37  delete pit->second;
38  ++pit;
39  }
40  }
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 */
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 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  std::string property;
69  config.configUnused(property, mx::app::iniFile::makeKey(section, propkey ));
70 
71  if(maps.props.count(property) > 0)
72  {
73  //If the property already exists we just check if it's the right type
74  if(maps.props[property]->getType() != type)
75  {
76  mxThrowException(mx::err::invalidconfig, "extracPropRule", "property " + property + " exists but is not correct type");
77  }
78 
79  *prop = maps.props[property];
80  }
81  else
82  {
83  //Otherwise we create it
84  *prop = new pcf::IndiProperty(type);
85  maps.props.insert(std::pair<std::string, pcf::IndiProperty*>({property, *prop}));
86 
87  ///\todo have to split device and propertyName
88  }
89 
90  config.configUnused(element, mx::app::iniFile::makeKey(section, elkey));
91 
92 }
93 
94 /// Load the rule and properties maps for a rule engine from a configuration file
95 /** ///\todo check for insertion failure
96  * ///\todo add a constructor that has priority, message, and comparison, to reduce duplication
97  */
98 void loadRuleConfig( indiRuleMaps & maps, ///< [out] contains the rule and property maps in which to place the items found in config
99  std::map<std::string, ruleRuleKeys> & rrkMap, ///< [out] Holds the ruleVal rule keys aside for later post-processing
100  mx::app::appConfigurator & config ///< [in] the application configuration structure
101  )
102 {
103  std::vector<std::string> sections;
104 
105  config.unusedSections(sections);
106 
107  if( sections.size() == 0 )
108  {
109  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "no rules found in config");
110  }
111 
112 
113  for(size_t i=0; i< sections.size(); ++i)
114  {
115  bool ruleTypeSet = config.isSetUnused(mx::app::iniFile::makeKey(sections[i], "ruleType" ));
116 
117  //If there is no ruleType then this isn't a rule
118  if( !ruleTypeSet ) continue;
119 
120  //If the rule already exists this is an error
121  if(maps.rules.count(sections[i]) != 0)
122  {
123  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "duplicate rule: " + sections[i]);
124  }
125 
126  std::string ruleType;
127  config.configUnused(ruleType, mx::app::iniFile::makeKey(sections[i], "ruleType" ));
128 
129  std::string priostr="none";
130  config.configUnused(priostr, mx::app::iniFile::makeKey(sections[i], "priority" ));
131  rulePriority priority = string2priority(priostr);
132 
133  std::string message;
134  config.configUnused(message, mx::app::iniFile::makeKey(sections[i], "message" ));
135 
136  std::string compstr="Eq";
137  config.configUnused(compstr, mx::app::iniFile::makeKey(sections[i], "comp" ));
138  ruleComparison comparison = string2comp(compstr);
139 
140  if(ruleType == numValRule::name)
141  {
142  numValRule * nvr = new numValRule;
143  maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], nvr}));
144 
145  nvr->priority(priority);
146  nvr->message(message);
147  nvr->comparison(comparison);
148 
149  pcf::IndiProperty * prop = nullptr;
150  std::string element;
151 
152  extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Number, config );
153  nvr->property(prop);
154  nvr->element(element);
155 
156  double target = nvr->target();
157  config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
158  nvr->target(target);
159 
160  double tol = nvr->tol();
161  config.configUnused(tol, mx::app::iniFile::makeKey(sections[i], "tol" ));
162  nvr->tol(tol);
163  }
164  else if(ruleType == txtValRule::name)
165  {
166  txtValRule * tvr = new txtValRule;
167  maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], tvr}));
168 
169  tvr->priority(priority);
170  tvr->message(message);
171  tvr->comparison(comparison);
172 
173  pcf::IndiProperty * prop = nullptr;
174  std::string element;
175 
176  extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Text, config );
177  tvr->property(prop);
178  tvr->element(element);
179 
180 
181  std::string target = tvr->target();
182  config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
183  tvr->target(target);
184 
185  }
186  else if(ruleType == swValRule::name)
187  {
188  swValRule * svr = new swValRule;
189  maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], svr}));
190 
191  svr->priority(priority);
192  svr->message(message);
193  svr->comparison(comparison);
194 
195  pcf::IndiProperty * prop = nullptr;
196  std::string element;
197 
198  extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Switch, config );
199  svr->property(prop);
200  svr->element(element);
201 
202  std::string target = "On";
203  config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
204  svr->target(target);
205  }
206  else if(ruleType == elCompNumRule::name)
207  {
208  elCompNumRule * nvr = new elCompNumRule;
209  maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], nvr}));
210 
211  nvr->priority(priority);
212  nvr->message(message);
213  nvr->comparison(comparison);
214 
215  pcf::IndiProperty * prop1;
216  std::string element1;
217 
218  extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Number, config );
219  nvr->property1(prop1);
220  nvr->element1(element1);
221 
222  pcf::IndiProperty * prop2;
223  std::string element2;
224 
225  extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Number, config );
226  nvr->property2(prop2);
227  nvr->element2(element2);
228  }
229  else if(ruleType == elCompTxtRule::name)
230  {
231  elCompTxtRule * tvr = new elCompTxtRule;
232  maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], tvr}));
233 
234  tvr->priority(priority);
235  tvr->message(message);
236  tvr->comparison(comparison);
237 
238  pcf::IndiProperty * prop1;
239  std::string element1;
240 
241  extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Text, config );
242  tvr->property1(prop1);
243  tvr->element1(element1);
244 
245  pcf::IndiProperty * prop2;
246  std::string element2;
247 
248  extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Text, config );
249  tvr->property2(prop2);
250  tvr->element2(element2);
251  }
252  else if(ruleType == elCompSwRule::name)
253  {
254  elCompSwRule * svr = new elCompSwRule;
255  maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], svr}));
256 
257  svr->priority(priority);
258  svr->message(message);
259  svr->comparison(comparison);
260 
261  pcf::IndiProperty * prop1;
262  std::string element1;
263 
264  extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Switch, config );
265  svr->property1(prop1);
266  svr->element1(element1);
267 
268  pcf::IndiProperty * prop2;
269  std::string element2;
270 
271  extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Switch, config );
272  svr->property2(prop2);
273  svr->element2(element2);
274  }
275  else if(ruleType == ruleCompRule::name)
276  {
277  //Here we have to hold the ruleVal keys separately for later processing after all the rules are created.
278 
279  if(rrkMap.count(sections[i]) > 0)
280  {
281  //This probably should be impossible, since we already checked maps.rules above...
282  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "duplicate ruleRule: " + sections[i]);
283  }
284 
285  ruleCompRule * rcr = new ruleCompRule;
286  maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], rcr}));
287 
288  rcr->priority(priority);
289  rcr->comparison(comparison);
290 
291  ruleRuleKeys rrk;
292 
293  config.configUnused(rrk.rule1, mx::app::iniFile::makeKey(sections[i], "rule1" ));
294  if(rrk.rule1 == "")
295  {
296  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 for ruleVal rule " + sections[i] + " not found");
297  }
298  if(rrk.rule1 == sections[i])
299  {
300  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 for ruleVal rule " + sections[i] + " can't equal rule name");
301  }
302 
303  config.configUnused(rrk.rule2, mx::app::iniFile::makeKey(sections[i], "rule2" ));
304  if(rrk.rule2 == "")
305  {
306  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 for ruleVal rule " + sections[i] + " not found");
307  }
308  if(rrk.rule2 == sections[i])
309  {
310  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 for ruleVal rule " + sections[i] + " can't equal rule name");
311  }
312 
313  rrkMap.insert(std::pair<std::string, ruleRuleKeys>(sections[i], rrk));
314  }
315  else
316  {
317  mxThrowException(mx::err::notimpl, "loadRuleConfig", "unknown rule type " + ruleType + " in " + sections[i]);
318  }
319  }
320 }
321 
322 /// Finalize ruleVal rules
323 /** ///\todo check for insertion failure
324  * ///\todo add a constructor that has priority, message, and comparison, to reduce duplication
325  */
326 void finalizeRuleValRules( indiRuleMaps & maps, /**< [in/out] contains the rule and property maps with rules ot finalize */
327  std::map<std::string, ruleRuleKeys> & rrkMap ///< [out] Holds the ruleVal rule keys aside for later post-processing
328  )
329 {
330  //Now set the rule pointers for any ruleVal rules
331  auto it=rrkMap.begin();
332  while(it != rrkMap.end())
333  {
334  if( maps.rules.count(it->first) == 0 )
335  {
336  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule parsing error for " + it->first);
337  }
338 
339  if( maps.rules.count(it->second.rule1) == 0 )
340  {
341  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 " + it->second.rule1 + " not found for ruleVal rule " + it->first );
342  }
343 
344  if( maps.rules.count(it->second.rule2) == 0 )
345  {
346  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 " + it->second.rule2 + " not found for ruleVal rule " + it->first );
347  }
348 
349  ruleCompRule * rcr = nullptr;
350 
351  try
352  {
353  rcr = dynamic_cast<ruleCompRule *>(maps.rules[it->first]);
354  }
355  catch(const std::exception & e)
356  {
357  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "error casting " + it->first + ": " + e.what() );
358  }
359 
360  if(rcr == nullptr)
361  {
362  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", it->first + " is not a ruleVal rule but has rules" );
363  }
364 
365  rcr->rule1(maps.rules[it->second.rule1]);
366  rcr->rule2(maps.rules[it->second.rule2]);
367 
368  ++it;
369  }
370 }
371 
372 #endif //stateRuleEngine_indiCompRuleConfig_hpp
void loadRuleConfig(indiRuleMaps &maps, std::map< std::string, ruleRuleKeys > &rrkMap, mx::app::appConfigurator &config)
Load the rule and properties maps for a rule engine from a configuration file.
void finalizeRuleValRules(indiRuleMaps &maps, std::map< std::string, ruleRuleKeys > &rrkMap)
Finalize ruleVal rules.
void extractRuleProp(pcf::IndiProperty **prop, std::string &element, indiRuleMaps &maps, const std::string &section, const std::string &propkey, const std::string &elkey, const pcf::IndiProperty::Type &type, mx::app::appConfigurator &config)
Extract a property from a rule configuration.
The rules for the MagAO-X stateRuleEngine.
ruleComparison
Logical comparisons for the INDI rules.
ruleComparison string2comp(const std::string &cstr)
Get the ruleComparison member from a string representation.
rulePriority string2priority(const std::string &pstr)
Get the rulePriority member from a string representation.
rulePriority
Reporting priorities for rules.
Compare two elements based on their numeric values.
static constexpr char name[]
Name of this rule, used by config system.
Compare two elements based on their switch values.
static constexpr char name[]
Name of this rule, used by config system.
Compare two elements based on their text values.
static constexpr char name[]
Name of this rule, used by config system.
void priority(const rulePriority &p)
Set priority of this rule.
void comparison(const ruleComparison &c)
Set the comparison for this rule.
void message(const std::string &m)
Set the message.
Structure to provide management of the rule and property maps.
std::map< std::string, pcf::IndiProperty * > propMapT
std::map< std::string, indiCompRule * > ruleMapT
Compare the value of a number element to a target.
void target(const double &tgt)
Set the target for the comparison.
void tol(const double &t)
Set the tolerance.
static constexpr char name[]
Name of this rule, used by config system.
void property(pcf::IndiProperty *property)
Set the property pointer.
void element(const std::string &el)
Set the element name.
A rule to compare two rules.
void rule2(indiCompRule *r)
Set the pointer to the second rule.
void rule1(indiCompRule *r)
Set the pointer to the first rule.
static constexpr char name[]
Name of this rule, used by config system.
Compare the value of a switch to a target value.
void target(const pcf::IndiElement::SwitchStateType &ss)
Set the target for the comparison.
static constexpr char name[]
Name of this rule, used by config system.
void property1(pcf::IndiProperty *property)
Set the first property pointer.
void element2(const std::string &el)
Set the second element name.
void element1(const std::string &el)
Set the first element name.
void property2(pcf::IndiProperty *property)
Set the second property pointer.
Compare the value of a text element to a target value.
void target(const std::string &target)
Set the target for the comparison.
static constexpr char name[]
Name of this rule, used by config system.