| Krzysztof Grobelny | c8e3a64 | 2020-10-23 12:29:16 +0200 | [diff] [blame] | 1 | #include "metric.hpp" | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 2 |  | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 3 | #include "details/collection_function.hpp" | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 4 | #include "types/report_types.hpp" | 
| Szymon Dompke | 3a61702 | 2021-07-19 18:23:02 +0200 | [diff] [blame] | 5 | #include "utils/labeled_tuple.hpp" | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 6 | #include "utils/transform.hpp" | 
 | 7 |  | 
| Krzysztof Grobelny | b8cc78d | 2021-11-29 15:54:53 +0100 | [diff] [blame] | 8 | #include <sdbusplus/exception.hpp> | 
 | 9 |  | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 10 | #include <algorithm> | 
 | 11 |  | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 12 | class Metric::CollectionData | 
 | 13 | { | 
 | 14 |   public: | 
 | 15 |     using ReadingItem = details::ReadingItem; | 
 | 16 |  | 
 | 17 |     virtual ~CollectionData() = default; | 
 | 18 |  | 
 | 19 |     virtual ReadingItem update(uint64_t timestamp) = 0; | 
 | 20 |     virtual ReadingItem update(uint64_t timestamp, double value) = 0; | 
 | 21 | }; | 
 | 22 |  | 
 | 23 | class Metric::DataPoint : public Metric::CollectionData | 
 | 24 | { | 
 | 25 |   public: | 
 | 26 |     ReadingItem update(uint64_t timestamp) override | 
 | 27 |     { | 
 | 28 |         return ReadingItem{lastTimestamp, lastReading}; | 
 | 29 |     } | 
 | 30 |  | 
 | 31 |     ReadingItem update(uint64_t timestamp, double reading) override | 
 | 32 |     { | 
 | 33 |         lastTimestamp = timestamp; | 
 | 34 |         lastReading = reading; | 
 | 35 |         return update(timestamp); | 
 | 36 |     } | 
 | 37 |  | 
 | 38 |   private: | 
 | 39 |     uint64_t lastTimestamp = 0u; | 
 | 40 |     double lastReading = 0.0; | 
 | 41 | }; | 
 | 42 |  | 
 | 43 | class Metric::DataInterval : public Metric::CollectionData | 
 | 44 | { | 
 | 45 |   public: | 
 | 46 |     DataInterval(std::shared_ptr<details::CollectionFunction> function, | 
 | 47 |                  CollectionDuration duration) : | 
 | 48 |         function(std::move(function)), | 
 | 49 |         duration(duration) | 
| Krzysztof Grobelny | b8cc78d | 2021-11-29 15:54:53 +0100 | [diff] [blame] | 50 |     { | 
 | 51 |         if (duration.t.count() == 0) | 
 | 52 |         { | 
 | 53 |             throw sdbusplus::exception::SdBusError( | 
 | 54 |                 static_cast<int>(std::errc::invalid_argument), | 
 | 55 |                 "Invalid CollectionDuration"); | 
 | 56 |         } | 
 | 57 |     } | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 58 |  | 
 | 59 |     ReadingItem update(uint64_t timestamp) override | 
 | 60 |     { | 
 | 61 |         if (readings.size() > 0) | 
 | 62 |         { | 
 | 63 |             auto it = readings.begin(); | 
 | 64 |             for (auto kt = std::next(readings.rbegin()); kt != readings.rend(); | 
 | 65 |                  ++kt) | 
 | 66 |             { | 
 | 67 |                 const auto& [nextItemTimestamp, nextItemReading] = | 
 | 68 |                     *std::prev(kt); | 
 | 69 |                 if (timestamp >= nextItemTimestamp && | 
 | 70 |                     static_cast<uint64_t>(timestamp - nextItemTimestamp) > | 
 | 71 |                         duration.t.count()) | 
 | 72 |                 { | 
 | 73 |                     it = kt.base(); | 
 | 74 |                     break; | 
 | 75 |                 } | 
 | 76 |             } | 
 | 77 |             readings.erase(readings.begin(), it); | 
 | 78 |  | 
 | 79 |             if (timestamp > duration.t.count()) | 
 | 80 |             { | 
 | 81 |                 readings.front().first = std::max( | 
 | 82 |                     readings.front().first, timestamp - duration.t.count()); | 
 | 83 |             } | 
 | 84 |         } | 
 | 85 |  | 
 | 86 |         return function->calculate(readings, timestamp); | 
 | 87 |     } | 
 | 88 |  | 
 | 89 |     ReadingItem update(uint64_t timestamp, double reading) override | 
 | 90 |     { | 
 | 91 |         readings.emplace_back(timestamp, reading); | 
 | 92 |         return update(timestamp); | 
 | 93 |     } | 
 | 94 |  | 
 | 95 |   private: | 
 | 96 |     std::shared_ptr<details::CollectionFunction> function; | 
 | 97 |     std::vector<ReadingItem> readings; | 
 | 98 |     CollectionDuration duration; | 
 | 99 | }; | 
 | 100 |  | 
 | 101 | class Metric::DataStartup : public Metric::CollectionData | 
 | 102 | { | 
 | 103 |   public: | 
 | 104 |     DataStartup(std::shared_ptr<details::CollectionFunction> function) : | 
 | 105 |         function(std::move(function)) | 
 | 106 |     {} | 
 | 107 |  | 
 | 108 |     ReadingItem update(uint64_t timestamp) override | 
 | 109 |     { | 
 | 110 |         return function->calculateForStartupInterval(readings, timestamp); | 
 | 111 |     } | 
 | 112 |  | 
 | 113 |     ReadingItem update(uint64_t timestamp, double reading) override | 
 | 114 |     { | 
 | 115 |         readings.emplace_back(timestamp, reading); | 
 | 116 |         return function->calculateForStartupInterval(readings, timestamp); | 
 | 117 |     } | 
 | 118 |  | 
 | 119 |   private: | 
 | 120 |     std::shared_ptr<details::CollectionFunction> function; | 
 | 121 |     std::vector<ReadingItem> readings; | 
 | 122 | }; | 
 | 123 |  | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 124 | Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn, | 
