blob: 582e5068679b67c211292afb0522c93e322d5616 [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:
Krzysztof Grobelnyfbeb5bf2022-01-03 09:41:29 +0100110 explicit DataStartup(
111 std::shared_ptr<details::CollectionFunction> function) :
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000112 function(std::move(function))
113 {}
114
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100115 std::optional<double> update(Milliseconds timestamp) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000116 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100117 if (readings.empty())
118 {
119 return std::nullopt;
120 }
121
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000122 return function->calculateForStartupInterval(readings, timestamp);
123 }
124
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100125 double update(Milliseconds timestamp, double reading) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000126 {
127 readings.emplace_back(timestamp, reading);
128 return function->calculateForStartupInterval(readings, timestamp);
129 }
130
131 private:
132 std::shared_ptr<details::CollectionFunction> function;
133 std::vector<ReadingItem> readings;
134};
135
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000136Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100137 std::string idIn, CollectionTimeScope timeScopeIn,
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000138 CollectionDuration collectionDurationIn,
139 std::unique_ptr<interfaces::Clock> clockIn) :
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100140 id(std::move(idIn)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000141 sensors(std::move(sensorsIn)), operationType(operationTypeIn),
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000142 collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
143 collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
144 collectionTimeScope,
145 collectionDuration)),
146 clock(std::move(clockIn))
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000147{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100148 readings = utils::transform(sensors, [this](const auto& sensor) {
149 return MetricValue{id, sensor->metadata(), 0.0, 0u};
150 });
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000151}
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100152
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000153Metric::~Metric() = default;
154
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100155void Metric::initialize()
156{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000157 for (const auto& sensor : sensors)
158 {
159 sensor->registerForUpdates(weak_from_this());
160 }
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100161}
162
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200163void Metric::deinitialize()
164{
165 for (const auto& sensor : sensors)
166 {
167 sensor->unregisterFromUpdates(weak_from_this());
168 }
169}
170
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000171std::vector<MetricValue> Metric::getReadings() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100172{
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100173 const auto steadyTimestamp = clock->steadyTimestamp();
174 const auto systemTimestamp = clock->systemTimestamp();
175
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000176 auto resultReadings = readings;
177
178 for (size_t i = 0; i < resultReadings.size(); ++i)
179 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100180 if (const auto value = collectionAlgorithms[i]->update(steadyTimestamp))
181 {
182 resultReadings[i].timestamp =
183 std::chrono::duration_cast<Milliseconds>(systemTimestamp)
184 .count();
185 resultReadings[i].value = *value;
186 }
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000187 }
188
189 return resultReadings;
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100190}
191
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100192void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100193{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000194 findAssociatedData(notifier).update(timestamp);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100195}
196
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100197void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp,
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100198 double value)
199{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000200 findAssociatedData(notifier).update(timestamp, value);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100201}
202
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000203Metric::CollectionData&
204 Metric::findAssociatedData(const interfaces::Sensor& notifier)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100205{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000206 auto it = std::find_if(
207 sensors.begin(), sensors.end(),
208 [&notifier](const auto& sensor) { return sensor.get() == &notifier; });
209 auto index = std::distance(sensors.begin(), it);
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000210 return *collectionAlgorithms.at(index);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100211}
212
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000213LabeledMetricParameters Metric::dumpConfiguration() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100214{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000215 auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100216 return LabeledSensorParameters(sensor->id().service, sensor->id().path,
217 sensor->metadata());
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000218 });
219
220 return LabeledMetricParameters(std::move(sensorPath), operationType, id,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100221 collectionTimeScope, collectionDuration);
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000222}
223
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000224std::vector<std::unique_ptr<Metric::CollectionData>>
225 Metric::makeCollectionData(size_t size, OperationType op,
226 CollectionTimeScope timeScope,
227 CollectionDuration duration)
228{
229 using namespace std::string_literals;
230
231 std::vector<std::unique_ptr<Metric::CollectionData>> result;
232
233 result.reserve(size);
234
235 switch (timeScope)
236 {
237 case CollectionTimeScope::interval:
238 std::generate_n(
239 std::back_inserter(result), size,
240 [cf = details::makeCollectionFunction(op), duration] {
241 return std::make_unique<DataInterval>(cf, duration);
242 });
243 break;
244 case CollectionTimeScope::point:
245 std::generate_n(std::back_inserter(result), size,
246 [] { return std::make_unique<DataPoint>(); });
247 break;
248 case CollectionTimeScope::startup:
249 std::generate_n(std::back_inserter(result), size,
250 [cf = details::makeCollectionFunction(op)] {
251 return std::make_unique<DataStartup>(cf);
252 });
253 break;
254 default:
255 throw std::runtime_error("timeScope: "s +
256 utils::enumToString(timeScope) +
257 " is not supported"s);
258 }
259
260 return result;
261}
262
Szymon Dompke3eb56862021-09-20 15:32:04 +0200263uint64_t Metric::sensorCount() const
264{
265 return sensors.size();
266}