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 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) 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 } 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 124 Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn, 125 std::string idIn, CollectionTimeScope timeScopeIn, 126 CollectionDuration collectionDurationIn, 127 std::unique_ptr<interfaces::Clock> clockIn) : 128 id(std::move(idIn)), 129 sensors(std::move(sensorsIn)), operationType(operationTypeIn), 130 collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn), 131 collectionAlgorithms(makeCollectionData(sensors.size(), operationType, 132 collectionTimeScope, 133 collectionDuration)), 134 clock(std::move(clockIn)) 135 { 136 readings = utils::transform(sensors, [this](const auto& sensor) { 137 return MetricValue{id, sensor->metadata(), 0.0, 0u}; 138 }); 139 } 140 141 Metric::~Metric() = default; 142 143 void Metric::initialize() 144 { 145 for (const auto& sensor : sensors) 146 { 147 sensor->registerForUpdates(weak_from_this()); 148 } 149 } 150 151 void Metric::deinitialize() 152 { 153 for (const auto& sensor : sensors) 154 { 155 sensor->unregisterFromUpdates(weak_from_this()); 156 } 157 } 158 159 std::vector<MetricValue> Metric::getReadings() const 160 { 161 const auto timestamp = clock->timestamp(); 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; 171 } 172 173 void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp) 174 { 175 findAssociatedData(notifier).update(timestamp); 176 } 177 178 void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp, 179 double value) 180 { 181 findAssociatedData(notifier).update(timestamp, value); 182 } 183 184 Metric::CollectionData& 185 Metric::findAssociatedData(const interfaces::Sensor& notifier) 186 { 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); 191 return *collectionAlgorithms.at(index); 192 } 193 194 LabeledMetricParameters Metric::dumpConfiguration() const 195 { 196 auto sensorPath = utils::transform(sensors, [this](const auto& sensor) { 197 return LabeledSensorParameters(sensor->id().service, sensor->id().path, 198 sensor->metadata()); 199 }); 200 201 return LabeledMetricParameters(std::move(sensorPath), operationType, id, 202 collectionTimeScope, collectionDuration); 203 } 204 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 244 uint64_t Metric::sensorCount() const 245 { 246 return sensors.size(); 247 } 248