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 std::vector<MetricValue> Metric::getReadings() const 145 { 146 const auto timestamp = clock->timestamp(); 147 148 auto resultReadings = readings; 149 150 for (size_t i = 0; i < resultReadings.size(); ++i) 151 { 152 std::tie(resultReadings[i].timestamp, resultReadings[i].value) = 153 collectionAlgorithms[i]->update(timestamp); 154 } 155 156 return resultReadings; 157 } 158 159 void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp) 160 { 161 findAssociatedData(notifier).update(timestamp); 162 } 163 164 void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp, 165 double value) 166 { 167 findAssociatedData(notifier).update(timestamp, value); 168 } 169 170 Metric::CollectionData& 171 Metric::findAssociatedData(const interfaces::Sensor& notifier) 172 { 173 auto it = std::find_if( 174 sensors.begin(), sensors.end(), 175 [¬ifier](const auto& sensor) { return sensor.get() == ¬ifier; }); 176 auto index = std::distance(sensors.begin(), it); 177 return *collectionAlgorithms.at(index); 178 } 179 180 LabeledMetricParameters Metric::dumpConfiguration() const 181 { 182 auto sensorPath = utils::transform(sensors, [this](const auto& sensor) { 183 return LabeledSensorParameters(sensor->id().service, sensor->id().path); 184 }); 185 186 return LabeledMetricParameters(std::move(sensorPath), operationType, id, 187 metadata, collectionTimeScope, 188 collectionDuration); 189 } 190 191 std::vector<std::unique_ptr<Metric::CollectionData>> 192 Metric::makeCollectionData(size_t size, OperationType op, 193 CollectionTimeScope timeScope, 194 CollectionDuration duration) 195 { 196 using namespace std::string_literals; 197 198 std::vector<std::unique_ptr<Metric::CollectionData>> result; 199 200 result.reserve(size); 201 202 switch (timeScope) 203 { 204 case CollectionTimeScope::interval: 205 std::generate_n( 206 std::back_inserter(result), size, 207 [cf = details::makeCollectionFunction(op), duration] { 208 return std::make_unique<DataInterval>(cf, duration); 209 }); 210 break; 211 case CollectionTimeScope::point: 212 std::generate_n(std::back_inserter(result), size, 213 [] { return std::make_unique<DataPoint>(); }); 214 break; 215 case CollectionTimeScope::startup: 216 std::generate_n(std::back_inserter(result), size, 217 [cf = details::makeCollectionFunction(op)] { 218 return std::make_unique<DataStartup>(cf); 219 }); 220 break; 221 default: 222 throw std::runtime_error("timeScope: "s + 223 utils::enumToString(timeScope) + 224 " is not supported"s); 225 } 226 227 return result; 228 } 229 230 void Metric::attemptUnpackJsonMetadata() 231 { 232 using MetricMetadata = 233 utils::LabeledTuple<std::tuple<std::vector<std::string>>, 234 utils::tstring::MetricProperties>; 235 236 using ReadingMetadata = 237 utils::LabeledTuple<std::tuple<std::string, std::string>, 238 utils::tstring::SensorDbusPath, 239 utils::tstring::SensorRedfishUri>; 240 try 241 { 242 const MetricMetadata parsedMetadata = 243 nlohmann::json::parse(metadata).get<MetricMetadata>(); 244 245 if (readings.size() == parsedMetadata.at_index<0>().size()) 246 { 247 for (size_t i = 0; i < readings.size(); ++i) 248 { 249 ReadingMetadata readingMetadata{ 250 sensors[i]->id().path, parsedMetadata.at_index<0>()[i]}; 251 readings[i].metadata = readingMetadata.dump(); 252 } 253 } 254 } 255 catch (const nlohmann::json::exception&) 256 {} 257 } 258