API
 
Loading...
Searching...
No Matches
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 */
58void 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/// \cond
95// strip leading and trailing whitespace and then opening and closing "". leaves spaces between "".
96inline void stripQuotesWS( std::string &str )
97{
98 if( str.size() == 0 )
99 {
100 return;
101 }
102
103 if( str[0] != '\"' && str[0] != ' ' && str.back() != ' ' ) // get out fast if we can
104 {
105 return;
106 }
107
108 // strip white space at front
109 size_t ns = str.find_first_not_of( " \t\r\n" );
110 if( ns != std::string::npos && ns != 0 )
111 {
112 str.erase( 0, ns );
113
114 if( str.size() == 0 )
115 {
116 return;
117 }
118 }
119 else if( ns == std::string::npos ) // the rare all spaces
120 {
121 str = "";
122 return;
123 }
124
125 // strip white space at back
126 ns = str.find_last_not_of( " \t\r\n" );
127 if( ns != std::string::npos && ns != str.size() - 1 )
128 {
129 str.erase( ns + 1 );
130
131 if( str.size() == 0 )
132 {
133 return;
134 }
135 }
136
137 if( str[0] == '\"' && str.back() == '\"' )
138 {
139 if( str.size() == 1 || str.size() == 2 )
140 {
141 str = "";
142 return;
143 }
144 str.erase( str.size() - 1, 1 );
145 str.erase( 0, 1 );
146 }
147 else if( str[0] == '\"')
148 {
149 if( str.size() == 1 )
150 {
151 str = "";
152 return;
153 }
154 str.erase( 0, 1 );
155 }
156 else if( str.back() == '\"' )
157 {
158 if( str.size() == 1 || str.size() == 2 )
159 {
160 str = "";
161 return;
162 }
163 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 */
173void 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 std::vector<std::string> sections;
179
180 config.unusedSections(sections);
181
182 if( sections.size() == 0 )
183 {
184 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "no rules found in config");
185 }
186
187
188 for(size_t i=0; i< sections.size(); ++i)
189 {
190 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 if( !ruleTypeSet ) continue;
194
195 //If the rule already exists this is an error
196 if(maps.rules.count(sections[i]) != 0)
197 {
198 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "duplicate rule: " + sections[i]);
199 }
200
201 std::string ruleType;
202 config.configUnused(ruleType, mx::app::iniFile::makeKey(sections[i], "ruleType" ));
203
204 std::string priostr="none";
205 config.configUnused(priostr, mx::app::iniFile::makeKey(sections[i], "priority" ));
206 rulePriority priority = string2priority(priostr);
207
208 std::string message;
209 config.configUnused(message, mx::app::iniFile::makeKey(sections[i], "message" ));
210 stripQuotesWS(message); //strips "" and any leading/trailing whitespace
211
212
213
214 std::string compstr="Eq";
215 config.configUnused(compstr, mx::app::iniFile::makeKey(sections[i], "comp" ));
216 ruleComparison comparison = string2comp(compstr);
217
218 if(ruleType == numValRule::name)
219 {
220 numValRule * nvr = new numValRule;
221 maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], nvr}));
222
223 nvr->priority(priority);
224 nvr->message(message);
225 nvr->comparison(comparison);
226
227 pcf::IndiProperty * prop = nullptr;
228 std::string element;
229
230 extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Number, config );
231 nvr->property(prop);
232 nvr->element(element);
233
234 double target = nvr->target();
235 config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
236 nvr->target(target);
237
238 double tol = nvr->tol();
239 config.configUnused(tol, mx::app::iniFile::makeKey(sections[i], "tol" ));
240 nvr->tol(tol);
241 }
242 else if(ruleType == txtValRule::name)
243 {
244 txtValRule * tvr = new txtValRule;
245 maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], tvr}));
246
247 tvr->priority(priority);
248 tvr->message(message);
249 tvr->comparison(comparison);
250
251 pcf::IndiProperty * prop = nullptr;
252 std::string element;
253
254 extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Text, config );
255 tvr->property(prop);
256 tvr->element(element);
257
258
259 std::string target = tvr->target();
260 config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
261 tvr->target(target);
262
263 }
264 else if(ruleType == swValRule::name)
265 {
266 swValRule * svr = new swValRule;
267 maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], svr}));
268
269 svr->priority(priority);
270 svr->message(message);
271 svr->comparison(comparison);
272
273 pcf::IndiProperty * prop = nullptr;
274 std::string element;
275
276 extractRuleProp( &prop, element, maps, sections[i], "property", "element", pcf::IndiProperty::Switch, config );
277 svr->property(prop);
278 svr->element(element);
279
280 std::string target = "On";
281 config.configUnused(target, mx::app::iniFile::makeKey(sections[i], "target" ));
282 svr->target(target);
283 }
284 else if(ruleType == elCompNumRule::name)
285 {
286 elCompNumRule * nvr = new elCompNumRule;
287 maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], nvr}));
288
289 nvr->priority(priority);
290 nvr->message(message);
291 nvr->comparison(comparison);
292
293 pcf::IndiProperty * prop1;
294 std::string element1;
295
296 extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Number, config );
297 nvr->property1(prop1);
298 nvr->element1(element1);
299
300 pcf::IndiProperty * prop2;
301 std::string element2;
302
303 extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Number, config );
304 nvr->property2(prop2);
305 nvr->element2(element2);
306 }
307 else if(ruleType == elCompTxtRule::name)
308 {
309 elCompTxtRule * tvr = new elCompTxtRule;
310 maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], tvr}));
311
312 tvr->priority(priority);
313 tvr->message(message);
314 tvr->comparison(comparison);
315
316 pcf::IndiProperty * prop1;
317 std::string element1;
318
319 extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Text, config );
320 tvr->property1(prop1);
321 tvr->element1(element1);
322
323 pcf::IndiProperty * prop2;
324 std::string element2;
325
326 extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Text, config );
327 tvr->property2(prop2);
328 tvr->element2(element2);
329 }
330 else if(ruleType == elCompSwRule::name)
331 {
332 elCompSwRule * svr = new elCompSwRule;
333 maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], svr}));
334
335 svr->priority(priority);
336 svr->message(message);
337 svr->comparison(comparison);
338
339 pcf::IndiProperty * prop1;
340 std::string element1;
341
342 extractRuleProp( &prop1, element1, maps, sections[i], "property1", "element1", pcf::IndiProperty::Switch, config );
343 svr->property1(prop1);
344 svr->element1(element1);
345
346 pcf::IndiProperty * prop2;
347 std::string element2;
348
349 extractRuleProp( &prop2, element2, maps, sections[i], "property2", "element2", pcf::IndiProperty::Switch, config );
350 svr->property2(prop2);
351 svr->element2(element2);
352 }
353 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 if(rrkMap.count(sections[i]) > 0)
358 {
359 //This probably should be impossible, since we already checked maps.rules above...
360 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "duplicate ruleRule: " + sections[i]);
361 }
362
363 ruleCompRule * rcr = new ruleCompRule;
364 maps.rules.insert(std::pair<std::string, indiCompRule*>({sections[i], rcr}));
365
366 rcr->priority(priority);
367 rcr->comparison(comparison);
368
369 ruleRuleKeys rrk;
370
371 config.configUnused(rrk.rule1, mx::app::iniFile::makeKey(sections[i], "rule1" ));
372 if(rrk.rule1 == "")
373 {
374 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 for ruleVal rule " + sections[i] + " not found");
375 }
376 if(rrk.rule1 == sections[i])
377 {
378 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 for ruleVal rule " + sections[i] + " can't equal rule name");
379 }
380
381 config.configUnused(rrk.rule2, mx::app::iniFile::makeKey(sections[i], "rule2" ));
382 if(rrk.rule2 == "")
383 {
384 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 for ruleVal rule " + sections[i] + " not found");
385 }
386 if(rrk.rule2 == sections[i])
387 {
388 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 for ruleVal rule " + sections[i] + " can't equal rule name");
389 }
390
391 rrkMap.insert(std::pair<std::string, ruleRuleKeys>(sections[i], rrk));
392 }
393 else
394 {
395 mxThrowException(mx::err::notimpl, "loadRuleConfig", "unknown rule type " + ruleType + " in " + sections[i]);
396 }
397 }
398}
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 */
404void 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 auto it=rrkMap.begin();
410 while(it != rrkMap.end())
411 {
412 if( maps.rules.count(it->first) == 0 )
413 {
414 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule parsing error for " + it->first);
415 }
416
417 if( maps.rules.count(it->second.rule1) == 0 )
418 {
419 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule1 " + it->second.rule1 + " not found for ruleVal rule " + it->first );
420 }
421
422 if( maps.rules.count(it->second.rule2) == 0 )
423 {
424 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "rule2 " + it->second.rule2 + " not found for ruleVal rule " + it->first );
425 }
426
427 ruleCompRule * rcr = nullptr;
428
429 try
430 {
431 rcr = dynamic_cast<ruleCompRule *>(maps.rules[it->first]);
432 }
433 catch(const std::exception & e)
434 {
435 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", "error casting " + it->first + ": " + e.what() );
436 }
437
438 if(rcr == nullptr)
439 {
440 mxThrowException(mx::err::invalidconfig, "loadRuleConfig", it->first + " is not a ruleVal rule but has rules" );
441 }
442
443 rcr->rule1(maps.rules[it->second.rule1]);
444 rcr->rule2(maps.rules[it->second.rule2]);
445
446 ++it;
447 }
448}
449
450#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.