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