1 #include "metrics/collection_data.hpp"
2 
3 #include "metrics/collection_function.hpp"
4 
5 namespace metrics
6 {
7 
8 bool CollectionData::updateLastValue(double value)
9 {
10     const bool changed = lastValue != value;
11     lastValue = value;
12     return changed;
13 }
14 
15 class DataPoint : public CollectionData
16 {
17   public:
18     std::optional<double> update(Milliseconds) override
19     {
20         return lastReading;
21     }
22 
23     double update(Milliseconds, double reading) override
24     {
25         lastReading = reading;
26         return reading;
27     }
28 
29   private:
30     std::optional<double> lastReading;
31 };
32 
33 class DataInterval : public CollectionData
34 {
35   public:
36     DataInterval(std::shared_ptr<CollectionFunction> function,
37                  CollectionDuration duration) :
38         function(std::move(function)),
39         duration(duration)
40     {
41         if (duration.t.count() == 0)
42         {
43             throw sdbusplus::exception::SdBusError(
44                 static_cast<int>(std::errc::invalid_argument),
45                 "Invalid CollectionDuration");
46         }
47     }
48 
49     std::optional<double> update(Milliseconds timestamp) override
50     {
51         if (readings.empty())
52         {
53             return std::nullopt;
54         }
55 
56         cleanup(timestamp);
57 
58         return function->calculate(readings, timestamp);
59     }
60 
61     double update(Milliseconds timestamp, double reading) override
62     {
63         readings.emplace_back(timestamp, reading);
64 
65         cleanup(timestamp);
66 
67         return function->calculate(readings, timestamp);
68     }
69 
70   private:
71     void cleanup(Milliseconds timestamp)
72     {
73         auto it = readings.begin();
74         for (auto kt = std::next(readings.rbegin()); kt != readings.rend();
75              ++kt)
76         {
77             const auto& [nextItemTimestamp, nextItemReading] = *std::prev(kt);
78             if (timestamp >= nextItemTimestamp &&
79                 timestamp - nextItemTimestamp > duration.t)
80             {
81                 it = kt.base();
82                 break;
83             }
84         }
85         readings.erase(readings.begin(), it);
86 
87         if (timestamp > duration.t)
88         {
89             readings.front().first =
90                 std::max(readings.front().first, timestamp - duration.t);
91         }
92     }
93 
94     std::shared_ptr<CollectionFunction> function;
95     std::vector<ReadingItem> readings;
96     CollectionDuration duration;
97 };
98 
99 class DataStartup : public CollectionData
100 {
101   public:
102     explicit DataStartup(std::shared_ptr<CollectionFunction> function) :
103         function(std::move(function))
104     {}
105 
106     std::optional<double> update(Milliseconds timestamp) override
107     {
108         if (readings.empty())
109         {
110             return std::nullopt;
111         }
112 
113         return function->calculateForStartupInterval(readings, timestamp);
114     }
115 
116     double update(Milliseconds timestamp, double reading) override
117     {
118         readings.emplace_back(timestamp, reading);
119         return function->calculateForStartupInterval(readings, timestamp);
120     }
121 
122   private:
123     std::shared_ptr<CollectionFunction> function;
124     std::vector<ReadingItem> readings;
125 };
126 
127 std::vector<std::unique_ptr<CollectionData>>
128     makeCollectionData(size_t size, OperationType op,
129                        CollectionTimeScope timeScope,
130                        CollectionDuration duration)
131 {
132     using namespace std::string_literals;
133 
134     std::vector<std::unique_ptr<CollectionData>> result;
135 
136     result.reserve(size);
137 
138     switch (timeScope)
139     {
140         case CollectionTimeScope::interval:
141             std::generate_n(std::back_inserter(result), size,
142                             [cf = makeCollectionFunction(op), duration] {
143                                 return std::make_unique<DataInterval>(cf,
144                                                                       duration);
145                             });
146             break;
147         case CollectionTimeScope::point:
148             std::generate_n(std::back_inserter(result), size,
149                             [] { return std::make_unique<DataPoint>(); });
150             break;
151         case CollectionTimeScope::startup:
152             std::generate_n(std::back_inserter(result), size,
153                             [cf = makeCollectionFunction(op)] {
154                                 return std::make_unique<DataStartup>(cf);
155                             });
156             break;
157     }
158 
159     return result;
160 }
161 
162 } // namespace metrics
163