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  mx::app::appConfigurator & config ///< [in] the application configuration structure
100  )
101 {
102  std::vector<std::string> sections;
103 
104  config.unusedSections(sections);
105 
106  if( sections.size() == 0 )
107  {
108  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "no rules found in config");
109  }
110 
111  std::map<std::string, ruleRuleKeys> rrkMap; // Holds the ruleVal rule keys aside for later post-processing
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 
299  config.configUnused(rrk.rule2, mx::app::iniFile::makeKey(sections[i], "rule2" ));
300  if(rrk.rule2 == "")
301  {
302  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 for ruleVal rule " + sections[i] + " not found");
303  }
304 
305  rrkMap.insert(std::pair<std::string, ruleRuleKeys>(sections[i], rrk));
306  }
307  else
308  {
309  mxThrowException(mx::err::notimpl, "loadRuleConfig", "unknown rule type " + ruleType + " in " + sections[i]);
310  }
311  }
312 
313  //Now set the rule pointers for any ruleVal rules
314  auto it=rrkMap.begin();
315  while(it != rrkMap.end())
316  {
317  if( maps.rules.count(it->first) == 0 )
318  {
319  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule parsing error for " + it->first);
320  }
321 
322  if( maps.rules.count(it->second.rule1) == 0 )
323  {
324  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 " + it->second.rule1 + " not found for ruleVal rule " + it->first );
325  }
326 
327  if( maps.rules.count(it->second.rule2) == 0 )
328  {
329  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 " + it->second.rule2 + " not found for ruleVal rule " + it->first );
330  }
331 
332  ruleCompRule * rcr = nullptr;
333 
334  try
335  {
336  rcr = dynamic_cast<ruleCompRule *>(maps.rules[it->first]);
337  }
338  catch(const std::exception & e)
339  {
340  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "error casting " + it->first + ": " + e.what() );
341  }
342 
343  if(rcr == nullptr)
344  {
345  mxThrowException(mx::err::invalidconfig, "loadRuleConfig", it->first + " is not a ruleVal rule but has rules" );
346  }
347 
348  rcr->rule1(maps.rules[it->second.rule1]);
349  rcr->rule2(maps.rules[it->second.rule2]);
350 
351  ++it;
352  }
353 }
354 
355 #endif //stateRuleEngine_indiCompRuleConfig_hpp
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.
void loadRuleConfig(indiRuleMaps &maps, mx::app::appConfigurator &config)
Load the rule and properties maps for a rule engine from a configuration file.
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.