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