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) :
47efdd03c2SMatthew Barth         IndexedConditional(conditionIndex),
48efdd03c2SMatthew Barth         medianOp(_medianOp), oneshot(oneshot)
49*3fe976ccSGeorge 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