blob: 42902932add06c8ac5e098a914a21405b40f40fb [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
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200144void Metric::deinitialize()
145{
146 for (const auto& sensor : sensors)
147 {
148 sensor->unregisterFromUpdates(weak_from_this());
149 }
150}
151
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000152std::vector<MetricValue> Metric::getReadings() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100153{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000154 const auto timestamp = clock->timestamp();
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000155 auto resultReadings = readings;
156
157 for (size_t i = 0; i < resultReadings.size(); ++i)
158 {
159 std::tie(resultReadings[i].timestamp, resultReadings[i].value) =
160 collectionAlgorithms[i]->update(timestamp);
161 }
162
163 return resultReadings;
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100164}
165
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000166void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100167{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000168 findAssociatedData(notifier).update(timestamp);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100169}
170
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000171void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp,
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100172 double value)
173{
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000174 findAssociatedData(notifier).update(timestamp, value);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100175}
176
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000177Metric::CollectionData&
178 Metric::findAssociatedData(const interfaces::Sensor& notifier)
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100179{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000180 auto it = std::find_if(
181 sensors.begin(), sensors.end(),
182 [&notifier](const auto& sensor) { return sensor.get() == &notifier; });
183 auto index = std::distance(sensors.begin(), it);
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000184 return *collectionAlgorithms.at(index);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100185}
186
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000187LabeledMetricParameters Metric::dumpConfiguration() const
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100188{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000189 auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
190 return LabeledSensorParameters(sensor->id().service, sensor->id().path);
191 });
192
193 return LabeledMetricParameters(std::move(sensorPath), operationType, id,
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000194 metadata, collectionTimeScope,
195 collectionDuration);
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000196}
197
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000198std::vector<std::unique_ptr<Metric::CollectionData>>
199 Metric::makeCollectionData(size_t size, OperationType op,
200 CollectionTimeScope timeScope,
201 CollectionDuration duration)
202{
203 using namespace std::string_literals;
204
205 std::vector<std::unique_ptr<Metric::CollectionData>> result;
206
207 result.reserve(size);
208
209 switch (timeScope)
210 {
211 case CollectionTimeScope::interval:
212 std::generate_n(
213 std::back_inserter(result), size,
214 [cf = details::makeCollectionFunction(op), duration] {
215 return std::make_unique<DataInterval>(cf, duration);
216 });
217 break;
218 case CollectionTimeScope::point:
219 std::generate_n(std::back_inserter(result), size,
220 [] { return std::make_unique<DataPoint>(); });
221 break;
222 case CollectionTimeScope::startup:
223 std::generate_n(std::back_inserter(result), size,
224 [cf = details::makeCollectionFunction(op)] {
225 return std::make_unique<DataStartup>(cf);
226 });
227 break;
228 default:
229 throw std::runtime_error("timeScope: "s +
230 utils::enumToString(timeScope) +
231 " is not supported"s);
232 }
233
234 return result;
235}
236
237void Metric::attemptUnpackJsonMetadata()
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000238{
Szymon Dompke3a617022021-07-19 18:23:02 +0200239 using MetricMetadata =
240 utils::LabeledTuple<std::tuple<std::vector<std::string>>,
241 utils::tstring::MetricProperties>;
242
243 using ReadingMetadata =
244 utils::LabeledTuple<std::tuple<std::string, std::string>,
245 utils::tstring::SensorDbusPath,
246 utils::tstring::SensorRedfishUri>;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000247 try
248 {
Szymon Dompke3a617022021-07-19 18:23:02 +0200249 const MetricMetadata parsedMetadata =
250 nlohmann::json::parse(metadata).get<MetricMetadata>();
251
252 if (readings.size() == parsedMetadata.at_index<0>().size())
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000253 {
Szymon Dompke3a617022021-07-19 18:23:02 +0200254 for (size_t i = 0; i < readings.size(); ++i)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000255 {
Szymon Dompke3a617022021-07-19 18:23:02 +0200256 ReadingMetadata readingMetadata{
257 sensors[i]->id().path, parsedMetadata.at_index<0>()[i]};
258 readings[i].metadata = readingMetadata.dump();
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000259 }
260 }
261 }
Krzysztof Grobelny80697712021-03-04 09:49:27 +0000262 catch (const nlohmann::json::exception&)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000263 {}
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +0100264}