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