1 #pragma once 2 3 #include "callback.hpp" 4 #include "data_types.hpp" 5 6 #include <algorithm> 7 #include <functional> 8 9 namespace phosphor 10 { 11 namespace dbus 12 { 13 namespace monitoring 14 { 15 16 /** @class MedianCondition 17 * @brief Determine a median from properties and apply a condition. 18 * 19 * When invoked, a median class instance performs its condition 20 * test against a median value that has been determined from a set 21 * of configured properties. 22 * 23 * Once the median value is determined, a C++ relational operator 24 * is applied to it and a value provided by the configuration file, 25 * which determines if the condition passes or not. 26 * 27 * Where no property values configured are found to determine a median from, 28 * the condition defaults to `true` and passes. 29 * 30 * If the oneshot parameter is true, then this condition won't pass 31 * again until it fails at least once. 32 */ 33 template <typename T> 34 class MedianCondition : public IndexedConditional 35 { 36 public: 37 MedianCondition() = delete; 38 MedianCondition(const MedianCondition&) = default; 39 MedianCondition(MedianCondition&&) = default; 40 MedianCondition& operator=(const MedianCondition&) = default; 41 MedianCondition& operator=(MedianCondition&&) = default; 42 ~MedianCondition() = default; 43 MedianCondition(const PropertyIndex & conditionIndex,const std::function<bool (T)> & _medianOp,bool oneshot=false)44 MedianCondition(const PropertyIndex& conditionIndex, 45 const std::function<bool(T)>& _medianOp, 46 bool oneshot = false) : 47 IndexedConditional(conditionIndex), medianOp(_medianOp), 48 oneshot(oneshot) 49 {} 50 operator ()()51 bool operator()() override 52 { 53 // Default the condition result to true 54 // if no property values are found to produce a median. 55 auto result = true; 56 std::vector<T> values; 57 for (const auto& item : index) 58 { 59 const auto& storage = std::get<storageIndex>(item.second); 60 // Don't count properties that don't exist. 61 if (!std::get<valueIndex>(storage.get()).has_value()) 62 { 63 continue; 64 } 65 values.emplace_back( 66 std::any_cast<T>(std::get<valueIndex>(storage.get()))); 67 } 68 69 if (!values.empty()) 70 { 71 auto median = values.front(); 72 // Get the determined median value 73 if (values.size() == 2) 74 { 75 // For 2 values, use the highest instead of the average 76 // for a worst case median value 77 median = *std::max_element(values.begin(), values.end()); 78 } 79 else if (values.size() > 2) 80 { 81 const auto oddIt = values.begin() + values.size() / 2; 82 std::nth_element(values.begin(), oddIt, values.end()); 83 median = *oddIt; 84 // Determine median for even number of values 85 if (index.size() % 2 == 0) 86 { 87 // Use average of middle 2 values for median 88 const auto evenIt = values.begin() + values.size() / 2 - 1; 89 std::nth_element(values.begin(), evenIt, values.end()); 90 median = (median + *evenIt) / 2; 91 } 92 } 93 94 // Now apply the condition to the median value. 95 result = medianOp(median); 96 } 97 98 // If this was a oneshot and the the condition has already 99 // passed, then don't let it pass again until the condition 100 // has gone back to false. 101 if (oneshot && result && lastResult) 102 { 103 return false; 104 } 105 106 lastResult = result; 107 return result; 108 } 109 110 private: 111 /** @brief The comparison to perform on the median value. */ 112 std::function<bool(T)> medianOp; 113 /** @brief If the condition can be allowed to pass again 114 on subsequent checks that are also true. */ 115 const bool oneshot; 116 /** @brief The result of the previous check. */ 117 bool lastResult = false; 118 }; 119 120 } // namespace monitoring 121 } // namespace dbus 122 } // namespace phosphor 123