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