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