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