API
 
Loading...
Searching...
No Matches
indiCompRules.hpp
Go to the documentation of this file.
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#include <vector>
12
13#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
14 //Included here for standalone testing of this file
15
16/// Logical comparisons for the INDI rules
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 */
38ruleComparison string2comp( const std::string &cstr )
39{
40 if( cstr == "Eq" )
41 {
42 return ruleComparison::Eq;
43 }
44 else if( cstr == "Neq" )
45 {
47 }
48 else if( cstr == "Lt" )
49 {
50 return ruleComparison::Lt;
51 }
52 else if( cstr == "Gt" )
53 {
54 return ruleComparison::Gt;
55 }
56 else if( cstr == "LtEq" )
57 {
59 }
60 else if( cstr == "GtEq" )
61 {
63 }
64 else if( cstr == "And" )
65 {
67 }
68 else if( cstr == "Nand" )
69 {
71 }
72 else if( cstr == "Or" )
73 {
74 return ruleComparison::Or;
75 }
76 else if( cstr == "Nor" )
77 {
79 }
80 else if( cstr == "Xor" )
81 {
83 }
84 else if( cstr == "Xnor" )
85 {
87 }
88 else if( cstr == "Imply" )
89 {
91 }
92 else if( cstr == "Nimply" )
93 {
95 }
96 else
97 {
98 throw mx::exception( mx::error_t::invalidarg, cstr + " is not a valid comparison" );
99 }
100}
101
102/// Get the string representation of a \ref ruleComparison member.
103/** Needed for processing configuration files.
104 */
105std::string comp2string( const ruleComparison &comparison /**< [in] the comparison enum value to stringify */ )
106{
107 switch( comparison )
108 {
110 return "Eq";
112 return "Neq";
114 return "Lt";
116 return "Gt";
118 return "LtEq";
120 return "GtEq";
122 return "And";
124 return "Nand";
126 return "Or";
128 return "Nor";
130 return "Imply";
132 return "Nimply";
133 default:
134 throw mx::exception( mx::error_t::invalidarg, "comparison is not valid" );
135 }
136}
137
138/// Reporting priorities for rules
139enum class rulePriority
140{
141 none, ///< Don't publish
142 info, ///< For information only
143 caution, ///< Caution -- make sure you know what you're doing
144 warning, ///< Warning -- something is probably wrong, you should check
145 alert ///< Alert -- something is definitely wrong, you should take action
146};
147
148/// Get the \ref rulePriority member from a string representation.
149/** Needed for processing configuration files
150 */
151rulePriority string2priority( const std::string &pstr )
152{
153 if( pstr == "none" )
154 {
155 return rulePriority::none;
156 }
157 else if( pstr == "info" )
158 {
159 return rulePriority::info;
160 }
161 else if( pstr == "caution" )
162 {
164 }
165 else if( pstr == "warning" )
166 {
168 }
169 else if( pstr == "alert" )
170 {
171 return rulePriority::alert;
172 }
173 else
174 {
175 throw mx::exception( mx::error_t::invalidarg, pstr + " is not a valid priority" );
176 }
177}
178
179/// Virtual base-class for all rules
180/** Provides error handling and comparison functions.
181 * Derived classes must implement valid() and value().
182 */
184{
185 public:
186 /// In-band error reporting type
187 typedef std::variant<bool, std::string> boolorerr_t;
188
189 /// Check if returned value indicates an error
190 bool isError( boolorerr_t rv /**< [in] the return value to check*/ )
191 {
192 return ( rv.index() > 0 );
193 }
194
195 static constexpr double default_info_msg_delay = 0; // Send once
196 static constexpr double default_caution_msg_delay = 60;
197 static constexpr double default_warning_msg_delay = 30;
198 static constexpr double default_alert_msg_delay = 5;
199
200 protected:
201 /// The reporting priority for this rule
203
204 /// The message used for notifications
205 std::string m_message;
206
207 timespec m_lastMsg{ 0, 0 }; ///< Time the message was last sent
208
209 double m_messageDelay{ 0 }; ///< Delay between sending messages
210
211 int m_messageCount{ 0 }; ///< Number of times the message has been sent
212
213 /// The comparison for this rule
215
216 public:
217 /// Virtual destructor
219 {
220 }
221
222 /// Get the default comparison for this rule type.
223 /**
224 * \returns the comparison that should be used when `comp` is omitted from configuration
225 */
227 {
228 return ruleComparison::Eq;
229 }
230
231 /// Set priority of this rule
232 /** Also sets the message delay, to default for priority if not set.
233 */
234 void priority( const rulePriority &p, /**< [in] the new priority */
235 double delay = -1 /**< [in] [opt] the message delay, if < 0 the default is used */
236 )
237 {
238 m_priority = p;
239
240 if( delay < 0 )
241 {
242 switch( m_priority )
243 {
246 break;
249 break;
252 break;
255 break;
256 default:
257 m_messageDelay = 0;
258 }
259 }
260 else
261 {
262 m_messageDelay = delay;
263 }
264 }
265
266 /// Get the rule priority
267 /**
268 * \returns the current rule priority
269 */
271 {
272 return m_priority;
273 }
274
275 /// Set the message
276 void message( const std::string &m /**< [in] the new message*/ )
277 {
278 m_message = m;
279 }
280
281 /// Get the message
282 /** Optionally sets the message time to now.
283 * \returns the current message
284 */
285 const std::string &message( bool settime = false /**< If true m_lastMsg is set to now */ )
286 {
287 if( settime )
288 {
289 if( clock_gettime( CLOCK_ISIO, &m_lastMsg ) < 0 )
290 {
291 throw mx::exception( mx::errno2error_t( errno ), "getting message time" );
292 }
293 }
294
295 return m_message;
296 }
297
298 const timespec &lastMsg()
299 {
300 return m_lastMsg;
301 }
302
303 /// Get the time since the last message
305 {
306 timespec ts;
307 if( clock_gettime( CLOCK_ISIO, &ts ) < 0 )
308 {
309 throw mx::exception( mx::errno2error_t( errno ), "getting current time" );
310 }
311
312 return ( 1.0 * ts.tv_sec + ts.tv_nsec / 1e9 ) - ( 1.0 * m_lastMsg.tv_sec + m_lastMsg.tv_nsec / 1e9 );
313 }
314
315 /// Check if it's time to send a message
316 /** If the message delay is <= 0, this is based on message count (i.e. has it been sent).
317 * Otherwise it's based on the time since last sent
318 */
320 {
321 if( m_messageDelay <= 0 )
322 {
323 if( m_messageCount == 0 )
324 {
325 return true;
326 }
327 else
328 {
329 return false;
330 }
331 }
332 else
333 {
335 {
336 return true;
337 }
338 else
339 {
340 return false;
341 }
342 }
343 }
344
345 /// Set the message delay
346 void messageDelay( double md /**< [in] the new message delay */ )
347 {
348 m_messageDelay = md;
349 }
350
351 /// Get the message delay
353 {
354 return m_messageDelay;
355 }
356
357 /// Set the message count
358 void messageCount( int mc /**< [in] the new message count */ )
359 {
360 m_messageCount = mc;
361 }
362
363 /// Increment the message count
365 {
367 return m_messageCount;
368 }
369
370 /// Get the message count
372 {
373 return m_messageCount;
374 }
375
376 /// Set the comparison for this rule
377 void comparison( const ruleComparison &c /**< [in] the new comparison*/ )
378 {
379 m_comparison = c;
380 }
381
382 /// Get the rule comparison
383 /**
384 * \returns the current rule comparison
385 *
386 */
388 {
389 return m_comparison;
390 }
391
392 /// Report whether the rule is valid as configured
393 /** If not valid, the return value is a std::string with the reason.
394 * If valid, the return value is a bool set to true.
395 */
396 virtual boolorerr_t valid() = 0;
397
398 /// Get the value of this rule
399 /**
400 * \returns the result of the comparison defined by the rule
401 */
402 virtual bool value() = 0;
403
404 /// Pop one pending runtime diagnostic, if any.
405 /**
406 * \returns true when a diagnostic string was returned
407 * \returns false when no diagnostic was pending
408 */
409 virtual bool popRuntimeDiagnostic( std::string &diagnostic /**< [out] the next pending diagnostic message */ )
410 {
411 diagnostic = "";
412 return false;
413 }
414
415 /// Compare two strings
416 /** String comparison can only be Eq or Neq.
417 *
418 * \returns true if the comparison is true
419 * \returns false if the comparison is false
420 * \returns std::string with error message if the comparison is not valid
421 */
422 boolorerr_t compTxt( const std::string &str1, ///< [in] the first string to compare
423 const std::string &str2 ///< [in] the second string to compare
424 )
425 {
426 boolorerr_t rv = false;
427
428 switch( m_comparison )
429 {
431 if( str1 == str2 )
432 rv = true;
433 break;
435 if( str1 != str2 )
436 rv = true;
437 break;
438 default:
439 rv = "operator not valid for string comparison";
440 }
441
442 return rv;
443 }
444
445 /// Compare two switches
446 /** Switch comparison can only be Eq or Neq.
447 *
448 * \returns true if the comparison is true
449 * \returns false if the comparison is false
450 * \returns std::string with error message if the comparison is not valid
451 */
452 boolorerr_t compSw( const pcf::IndiElement::SwitchStateType &sw1, ///< [in] the first switch to compare
453 const pcf::IndiElement::SwitchStateType &sw2 ///< [in] the first switch to compare
454 )
455 {
456 boolorerr_t rv = false;
457
458 switch( m_comparison )
459 {
461 if( sw1 == sw2 )
462 rv = true;
463 break;
465 if( sw1 != sw2 )
466 rv = true;
467 break;
468 default:
469 rv = "operator not valid for switch comparison";
470 }
471
472 return rv;
473 }
474
475 /// Compare two numbers
476 /** The comparison is (num1 comp num2), e.g. (num1 < num2).
477 * A tolerance is included for floating point equality.
478 *
479 * \returns true if the comparison is true
480 * \returns false if the comparison is false
481 * \returns std::string with error message if the comparison is not valid
482 */
483 boolorerr_t compNum( const double &num1, ///< [in] the first number to compare
484 const double &num2, ///< [in] the second number to compare
485 const double &tol ///< [in] the tolerance for the comparison
486 )
487 {
488 boolorerr_t rv = false;
489
490 switch( m_comparison )
491 {
493 if( fabs( num1 - num2 ) <= tol )
494 rv = true;
495 break;
497 if( fabs( num1 - num2 ) > tol )
498 rv = true;
499 break;
501 if( num1 < num2 )
502 rv = true;
503 break;
505 if( num1 > num2 )
506 rv = true;
507 break;
509 if( fabs( num1 - num2 ) <= tol )
510 rv = true;
511 else if( num1 < num2 )
512 rv = true;
513 break;
515 if( fabs( num1 - num2 ) <= tol )
516 rv = true;
517 else if( num1 > num2 )
518 rv = true;
519 break;
520 default:
521 rv = "operator not valid for compNum";
522 }
523
524 return rv;
525 }
526
527 /// Compare two booleans
528 /**
529 * \returns true if the comparison is true
530 * \returns false if the comparison is false
531 * \returns std::string with error message if the comparison is not valid
532 */
533 boolorerr_t compBool( const bool &b1, ///< [in] the first bool to compare
534 const bool &b2 ///< [in] the second bool to compare
535 )
536 {
537 boolorerr_t rv = false;
538
539 switch( m_comparison )
540 {
542 if( b1 == b2 )
543 rv = true;
544 break;
546 if( b1 != b2 )
547 rv = true;
548 break;
550 if( b1 && b2 )
551 rv = true;
552 break;
554 if( !( b1 && b2 ) )
555 rv = true;
556 break;
558 if( b1 || b2 )
559 rv = true;
560 break;
562 if( !b1 && !b2 )
563 rv = true;
564 break;
566 // https://en.wikipedia.org/wiki/Material_conditional
567 if( !b1 || b2 )
568 rv = true;
569 break;
571 // https://en.wikipedia.org/wiki/Material_nonimplication
572 if( b1 && !b2 )
573 rv = true;
574 break;
575 default:
576 rv = "operator not valid for ruleCompRule";
577 }
578
579 return rv;
580 }
581};
582
583/// A rule base class for testing an element in one property
585{
586
587 protected:
588 int m_type; ///< The property type, from pcf::IndiProperty::Type
589
590 pcf::IndiProperty *m_property{ nullptr }; ///< Pointer to the property
591
592 std::string m_element; ///< The element name within the property
593
594 public:
595 // Default c'tor is deleted, you must supply the property type
596 onePropRule() = delete;
597
598 /// Constructor. You must provide the property type to construct a onePropRule
599 explicit onePropRule( int type ) : m_type( type /**< The property type, from pcf::IndiProperty::Type*/ )
600 {
601 }
602
603 /// Set the property pointer
604 /**
605 * \throws mx::err::invalidarg if \p property is nullptr
606 * \throws mx::err::invalidconfig if the supplied property has the wrong type
607 */
608 void property( pcf::IndiProperty *property /**< [in] the new property pointer*/ )
609 {
610 if( property == nullptr )
611 {
612 throw mx::exception( mx::error_t::invalidarg, "property is nullptr" );
613 }
614
615 if( property->getType() != m_type )
616 {
617 throw mx::exception( mx::error_t::invalidconfig, "property is not correct type" );
618 }
619
621 }
622
623 /// Get the property pointer
624 /**
625 * \returns the current value of m_property
626 */
627 const pcf::IndiProperty *property()
628 {
629 return m_property;
630 }
631
632 /// Set the element name
633 void element( const std::string &el /**< [in] the new element name*/ )
634 {
635 m_element = el;
636 }
637
638 /// Get the element name
639 /**
640 * \returns the current value of m_element
641 */
642 const std::string &element()
643 {
644 return m_element;
645 }
646
647 /// Check if this rule is valid
648 /** The rule is valid if the property pointer is not null, and the element
649 * is contained within the property.
650 *
651 * If not valid, the return value is a std::string with the reason.
652 * If valid, the return value is a bool set to true.
653 */
655 {
656 boolorerr_t rv;
657 if( m_property == nullptr )
658 {
659 rv = "property is null";
660 }
661 else if( !m_property->find( m_element ) )
662 {
663 rv = "element is not found";
664 }
665 else
666 {
667 rv = true;
668 }
669
670 return rv;
671 }
672};
673
674/// A rule base class for testing elements in two properties
676{
677
678 protected:
679 int m_type; ///< The property type, from pcf::IndiProperty::Type
680
681 pcf::IndiProperty *m_property1{ nullptr }; ///< Pointer to the first property
682
683 std::string m_element1; ///< The element name within the first property
684
685 pcf::IndiProperty *m_property2{ nullptr }; ///< Pointer to the second property
686
687 std::string m_element2; ///< The element name within the second property
688
689 public:
690 // Default c'tor is deleted, you must supply the property type
691 twoPropRule() = delete;
692
693 /// Constructor. You must provide the property type to construct a twoPropRule
694 explicit twoPropRule( int type ) : m_type( type /**< The property type, from pcf::IndiProperty::Type*/ )
695 {
696 }
697
698 /// Set the first property pointer
699 /**
700 * \throws mx::err::invalidarg if \p property is nullptr
701 * \throws mx::err::invalidconfig if the supplied property has the wrong type
702 */
703 void property1( pcf::IndiProperty *property /**< [in] the new property pointer*/ )
704 {
705 if( property == nullptr )
706 {
707 throw mx::exception( mx::error_t::invalidarg, "property is nullptr" );
708 }
709
710 if( property->getType() != m_type )
711 {
712 throw mx::exception( mx::error_t::invalidconfig, "property is not correct type" );
713 }
714
715 m_property1 = property;
716 }
717
718 /// Get the first property pointer
719 /**
720 * \returns the current value of m_property1
721 */
722 const pcf::IndiProperty *property1()
723 {
724 return m_property1;
725 }
726
727 /// Set the first element name
728 void element1( const std::string &el /**< [in] the new element name*/ )
729 {
730 m_element1 = el;
731 }
732
733 /// Get the first element name
734 /**
735 * \returns the current value of m_element1
736 */
737 const std::string &element1()
738 {
739 return m_element1;
740 }
741
742 /// Set the second property pointer
743 /**
744 * \throws mx::err::invalidarg if \p property is nullptr
745 * \throws mx::err::invalidconfig if the supplied property has the wrong type
746 */
747 void property2( pcf::IndiProperty *property /**< [in] the new property pointer*/ )
748 {
749 if( property == nullptr )
750 {
751 throw mx::exception( mx::error_t::invalidarg, "property is nullptr" );
752 }
753
754 if( property->getType() != m_type )
755 {
756 throw mx::exception( mx::error_t::invalidconfig, "property is not correct type" );
757 }
758
759 m_property2 = property;
760 }
761
762 /// Get the second property pointer
763 /**
764 * \returns the current value of m_property2
765 */
766 const pcf::IndiProperty *property2()
767 {
768 return m_property2;
769 }
770
771 /// Set the second element name
772 void element2( const std::string &el /**< [in] the new element name*/ )
773 {
774 m_element2 = el;
775 }
776
777 /// Get the second element name
778 /**
779 * \returns the current value of m_element2
780 */
781 const std::string &element2()
782 {
783 return m_element2;
784 }
785
786 /// Check if this rule is valid
787 /** The rule is valid if both property pointers are not null, and the elements
788 * are contained within their respective properties.
789 *
790 * If not valid, the return value is a std::string with the reason.
791 * If valid, the return value is a bool set to true.
792 */
794 {
795 boolorerr_t rv;
796
797 if( m_property1 == nullptr )
798 {
799 rv = "property1 is null";
800 return rv;
801 }
802
803 if( !m_property1->find( m_element1 ) )
804 {
805 rv = "element1 is not found";
806 return rv;
807 }
808
809 if( m_property2 == nullptr )
810 {
811 rv = "property2 is null";
812 return rv;
813 }
814
815 if( !m_property2->find( m_element2 ) )
816 {
817 rv = "element2 is not found";
818 return rv;
819 }
820
821 rv = true;
822
823 return rv;
824 }
825};
826
827/// Compare the value of a number element to a target
828/**
829 */
830struct numValRule : public onePropRule
831{
832
833 public:
834 /// Name of this rule, used by config system
835 static constexpr char name[] = "numVal";
836
837 protected:
838 double m_target{ 0 }; ///< The target value for comparison
839 double m_tol{ 1e-6 }; ///< The tolerance for the comparison
840
841 public:
842 /// Default c'tor.
843 numValRule() : onePropRule( pcf::IndiProperty::Number )
844 {
845 }
846
847 /// Set the target for the comparison
848 void target( const double &tgt /**< [in] The new target*/ )
849 {
850 m_target = tgt;
851 }
852
853 /// Get the target
854 /**
855 * \returns the current value of m_target
856 */
857 const double &target()
858 {
859 return m_target;
860 }
861
862 /// Set the tolerance
863 /** This is used for equality comparison to allow for floating point precision
864 * and text conversions in INDI. Set to 0 for strict comparison.
865 *
866 * \throws mx::err:invalidarg if the new value is negative
867 */
868 void tol( const double &t /**< [in] the new tolerance*/ )
869 {
870 if( t < 0 )
871 {
872 throw mx::exception( mx::error_t::invalidarg, "tolerance can't be negative" );
873 }
874
875 m_tol = t;
876 }
877
878 /// Get the tolerance
879 /**
880 * \returns the current value of m_tol
881 */
882 const double &tol()
883 {
884 return m_tol;
885 }
886
887 /// Get the value of this rule
888 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
889 *
890 * \returns the value of the comparison, true or false
891 *
892 * \throws mx::err::invalidconfig if the rule is not currently valid
893 * \throws mx::err::invalidconfig on an error from the comparison
894 *
895 */
896 virtual bool value()
897 {
898 boolorerr_t rv = valid();
899 if( isError( rv ) )
900 {
901 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
902 }
903
904 double val = ( *m_property )[m_element].get<double>();
905
906 rv = compNum( val, m_target, m_tol );
907 if( isError( rv ) )
908 {
909 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
910 }
911
912 return std::get<bool>( rv );
913 }
914};
915
916/// Compare the value of a text element to a target value
917/** Can only be Eq or Neq.
918 */
919struct txtValRule : public onePropRule
920{
921
922 public:
923 /// Name of this rule, used by config system
924 static constexpr char name[] = "txtVal";
925
926 protected:
927 std::string m_target; ///< The target value for comparison
928
929 public:
930 /// Default c'tor.
931 txtValRule() : onePropRule( pcf::IndiProperty::Text )
932 {
933 }
934
935 /// Set the target for the comparison
936 void target( const std::string &target /**< [in] The new target*/ )
937 {
939 }
940
941 /// Get the target
942 /**
943 * \returns the current value of m_target
944 */
945 const std::string &target()
946 {
947 return m_target;
948 }
949
950 /// Get the value of this rule
951 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
952 *
953 * \returns the value of the comparison, true or false
954 *
955 * \throws mx::err::invalidconfig if the rule is not currently valid
956 * \throws mx::err::invalidconfig on an error from the comparison
957 *
958 */
959 virtual bool value()
960 {
961 boolorerr_t rv = valid();
962 if( isError( rv ) )
963 {
964 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
965 }
966
967 rv = compTxt( ( *m_property )[m_element].get(), m_target );
968 if( isError( rv ) )
969 {
970 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
971 }
972
973 return std::get<bool>( rv );
974 }
975};
976
977/// Compare the value of a switch to a target value
978/** Can only be Eq or Neq to On or Off.
979 */
980struct swValRule : public onePropRule
981{
982
983 public:
984 /// Name of this rule, used by config system
985 static constexpr char name[] = "swVal";
986
987 protected:
988 pcf::IndiElement::SwitchStateType m_target{
989 pcf::IndiElement::UnknownSwitchState }; ///< The target value for comparison
990
991 public:
992 /// Default c'tor.
993 swValRule() : onePropRule( pcf::IndiProperty::Switch )
994 {
995 }
996
997 /// Set the target for the comparison
998 void target( const pcf::IndiElement::SwitchStateType &ss /**< [in] The new target*/ )
999 {
1000 m_target = ss;
1001 }
1002
1003 /// Set the target for the comparison
1004 /** This version provided for config file processing.
1005 *
1006 * \throws mx::err::invalidarg if switchState is something other than "On" or Off
1007 */
1008 void target( const std::string &switchState /**< [in] The new target*/ )
1009 {
1010 if( switchState == "On" )
1011 {
1012 m_target = pcf::IndiElement::On;
1013 }
1014 else if( switchState == "Off" )
1015 {
1016 m_target = pcf::IndiElement::Off;
1017 }
1018 else
1019 {
1020 throw mx::exception( mx::error_t::invalidarg, "invalid switch state" );
1021 }
1022 }
1023
1024 /// Get the target
1025 /**
1026 * \returns the current value of m_target
1027 */
1028 const pcf::IndiElement::SwitchStateType &target()
1029 {
1030 return m_target;
1031 }
1032
1033 /// Get the value of this rule
1034 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
1035 *
1036 * \returns the value of the comparison, true or false
1037 *
1038 * \throws mx::err::invalidconfig if the rule is not currently valid
1039 * \throws mx::err::invalidconfig on an error from the comparison
1040 *
1041 */
1042 virtual bool value()
1043 {
1044 boolorerr_t rv = valid();
1045 if( isError( rv ) )
1046 {
1047 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1048 }
1049
1050 rv = compSw( ( *m_property )[m_element].getSwitchState(), m_target );
1051 if( isError( rv ) )
1052 {
1053 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1054 }
1055
1056 return std::get<bool>( rv );
1057 }
1058};
1059
1060/// Compare the difference in time between a value and now
1061/** Now is the time of evaluation of the rule
1062 */
1064{
1065
1066 public:
1067 /// Name of this rule, used by config system
1068 static constexpr char name[] = "timeDiff";
1069
1070 protected:
1071 double m_target{ 0 }; ///< The target value for comparison
1072 double m_tol{ 1e-6 }; ///< The tolerance for the comparison
1073
1074 public:
1075 /// Default c'tor.
1076 timeDiffRule() : onePropRule( pcf::IndiProperty::Number )
1077 {
1078 }
1079
1080 /// Set the target for the comparison
1081 void target( const double &tgt /**< [in] The new target*/ )
1082 {
1083 m_target = tgt;
1084 }
1085
1086 /// Get the target
1087 /**
1088 * \returns the current value of m_target
1089 */
1090 const double &target()
1091 {
1092 return m_target;
1093 }
1094
1095 /// Set the tolerance
1096 /** This is used for equality comparison to allow for floating point precision
1097 * and text conversions in INDI. Set to 0 for strict comparison.
1098 *
1099 * \throws mx::err:invalidarg if the new value is negative
1100 */
1101 void tol( const double &t /**< [in] the new tolerance*/ )
1102 {
1103 if( t < 0 )
1104 {
1105 throw mx::exception( mx::error_t::invalidarg, "tolerance can't be negative" );
1106 }
1107
1108 m_tol = t;
1109 }
1110
1111 /// Get the tolerance
1112 /**
1113 * \returns the current value of m_tol
1114 */
1115 const double &tol()
1116 {
1117 return m_tol;
1118 }
1119
1120 /// Get the value of this rule
1121 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
1122 *
1123 * \returns the value of the comparison, true or false
1124 *
1125 * \throws mx::err::invalidconfig if the rule is not currently valid
1126 * \throws mx::err::invalidconfig on an error from the comparison
1127 *
1128 */
1129 virtual bool value()
1130 {
1131 boolorerr_t rv = valid();
1132 if( isError( rv ) )
1133 {
1134 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1135 }
1136
1137 timespec now;
1138 clock_gettime( CLOCK_ISIO, &now );
1139
1140 double val = ( 1.0 * now.tv_sec + now.tv_nsec / 1e9 ) - ( *m_property )[m_element].get<double>();
1141
1142 rv = compNum( val, m_target, m_tol );
1143 if( isError( rv ) )
1144 {
1145 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1146 }
1147
1148 return std::get<bool>( rv );
1149 }
1150};
1151
1152/// Compare two elements based on their numeric values
1154{
1155
1156 public:
1157 /// Name of this rule, used by config system
1158 static constexpr char name[] = "elCompNum";
1159
1160 protected:
1161 double m_tol{ 1e-6 }; ///< The tolerance for the comparison
1162
1163 public:
1164 /// Default c'tor.
1165 elCompNumRule() : twoPropRule( pcf::IndiProperty::Number )
1166 {
1167 }
1168
1169 /// Set the tolerance
1170 /** This is used for equality comparison to allow for floating point precision
1171 * and text conversions in INDI. Set to 0 for strict comparison.
1172 *
1173 * \throws mx::err:invalidarg if the new value is negative
1174 */
1175 void tol( const double &t /**< [in] the new tolerance*/ )
1176 {
1177 if( t < 0 )
1178 {
1179 throw mx::exception( mx::error_t::invalidarg, "tolerance can't be negative" );
1180 }
1181
1182 m_tol = t;
1183 }
1184
1185 /// Get the tolerance
1186 /**
1187 * \returns the current value of m_tol
1188 */
1189 const double &tol()
1190 {
1191 return m_tol;
1192 }
1193
1194 /// Get the value of this rule
1195 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
1196 *
1197 * \returns the value of the comparison, true or false
1198 *
1199 * \throws mx::err::invalidconfig if the rule is not currently valid
1200 * \throws mx::err::invalidconfig on an error from the comparison
1201 *
1202 */
1203 virtual bool value()
1204 {
1205 boolorerr_t rv = valid();
1206 if( isError( rv ) )
1207 {
1208 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1209 }
1210
1211 rv = compNum( ( *m_property1 )[m_element1].get<double>(), ( *m_property2 )[m_element2].get<double>(), m_tol );
1212 if( isError( rv ) )
1213 {
1214 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1215 }
1216
1217 return std::get<bool>( rv );
1218 }
1219};
1220
1221/// Compare two elements based on their text values
1223{
1224 public:
1225 /// Name of this rule, used by config system
1226 static constexpr char name[] = "elCompTxt";
1227
1228 /// Default c'tor.
1229 elCompTxtRule() : twoPropRule( pcf::IndiProperty::Text )
1230 {
1231 }
1232
1233 /// Get the value of this rule
1234 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
1235 *
1236 * \returns the value of the comparison, true or false
1237 *
1238 * \throws mx::err::invalidconfig if the rule is not currently valid
1239 * \throws mx::err::invalidconfig on an error from the comparison
1240 *
1241 */
1242 virtual bool value()
1243 {
1244 boolorerr_t rv = valid();
1245 if( isError( rv ) )
1246 {
1247 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1248 }
1249
1250 rv = compTxt( ( *m_property1 )[m_element1].get(), ( *m_property2 )[m_element2].get() );
1251 if( isError( rv ) )
1252 {
1253 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1254 }
1255
1256 return std::get<bool>( rv );
1257 }
1258};
1259
1260/// Compare two elements based on their switch values
1262{
1263
1264 public:
1265 /// Name of this rule, used by config system
1266 static constexpr char name[] = "elCompSw";
1267
1268 /// Default c'tor.
1269 elCompSwRule() : twoPropRule( pcf::IndiProperty::Switch )
1270 {
1271 }
1272
1273 /// Get the value of this rule
1274 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
1275 *
1276 * \returns the value of the comparison, true or false
1277 *
1278 * \throws mx::err::invalidconfig if the rule is not currently valid
1279 * \throws mx::err::invalidconfig on an error from the comparison
1280 *
1281 */
1282 virtual bool value()
1283 {
1284 boolorerr_t rv = valid();
1285 if( isError( rv ) )
1286 {
1287 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1288 }
1289
1290 rv = compSw( ( *m_property1 )[m_element1].getSwitchState(), ( *m_property2 )[m_element2].getSwitchState() );
1291 if( isError( rv ) )
1292 {
1293 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1294 }
1295
1296 return std::get<bool>( rv );
1297 }
1298};
1299
1300/// Build and compare a switch-name combination against a target switch vector.
1301/**
1302 * This rule reads the currently active element name from each source switch
1303 * property, combines those names with a literal `{}` placeholder format string,
1304 * and compares the result against the currently active element name in a target
1305 * switch property.
1306 */
1308{
1309
1310 public:
1311 /// Name of this rule, used by config system
1312 static constexpr char name[] = "multiSwitchCombo";
1313
1314 protected:
1315 /// The configuration-section name of this rule, used in diagnostics.
1316 std::string m_ruleName;
1317
1318 /// The source switch properties in format-substitution order.
1319 std::vector<pcf::IndiProperty *> m_properties;
1320
1321 /// The config keys for the source switch properties, used in diagnostics.
1322 std::vector<std::string> m_propertyKeys;
1323
1324 /// Per-source latch state used to avoid repeated multi-On diagnostics.
1325 std::vector<bool> m_multiOn;
1326
1327 /// The literal format string used to combine source switch names.
1328 std::string m_format;
1329
1330 /// The target switch property whose active element name is compared.
1331 pcf::IndiProperty *m_targetProperty{ nullptr };
1332
1333 /// The config key for the target property, used in diagnostics.
1335
1336 /// Latch state used to avoid repeated target multi-On diagnostics.
1337 bool m_targetMultiOn{ false };
1338
1339 /// Runtime diagnostics that should be logged without failing evaluation.
1340 std::vector<std::string> m_pendingDiagnostics;
1341
1342 /// Count plain `{}` placeholders and reject any other brace usage.
1343 size_t formatPlaceholders( bool &invalidBraces /**< [out] true when unsupported brace syntax is present */ ) const
1344 {
1345 invalidBraces = false;
1346
1347 size_t count = 0;
1348 for( size_t n = 0; n < m_format.size(); ++n )
1349 {
1350 if( m_format[n] == '{' )
1351 {
1352 if( n + 1 < m_format.size() && m_format[n + 1] == '}' )
1353 {
1354 ++count;
1355 ++n;
1356 }
1357 else
1358 {
1359 invalidBraces = true;
1360 return count;
1361 }
1362 }
1363 else if( m_format[n] == '}' )
1364 {
1365 invalidBraces = true;
1366 return count;
1367 }
1368 }
1369
1370 return count;
1371 }
1372
1373 /// Resolve the active element name for a switch property.
1374 std::string activeName( pcf::IndiProperty *property, /**< [in] the property to inspect */
1375 const std::string &propertyKey, /**< [in] the config key for diagnostics */
1376 bool &multiOnLatched /**< [in/out] the multi-On diagnostic latch */
1377 )
1378 {
1379 size_t onCount = 0;
1380 std::string active;
1381
1382 for( auto &&el : property->getElements() )
1383 {
1384 if( el.second.getSwitchState() == pcf::IndiElement::On )
1385 {
1386 if( onCount == 0 )
1387 {
1388 active = el.first;
1389 }
1390
1391 ++onCount;
1392 }
1393 }
1394
1395 if( onCount == 0 )
1396 {
1397 multiOnLatched = false;
1398 return "";
1399 }
1400
1401 if( onCount == 1 )
1402 {
1403 multiOnLatched = false;
1404 return active;
1405 }
1406
1407 if( !multiOnLatched )
1408 {
1409 std::string ruleName = m_ruleName;
1410 if( ruleName == "" )
1411 {
1412 ruleName = "<unnamed>";
1413 }
1414
1415 m_pendingDiagnostics.push_back(
1416 std::format( "multiSwitchCombo rule {} found multiple switch elements On in {}; using empty string",
1417 ruleName,
1418 propertyKey ) );
1419 }
1420
1421 multiOnLatched = true;
1422
1423 return "";
1424 }
1425
1426 /// Apply literal `{}` substitution to build the comparison string.
1427 std::string
1428 formatCombo( const std::vector<std::string> &values /**< [in] the source switch names in order */ ) const
1429 {
1430 std::string combo;
1431 size_t valueIndex = 0;
1432
1433 for( size_t n = 0; n < m_format.size(); ++n )
1434 {
1435 if( m_format[n] == '{' && n + 1 < m_format.size() && m_format[n + 1] == '}' )
1436 {
1437 combo += values[valueIndex];
1438 ++valueIndex;
1439 ++n;
1440 }
1441 else
1442 {
1443 combo += m_format[n];
1444 }
1445 }
1446
1447 return combo;
1448 }
1449
1450 public:
1451 /// Default c'tor.
1452 /** Changes the default comparison to Neq for mismatch detection.
1453 */
1458
1459 /// Get the default comparison for this rule type.
1460 /**
1461 * \returns `ruleComparison::Neq`
1462 */
1464 {
1465 return ruleComparison::Neq;
1466 }
1467
1468 /// Set the rule name used in diagnostics.
1469 void ruleName( const std::string &ruleName /**< [in] the config-section name of this rule */ )
1470 {
1472 }
1473
1474 /// Get the configured rule name.
1475 /**
1476 * \returns the current value of m_ruleName
1477 */
1478 const std::string &ruleName()
1479 {
1480 return m_ruleName;
1481 }
1482
1483 /// Append one source switch property.
1484 void property( pcf::IndiProperty *property, /**< [in] the next source switch property */
1485 const std::string &propertyKey /**< [in] the config key for the source property */
1486 )
1487 {
1488 if( property == nullptr )
1489 {
1490 throw mx::exception( mx::error_t::invalidarg, "property is nullptr" );
1491 }
1492
1493 if( property->getType() != pcf::IndiProperty::Switch )
1494 {
1495 throw mx::exception( mx::error_t::invalidconfig, "property is not correct type" );
1496 }
1497
1498 m_properties.push_back( property );
1499 m_propertyKeys.push_back( propertyKey );
1500 m_multiOn.push_back( false );
1501 }
1502
1503 /// Get a source switch property by index.
1504 /**
1505 * \returns the current source switch property at index `n`
1506 */
1507 const pcf::IndiProperty *property( size_t n /**< [in] the zero-based source property index */ )
1508 {
1509 if( n >= m_properties.size() )
1510 {
1511 throw mx::exception( mx::error_t::invalidarg, "source property index out of range" );
1512 }
1513
1514 return m_properties[n];
1515 }
1516
1517 /// Get a source property key by index.
1518 /**
1519 * \returns the configured property key at index `n`
1520 */
1521 const std::string &propertyKey( size_t n /**< [in] the zero-based source property index */ )
1522 {
1523 if( n >= m_propertyKeys.size() )
1524 {
1525 throw mx::exception( mx::error_t::invalidarg, "source property key index out of range" );
1526 }
1527
1528 return m_propertyKeys[n];
1529 }
1530
1531 /// Get the number of configured source switch properties.
1532 /**
1533 * \returns the current number of source properties
1534 */
1536 {
1537 return m_properties.size();
1538 }
1539
1540 /// Set the literal format string for the source switch names.
1541 void format( const std::string &format /**< [in] the literal `{}` placeholder format string */ )
1542 {
1543 m_format = format;
1544 }
1545
1546 /// Get the literal format string.
1547 /**
1548 * \returns the current value of m_format
1549 */
1550 const std::string &format()
1551 {
1552 return m_format;
1553 }
1554
1555 /// Set the target switch property.
1556 void targetProperty( pcf::IndiProperty *property /**< [in] the target switch property */ )
1557 {
1558 if( property == nullptr )
1559 {
1560 throw mx::exception( mx::error_t::invalidarg, "targetProperty is nullptr" );
1561 }
1562
1563 if( property->getType() != pcf::IndiProperty::Switch )
1564 {
1565 throw mx::exception( mx::error_t::invalidconfig, "targetProperty is not correct type" );
1566 }
1567
1569 }
1570
1571 /// Get the target switch property.
1572 /**
1573 * \returns the current target switch property
1574 */
1575 const pcf::IndiProperty *targetProperty()
1576 {
1577 return m_targetProperty;
1578 }
1579
1580 /// Set the target property key used in diagnostics.
1581 void targetPropertyKey( const std::string &propertyKey /**< [in] the config key for the target property */ )
1582 {
1584 }
1585
1586 /// Get the target property key used in diagnostics.
1587 /**
1588 * \returns the current value of m_targetPropertyKey
1589 */
1590 const std::string &targetPropertyKey()
1591 {
1592 return m_targetPropertyKey;
1593 }
1594
1595 /// Check if this rule is valid as configured.
1597 {
1598 if( m_properties.size() == 0 )
1599 {
1600 return "no source switch properties configured";
1601 }
1602
1603 if( m_properties.size() != m_propertyKeys.size() || m_properties.size() != m_multiOn.size() )
1604 {
1605 return "source switch configuration is inconsistent";
1606 }
1607
1608 for( size_t n = 0; n < m_properties.size(); ++n )
1609 {
1610 if( m_properties[n] == nullptr )
1611 {
1612 return std::format( "property{} is nullptr", n + 1 );
1613 }
1614
1615 if( m_properties[n]->getType() != pcf::IndiProperty::Switch )
1616 {
1617 return std::format( "property{} is not a switch property", n + 1 );
1618 }
1619
1620 if( m_propertyKeys[n] == "" )
1621 {
1622 return std::format( "property{} key is empty", n + 1 );
1623 }
1624 }
1625
1626 if( m_targetProperty == nullptr )
1627 {
1628 return "targetProperty is nullptr";
1629 }
1630
1631 if( m_targetProperty->getType() != pcf::IndiProperty::Switch )
1632 {
1633 return "targetProperty is not a switch property";
1634 }
1635
1636 if( m_targetPropertyKey == "" )
1637 {
1638 return "targetProperty key is empty";
1639 }
1640
1641 bool invalidBraces = false;
1642 size_t placeholders = formatPlaceholders( invalidBraces );
1643 if( invalidBraces )
1644 {
1645 return "format only supports literal {} placeholders";
1646 }
1647
1648 if( placeholders != m_properties.size() )
1649 {
1650 return std::format(
1651 "format placeholder count {} does not match numSwitches {}", placeholders, m_properties.size() );
1652 }
1653
1655 {
1656 return "operator not valid for multiSwitchComboRule";
1657 }
1658
1659 return true;
1660 }
1661
1662 /// Get the value of this rule.
1663 /** First checks if the rule is currently valid. Then performs the combo-name
1664 * comparison and returns the result.
1665 *
1666 * \returns the value of the comparison, true or false
1667 *
1668 * \throws mx::err::invalidconfig if the rule is not currently valid
1669 * \throws mx::err::invalidconfig on an error from the comparison
1670 */
1671 virtual bool value()
1672 {
1673 boolorerr_t rv = valid();
1674 if( isError( rv ) )
1675 {
1676 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1677 }
1678
1679 std::vector<std::string> activeNames;
1680 activeNames.reserve( m_properties.size() );
1681 for( size_t n = 0; n < m_properties.size(); ++n )
1682 {
1683 bool multiOn = m_multiOn[n];
1684 activeNames.push_back( activeName( m_properties[n], m_propertyKeys[n], multiOn ) );
1685 m_multiOn[n] = multiOn;
1686 }
1687
1688 bool targetMultiOn = m_targetMultiOn;
1689
1690 std::string comboName = formatCombo( activeNames );
1691 std::string targetName = activeName( m_targetProperty, m_targetPropertyKey, targetMultiOn );
1692 m_targetMultiOn = targetMultiOn;
1693
1694 rv = compTxt( comboName, targetName );
1695 if( isError( rv ) )
1696 {
1697 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1698 }
1699
1700 return std::get<bool>( rv );
1701 }
1702
1703 /// Pop one pending runtime diagnostic, if any.
1704 virtual bool popRuntimeDiagnostic( std::string &diagnostic /**< [out] the next pending diagnostic message */ )
1705 {
1706 if( m_pendingDiagnostics.size() == 0 )
1707 {
1708 diagnostic = "";
1709 return false;
1710 }
1711
1712 diagnostic = m_pendingDiagnostics.front();
1714
1715 return true;
1716 }
1717};
1718
1719/// A rule to compare two rules
1720/**
1721 *
1722 */
1724{
1725
1726 public:
1727 /// Name of this rule, used by config system
1728 static constexpr char name[] = "ruleComp";
1729
1730 protected:
1731 indiCompRule *m_rule1{ nullptr }; ///< rule one
1732 indiCompRule *m_rule2{ nullptr }; ///< rule two
1733
1734 public:
1735 /// Default c'tor
1736 /** Changes default comparison to And for ruleCompRule
1737 */
1739 {
1741 }
1742
1743 /// Get the default comparison for this rule type.
1744 /**
1745 * \returns `ruleComparison::And`
1746 */
1748 {
1749 return ruleComparison::And;
1750 }
1751
1752 /// Set the pointer to the first rule
1753 void rule1( indiCompRule *r /**< [in] the new pointer to rule1*/ )
1754 {
1755 m_rule1 = r;
1756 }
1757
1758 /// Get the pointer to the first rule
1759 /**
1760 * \returns the current value of m_rule1
1761 */
1763 {
1764 return m_rule1;
1765 }
1766
1767 /// Set the pointer to the second rule
1768 void rule2( indiCompRule *r /**< [in] the new pointer to rule2*/ )
1769 {
1770 m_rule2 = r;
1771 }
1772
1773 /// Get the pointer to the first rule
1774 /**
1775 * \returns the current value of m_rule2
1776 */
1778 {
1779 return m_rule2;
1780 }
1781
1782 /// Check if this rule is valid
1783 /** The rule is valid if the rule pointers are not nullptr, and if each rule is itself valid.
1784 *
1785 * If not valid, the return value is a std::string with the reason.
1786 * If valid, the return value is a bool set to true.
1787 */
1789 {
1790 boolorerr_t rv;
1791 if( m_rule1 == nullptr )
1792 {
1793 rv = "rule1 is nullptr";
1794 }
1795 else if( m_rule2 == nullptr )
1796 {
1797 rv = "rule2 is nullptr";
1798 }
1799 else
1800 {
1801 rv = m_rule1->valid();
1802 if( isError( rv ) )
1803 {
1804 return rv;
1805 }
1806
1807 rv = m_rule2->valid();
1808 if( isError( rv ) )
1809 {
1810 return rv;
1811 }
1812
1813 rv = true;
1814 }
1815
1816 return rv;
1817 }
1818
1819 /// Get the value of this rule
1820 /** First checks if the rule is currently valid. The performs the comparison and returns the result.
1821 *
1822 * \returns the value of the comparison, true or false
1823 *
1824 * \throws mx::err::invalidconfig if the rule is not currently valid
1825 * \throws mx::err::invalidconfig on an error from the comparison
1826 *
1827 */
1828 virtual bool value()
1829 {
1830 boolorerr_t rv = valid();
1831 if( isError( rv ) )
1832 {
1833 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1834 }
1835
1836 rv = compBool( m_rule1->value(), m_rule2->value() );
1837 if( isError( rv ) )
1838 {
1839 throw mx::exception( mx::error_t::invalidconfig, std::get<std::string>( rv ) );
1840 }
1841
1842 return std::get<bool>( rv );
1843 }
1844
1845 /// Pop one pending runtime diagnostic from either child rule.
1846 virtual bool popRuntimeDiagnostic( std::string &diagnostic /**< [out] the next pending diagnostic message */ )
1847 {
1848 if( m_rule1 != nullptr && m_rule1->popRuntimeDiagnostic( diagnostic ) )
1849 {
1850 return true;
1851 }
1852
1853 if( m_rule2 != nullptr && m_rule2->popRuntimeDiagnostic( diagnostic ) )
1854 {
1855 return true;
1856 }
1857
1858 diagnostic = "";
1859 return false;
1860 }
1861};
1862
1863#endif // stateRuleEngine_indiCompRules_hpp
std::string comp2string(const ruleComparison &comparison)
Get the string representation of a ruleComparison member.
ruleComparison
Logical comparisons for the INDI rules.
@ Gt
Greater than.
@ Xnor
boolean xnor, equivalent to equal
@ Or
boolean or
@ Lt
Less than.
@ LtEq
Less than or equal to.
@ Nor
boolean nor
@ Xor
boolean xor, equivalent to not equal
@ GtEq
Greater than or equal to.
@ And
boolean and
@ Nand
boolean nand
@ Neq
Not equal.
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.
@ none
Don't publish.
@ caution
Caution – make sure you know what you're doing.
@ warning
Warning – something is probably wrong, you should check.
@ alert
Alert – something is definitely wrong, you should take action.
@ info
For information only.
Compare two elements based on their numeric values.
virtual bool value()
Get the value of this rule.
const double & tol()
Get the tolerance.
double m_tol
The tolerance for the comparison.
elCompNumRule()
Default c'tor.
void tol(const double &t)
Set the tolerance.
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.
elCompSwRule()
Default c'tor.
virtual bool value()
Get the value of this rule.
Compare two elements based on their text values.
virtual bool value()
Get the value of this rule.
elCompTxtRule()
Default c'tor.
static constexpr char name[]
Name of this rule, used by config system.
Virtual base-class for all rules.
static constexpr double default_alert_msg_delay
int m_messageCount
Number of times the message has been sent.
void messageCount(int mc)
Set the message count.
virtual ~indiCompRule()
Virtual destructor.
rulePriority m_priority
The reporting priority for this rule.
const timespec & lastMsg()
double m_messageDelay
Delay between sending messages.
virtual boolorerr_t valid()=0
Report whether the rule is valid as configured.
int messageCount()
Get the message count.
boolorerr_t compSw(const pcf::IndiElement::SwitchStateType &sw1, const pcf::IndiElement::SwitchStateType &sw2)
Compare two switches.
void messageDelay(double md)
Set the message delay.
bool timeToSend()
Check if it's time to send a message.
boolorerr_t compNum(const double &num1, const double &num2, const double &tol)
Compare two numbers.
virtual bool value()=0
Get the value of this rule.
std::variant< bool, std::string > boolorerr_t
In-band error reporting type.
boolorerr_t compTxt(const std::string &str1, const std::string &str2)
Compare two strings.
const ruleComparison & comparison()
Get the rule comparison.
timespec m_lastMsg
Time the message was last sent.
static constexpr double default_warning_msg_delay
void priority(const rulePriority &p, double delay=-1)
Set priority of this rule.
void comparison(const ruleComparison &c)
Set the comparison for this rule.
ruleComparison m_comparison
The comparison for this rule.
static constexpr double default_info_msg_delay
bool isError(boolorerr_t rv)
Check if returned value indicates an error.
virtual ruleComparison defaultComparison() const
Get the default comparison for this rule type.
std::string m_message
The message used for notifications.
virtual bool popRuntimeDiagnostic(std::string &diagnostic)
Pop one pending runtime diagnostic, if any.
const std::string & message(bool settime=false)
Get the message.
double messageDelay()
Get the message delay.
void message(const std::string &m)
Set the message.
const rulePriority & priority()
Get the rule priority.
boolorerr_t compBool(const bool &b1, const bool &b2)
Compare two booleans.
static constexpr double default_caution_msg_delay
double sinceLastMsg()
Get the time since the last message.
int incMessageCount()
Increment the message count.
Build and compare a switch-name combination against a target switch vector.
const std::string & format()
Get the literal format string.
std::vector< std::string > m_pendingDiagnostics
Runtime diagnostics that should be logged without failing evaluation.
std::vector< bool > m_multiOn
Per-source latch state used to avoid repeated multi-On diagnostics.
std::string m_ruleName
The configuration-section name of this rule, used in diagnostics.
const pcf::IndiProperty * targetProperty()
Get the target switch property.
const pcf::IndiProperty * property(size_t n)
Get a source switch property by index.
size_t formatPlaceholders(bool &invalidBraces) const
Count plain {} placeholders and reject any other brace usage.
std::vector< pcf::IndiProperty * > m_properties
The source switch properties in format-substitution order.
std::vector< std::string > m_propertyKeys
The config keys for the source switch properties, used in diagnostics.
std::string m_format
The literal format string used to combine source switch names.
const std::string & targetPropertyKey()
Get the target property key used in diagnostics.
const std::string & propertyKey(size_t n)
Get a source property key by index.
std::string activeName(pcf::IndiProperty *property, const std::string &propertyKey, bool &multiOnLatched)
Resolve the active element name for a switch property.
virtual ruleComparison defaultComparison() const
Get the default comparison for this rule type.
void targetProperty(pcf::IndiProperty *property)
Set the target switch property.
void property(pcf::IndiProperty *property, const std::string &propertyKey)
Append one source switch property.
virtual boolorerr_t valid()
Check if this rule is valid as configured.
static constexpr char name[]
Name of this rule, used by config system.
virtual bool popRuntimeDiagnostic(std::string &diagnostic)
Pop one pending runtime diagnostic, if any.
const std::string & ruleName()
Get the configured rule name.
std::string formatCombo(const std::vector< std::string > &values) const
Apply literal {} substitution to build the comparison string.
void format(const std::string &format)
Set the literal format string for the source switch names.
virtual bool value()
Get the value of this rule.
multiSwitchComboRule()
Default c'tor.
size_t numSwitches()
Get the number of configured source switch properties.
void targetPropertyKey(const std::string &propertyKey)
Set the target property key used in diagnostics.
std::string m_targetPropertyKey
The config key for the target property, used in diagnostics.
pcf::IndiProperty * m_targetProperty
The target switch property whose active element name is compared.
bool m_targetMultiOn
Latch state used to avoid repeated target multi-On diagnostics.
void ruleName(const std::string &ruleName)
Set the rule name used in diagnostics.
Compare the value of a number element to a target.
const double & target()
Get the target.
numValRule()
Default c'tor.
double m_tol
The tolerance for the comparison.
double m_target
The target value for comparison.
const double & tol()
Get the tolerance.
void target(const double &tgt)
Set the target for the comparison.
void tol(const double &t)
Set the tolerance.
virtual bool value()
Get the value of this rule.
static constexpr char name[]
Name of this rule, used by config system.
A rule base class for testing an element in one property.
const pcf::IndiProperty * property()
Get the property pointer.
std::string m_element
The element name within the property.
int m_type
The property type, from pcf::IndiProperty::Type.
onePropRule()=delete
onePropRule(int type)
Constructor. You must provide the property type to construct a onePropRule.
const std::string & element()
Get the element name.
void property(pcf::IndiProperty *property)
Set the property pointer.
void element(const std::string &el)
Set the element name.
pcf::IndiProperty * m_property
Pointer to the property.
virtual boolorerr_t valid()
Check if this rule is valid.
A rule to compare two rules.
ruleCompRule()
Default c'tor.
const indiCompRule * rule2()
Get the pointer to the first rule.
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.
indiCompRule * m_rule1
rule one
const indiCompRule * rule1()
Get the pointer to the first rule.
virtual ruleComparison defaultComparison() const
Get the default comparison for this rule type.
virtual boolorerr_t valid()
Check if this rule is valid.
virtual bool popRuntimeDiagnostic(std::string &diagnostic)
Pop one pending runtime diagnostic from either child rule.
virtual bool value()
Get the value of this rule.
indiCompRule * m_rule2
rule two
Compare the value of a switch to a target value.
virtual bool value()
Get the value of this rule.
void target(const pcf::IndiElement::SwitchStateType &ss)
Set the target for the comparison.
const pcf::IndiElement::SwitchStateType & target()
Get the target.
swValRule()
Default c'tor.
void target(const std::string &switchState)
Set the target for the comparison.
pcf::IndiElement::SwitchStateType m_target
The target value for comparison.
static constexpr char name[]
Name of this rule, used by config system.
Compare the difference in time between a value and now.
timeDiffRule()
Default c'tor.
static constexpr char name[]
Name of this rule, used by config system.
void target(const double &tgt)
Set the target for the comparison.
const double & target()
Get the target.
double m_tol
The tolerance for the comparison.
void tol(const double &t)
Set the tolerance.
const double & tol()
Get the tolerance.
virtual bool value()
Get the value of this rule.
double m_target
The target value for comparison.
A rule base class for testing elements in two properties.
twoPropRule()=delete
int m_type
The property type, from pcf::IndiProperty::Type.
const std::string & element1()
Get the first element name.
void property1(pcf::IndiProperty *property)
Set the first property pointer.
virtual boolorerr_t valid()
Check if this rule is valid.
void element2(const std::string &el)
Set the second element name.
std::string m_element2
The element name within the second property.
twoPropRule(int type)
Constructor. You must provide the property type to construct a twoPropRule.
void element1(const std::string &el)
Set the first element name.
const pcf::IndiProperty * property2()
Get the second property pointer.
const std::string & element2()
Get the second element name.
void property2(pcf::IndiProperty *property)
Set the second property pointer.
std::string m_element1
The element name within the first property.
pcf::IndiProperty * m_property2
Pointer to the second property.
const pcf::IndiProperty * property1()
Get the first property pointer.
pcf::IndiProperty * m_property1
Pointer to the first property.
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.
std::string m_target
The target value for comparison.
txtValRule()
Default c'tor.
virtual bool value()
Get the value of this rule.
const std::string & target()
Get the target.