Line data Source code
1 : /** \file indiCompRules.hpp
2 : * \brief The rules for the MagAO-X stateRuleEngine
3 : *
4 : * \ingroup stateRuleEngine_files
5 : */
6 :
7 : #ifndef stateRuleEngine_indiCompRules_hpp
8 : #define stateRuleEngine_indiCompRules_hpp
9 :
10 : #include <variant>
11 :
12 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
13 : //Included here for standalone testing of this file
14 : #include <mx/mxException.hpp>
15 :
16 : /// Logical comparisons for the INDI rules
17 : enum class ruleComparison
18 : {
19 : Eq, ///< Equal
20 : Neq, ///< Not equal
21 : Lt, ///< Less than
22 : Gt, ///< Greater than
23 : LtEq, ///< Less than or equal to
24 : GtEq, ///< Greater than or equal to
25 : And, ///< boolean and
26 : Nand, ///< boolean nand
27 : Or, ///< boolean or
28 : Nor, ///< boolean nor
29 : Imply,
30 : Nimply,
31 : Xor = Neq, ///< boolean xor, equivalent to not equal
32 : Xnor = Eq ///< boolean xnor, equivalent to equal
33 : };
34 :
35 : /// Get the \ref ruleComparison member from a string representation.
36 : /** Needed for processing configuration files
37 : */
38 0 : ruleComparison string2comp( const std::string & cstr )
39 : {
40 0 : if(cstr == "Eq")
41 : {
42 0 : return ruleComparison::Eq;
43 : }
44 0 : else if(cstr == "Neq")
45 : {
46 0 : return ruleComparison::Neq;
47 : }
48 0 : else if(cstr == "Lt")
49 : {
50 0 : return ruleComparison::Lt;
51 : }
52 0 : else if(cstr == "Gt")
53 : {
54 0 : return ruleComparison::Gt;
55 : }
56 0 : else if(cstr == "LtEq")
57 : {
58 0 : return ruleComparison::LtEq;
59 : }
60 0 : else if(cstr == "GtEq")
61 : {
62 0 : return ruleComparison::GtEq;
63 : }
64 0 : else if(cstr == "And")
65 : {
66 0 : return ruleComparison::And;
67 : }
68 0 : else if(cstr == "Nand")
69 : {
70 0 : return ruleComparison::Nand;
71 : }
72 0 : else if(cstr == "Or")
73 : {
74 0 : return ruleComparison::Or;
75 : }
76 0 : else if(cstr == "Nor")
77 : {
78 0 : return ruleComparison::Nor;
79 : }
80 0 : else if(cstr == "Xor")
81 : {
82 0 : return ruleComparison::Xor;
83 : }
84 0 : else if(cstr == "Xnor")
85 : {
86 0 : return ruleComparison::Xnor;
87 : }
88 0 : else if(cstr == "Imply")
89 : {
90 0 : return ruleComparison::Imply;
91 : }
92 0 : else if(cstr == "Nimply")
93 : {
94 0 : return ruleComparison::Nimply;
95 : }
96 : else
97 : {
98 0 : mxThrowException(mx::err::invalidarg, "string2comp", cstr + " is not a valid comparison");
99 : }
100 : }
101 :
102 : /// Reporting priorities for rules
103 : enum class rulePriority
104 : {
105 : none, ///< Don't publish
106 : info, ///< For information only
107 : caution, ///< Caution -- make sure you know what you're doing
108 : warning, ///< Warning -- something is probably wrong, you should check
109 : alert ///< Alert -- something is definitely wrong, you should take action
110 : };
111 :
112 : /// Get the \ref rulePriority member from a string representation.
113 : /** Needed for processing configuration files
114 : */
115 0 : rulePriority string2priority(const std::string & pstr)
116 : {
117 0 : if(pstr == "none")
118 : {
119 0 : return rulePriority::none;
120 : }
121 0 : else if(pstr == "info")
122 : {
123 0 : return rulePriority::info;
124 : }
125 0 : else if(pstr == "caution")
126 : {
127 0 : return rulePriority::caution;
128 : }
129 0 : else if(pstr == "warning")
130 : {
131 0 : return rulePriority::warning;
132 : }
133 0 : else if(pstr == "alert")
134 : {
135 0 : return rulePriority::alert;
136 : }
137 : else
138 : {
139 0 : mxThrowException(mx::err::invalidarg, "string2priority", pstr + " is not a valid priority");
140 : }
141 : }
142 :
143 : /// Virtual base-class for all rules
144 : /** Provides error handling and comparison functions.
145 : * Derived classes must implement valid() and value().
146 : */
147 : struct indiCompRule
148 : {
149 : public:
150 :
151 : /// In-band error reporting type
152 : typedef std::variant<bool, std::string> boolorerr_t;
153 :
154 : /// Check if returned value indicates an error
155 152 : bool isError(boolorerr_t rv /**< [in] the return value to check*/)
156 : {
157 152 : return (rv.index() > 0);
158 : }
159 :
160 : protected:
161 :
162 : /// The reporting priority for this rule
163 : rulePriority m_priority {rulePriority::none};
164 :
165 : /// The message used for notifications
166 : std::string m_message;
167 :
168 : /// The comparison for this rule
169 : ruleComparison m_comparison {ruleComparison::Eq};
170 :
171 : public:
172 :
173 : /// Virtual destructor
174 63 : virtual ~indiCompRule()
175 63 : {}
176 :
177 : /// Set priority of this rule
178 0 : void priority( const rulePriority & p /**< [in] the new priority */)
179 : {
180 0 : m_priority = p;
181 0 : }
182 :
183 : /// Get the rule priority
184 : /**
185 : * \returns the current rule priority
186 : */
187 0 : const rulePriority & priority()
188 : {
189 0 : return m_priority;
190 : }
191 :
192 : /// Set the message
193 0 : void message(const std::string & m /**< [in] the new message*/)
194 : {
195 0 : m_message = m;
196 0 : }
197 :
198 : /// Get the message
199 : /**
200 : * \returns the current message
201 : */
202 0 : const std::string & message()
203 : {
204 0 : return m_message;
205 : }
206 :
207 : /// Set the comparison for this rule
208 72 : void comparison( const ruleComparison & c /**< [in] the new comparison*/)
209 : {
210 72 : m_comparison = c;
211 72 : }
212 :
213 : /// Get the rule comparison
214 : /**
215 : * \returns the current rule comparison
216 : *
217 : */
218 : const ruleComparison & comparison()
219 : {
220 : return m_comparison;
221 : }
222 :
223 : /// Report whether the rule is valid as configured
224 : /** If not valid, the return value is a std::string with the reason.
225 : * If valid, the return value is a bool set to true.
226 : */
227 : virtual boolorerr_t valid() = 0;
228 :
229 : /// Get the value of this rule
230 : /**
231 : * \returns the result of the comparison defined by the rule
232 : */
233 : virtual bool value() = 0;
234 :
235 : /// Compare two strings
236 : /** String comparison can only be Eq or Neq.
237 : *
238 : * \returns true if the comparison is true
239 : * \returns false if the comparison is false
240 : * \returns std::string with error message if the comparison is not valid
241 : */
242 22 : boolorerr_t compTxt( const std::string & str1, ///< [in] the first string to compare
243 : const std::string & str2 ///< [in] the second string to compare
244 : )
245 : {
246 22 : boolorerr_t rv = false;
247 :
248 22 : switch(m_comparison)
249 : {
250 18 : case ruleComparison::Eq:
251 18 : if(str1 == str2) rv = true;
252 18 : break;
253 4 : case ruleComparison::Neq:
254 4 : if(str1 != str2) rv = true;
255 4 : break;
256 0 : default:
257 0 : rv = "operator not valid for string comparison";
258 : }
259 :
260 22 : return rv;
261 0 : }
262 :
263 : /// Compare two switches
264 : /** Switch comparison can only be Eq or Neq.
265 : *
266 : * \returns true if the comparison is true
267 : * \returns false if the comparison is false
268 : * \returns std::string with error message if the comparison is not valid
269 : */
270 16 : boolorerr_t compSw( const pcf::IndiElement::SwitchStateType & sw1, ///< [in] the first switch to compare
271 : const pcf::IndiElement::SwitchStateType & sw2 ///< [in] the first switch to compare
272 : )
273 : {
274 16 : boolorerr_t rv = false;
275 :
276 16 : switch(m_comparison)
277 : {
278 8 : case ruleComparison::Eq:
279 8 : if(sw1 == sw2) rv = true;
280 8 : break;
281 8 : case ruleComparison::Neq:
282 8 : if(sw1 != sw2) rv = true;
283 8 : break;
284 0 : default:
285 0 : rv = "operator not valid for switch comparison";
286 : }
287 :
288 16 : return rv;
289 0 : }
290 :
291 : /// Compare two numbers
292 : /** The comparison is (num1 comp num2), e.g. (num1 \< num2).
293 : * A tolerance is included for floating point equality.
294 : *
295 : * \returns true if the comparison is true
296 : * \returns false if the comparison is false
297 : * \returns std::string with error message if the comparison is not valid
298 : */
299 16 : boolorerr_t compNum( const double & num1, ///< [in] the first number to compare
300 : const double & num2, ///< [in] the second number to compare
301 : const double & tol ///< [in] the tolerance for the comparison
302 : )
303 : {
304 16 : boolorerr_t rv = false;
305 :
306 16 : switch(m_comparison)
307 : {
308 6 : case ruleComparison::Eq:
309 6 : if( fabs(num1 - num2) <= tol ) rv = true;
310 6 : break;
311 0 : case ruleComparison::Neq:
312 0 : if( fabs(num1 - num2) > tol ) rv = true;
313 0 : break;
314 2 : case ruleComparison::Lt:
315 2 : if( num1 < num2) rv = true;
316 2 : break;
317 2 : case ruleComparison::Gt:
318 2 : if( num1 > num2) rv = true;
319 2 : break;
320 3 : case ruleComparison::LtEq:
321 3 : if( fabs(num1 - num2) <= tol ) rv = true;
322 2 : else if (num1 < num2) rv = true;
323 3 : break;
324 3 : case ruleComparison::GtEq:
325 3 : if( fabs(num1 - num2) <= tol ) rv = true;
326 2 : else if (num1 > num2) rv = true;
327 3 : break;
328 0 : default:
329 0 : rv = "operator not valid for compNum";
330 : }
331 :
332 16 : return rv;
333 0 : }
334 :
335 : /// Compare two booleans
336 : /**
337 : * \returns true if the comparison is true
338 : * \returns false if the comparison is false
339 : * \returns std::string with error message if the comparison is not valid
340 : */
341 9 : boolorerr_t compBool( const bool & b1, ///< [in] the first bool to compare
342 : const bool & b2 ///< [in] the second bool to compare
343 : )
344 : {
345 9 : boolorerr_t rv = false;
346 :
347 9 : switch(m_comparison)
348 : {
349 0 : case ruleComparison::Eq:
350 0 : if(b1 == b2) rv = true;
351 0 : break;
352 0 : case ruleComparison::Neq:
353 0 : if(b1 != b2) rv = true;
354 0 : break;
355 5 : case ruleComparison::And:
356 5 : if(b1 && b2) rv = true;
357 5 : break;
358 0 : case ruleComparison::Nand:
359 0 : if(!(b1 && b2)) rv = true;
360 0 : break;
361 4 : case ruleComparison::Or:
362 4 : if(b1 || b2) rv = true;
363 4 : break;
364 0 : case ruleComparison::Nor:
365 0 : if(!b1 && !b2) rv = true;
366 0 : break;
367 0 : case ruleComparison::Imply:
368 : // https://en.wikipedia.org/wiki/Material_conditional
369 0 : if(!b1 || b2) rv=true;
370 0 : break;
371 0 : case ruleComparison::Nimply:
372 : // https://en.wikipedia.org/wiki/Material_nonimplication
373 0 : if(b1 && !b2) rv=true;
374 0 : break;
375 0 : default:
376 0 : rv = "operator not valid for ruleCompRule";
377 : }
378 :
379 9 : return rv;
380 0 : }
381 : };
382 :
383 : /// A rule base class for testing an element in one property
384 : struct onePropRule : public indiCompRule
385 : {
386 :
387 : protected:
388 :
389 : int m_type; ///< The property type, from pcf::IndiProperty::Type
390 :
391 : pcf::IndiProperty * m_property {nullptr}; ///< Pointer to the property
392 :
393 : std::string m_element; ///< The element name within the property
394 :
395 : public:
396 :
397 : //Default c'tor is deleted, you must supply the property type
398 : onePropRule() = delete;
399 :
400 : /// Constructor. You must provide the property type to construct a onePropRule
401 40 : explicit onePropRule( int type ) : m_type(type /**< The property type, from pcf::IndiProperty::Type*/)
402 40 : {}
403 :
404 : /// Set the property pointer
405 : /**
406 : * \throws mx::err::invalidarg if \p property is nullptr
407 : * \throws mx::err::invalidconfig if the supplied property has the wrong type
408 : */
409 40 : void property( pcf::IndiProperty * property /**< [in] the new property pointer*/)
410 : {
411 40 : if(property == nullptr)
412 : {
413 0 : mxThrowException(mx::err::invalidarg, "onePropRule::property", "property is nullptr");
414 : }
415 :
416 40 : if(property->getType() != m_type)
417 : {
418 0 : mxThrowException(mx::err::invalidconfig, "onePropRule::property", "property is not correct type");
419 : }
420 :
421 40 : m_property = property;
422 40 : }
423 :
424 : /// Get the property pointer
425 : /**
426 : * \returns the current value of m_property
427 : */
428 : const pcf::IndiProperty * property()
429 : {
430 : return m_property;
431 : }
432 :
433 : /// Set the element name
434 40 : void element(const std::string & el /**< [in] the new element name*/)
435 : {
436 40 : m_element = el;
437 40 : }
438 :
439 : /// Get the element name
440 : /**
441 : * \returns the current value of m_element
442 : */
443 : const std::string & element()
444 : {
445 : return m_element;
446 : }
447 :
448 : /// Check if this rule is valid
449 : /** The rule is valid if the property pointer is not null, and the element
450 : * is contained within the property.
451 : *
452 : * If not valid, the return value is a std::string with the reason.
453 : * If valid, the return value is a bool set to true.
454 : */
455 62 : virtual boolorerr_t valid()
456 : {
457 62 : boolorerr_t rv;
458 62 : if(m_property == nullptr)
459 : {
460 0 : rv = "property is null";
461 : }
462 62 : else if(!m_property->find(m_element))
463 : {
464 0 : rv = "element is not found";
465 : }
466 : else
467 : {
468 62 : rv = true;
469 : }
470 :
471 62 : return rv;
472 0 : }
473 : };
474 :
475 : /// A rule base class for testing elements in two properties
476 : struct twoPropRule : public indiCompRule
477 : {
478 :
479 : protected:
480 :
481 : int m_type; ///< The property type, from pcf::IndiProperty::Type
482 :
483 : pcf::IndiProperty * m_property1 {nullptr}; ///< Pointer to the first property
484 :
485 : std::string m_element1; ///< The element name within the first property
486 :
487 : pcf::IndiProperty * m_property2 {nullptr}; ///< Pointer to the second property
488 :
489 : std::string m_element2; ///< The element name within the second property
490 :
491 : public:
492 :
493 : //Default c'tor is deleted, you must supply the property type
494 : twoPropRule() = delete;
495 :
496 : /// Constructor. You must provide the property type to construct a twoPropRule
497 14 : explicit twoPropRule( int type ) : m_type(type /**< The property type, from pcf::IndiProperty::Type*/)
498 14 : {}
499 :
500 : /// Set the first property pointer
501 : /**
502 : * \throws mx::err::invalidarg if \p property is nullptr
503 : * \throws mx::err::invalidconfig if the supplied property has the wrong type
504 : */
505 14 : void property1( pcf::IndiProperty * property /**< [in] the new property pointer*/)
506 : {
507 14 : if(property == nullptr)
508 : {
509 0 : mxThrowException(mx::err::invalidarg, "twoPropRule::property1", "property is nullptr");
510 : }
511 :
512 14 : if(property->getType() != m_type)
513 : {
514 0 : mxThrowException(mx::err::invalidconfig, "twoPropRule::property1", "property is not correct type");
515 : }
516 :
517 14 : m_property1 = property;
518 14 : }
519 :
520 : /// Get the first property pointer
521 : /**
522 : * \returns the current value of m_property1
523 : */
524 : const pcf::IndiProperty * property1()
525 : {
526 : return m_property1;
527 : }
528 :
529 : /// Set the first element name
530 14 : void element1(const std::string & el /**< [in] the new element name*/)
531 : {
532 14 : m_element1 = el;
533 14 : }
534 :
535 : /// Get the first element name
536 : /**
537 : * \returns the current value of m_element1
538 : */
539 : const std::string & element1()
540 : {
541 : return m_element1;
542 : }
543 :
544 : /// Set the second property pointer
545 : /**
546 : * \throws mx::err::invalidarg if \p property is nullptr
547 : * \throws mx::err::invalidconfig if the supplied property has the wrong type
548 : */
549 14 : void property2( pcf::IndiProperty * property /**< [in] the new property pointer*/)
550 : {
551 14 : if(property == nullptr)
552 : {
553 0 : mxThrowException(mx::err::invalidarg, "twoPropRule::property2", "property is nullptr");
554 : }
555 :
556 14 : if(property->getType() != m_type)
557 : {
558 0 : mxThrowException(mx::err::invalidconfig, "twoPropRule::property2", "property is not correct type");
559 : }
560 :
561 14 : m_property2 = property;
562 14 : }
563 :
564 : /// Get the second property pointer
565 : /**
566 : * \returns the current value of m_property2
567 : */
568 : const pcf::IndiProperty * property2()
569 : {
570 : return m_property2;
571 : }
572 :
573 : /// Set the second element name
574 14 : void element2(const std::string & el /**< [in] the new element name*/)
575 : {
576 14 : m_element2 = el;
577 14 : }
578 :
579 : /// Get the second element name
580 : /**
581 : * \returns the current value of m_element2
582 : */
583 : const std::string & element2()
584 : {
585 : return m_element2;
586 : }
587 :
588 : /// Check if this rule is valid
589 : /** The rule is valid if both property pointers are not null, and the elements
590 : * are contained within their respective properties.
591 : *
592 : * If not valid, the return value is a std::string with the reason.
593 : * If valid, the return value is a bool set to true.
594 : */
595 14 : virtual boolorerr_t valid()
596 : {
597 14 : boolorerr_t rv;
598 :
599 14 : if(m_property1 == nullptr)
600 : {
601 0 : rv = "property1 is null";
602 0 : return rv;
603 : }
604 :
605 14 : if(!m_property1->find(m_element1))
606 : {
607 0 : rv = "element1 is not found";
608 0 : return rv;
609 : }
610 :
611 14 : if(m_property2 == nullptr)
612 : {
613 0 : rv = "property2 is null";
614 0 : return rv;
615 : }
616 :
617 14 : if(!m_property2->find(m_element2))
618 : {
619 0 : rv = "element2 is not found";
620 0 : return rv;
621 : }
622 :
623 14 : rv = true;
624 :
625 14 : return rv;
626 0 : }
627 : };
628 :
629 : /// Compare the value of a number element to a target
630 : /**
631 : */
632 : struct numValRule : public onePropRule
633 : {
634 :
635 : public:
636 :
637 : /// Name of this rule, used by config system
638 : static constexpr char name[] = "numVal";
639 :
640 : protected:
641 :
642 : double m_target {0}; ///< The target value for comparison
643 : double m_tol {1e-6}; ///< The tolerance for the comparison
644 :
645 : public:
646 :
647 : /// Default c'tor.
648 14 : numValRule() : onePropRule(pcf::IndiProperty::Number)
649 14 : {}
650 :
651 : /// Set the target for the comparison
652 14 : void target( const double & tgt /**< [in] The new target*/)
653 : {
654 14 : m_target = tgt;
655 14 : }
656 :
657 : /// Get the target
658 : /**
659 : * \returns the current value of m_target
660 : */
661 0 : const double & target()
662 : {
663 0 : return m_target;
664 : }
665 :
666 : /// Set the tolerance
667 : /** This is used for equality comparison to allow for floating point precision
668 : * and text conversions in INDI. Set to 0 for strict comparison.
669 : *
670 : * \throws mx::err:invalidarg if the new value is negative
671 : */
672 1 : void tol( const double & t /**< [in] the new tolerance*/)
673 : {
674 1 : if(t < 0)
675 : {
676 0 : mxThrowException(mx::err::invalidarg, "numValRule::tol", "tolerance can't be negative");
677 : }
678 :
679 1 : m_tol = t;
680 1 : }
681 :
682 : /// Get the tolerance
683 : /**
684 : * \returns the current value of m_tol
685 : */
686 0 : const double & tol()
687 : {
688 0 : return m_tol;
689 : }
690 :
691 : /// Get the value of this rule
692 : /** First checks if the rule is currently valid. The performs the comparison and returns the result.
693 : *
694 : * \returns the value of the comparison, true or false
695 : *
696 : * \throws mx::err::invalidconfig if the rule is not currently valid
697 : * \throws mx::err::invalidconfig on an error from the comparison
698 : *
699 : */
700 14 : virtual bool value()
701 : {
702 14 : boolorerr_t rv = valid();
703 14 : if(isError(rv))
704 : {
705 0 : mxThrowException(mx::err::invalidconfig, "numValRule::value", std::get<std::string>(rv));
706 : }
707 :
708 14 : double val = (*m_property)[m_element].get<double>();
709 :
710 14 : rv = compNum(val, m_target, m_tol);
711 14 : if(isError(rv))
712 : {
713 0 : mxThrowException(mx::err::invalidconfig, "numValRule::value", std::get<std::string>(rv));
714 : }
715 :
716 28 : return std::get<bool>(rv);
717 14 : }
718 : };
719 :
720 : /// Compare the value of a text element to a target value
721 : /** Can only be Eq or Neq.
722 : */
723 : struct txtValRule : public onePropRule
724 : {
725 :
726 : public:
727 :
728 : /// Name of this rule, used by config system
729 : static constexpr char name[] = "txtVal";
730 :
731 : protected:
732 : std::string m_target; ///< The target value for comparison
733 :
734 : public:
735 :
736 : /// Default c'tor.
737 18 : txtValRule() : onePropRule(pcf::IndiProperty::Text)
738 18 : {}
739 :
740 : /// Set the target for the comparison
741 18 : void target(const std::string & target /**< [in] The new target*/)
742 : {
743 18 : m_target = target;
744 18 : }
745 :
746 : /// Get the target
747 : /**
748 : * \returns the current value of m_target
749 : */
750 0 : const std::string & target()
751 : {
752 0 : return m_target;
753 : }
754 :
755 : /// Get the value of this rule
756 : /** First checks if the rule is currently valid. The performs the comparison and returns the result.
757 : *
758 : * \returns the value of the comparison, true or false
759 : *
760 : * \throws mx::err::invalidconfig if the rule is not currently valid
761 : * \throws mx::err::invalidconfig on an error from the comparison
762 : *
763 : */
764 18 : virtual bool value()
765 : {
766 18 : boolorerr_t rv = valid();
767 18 : if(isError(rv))
768 : {
769 0 : mxThrowException(mx::err::invalidconfig, "txtValRule::value", std::get<std::string>(rv));
770 : }
771 :
772 18 : rv = compTxt((*m_property)[m_element].get(), m_target);
773 18 : if(isError(rv))
774 : {
775 0 : mxThrowException(mx::err::invalidconfig, "txtValRule::value()", std::get<std::string>(rv));
776 : }
777 :
778 36 : return std::get<bool>(rv);
779 18 : }
780 : };
781 :
782 : /// Compare the value of a switch to a target value
783 : /** Can only be Eq or Neq to On or Off.
784 : */
785 : struct swValRule : public onePropRule
786 : {
787 :
788 : public:
789 :
790 : /// Name of this rule, used by config system
791 : static constexpr char name[] = "swVal";
792 :
793 : protected:
794 : pcf::IndiElement::SwitchStateType m_target {pcf::IndiElement::UnknownSwitchState}; ///< The target value for comparison
795 :
796 : public:
797 :
798 : /// Default c'tor.
799 8 : swValRule() : onePropRule(pcf::IndiProperty::Switch )
800 8 : {}
801 :
802 : /// Set the target for the comparison
803 : void target(const pcf::IndiElement::SwitchStateType & ss /**< [in] The new target*/)
804 : {
805 : m_target = ss;
806 : }
807 :
808 : /// Set the target for the comparison
809 : /** This version provided for config file processing.
810 : *
811 : * \throws mx::err::invalidarg if switchState is something other than "On" or Off
812 : */
813 8 : void target(const std::string & switchState /**< [in] The new target*/)
814 : {
815 8 : if(switchState == "On")
816 : {
817 4 : m_target = pcf::IndiElement::On;
818 : }
819 4 : else if(switchState == "Off")
820 : {
821 4 : m_target = pcf::IndiElement::Off;
822 : }
823 : else
824 : {
825 0 : mxThrowException(mx::err::invalidarg, "swValRule::target", "invalid switch state");
826 : }
827 8 : }
828 :
829 : /// Get the target
830 : /**
831 : * \returns the current value of m_target
832 : */
833 : const pcf::IndiElement::SwitchStateType & target()
834 : {
835 : return m_target;
836 : }
837 :
838 : /// Get the value of this rule
839 : /** First checks if the rule is currently valid. The performs the comparison and returns the result.
840 : *
841 : * \returns the value of the comparison, true or false
842 : *
843 : * \throws mx::err::invalidconfig if the rule is not currently valid
844 : * \throws mx::err::invalidconfig on an error from the comparison
845 : *
846 : */
847 8 : virtual bool value()
848 : {
849 8 : boolorerr_t rv = valid();
850 8 : if(isError(rv))
851 : {
852 0 : mxThrowException(mx::err::invalidconfig, "swValRule::value", std::get<std::string>(rv));
853 : }
854 :
855 8 : rv = compSw((*m_property)[m_element].getSwitchState(), m_target);
856 8 : if(isError(rv))
857 : {
858 0 : mxThrowException(mx::err::invalidconfig, "elCompSwRule::value()", std::get<std::string>(rv));
859 : }
860 :
861 16 : return std::get<bool>(rv);
862 8 : }
863 : };
864 :
865 : /// Compare two elements based on their numeric values
866 : struct elCompNumRule : public twoPropRule
867 : {
868 :
869 : public:
870 :
871 : /// Name of this rule, used by config system
872 : static constexpr char name[] = "elCompNum";
873 :
874 : protected:
875 : double m_tol {1e-6}; ///< The tolerance for the comparison
876 :
877 : public:
878 :
879 : /// Default c'tor.
880 2 : elCompNumRule() : twoPropRule(pcf::IndiProperty::Number)
881 2 : {}
882 :
883 : /// Set the tolerance
884 : /** This is used for equality comparison to allow for floating point precision
885 : * and text conversions in INDI. Set to 0 for strict comparison.
886 : *
887 : * \throws mx::err:invalidarg if the new value is negative
888 : */
889 : void tol( const double & t /**< [in] the new tolerance*/)
890 : {
891 : if(t < 0)
892 : {
893 : mxThrowException(mx::err::invalidarg, "numValRule::tol", "tolerance can't be negative");
894 : }
895 :
896 : m_tol = t;
897 : }
898 :
899 : /// Get the tolerance
900 : /**
901 : * \returns the current value of m_tol
902 : */
903 : const double & tol()
904 : {
905 : return m_tol;
906 : }
907 :
908 : /// Get the value of this rule
909 : /** First checks if the rule is currently valid. The performs the comparison and returns the result.
910 : *
911 : * \returns the value of the comparison, true or false
912 : *
913 : * \throws mx::err::invalidconfig if the rule is not currently valid
914 : * \throws mx::err::invalidconfig on an error from the comparison
915 : *
916 : */
917 2 : virtual bool value()
918 : {
919 2 : boolorerr_t rv = valid();
920 2 : if(isError(rv))
921 : {
922 0 : mxThrowException(mx::err::invalidconfig, "elCompNumRule::value", std::get<std::string>(rv));
923 : }
924 :
925 2 : rv = compNum((*m_property1)[m_element1].get<double>(), (*m_property2)[m_element2].get<double>(), m_tol);
926 2 : if(isError(rv))
927 : {
928 0 : mxThrowException(mx::err::invalidconfig, "elCompNumRule::value()", std::get<std::string>(rv));
929 : }
930 :
931 4 : return std::get<bool>(rv);
932 2 : }
933 : };
934 :
935 : /// Compare two elements based on their text values
936 : struct elCompTxtRule : public twoPropRule
937 : {
938 : public:
939 :
940 : /// Name of this rule, used by config system
941 : static constexpr char name[] = "elCompTxt";
942 :
943 : /// Default c'tor.
944 4 : elCompTxtRule() : twoPropRule(pcf::IndiProperty::Text)
945 4 : {}
946 :
947 : /// Get the value of this rule
948 : /** First checks if the rule is currently valid. The performs the comparison and returns the result.
949 : *
950 : * \returns the value of the comparison, true or false
951 : *
952 : * \throws mx::err::invalidconfig if the rule is not currently valid
953 : * \throws mx::err::invalidconfig on an error from the comparison
954 : *
955 : */
956 4 : virtual bool value()
957 : {
958 4 : boolorerr_t rv = valid();
959 4 : if(isError(rv))
960 : {
961 0 : mxThrowException(mx::err::invalidconfig, "elCompTxtRule::value", std::get<std::string>(rv));
962 : }
963 :
964 4 : rv = compTxt((*m_property1)[m_element1].get(), (*m_property2)[m_element2].get());
965 4 : if(isError(rv))
966 : {
967 0 : mxThrowException(mx::err::invalidconfig, "elCompTxtRule::value()", std::get<std::string>(rv));
968 : }
969 :
970 8 : return std::get<bool>(rv);
971 4 : }
972 : };
973 :
974 : /// Compare two elements based on their switch values
975 : struct elCompSwRule : public twoPropRule
976 : {
977 :
978 : public:
979 :
980 : /// Name of this rule, used by config system
981 : static constexpr char name[] = "elCompSw";
982 :
983 : /// Default c'tor.
984 8 : elCompSwRule() : twoPropRule(pcf::IndiProperty::Switch)
985 8 : {}
986 :
987 : /// Get the value of this rule
988 : /** First checks if the rule is currently valid. The performs the comparison and returns the result.
989 : *
990 : * \returns the value of the comparison, true or false
991 : *
992 : * \throws mx::err::invalidconfig if the rule is not currently valid
993 : * \throws mx::err::invalidconfig on an error from the comparison
994 : *
995 : */
996 8 : virtual bool value()
997 : {
998 8 : boolorerr_t rv = valid();
999 8 : if(isError(rv))
1000 : {
1001 0 : mxThrowException(mx::err::invalidconfig, "elCompSwRule::value", std::get<std::string>(rv));
1002 : }
1003 :
1004 8 : rv = compSw((*m_property1)[m_element1].getSwitchState(), (*m_property2)[m_element2].getSwitchState());
1005 8 : if(isError(rv))
1006 : {
1007 0 : mxThrowException(mx::err::invalidconfig, "elCompSwRule::value()", std::get<std::string>(rv));
1008 : }
1009 :
1010 16 : return std::get<bool>(rv);
1011 8 : }
1012 : };
1013 :
1014 : /// A rule to compare two rules
1015 : /**
1016 : *
1017 : */
1018 : struct ruleCompRule: public indiCompRule
1019 : {
1020 :
1021 : public:
1022 :
1023 : /// Name of this rule, used by config system
1024 : static constexpr char name[] = "ruleComp";
1025 :
1026 : protected:
1027 : indiCompRule * m_rule1 {nullptr}; ///< rule one
1028 : indiCompRule * m_rule2 {nullptr}; ///< rule two
1029 :
1030 : public:
1031 :
1032 : /// Default c'tor
1033 : /** Changes default comparison to And for ruleCompRule
1034 : */
1035 9 : ruleCompRule()
1036 9 : {
1037 9 : comparison(ruleComparison::And);
1038 9 : }
1039 :
1040 : /// Set the pointer to the first rule
1041 9 : void rule1( indiCompRule * r /**< [in] the new pointer to rule1*/)
1042 : {
1043 9 : m_rule1 = r;
1044 9 : }
1045 :
1046 : /// Get the pointer to the first rule
1047 : /**
1048 : * \returns the current value of m_rule1
1049 : */
1050 : const indiCompRule * rule1()
1051 : {
1052 : return m_rule1;
1053 : }
1054 :
1055 : /// Set the pointer to the second rule
1056 9 : void rule2( indiCompRule * r /**< [in] the new pointer to rule2*/)
1057 : {
1058 9 : m_rule2 = r;
1059 9 : }
1060 :
1061 : /// Get the pointer to the first rule
1062 : /**
1063 : * \returns the current value of m_rule2
1064 : */
1065 : const indiCompRule * rule2()
1066 : {
1067 : return m_rule2;
1068 : }
1069 :
1070 : /// Check if this rule is valid
1071 : /** The rule is valid if the rule pointers are not nullptr, and if each rule is itself valid.
1072 : *
1073 : * If not valid, the return value is a std::string with the reason.
1074 : * If valid, the return value is a bool set to true.
1075 : */
1076 13 : virtual boolorerr_t valid()
1077 : {
1078 13 : boolorerr_t rv;
1079 13 : if(m_rule1 == nullptr)
1080 : {
1081 0 : rv = "rule1 is nullptr";
1082 : }
1083 13 : else if(m_rule2 == nullptr)
1084 : {
1085 0 : rv = "rule2 is nullptr";
1086 : }
1087 : else
1088 : {
1089 13 : rv = m_rule1->valid();
1090 13 : if(isError(rv))
1091 : {
1092 0 : return rv;
1093 : }
1094 :
1095 13 : rv = m_rule2->valid();
1096 13 : if(isError(rv))
1097 : {
1098 0 : return rv;
1099 : }
1100 :
1101 13 : rv = true;
1102 : }
1103 :
1104 13 : return rv;
1105 0 : }
1106 :
1107 : /// Get the value of this rule
1108 : /** First checks if the rule is currently valid. The performs the comparison and returns the result.
1109 : *
1110 : * \returns the value of the comparison, true or false
1111 : *
1112 : * \throws mx::err::invalidconfig if the rule is not currently valid
1113 : * \throws mx::err::invalidconfig on an error from the comparison
1114 : *
1115 : */
1116 9 : virtual bool value()
1117 : {
1118 9 : boolorerr_t rv = valid();
1119 9 : if(isError(rv))
1120 : {
1121 0 : mxThrowException(mx::err::invalidconfig, "ruleCompRule::value", std::get<std::string>(rv));
1122 : }
1123 :
1124 9 : rv = compBool(m_rule1->value(), m_rule2->value());
1125 9 : if(isError(rv))
1126 : {
1127 0 : mxThrowException(mx::err::invalidconfig, "ruleCompRule::value", std::get<std::string>(rv));
1128 : }
1129 :
1130 18 : return std::get<bool>(rv);
1131 9 : }
1132 : };
1133 :
1134 : #endif //stateRuleEngine_indiCompRules_hpp
|