xref: /openbmc/telemetry/src/metric.cpp (revision b8cc78dd)
1 #include "metric.hpp"
2 
3 #include "details/collection_function.hpp"
4 #include "types/report_types.hpp"
5 #include "utils/labeled_tuple.hpp"
6 #include "utils/transform.hpp"
7 
8 #include <sdbusplus/exception.hpp>
9 
10 #include <algorithm>
11 
12 class Metric::CollectionData
13 {
14   public:
15     using ReadingItem = details::ReadingItem;
16 
17     virtual ~CollectionData() = default;
18 
19     virtual ReadingItem update(uint64_t timestamp) = 0;
20     virtual ReadingItem update(uint64_t timestamp, double value) = 0;
21 };
22 
23 class Metric::DataPoint : public Metric::CollectionData
24 {
25   public:
26     ReadingItem update(uint64_t timestamp) override
27     {
28         return ReadingItem{lastTimestamp, lastReading};
29     }
30 
31     ReadingItem update(uint64_t timestamp, double reading) override
32     {
33         lastTimestamp = timestamp;
34         lastReading = reading;
35         return update(timestamp);
36     }
37 
38   private:
39     uint64_t lastTimestamp = 0u;
40     double lastReading = 0.0;
41 };
42 
43 class Metric::DataInterval : public Metric::CollectionData
44 {
45   public:
46     DataInterval(std::shared_ptr<details::CollectionFunction> function,
47                  CollectionDuration duration) :
48         function(std::move(function)),
49         duration(duration)
50     {
51         if (duration.t.count() == 0)
52         {
53             throw sdbusplus::exception::SdBusError(
54                 static_cast<int>(std::errc::invalid_argument),
55                 "Invalid CollectionDuration");
56         }
57     }
58 
59     ReadingItem update(uint64_t timestamp) override
60     {
61         if (readings.size() > 0)
62         {
63             auto it = readings.begin();
64             for (auto kt = std::next(readings.rbegin()); kt != readings.rend();
65                  ++kt)
66             {
67                 const auto& [nextItemTimestamp, nextItemReading] =
68                     *std::prev(kt);
69                 if (timestamp >= nextItemTimestamp &&
70                     static_cast<uint64_t>(timestamp - nextItemTimestamp) >
71                         duration.t.count())
72                 {
73                     it = kt.base();
74                     break;
75                 }
76             }
77             readings.erase(readings.begin(), it);
78 
79             if (timestamp > duration.t.count())
80             {
81                 readings.front().first = std::max(
82                     readings.front().first, timestamp - duration.t.count());
83             }
84         }
85 
86         return function->calculate(readings, timestamp);
87     }
88 
89     ReadingItem update(uint64_t timestamp, double reading) override
90     {
91         readings.emplace_back(timestamp, reading);
92         return update(timestamp);
93     }
94 
95   private:
96     std::shared_ptr<details::CollectionFunction> function;
97     std::vector<ReadingItem> readings;
98     CollectionDuration duration;
99 };
100 
101 class Metric::DataStartup : public Metric::CollectionData
102 {
103   public:
104     DataStartup(std::shared_ptr<details::CollectionFunction> function) :
105         function(std::move(function))
106     {}
107 
108     ReadingItem update(uint64_t timestamp) override
109     {
110         return function->calculateForStartupInterval(readings, timestamp);
111     }
112 
113     ReadingItem update(uint64_t timestamp, double reading) override
114     {
115         readings.emplace_back(timestamp, reading);
116         return function->calculateForStartupInterval(readings, timestamp);
117     }
118 
119   private:
120     std::shared_ptr<details::CollectionFunction> function;
121     std::vector<ReadingItem> readings;
122 };
123 
124 Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
125                std::string idIn, CollectionTimeScope timeScopeIn,
126                CollectionDuration collectionDurationIn,
127                std::unique_ptr<interfaces::Clock> clockIn) :
128     id(std::move(idIn)),
129     sensors(std::move(sensorsIn)), operationType(operationTypeIn),
130     collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
131     collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
132                                             collectionTimeScope,
133                                             collectionDuration)),
134     clock(std::move(clockIn))
135 {
136     readings = utils::transform(sensors, [this](const auto& sensor) {
137         return MetricValue{id, sensor->metadata(), 0.0, 0u};
138     });
139 }
140 
141 Metric::~Metric() = default;
142 
143 void Metric::initialize()
144 {
145     for (const auto& sensor : sensors)
146     {
147         sensor->registerForUpdates(weak_from_this());
148     }
149 }
150 
151 void Metric::deinitialize()
152 {
153     for (const auto& sensor : sensors)
154     {
155         sensor->unregisterFromUpdates(weak_from_this());
156     }
157 }
158 
159 std::vector<MetricValue> Metric::getReadings() const
160 {
161     const auto timestamp = clock->timestamp();
162     auto resultReadings = readings;
163 
164     for (size_t i = 0; i < resultReadings.size(); ++i)
165     {
166         std::tie(resultReadings[i].timestamp, resultReadings[i].value) =
167             collectionAlgorithms[i]->update(timestamp);
168     }
169 
170     return resultReadings;
171 }
172 
173 void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp)
174 {
175     findAssociatedData(notifier).update(timestamp);
176 }
177 
178 void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp,
179                            double value)
180 {
181     findAssociatedData(notifier).update(timestamp, value);
182 }
183 
184 Metric::CollectionData&
185     Metric::findAssociatedData(const interfaces::Sensor& notifier)
186 {
187     auto it = std::find_if(
188         sensors.begin(), sensors.end(),
189         [&notifier](const auto& sensor) { return sensor.get() == &notifier; });
190     auto index = std::distance(sensors.begin(), it);
191     return *collectionAlgorithms.at(index);
192 }
193 
194 LabeledMetricParameters Metric::dumpConfiguration() const
195 {
196     auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
197         return LabeledSensorParameters(sensor->id().service, sensor->id().path,
198                                        sensor->metadata());
199     });
200 
201     return LabeledMetricParameters(std::move(sensorPath), operationType, id,
202                                    collectionTimeScope, collectionDuration);
203 }
204 
205 std::vector<std::unique_ptr<Metric::CollectionData>>
206     Metric::makeCollectionData(size_t size, OperationType op,
207                                CollectionTimeScope timeScope,
208                                CollectionDuration duration)
209 {
210     using namespace std::string_literals;
211 
212     std::vector<std::unique_ptr<Metric::CollectionData>> result;
213 
214     result.reserve(size);
215 
216     switch (timeScope)
217     {
218         case CollectionTimeScope::interval:
219             std::generate_n(
220                 std::back_inserter(result), size,
221                 [cf = details::makeCollectionFunction(op), duration] {
222                     return std::make_unique<DataInterval>(cf, duration);
223                 });
224             break;
225         case CollectionTimeScope::point:
226             std::generate_n(std::back_inserter(result), size,
227                             [] { return std::make_unique<DataPoint>(); });
228             break;
229         case CollectionTimeScope::startup:
230             std::generate_n(std::back_inserter(result), size,
231                             [cf = details::makeCollectionFunction(op)] {
232                                 return std::make_unique<DataStartup>(cf);
233                             });
234             break;
235         default:
236             throw std::runtime_error("timeScope: "s +
237                                      utils::enumToString(timeScope) +
238                                      " is not supported"s);
239     }
240 
241     return result;
242 }
243 
244 uint64_t Metric::sensorCount() const
245 {
246     return sensors.size();
247 }
248