blob: 04d3fe9a303aec88249cb2ab08442079390fee40 [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
8#include <algorithm>
9
Krzysztof Grobelny80697712021-03-04 09:49:27 +000010class Metric::CollectionData
11{
12 public:
13 using ReadingItem = details::ReadingItem;
14
15 virtual ~CollectionData() = default;
16
17 virtual ReadingItem update(uint64_t timestamp) = 0;
18 virtual ReadingItem update(uint64_t timestamp, double value) = 0;
19};
20
21class Metric::DataPoint : public Metric::CollectionData
22{
23 public:
24 ReadingItem update(uint64_t timestamp) override
25 {
26 return ReadingItem{lastTimestamp, lastReading};
27 }
28
29 ReadingItem update(uint64_t timestamp, double reading) override
30 {
31 lastTimestamp = timestamp;
32 lastReading = reading;
33 return update(timestamp);
34 }
35
36 private:
37 uint64_t lastTimestamp = 0u;
38 double lastReading = 0.0;
39};
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)
48 {}
49
50 ReadingItem update(uint64_t timestamp) override
51 {
52 if (readings.size() > 0)
53 {
54 auto it = readings.begin();
55 for (auto kt = std::next(readings.rbegin()); kt != readings.rend();
56 ++kt)
57 {
58 const auto& [nextItemTimestamp, nextItemReading] =
59 *std::prev(kt);
60 if (timestamp >= nextItemTimestamp &&
61 static_cast<uint64_t>(timestamp - nextItemTimestamp) >
62 duration.t.count())
63 {
64 it = kt.base();
65 break;
66 }
67 }
68 readings.erase(readings.begin(), it);
69
70 if (timestamp > duration.t.count())
71 {
72 readings.front().first = std::max(
73 readings.front().first, timestamp - duration.t.count());
74 }
75 }
76
77 return function->calculate(readings, timestamp);
78 }
79
80 ReadingItem update(uint64_t timestamp, double reading) override
81 {
82 readings.emplace_back(timestamp, reading);
83 return update(timestamp);
84 }
85
86 private:
87 std::shared_ptr<details::CollectionFunction> function;
88 std::vector<ReadingItem> readings;
89 CollectionDuration duration;
90};
91
92class Metric::DataStartup : public Metric::CollectionData
93{
94 public:
95 DataStartup(std::shared_ptr<details::CollectionFunction> function) :
96 function(std::move(function))
97 {}
98
99 ReadingItem update(uint64_t timestamp) override
100 {
101 return function->calculateForStartupInterval(readings, timestamp);
102 }
103
104 ReadingItem update(uint64_t timestamp, double reading) override
105 {
106 readings.emplace_back(timestamp, reading);
107 return function->calculateForStartupInterval(readings, timestamp);
108 }
109
110 private:
111 std::shared_ptr<details::CollectionFunction> function;
112 std::vector<ReadingItem> readings;
113};
114
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000115Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
116 std::string idIn, std::string metadataIn,
117 CollectionTimeScope timeScopeIn,
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000118 CollectionDuration collectionDurationIn,
119 std::unique_ptr<interfaces::Clock> clockIn) :
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000120 id(idIn),
121 metadata(metadataIn),
122 readings(sensorsIn.size(),
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000123 MetricValue{std::move(idIn), std::move(metadataIn), 0.0, 0u}),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000124 sensors(std::move(sensorsIn)), operationType(operationTypeIn),
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000125 collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
126 collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
127 collectionTimeScope,
128 collectionDuration)),
129 clock(std::move(clockIn))
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000130{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000131 attemptUnpackJsonMetadata();
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000132}
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100133
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000134Metric::~Metric() = default;
135
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100136void Metric::initialize()
137{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000138 for (const auto& sensor : sensors)
139 {
140 sensor->registerForUpdates(weak_from_this());
141 }
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100142}
143
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000144std::vector<MetricValue> Metric::getReadings() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100145{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000146 const auto timestamp = clock->timestamp();
147
148 auto resultReadings = readings;
149
150 for (size_t i = 0; i < resultReadings.size(); ++i)
151 {
152 std::tie(resultReadings[i].timestamp, resultReadings[i].value) =
153 collectionAlgorithms[i]->update(timestamp);
154 }
155
156 return resultReadings;
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100157}
158
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000159void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100160{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000161 findAssociatedData(notifier).update(timestamp);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100162}
163
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000164void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp,
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100165 double value)
166{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000167 findAssociatedData(notifier).update(timestamp, value);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100168}
169
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000170Metric::CollectionData&
171 Metric::findAssociatedData(const interfaces::Sensor& notifier)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100172{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000173 auto it = std::find_if(
174 sensors.begin(), sensors.end(),
175 [&notifier](const auto& sensor) { return sensor.get() == &notifier; });
176 auto index = std::distance(sensors.begin(), it);
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000177 return *collectionAlgorithms.at(index);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100178}
179
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000180LabeledMetricParameters Metric::dumpConfiguration() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100181{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000182 auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
183 return LabeledSensorParameters(sensor->id().service, sensor->id().path);
184 });
185
186 return LabeledMetricParameters(std::move(sensorPath), operationType, id,
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000187 metadata, collectionTimeScope,
188 collectionDuration);
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000189}
190
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000191std::vector<std::unique_ptr<Metric::CollectionData>>
192 Metric::makeCollectionData(size_t size, OperationType op,
193 CollectionTimeScope timeScope,
194 CollectionDuration duration)
195{
196 using namespace std::string_literals;
197
198 std::vector<std::unique_ptr<Metric::CollectionData>> result;
199
200 result.reserve(size);
201
202 switch (timeScope)
203 {
204 case CollectionTimeScope::interval:
205 std::generate_n(
206 std::back_inserter(result), size,
207 [cf = details::makeCollectionFunction(op), duration] {
208 return std::make_unique<DataInterval>(cf, duration);
209 });
210 break;
211 case CollectionTimeScope::point:
212 std::generate_n(std::back_inserter(result), size,
213 [] { return std::make_unique<DataPoint>(); });
214 break;
215 case CollectionTimeScope::startup:
216 std::generate_n(std::back_inserter(result), size,
217 [cf = details::makeCollectionFunction(op)] {
218 return std::make_unique<DataStartup>(cf);
219 });
220 break;
221 default:
222 throw std::runtime_error("timeScope: "s +
223 utils::enumToString(timeScope) +
224 " is not supported"s);
225 }
226
227 return result;
228}
229
230void Metric::attemptUnpackJsonMetadata()
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000231{
Szymon Dompke3a617022021-07-19 18:23:02 +0200232 using MetricMetadata =
233 utils::LabeledTuple<std::tuple<std::vector<std::string>>,
234 utils::tstring::MetricProperties>;
235
236 using ReadingMetadata =
237 utils::LabeledTuple<std::tuple<std::string, std::string>,
238 utils::tstring::SensorDbusPath,
239 utils::tstring::SensorRedfishUri>;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000240 try
241 {
Szymon Dompke3a617022021-07-19 18:23:02 +0200242 const MetricMetadata parsedMetadata =
243 nlohmann::json::parse(metadata).get<MetricMetadata>();
244
245 if (readings.size() == parsedMetadata.at_index<0>().size())
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000246 {
Szymon Dompke3a617022021-07-19 18:23:02 +0200247 for (size_t i = 0; i < readings.size(); ++i)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000248 {
Szymon Dompke3a617022021-07-19 18:23:02 +0200249 ReadingMetadata readingMetadata{
250 sensors[i]->id().path, parsedMetadata.at_index<0>()[i]};
251 readings[i].metadata = readingMetadata.dump();
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000252 }
253 }
254 }
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000255 catch (const nlohmann::json::exception&)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000256 {}
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100257}