xref: /openbmc/telemetry/src/metric.cpp (revision 7e098e93ef0974739459d296f99ddfab54722c23)
1c8e3a64aSKrzysztof Grobelny #include "metric.hpp"
26ccfcbf5SKrzysztof Grobelny 
38069771cSKrzysztof Grobelny #include "details/collection_function.hpp"
4dcc4e193SKrzysztof Grobelny #include "types/report_types.hpp"
53a617023SSzymon Dompke #include "utils/labeled_tuple.hpp"
66ccfcbf5SKrzysztof Grobelny #include "utils/transform.hpp"
76ccfcbf5SKrzysztof Grobelny 
86ccfcbf5SKrzysztof Grobelny #include <algorithm>
96ccfcbf5SKrzysztof Grobelny 
108069771cSKrzysztof Grobelny class Metric::CollectionData
118069771cSKrzysztof Grobelny {
128069771cSKrzysztof Grobelny   public:
138069771cSKrzysztof Grobelny     using ReadingItem = details::ReadingItem;
148069771cSKrzysztof Grobelny 
158069771cSKrzysztof Grobelny     virtual ~CollectionData() = default;
168069771cSKrzysztof Grobelny 
178069771cSKrzysztof Grobelny     virtual ReadingItem update(uint64_t timestamp) = 0;
188069771cSKrzysztof Grobelny     virtual ReadingItem update(uint64_t timestamp, double value) = 0;
198069771cSKrzysztof Grobelny };
208069771cSKrzysztof Grobelny 
218069771cSKrzysztof Grobelny class Metric::DataPoint : public Metric::CollectionData
228069771cSKrzysztof Grobelny {
238069771cSKrzysztof Grobelny   public:
248069771cSKrzysztof Grobelny     ReadingItem update(uint64_t timestamp) override
258069771cSKrzysztof Grobelny     {
268069771cSKrzysztof Grobelny         return ReadingItem{lastTimestamp, lastReading};
278069771cSKrzysztof Grobelny     }
288069771cSKrzysztof Grobelny 
298069771cSKrzysztof Grobelny     ReadingItem update(uint64_t timestamp, double reading) override
308069771cSKrzysztof Grobelny     {
318069771cSKrzysztof Grobelny         lastTimestamp = timestamp;
328069771cSKrzysztof Grobelny         lastReading = reading;
338069771cSKrzysztof Grobelny         return update(timestamp);
348069771cSKrzysztof Grobelny     }
358069771cSKrzysztof Grobelny 
368069771cSKrzysztof Grobelny   private:
378069771cSKrzysztof Grobelny     uint64_t lastTimestamp = 0u;
388069771cSKrzysztof Grobelny     double lastReading = 0.0;
398069771cSKrzysztof Grobelny };
408069771cSKrzysztof Grobelny 
418069771cSKrzysztof Grobelny class Metric::DataInterval : public Metric::CollectionData
428069771cSKrzysztof Grobelny {
438069771cSKrzysztof Grobelny   public:
448069771cSKrzysztof Grobelny     DataInterval(std::shared_ptr<details::CollectionFunction> function,
458069771cSKrzysztof Grobelny                  CollectionDuration duration) :
468069771cSKrzysztof Grobelny         function(std::move(function)),
478069771cSKrzysztof Grobelny         duration(duration)
488069771cSKrzysztof Grobelny     {}
498069771cSKrzysztof Grobelny 
508069771cSKrzysztof Grobelny     ReadingItem update(uint64_t timestamp) override
518069771cSKrzysztof Grobelny     {
528069771cSKrzysztof Grobelny         if (readings.size() > 0)
538069771cSKrzysztof Grobelny         {
548069771cSKrzysztof Grobelny             auto it = readings.begin();
558069771cSKrzysztof Grobelny             for (auto kt = std::next(readings.rbegin()); kt != readings.rend();
568069771cSKrzysztof Grobelny                  ++kt)
578069771cSKrzysztof Grobelny             {
588069771cSKrzysztof Grobelny                 const auto& [nextItemTimestamp, nextItemReading] =
598069771cSKrzysztof Grobelny                     *std::prev(kt);
608069771cSKrzysztof Grobelny                 if (timestamp >= nextItemTimestamp &&
618069771cSKrzysztof Grobelny                     static_cast<uint64_t>(timestamp - nextItemTimestamp) >
628069771cSKrzysztof Grobelny                         duration.t.count())
638069771cSKrzysztof Grobelny                 {
648069771cSKrzysztof Grobelny                     it = kt.base();
658069771cSKrzysztof Grobelny                     break;
668069771cSKrzysztof Grobelny                 }
678069771cSKrzysztof Grobelny             }
688069771cSKrzysztof Grobelny             readings.erase(readings.begin(), it);
698069771cSKrzysztof Grobelny 
708069771cSKrzysztof Grobelny             if (timestamp > duration.t.count())
718069771cSKrzysztof Grobelny             {
728069771cSKrzysztof Grobelny                 readings.front().first = std::max(
738069771cSKrzysztof Grobelny                     readings.front().first, timestamp - duration.t.count());
748069771cSKrzysztof Grobelny             }
758069771cSKrzysztof Grobelny         }
768069771cSKrzysztof Grobelny 
778069771cSKrzysztof Grobelny         return function->calculate(readings, timestamp);
788069771cSKrzysztof Grobelny     }
798069771cSKrzysztof Grobelny 
808069771cSKrzysztof Grobelny     ReadingItem update(uint64_t timestamp, double reading) override
818069771cSKrzysztof Grobelny     {
828069771cSKrzysztof Grobelny         readings.emplace_back(timestamp, reading);
838069771cSKrzysztof Grobelny         return update(timestamp);
848069771cSKrzysztof Grobelny     }
858069771cSKrzysztof Grobelny 
868069771cSKrzysztof Grobelny   private:
878069771cSKrzysztof Grobelny     std::shared_ptr<details::CollectionFunction> function;
888069771cSKrzysztof Grobelny     std::vector<ReadingItem> readings;
898069771cSKrzysztof Grobelny     CollectionDuration duration;
908069771cSKrzysztof Grobelny };
918069771cSKrzysztof Grobelny 
928069771cSKrzysztof Grobelny class Metric::DataStartup : public Metric::CollectionData
938069771cSKrzysztof Grobelny {
948069771cSKrzysztof Grobelny   public:
958069771cSKrzysztof Grobelny     DataStartup(std::shared_ptr<details::CollectionFunction> function) :
968069771cSKrzysztof Grobelny         function(std::move(function))
978069771cSKrzysztof Grobelny     {}
988069771cSKrzysztof Grobelny 
998069771cSKrzysztof Grobelny     ReadingItem update(uint64_t timestamp) override
1008069771cSKrzysztof Grobelny     {
1018069771cSKrzysztof Grobelny         return function->calculateForStartupInterval(readings, timestamp);
1028069771cSKrzysztof Grobelny     }
1038069771cSKrzysztof Grobelny 
1048069771cSKrzysztof Grobelny     ReadingItem update(uint64_t timestamp, double reading) override
1058069771cSKrzysztof Grobelny     {
1068069771cSKrzysztof Grobelny         readings.emplace_back(timestamp, reading);
1078069771cSKrzysztof Grobelny         return function->calculateForStartupInterval(readings, timestamp);
1088069771cSKrzysztof Grobelny     }
1098069771cSKrzysztof Grobelny 
1108069771cSKrzysztof Grobelny   private:
1118069771cSKrzysztof Grobelny     std::shared_ptr<details::CollectionFunction> function;
1128069771cSKrzysztof Grobelny     std::vector<ReadingItem> readings;
1138069771cSKrzysztof Grobelny };
1148069771cSKrzysztof Grobelny 
115dcc4e193SKrzysztof Grobelny Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
116dcc4e193SKrzysztof Grobelny                std::string idIn, std::string metadataIn,
117dcc4e193SKrzysztof Grobelny                CollectionTimeScope timeScopeIn,
1188069771cSKrzysztof Grobelny                CollectionDuration collectionDurationIn,
1198069771cSKrzysztof Grobelny                std::unique_ptr<interfaces::Clock> clockIn) :
120dcc4e193SKrzysztof Grobelny     id(idIn),
121dcc4e193SKrzysztof Grobelny     metadata(metadataIn),
122dcc4e193SKrzysztof Grobelny     readings(sensorsIn.size(),
1238069771cSKrzysztof Grobelny              MetricValue{std::move(idIn), std::move(metadataIn), 0.0, 0u}),
124dcc4e193SKrzysztof Grobelny     sensors(std::move(sensorsIn)), operationType(operationTypeIn),
1258069771cSKrzysztof Grobelny     collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
1268069771cSKrzysztof Grobelny     collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
1278069771cSKrzysztof Grobelny                                             collectionTimeScope,
1288069771cSKrzysztof Grobelny                                             collectionDuration)),
1298069771cSKrzysztof Grobelny     clock(std::move(clockIn))
130dcc4e193SKrzysztof Grobelny {
1318069771cSKrzysztof Grobelny     attemptUnpackJsonMetadata();
132dcc4e193SKrzysztof Grobelny }
1336ccfcbf5SKrzysztof Grobelny 
1348069771cSKrzysztof Grobelny Metric::~Metric() = default;
1358069771cSKrzysztof Grobelny 
1366ccfcbf5SKrzysztof Grobelny void Metric::initialize()
1376ccfcbf5SKrzysztof Grobelny {
138dcc4e193SKrzysztof Grobelny     for (const auto& sensor : sensors)
139dcc4e193SKrzysztof Grobelny     {
1406ccfcbf5SKrzysztof Grobelny         sensor->registerForUpdates(weak_from_this());
1416ccfcbf5SKrzysztof Grobelny     }
142dcc4e193SKrzysztof Grobelny }
143e8fc5751SKrzysztof Grobelny 
144*7e098e93SLukasz Kazmierczak void Metric::deinitialize()
145*7e098e93SLukasz Kazmierczak {
146*7e098e93SLukasz Kazmierczak     for (const auto& sensor : sensors)
147*7e098e93SLukasz Kazmierczak     {
148*7e098e93SLukasz Kazmierczak         sensor->unregisterFromUpdates(weak_from_this());
149*7e098e93SLukasz Kazmierczak     }
150*7e098e93SLukasz Kazmierczak }
151*7e098e93SLukasz Kazmierczak 
1528069771cSKrzysztof Grobelny std::vector<MetricValue> Metric::getReadings() const
153e8fc5751SKrzysztof Grobelny {
1548069771cSKrzysztof Grobelny     const auto timestamp = clock->timestamp();
1558069771cSKrzysztof Grobelny     auto resultReadings = readings;
1568069771cSKrzysztof Grobelny 
1578069771cSKrzysztof Grobelny     for (size_t i = 0; i < resultReadings.size(); ++i)
1588069771cSKrzysztof Grobelny     {
1598069771cSKrzysztof Grobelny         std::tie(resultReadings[i].timestamp, resultReadings[i].value) =
1608069771cSKrzysztof Grobelny             collectionAlgorithms[i]->update(timestamp);
1618069771cSKrzysztof Grobelny     }
1628069771cSKrzysztof Grobelny 
1638069771cSKrzysztof Grobelny     return resultReadings;
1646ccfcbf5SKrzysztof Grobelny }
1656ccfcbf5SKrzysztof Grobelny 
166e8fc5751SKrzysztof Grobelny void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp)
1676ccfcbf5SKrzysztof Grobelny {
1688069771cSKrzysztof Grobelny     findAssociatedData(notifier).update(timestamp);
1696ccfcbf5SKrzysztof Grobelny }
1706ccfcbf5SKrzysztof Grobelny 
171e8fc5751SKrzysztof Grobelny void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp,
1726ccfcbf5SKrzysztof Grobelny                            double value)
1736ccfcbf5SKrzysztof Grobelny {
1748069771cSKrzysztof Grobelny     findAssociatedData(notifier).update(timestamp, value);
1756ccfcbf5SKrzysztof Grobelny }
1766ccfcbf5SKrzysztof Grobelny 
1778069771cSKrzysztof Grobelny Metric::CollectionData&
1788069771cSKrzysztof Grobelny     Metric::findAssociatedData(const interfaces::Sensor& notifier)
1796ccfcbf5SKrzysztof Grobelny {
180dcc4e193SKrzysztof Grobelny     auto it = std::find_if(
181dcc4e193SKrzysztof Grobelny         sensors.begin(), sensors.end(),
182dcc4e193SKrzysztof Grobelny         [&notifier](const auto& sensor) { return sensor.get() == &notifier; });
183dcc4e193SKrzysztof Grobelny     auto index = std::distance(sensors.begin(), it);
1848069771cSKrzysztof Grobelny     return *collectionAlgorithms.at(index);
1856ccfcbf5SKrzysztof Grobelny }
1866ccfcbf5SKrzysztof Grobelny 
187d2238194SKrzysztof Grobelny LabeledMetricParameters Metric::dumpConfiguration() const
1886ccfcbf5SKrzysztof Grobelny {
189dcc4e193SKrzysztof Grobelny     auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
190dcc4e193SKrzysztof Grobelny         return LabeledSensorParameters(sensor->id().service, sensor->id().path);
191dcc4e193SKrzysztof Grobelny     });
192dcc4e193SKrzysztof Grobelny 
193dcc4e193SKrzysztof Grobelny     return LabeledMetricParameters(std::move(sensorPath), operationType, id,
1948069771cSKrzysztof Grobelny                                    metadata, collectionTimeScope,
1958069771cSKrzysztof Grobelny                                    collectionDuration);
196dcc4e193SKrzysztof Grobelny }
197dcc4e193SKrzysztof Grobelny 
1988069771cSKrzysztof Grobelny std::vector<std::unique_ptr<Metric::CollectionData>>
1998069771cSKrzysztof Grobelny     Metric::makeCollectionData(size_t size, OperationType op,
2008069771cSKrzysztof Grobelny                                CollectionTimeScope timeScope,
2018069771cSKrzysztof Grobelny                                CollectionDuration duration)
2028069771cSKrzysztof Grobelny {
2038069771cSKrzysztof Grobelny     using namespace std::string_literals;
2048069771cSKrzysztof Grobelny 
2058069771cSKrzysztof Grobelny     std::vector<std::unique_ptr<Metric::CollectionData>> result;
2068069771cSKrzysztof Grobelny 
2078069771cSKrzysztof Grobelny     result.reserve(size);
2088069771cSKrzysztof Grobelny 
2098069771cSKrzysztof Grobelny     switch (timeScope)
2108069771cSKrzysztof Grobelny     {
2118069771cSKrzysztof Grobelny         case CollectionTimeScope::interval:
2128069771cSKrzysztof Grobelny             std::generate_n(
2138069771cSKrzysztof Grobelny                 std::back_inserter(result), size,
2148069771cSKrzysztof Grobelny                 [cf = details::makeCollectionFunction(op), duration] {
2158069771cSKrzysztof Grobelny                     return std::make_unique<DataInterval>(cf, duration);
2168069771cSKrzysztof Grobelny                 });
2178069771cSKrzysztof Grobelny             break;
2188069771cSKrzysztof Grobelny         case CollectionTimeScope::point:
2198069771cSKrzysztof Grobelny             std::generate_n(std::back_inserter(result), size,
2208069771cSKrzysztof Grobelny                             [] { return std::make_unique<DataPoint>(); });
2218069771cSKrzysztof Grobelny             break;
2228069771cSKrzysztof Grobelny         case CollectionTimeScope::startup:
2238069771cSKrzysztof Grobelny             std::generate_n(std::back_inserter(result), size,
2248069771cSKrzysztof Grobelny                             [cf = details::makeCollectionFunction(op)] {
2258069771cSKrzysztof Grobelny                                 return std::make_unique<DataStartup>(cf);
2268069771cSKrzysztof Grobelny                             });
2278069771cSKrzysztof Grobelny             break;
2288069771cSKrzysztof Grobelny         default:
2298069771cSKrzysztof Grobelny             throw std::runtime_error("timeScope: "s +
2308069771cSKrzysztof Grobelny                                      utils::enumToString(timeScope) +
2318069771cSKrzysztof Grobelny                                      " is not supported"s);
2328069771cSKrzysztof Grobelny     }
2338069771cSKrzysztof Grobelny 
2348069771cSKrzysztof Grobelny     return result;
2358069771cSKrzysztof Grobelny }
2368069771cSKrzysztof Grobelny 
2378069771cSKrzysztof Grobelny void Metric::attemptUnpackJsonMetadata()
238dcc4e193SKrzysztof Grobelny {
2393a617023SSzymon Dompke     using MetricMetadata =
2403a617023SSzymon Dompke         utils::LabeledTuple<std::tuple<std::vector<std::string>>,
2413a617023SSzymon Dompke                             utils::tstring::MetricProperties>;
2423a617023SSzymon Dompke 
2433a617023SSzymon Dompke     using ReadingMetadata =
2443a617023SSzymon Dompke         utils::LabeledTuple<std::tuple<std::string, std::string>,
2453a617023SSzymon Dompke                             utils::tstring::SensorDbusPath,
2463a617023SSzymon Dompke                             utils::tstring::SensorRedfishUri>;
247dcc4e193SKrzysztof Grobelny     try
248dcc4e193SKrzysztof Grobelny     {
2493a617023SSzymon Dompke         const MetricMetadata parsedMetadata =
2503a617023SSzymon Dompke             nlohmann::json::parse(metadata).get<MetricMetadata>();
2513a617023SSzymon Dompke 
2523a617023SSzymon Dompke         if (readings.size() == parsedMetadata.at_index<0>().size())
253dcc4e193SKrzysztof Grobelny         {
254dcc4e193SKrzysztof Grobelny             for (size_t i = 0; i < readings.size(); ++i)
255dcc4e193SKrzysztof Grobelny             {
2563a617023SSzymon Dompke                 ReadingMetadata readingMetadata{
2573a617023SSzymon Dompke                     sensors[i]->id().path, parsedMetadata.at_index<0>()[i]};
2583a617023SSzymon Dompke                 readings[i].metadata = readingMetadata.dump();
259dcc4e193SKrzysztof Grobelny             }
260dcc4e193SKrzysztof Grobelny         }
261dcc4e193SKrzysztof Grobelny     }
2628069771cSKrzysztof Grobelny     catch (const nlohmann::json::exception&)
263dcc4e193SKrzysztof Grobelny     {}
2646ccfcbf5SKrzysztof Grobelny }
265