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