blob: 3e450d6a234c51c7f3d754e0458e2330dce7a419 [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 Dompke94f71c52021-12-10 07:16:33 +01005#include "types/sensor_types.hpp"
Szymon Dompke3a617022021-07-19 18:23:02 +02006#include "utils/labeled_tuple.hpp"
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +01007#include "utils/transform.hpp"
8
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01009#include <sdbusplus/exception.hpp>
10
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +010011#include <algorithm>
12
Krzysztof Grobelny80697712021-03-04 09:49:27 +000013class Metric::CollectionData
14{
15 public:
16 using ReadingItem = details::ReadingItem;
17
18 virtual ~CollectionData() = default;
19
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010020 virtual std::optional<double> update(Milliseconds timestamp) = 0;
21 virtual double update(Milliseconds timestamp, double value) = 0;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000022};
23
24class Metric::DataPoint : public Metric::CollectionData
25{
26 public:
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010027 std::optional<double> update(Milliseconds) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000028 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010029 return lastReading;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000030 }
31
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010032 double update(Milliseconds, double reading) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000033 {
Krzysztof Grobelny80697712021-03-04 09:49:27 +000034 lastReading = reading;
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010035 return reading;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000036 }
37
38 private:
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010039 std::optional<double> lastReading;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000040};
41
42class Metric::DataInterval : public Metric::CollectionData
43{
44 public:
45 DataInterval(std::shared_ptr<details::CollectionFunction> function,
46 CollectionDuration duration) :
47 function(std::move(function)),
48 duration(duration)
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010049 {
50 if (duration.t.count() == 0)
51 {
52 throw sdbusplus::exception::SdBusError(
53 static_cast<int>(std::errc::invalid_argument),
54 "Invalid CollectionDuration");
55 }
56 }
Krzysztof Grobelny80697712021-03-04 09:49:27 +000057
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010058 std::optional<double> update(Milliseconds timestamp) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000059 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010060 if (readings.empty())
Krzysztof Grobelny80697712021-03-04 09:49:27 +000061 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010062 return std::nullopt;
Krzysztof Grobelny80697712021-03-04 09:49:27 +000063 }
64
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010065 cleanup(timestamp);
66
Krzysztof Grobelny80697712021-03-04 09:49:27 +000067 return function->calculate(readings, timestamp);
68 }
69
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010070 double update(Milliseconds timestamp, double reading) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +000071 {
72 readings.emplace_back(timestamp, reading);
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010073
74 cleanup(timestamp);
75
76 return function->calculate(readings, timestamp);
Krzysztof Grobelny80697712021-03-04 09:49:27 +000077 }
78
79 private:
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010080 void cleanup(Milliseconds timestamp)
81 {
82 auto it = readings.begin();
83 for (auto kt = std::next(readings.rbegin()); kt != readings.rend();
84 ++kt)
85 {
86 const auto& [nextItemTimestamp, nextItemReading] = *std::prev(kt);
87 if (timestamp >= nextItemTimestamp &&
88 timestamp - nextItemTimestamp > duration.t)
89 {
90 it = kt.base();
91 break;
92 }
93 }
94 readings.erase(readings.begin(), it);
95
96 if (timestamp > duration.t)
97 {
98 readings.front().first =
99 std::max(readings.front().first, timestamp - duration.t);
100 }
101 }
102
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000103 std::shared_ptr<details::CollectionFunction> function;
104 std::vector<ReadingItem> readings;
105 CollectionDuration duration;
106};
107
108class Metric::DataStartup : public Metric::CollectionData
109{
110 public:
Krzysztof Grobelnyfbeb5bf2022-01-03 09:41:29 +0100111 explicit DataStartup(
112 std::shared_ptr<details::CollectionFunction> function) :
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000113 function(std::move(function))
114 {}
115
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100116 std::optional<double> update(Milliseconds timestamp) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000117 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100118 if (readings.empty())
119 {
120 return std::nullopt;
121 }
122
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000123 return function->calculateForStartupInterval(readings, timestamp);
124 }
125
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100126 double update(Milliseconds timestamp, double reading) override
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000127 {
128 readings.emplace_back(timestamp, reading);
129 return function->calculateForStartupInterval(readings, timestamp);
130 }
131
132 private:
133 std::shared_ptr<details::CollectionFunction> function;
134 std::vector<ReadingItem> readings;
135};
136
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000137Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100138 std::string idIn, CollectionTimeScope timeScopeIn,
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000139 CollectionDuration collectionDurationIn,
140 std::unique_ptr<interfaces::Clock> clockIn) :
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100141 id(std::move(idIn)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000142 sensors(std::move(sensorsIn)), operationType(operationTypeIn),
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000143 collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
144 collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
145 collectionTimeScope,
146 collectionDuration)),
147 clock(std::move(clockIn))
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000148{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100149 readings = utils::transform(sensors, [this](const auto& sensor) {
150 return MetricValue{id, sensor->metadata(), 0.0, 0u};
151 });
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000152}
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100153
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000154Metric::~Metric() = default;
155
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100156void Metric::initialize()
157{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000158 for (const auto& sensor : sensors)
159 {
160 sensor->registerForUpdates(weak_from_this());
161 }
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100162}
163
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200164void Metric::deinitialize()
165{
166 for (const auto& sensor : sensors)
167 {
168 sensor->unregisterFromUpdates(weak_from_this());
169 }
170}
171
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000172std::vector<MetricValue> Metric::getReadings() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100173{
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100174 const auto steadyTimestamp = clock->steadyTimestamp();
175 const auto systemTimestamp = clock->systemTimestamp();
176
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000177 auto resultReadings = readings;
178
179 for (size_t i = 0; i < resultReadings.size(); ++i)
180 {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100181 if (const auto value = collectionAlgorithms[i]->update(steadyTimestamp))
182 {
183 resultReadings[i].timestamp =
184 std::chrono::duration_cast<Milliseconds>(systemTimestamp)
185 .count();
186 resultReadings[i].value = *value;
187 }
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000188 }
189
190 return resultReadings;
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100191}
192
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100193void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100194{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000195 findAssociatedData(notifier).update(timestamp);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100196}
197
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100198void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp,
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100199 double value)
200{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000201 findAssociatedData(notifier).update(timestamp, value);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100202}
203
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000204Metric::CollectionData&
205 Metric::findAssociatedData(const interfaces::Sensor& notifier)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100206{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000207 auto it = std::find_if(
208 sensors.begin(), sensors.end(),
209 [&notifier](const auto& sensor) { return sensor.get() == &notifier; });
210 auto index = std::distance(sensors.begin(), it);
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000211 return *collectionAlgorithms.at(index);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100212}
213
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000214LabeledMetricParameters Metric::dumpConfiguration() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100215{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000216 auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
Szymon Dompke94f71c52021-12-10 07:16:33 +0100217 return LabeledSensorInfo(sensor->id().service, sensor->id().path,
218 sensor->metadata());
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000219 });
220
221 return LabeledMetricParameters(std::move(sensorPath), operationType, id,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100222 collectionTimeScope, collectionDuration);
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000223}
224
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000225std::vector<std::unique_ptr<Metric::CollectionData>>
226 Metric::makeCollectionData(size_t size, OperationType op,
227 CollectionTimeScope timeScope,
228 CollectionDuration duration)
229{
230 using namespace std::string_literals;
231
232 std::vector<std::unique_ptr<Metric::CollectionData>> result;
233
234 result.reserve(size);
235
236 switch (timeScope)
237 {
238 case CollectionTimeScope::interval:
239 std::generate_n(
240 std::back_inserter(result), size,
241 [cf = details::makeCollectionFunction(op), duration] {
242 return std::make_unique<DataInterval>(cf, duration);
243 });
244 break;
245 case CollectionTimeScope::point:
246 std::generate_n(std::back_inserter(result), size,
247 [] { return std::make_unique<DataPoint>(); });
248 break;
249 case CollectionTimeScope::startup:
250 std::generate_n(std::back_inserter(result), size,
251 [cf = details::makeCollectionFunction(op)] {
252 return std::make_unique<DataStartup>(cf);
253 });
254 break;
255 default:
256 throw std::runtime_error("timeScope: "s +
257 utils::enumToString(timeScope) +
258 " is not supported"s);
259 }
260
261 return result;
262}
263
Szymon Dompke3eb56862021-09-20 15:32:04 +0200264uint64_t Metric::sensorCount() const
265{
266 return sensors.size();
267}