blob: d8241b937924d83f80aad3bfb0e10c42e306ddae [file] [log] [blame]
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +02001#include "metric.hpp"
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +01002
Krzysztof Grobelny80697712021-03-04 09:49:27 +00003#include "details/collection_function.hpp"
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +00004#include "types/report_types.hpp"
Szymon Dompke3a617022021-07-19 18:23:02 +02005#include "utils/labeled_tuple.hpp"
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +01006#include "utils/transform.hpp"
7
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01008#include <sdbusplus/exception.hpp>
9
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +010010#include <algorithm>
11
Krzysztof Grobelny80697712021-03-04 09:49:27 +000012class Metric::CollectionData
13{
14 public:
15 using ReadingItem = details::ReadingItem;
16
17 virtual ~CollectionData() = default;
18
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010019 virtual std::optional<double> update(Milliseconds timestamp) = 0;
20 virtual double update(Milliseconds timestamp, double value) = 0;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000021};
22
23class Metric::DataPoint : public Metric::CollectionData
24{
25 public:
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010026 std::optional<double> update(Milliseconds) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000027 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010028 return lastReading;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000029 }
30
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010031 double update(Milliseconds, double reading) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000032 {
Krzysztof Grobelny80697712021-03-04 09:49:27 +000033 lastReading = reading;
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010034 return reading;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000035 }
36
37 private:
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010038 std::optional<double> lastReading;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000039};
40
41class Metric::DataInterval : public Metric::CollectionData
42{
43 public:
44 DataInterval(std::shared_ptr<details::CollectionFunction> function,
45 CollectionDuration duration) :
46 function(std::move(function)),
47 duration(duration)
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010048 {
49 if (duration.t.count() == 0)
50 {
51 throw sdbusplus::exception::SdBusError(
52 static_cast<int>(std::errc::invalid_argument),
53 "Invalid CollectionDuration");
54 }
55 }
Krzysztof Grobelny80697712021-03-04 09:49:27 +000056
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010057 std::optional<double> update(Milliseconds timestamp) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000058 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010059 if (readings.empty())
Krzysztof Grobelny80697712021-03-04 09:49:27 +000060 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010061 return std::nullopt;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000062 }
63
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010064 cleanup(timestamp);
65
Krzysztof Grobelny80697712021-03-04 09:49:27 +000066 return function->calculate(readings, timestamp);
67 }
68
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010069 double update(Milliseconds timestamp, double reading) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000070 {
71 readings.emplace_back(timestamp, reading);
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010072
73 cleanup(timestamp);
74
75 return function->calculate(readings, timestamp);
Krzysztof Grobelny80697712021-03-04 09:49:27 +000076 }
77
78 private:
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010079 void cleanup(Milliseconds timestamp)
80 {
81 auto it = readings.begin();
82 for (auto kt = std::next(readings.rbegin()); kt != readings.rend();
83 ++kt)
84 {
85 const auto& [nextItemTimestamp, nextItemReading] = *std::prev(kt);
86 if (timestamp >= nextItemTimestamp &&
87 timestamp - nextItemTimestamp > duration.t)
88 {
89 it = kt.base();
90 break;
91 }
92 }
93 readings.erase(readings.begin(), it);
94
95 if (timestamp > duration.t)
96 {
97 readings.front().first =
98 std::max(readings.front().first, timestamp - duration.t);
99 }
100 }
101
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000102 std::shared_ptr<details::CollectionFunction> function;
103 std::vector<ReadingItem> readings;
104 CollectionDuration duration;
105};
106
107class Metric::DataStartup : public Metric::CollectionData
108{
109 public:
110 DataStartup(std::shared_ptr<details::CollectionFunction> function) :
111 function(std::move(function))
112 {}
113
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100114 std::optional<double> update(Milliseconds timestamp) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000115 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100116 if (readings.empty())
117 {
118 return std::nullopt;
119 }
120
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000121 return function->calculateForStartupInterval(readings, timestamp);
122 }
123
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100124 double update(Milliseconds timestamp, double reading) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000125 {
126 readings.emplace_back(timestamp, reading);
127 return function->calculateForStartupInterval(readings, timestamp);
128 }
129
130 private:
131 std::shared_ptr<details::CollectionFunction> function;
132 std::vector<ReadingItem> readings;
133};
134
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000135Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100136 std::string idIn, CollectionTimeScope timeScopeIn,
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000137 CollectionDuration collectionDurationIn,
138 std::unique_ptr<interfaces::Clock> clockIn) :
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100139 id(std::move(idIn)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000140 sensors(std::move(sensorsIn)), operationType(operationTypeIn),
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000141 collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
142 collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
143 collectionTimeScope,
144 collectionDuration)),
145 clock(std::move(clockIn))
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000146{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100147 readings = utils::transform(sensors, [this](const auto& sensor) {
148 return MetricValue{id, sensor->metadata(), 0.0, 0u};
149 });
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000150}
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100151
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000152Metric::~Metric() = default;
153
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100154void Metric::initialize()
155{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000156 for (const auto& sensor : sensors)
157 {
158 sensor->registerForUpdates(weak_from_this());
159 }
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100160}
161
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200162void Metric::deinitialize()
163{
164 for (const auto& sensor : sensors)
165 {
166 sensor->unregisterFromUpdates(weak_from_this());
167 }
168}
169
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000170std::vector<MetricValue> Metric::getReadings() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100171{
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100172 const auto steadyTimestamp = clock->steadyTimestamp();
173 const auto systemTimestamp = clock->systemTimestamp();
174
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000175 auto resultReadings = readings;
176
177 for (size_t i = 0; i < resultReadings.size(); ++i)
178 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100179 if (const auto value = collectionAlgorithms[i]->update(steadyTimestamp))
180 {
181 resultReadings[i].timestamp =
182 std::chrono::duration_cast<Milliseconds>(systemTimestamp)
183 .count();
184 resultReadings[i].value = *value;
185 }
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000186 }
187
188 return resultReadings;
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100189}
190
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100191void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100192{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000193 findAssociatedData(notifier).update(timestamp);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100194}
195
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100196void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp,
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100197 double value)
198{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000199 findAssociatedData(notifier).update(timestamp, value);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100200}
201
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000202Metric::CollectionData&
203 Metric::findAssociatedData(const interfaces::Sensor& notifier)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100204{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000205 auto it = std::find_if(
206 sensors.begin(), sensors.end(),
207 [&notifier](const auto& sensor) { return sensor.get() == &notifier; });
208 auto index = std::distance(sensors.begin(), it);
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000209 return *collectionAlgorithms.at(index);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100210}
211
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000212LabeledMetricParameters Metric::dumpConfiguration() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100213{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000214 auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100215 return LabeledSensorParameters(sensor->id().service, sensor->id().path,
216 sensor->metadata());
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000217 });
218
219 return LabeledMetricParameters(std::move(sensorPath), operationType, id,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100220 collectionTimeScope, collectionDuration);
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000221}
222
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000223std::vector<std::unique_ptr<Metric::CollectionData>>
224 Metric::makeCollectionData(size_t size, OperationType op,
225 CollectionTimeScope timeScope,
226 CollectionDuration duration)
227{
228 using namespace std::string_literals;
229
230 std::vector<std::unique_ptr<Metric::CollectionData>> result;
231
232 result.reserve(size);
233
234 switch (timeScope)
235 {
236 case CollectionTimeScope::interval:
237 std::generate_n(
238 std::back_inserter(result), size,
239 [cf = details::makeCollectionFunction(op), duration] {
240 return std::make_unique<DataInterval>(cf, duration);
241 });
242 break;
243 case CollectionTimeScope::point:
244 std::generate_n(std::back_inserter(result), size,
245 [] { return std::make_unique<DataPoint>(); });
246 break;
247 case CollectionTimeScope::startup:
248 std::generate_n(std::back_inserter(result), size,
249 [cf = details::makeCollectionFunction(op)] {
250 return std::make_unique<DataStartup>(cf);
251 });
252 break;
253 default:
254 throw std::runtime_error("timeScope: "s +
255 utils::enumToString(timeScope) +
256 " is not supported"s);
257 }
258
259 return result;
260}
261
Szymon Dompke3eb56862021-09-20 15:32:04 +0200262uint64_t Metric::sensorCount() const
263{
264 return sensors.size();
265}