| Krzysztof Grobelny | b8cc78d | 2021-11-29 15:54:53 +0100 | [diff] [blame] | 125 |                std::string idIn, CollectionTimeScope timeScopeIn, | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 126 |                CollectionDuration collectionDurationIn, | 
 | 127 |                std::unique_ptr<interfaces::Clock> clockIn) : | 
| Krzysztof Grobelny | b8cc78d | 2021-11-29 15:54:53 +0100 | [diff] [blame] | 128 |     id(std::move(idIn)), | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 129 |     sensors(std::move(sensorsIn)), operationType(operationTypeIn), | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 130 |     collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn), | 
 | 131 |     collectionAlgorithms(makeCollectionData(sensors.size(), operationType, | 
 | 132 |                                             collectionTimeScope, | 
 | 133 |                                             collectionDuration)), | 
 | 134 |     clock(std::move(clockIn)) | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 135 | { | 
| Krzysztof Grobelny | b8cc78d | 2021-11-29 15:54:53 +0100 | [diff] [blame] | 136 |     readings = utils::transform(sensors, [this](const auto& sensor) { | 
 | 137 |         return MetricValue{id, sensor->metadata(), 0.0, 0u}; | 
 | 138 |     }); | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 139 | } | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 140 |  | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 141 | Metric::~Metric() = default; | 
 | 142 |  | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 143 | void Metric::initialize() | 
 | 144 | { | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 145 |     for (const auto& sensor : sensors) | 
 | 146 |     { | 
 | 147 |         sensor->registerForUpdates(weak_from_this()); | 
 | 148 |     } | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 149 | } | 
 | 150 |  | 
| Lukasz Kazmierczak | 7e098e9 | 2021-09-16 15:59:56 +0200 | [diff] [blame] | 151 | void Metric::deinitialize() | 
 | 152 | { | 
 | 153 |     for (const auto& sensor : sensors) | 
 | 154 |     { | 
 | 155 |         sensor->unregisterFromUpdates(weak_from_this()); | 
 | 156 |     } | 
 | 157 | } | 
 | 158 |  | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 159 | std::vector<MetricValue> Metric::getReadings() const | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 160 | { | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 161 |     const auto timestamp = clock->timestamp(); | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 162 |     auto resultReadings = readings; | 
 | 163 |  | 
 | 164 |     for (size_t i = 0; i < resultReadings.size(); ++i) | 
 | 165 |     { | 
 | 166 |         std::tie(resultReadings[i].timestamp, resultReadings[i].value) = | 
 | 167 |             collectionAlgorithms[i]->update(timestamp); | 
 | 168 |     } | 
 | 169 |  | 
 | 170 |     return resultReadings; | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 171 | } | 
 | 172 |  | 
| Krzysztof Grobelny | e8fc575 | 2021-02-05 14:30:45 +0000 | [diff] [blame] | 173 | void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp) | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 174 | { | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 175 |     findAssociatedData(notifier).update(timestamp); | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 176 | } | 
 | 177 |  | 
| Krzysztof Grobelny | e8fc575 | 2021-02-05 14:30:45 +0000 | [diff] [blame] | 178 | void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp, | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 179 |                            double value) | 
 | 180 | { | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 181 |     findAssociatedData(notifier).update(timestamp, value); | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 182 | } | 
 | 183 |  | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 184 | Metric::CollectionData& | 
 | 185 |     Metric::findAssociatedData(const interfaces::Sensor& notifier) | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 186 | { | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 187 |     auto it = std::find_if( | 
 | 188 |         sensors.begin(), sensors.end(), | 
 | 189 |         [¬ifier](const auto& sensor) { return sensor.get() == ¬ifier; }); | 
 | 190 |     auto index = std::distance(sensors.begin(), it); | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 191 |     return *collectionAlgorithms.at(index); | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 192 | } | 
 | 193 |  | 
| Krzysztof Grobelny | d223819 | 2020-12-02 09:27:28 +0000 | [diff] [blame] | 194 | LabeledMetricParameters Metric::dumpConfiguration() const | 
| Krzysztof Grobelny | 6ccfcbf | 2020-11-04 09:31:36 +0100 | [diff] [blame] | 195 | { | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 196 |     auto sensorPath = utils::transform(sensors, [this](const auto& sensor) { | 
| Krzysztof Grobelny | b8cc78d | 2021-11-29 15:54:53 +0100 | [diff] [blame] | 197 |         return LabeledSensorParameters(sensor->id().service, sensor->id().path, | 
 | 198 |                                        sensor->metadata()); | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 199 |     }); | 
 | 200 |  | 
 | 201 |     return LabeledMetricParameters(std::move(sensorPath), operationType, id, | 
| Krzysztof Grobelny | b8cc78d | 2021-11-29 15:54:53 +0100 | [diff] [blame] | 202 |                                    collectionTimeScope, collectionDuration); | 
| Krzysztof Grobelny | dcc4e19 | 2021-03-08 09:09:34 +0000 | [diff] [blame] | 203 | } | 
 | 204 |  | 
| Krzysztof Grobelny | 8069771 | 2021-03-04 09:49:27 +0000 | [diff] [blame] | 205 | std::vector<std::unique_ptr<Metric::CollectionData>> | 
 | 206 |     Metric::makeCollectionData(size_t size, OperationType op, | 
 | 207 |                                CollectionTimeScope timeScope, | 
 | 208 |                                CollectionDuration duration) | 
 | 209 | { | 
 | 210 |     using namespace std::string_literals; | 
 | 211 |  | 
 | 212 |     std::vector<std::unique_ptr<Metric::CollectionData>> result; | 
 | 213 |  | 
 | 214 |     result.reserve(size); | 
 | 215 |  | 
 | 216 |     switch (timeScope) | 
 | 217 |     { | 
 | 218 |         case CollectionTimeScope::interval: | 
 | 219 |             std::generate_n( | 
 | 220 |                 std::back_inserter(result), size, | 
 | 221 |                 [cf = details::makeCollectionFunction(op), duration] { | 
 | 222 |                     return std::make_unique<DataInterval>(cf, duration); | 
 | 223 |                 }); | 
 | 224 |             break; | 
 | 225 |         case CollectionTimeScope::point: | 
 | 226 |             std::generate_n(std::back_inserter(result), size, | 
 | 227 |                             [] { return std::make_unique<DataPoint>(); }); | 
 | 228 |             break; | 
 | 229 |         case CollectionTimeScope::startup: | 
 | 230 |             std::generate_n(std::back_inserter(result), size, | 
 | 231 |                             [cf = details::makeCollectionFunction(op)] { | 
 | 232 |                                 return std::make_unique<DataStartup>(cf); | 
 | 233 |                             }); | 
 | 234 |             break; | 
 | 235 |         default: | 
 | 236 |             throw std::runtime_error("timeScope: "s + | 
 | 237 |                                      utils::enumToString(timeScope) + | 
 | 238 |                                      " is not supported"s); | 
 | 239 |     } | 
 | 240 |  | 
 | 241 |     return result; | 
 | 242 | } | 
 | 243 |  | 
| Szymon Dompke | 3eb5686 | 2021-09-20 15:32:04 +0200 | [diff] [blame] | 244 | uint64_t Metric::sensorCount() const | 
 | 245 | { | 
 | 246 |     return sensors.size(); | 
 | 247 | } |