blob: 04d3fe9a303aec88249cb2ab08442079390fee40 [file] [log] [blame]
#include "metric.hpp"
#include "details/collection_function.hpp"
#include "types/report_types.hpp"
#include "utils/labeled_tuple.hpp"
#include "utils/transform.hpp"
#include <algorithm>
class Metric::CollectionData
{
public:
using ReadingItem = details::ReadingItem;
virtual ~CollectionData() = default;
virtual ReadingItem update(uint64_t timestamp) = 0;
virtual ReadingItem update(uint64_t timestamp, double value) = 0;
};
class Metric::DataPoint : public Metric::CollectionData
{
public:
ReadingItem update(uint64_t timestamp) override
{
return ReadingItem{lastTimestamp, lastReading};
}
ReadingItem update(uint64_t timestamp, double reading) override
{
lastTimestamp = timestamp;
lastReading = reading;
return update(timestamp);
}
private:
uint64_t lastTimestamp = 0u;
double lastReading = 0.0;
};
class Metric::DataInterval : public Metric::CollectionData
{
public:
DataInterval(std::shared_ptr<details::CollectionFunction> function,
CollectionDuration duration) :
function(std::move(function)),
duration(duration)
{}
ReadingItem update(uint64_t timestamp) override
{
if (readings.size() > 0)
{
auto it = readings.begin();
for (auto kt = std::next(readings.rbegin()); kt != readings.rend();
++kt)
{
const auto& [nextItemTimestamp, nextItemReading] =
*std::prev(kt);
if (timestamp >= nextItemTimestamp &&
static_cast<uint64_t>(timestamp - nextItemTimestamp) >
duration.t.count())
{
it = kt.base();
break;
}
}
readings.erase(readings.begin(), it);
if (timestamp > duration.t.count())
{
readings.front().first = std::max(
readings.front().first, timestamp - duration.t.count());
}
}
return function->calculate(readings, timestamp);
}
ReadingItem update(uint64_t timestamp, double reading) override
{
readings.emplace_back(timestamp, reading);
return update(timestamp);
}
private:
std::shared_ptr<details::CollectionFunction> function;
std::vector<ReadingItem> readings;
CollectionDuration duration;
};
class Metric::DataStartup : public Metric::CollectionData
{
public:
DataStartup(std::shared_ptr<details::CollectionFunction> function) :
function(std::move(function))
{}
ReadingItem update(uint64_t timestamp) override
{
return function->calculateForStartupInterval(readings, timestamp);
}
ReadingItem update(uint64_t timestamp, double reading) override
{
readings.emplace_back(timestamp, reading);
return function->calculateForStartupInterval(readings, timestamp);
}
private:
std::shared_ptr<details::CollectionFunction> function;
std::vector<ReadingItem> readings;
};
Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
std::string idIn, std::string metadataIn,
CollectionTimeScope timeScopeIn,
CollectionDuration collectionDurationIn,
std::unique_ptr<interfaces::Clock> clockIn) :
id(idIn),
metadata(metadataIn),
readings(sensorsIn.size(),
MetricValue{std::move(idIn), std::move(metadataIn), 0.0, 0u}),
sensors(std::move(sensorsIn)), operationType(operationTypeIn),
collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
collectionTimeScope,
collectionDuration)),
clock(std::move(clockIn))
{
attemptUnpackJsonMetadata();
}
Metric::~Metric() = default;
void Metric::initialize()
{
for (const auto& sensor : sensors)
{
sensor->registerForUpdates(weak_from_this());
}
}
std::vector<MetricValue> Metric::getReadings() const
{
const auto timestamp = clock->timestamp();
auto resultReadings = readings;
for (size_t i = 0; i < resultReadings.size(); ++i)
{
std::tie(resultReadings[i].timestamp, resultReadings[i].value) =
collectionAlgorithms[i]->update(timestamp);
}
return resultReadings;
}
void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp)
{
findAssociatedData(notifier).update(timestamp);
}
void Metric::sensorUpdated(interfaces::Sensor& notifier, uint64_t timestamp,
double value)
{
findAssociatedData(notifier).update(timestamp, value);
}
Metric::CollectionData&
Metric::findAssociatedData(const interfaces::Sensor& notifier)
{
auto it = std::find_if(
sensors.begin(), sensors.end(),
[&notifier](const auto& sensor) { return sensor.get() == &notifier; });
auto index = std::distance(sensors.begin(), it);
return *collectionAlgorithms.at(index);
}
LabeledMetricParameters Metric::dumpConfiguration() const
{
auto sensorPath = utils::transform(sensors, [this](const auto& sensor) {
return LabeledSensorParameters(sensor->id().service, sensor->id().path);
});
return LabeledMetricParameters(std::move(sensorPath), operationType, id,
metadata, collectionTimeScope,
collectionDuration);
}
std::vector<std::unique_ptr<Metric::CollectionData>>
Metric::makeCollectionData(size_t size, OperationType op,
CollectionTimeScope timeScope,
CollectionDuration duration)
{
using namespace std::string_literals;
std::vector<std::unique_ptr<Metric::CollectionData>> result;
result.reserve(size);
switch (timeScope)
{
case CollectionTimeScope::interval:
std::generate_n(
std::back_inserter(result), size,
[cf = details::makeCollectionFunction(op), duration] {
return std::make_unique<DataInterval>(cf, duration);
});
break;
case CollectionTimeScope::point:
std::generate_n(std::back_inserter(result), size,
[] { return std::make_unique<DataPoint>(); });
break;
case CollectionTimeScope::startup:
std::generate_n(std::back_inserter(result), size,
[cf = details::makeCollectionFunction(op)] {
return std::make_unique<DataStartup>(cf);
});
break;
default:
throw std::runtime_error("timeScope: "s +
utils::enumToString(timeScope) +
" is not supported"s);
}
return result;
}
void Metric::attemptUnpackJsonMetadata()
{
using MetricMetadata =
utils::LabeledTuple<std::tuple<std::vector<std::string>>,
utils::tstring::MetricProperties>;
using ReadingMetadata =
utils::LabeledTuple<std::tuple<std::string, std::string>,
utils::tstring::SensorDbusPath,
utils::tstring::SensorRedfishUri>;
try
{
const MetricMetadata parsedMetadata =
nlohmann::json::parse(metadata).get<MetricMetadata>();
if (readings.size() == parsedMetadata.at_index<0>().size())
{
for (size_t i = 0; i < readings.size(); ++i)
{
ReadingMetadata readingMetadata{
sensors[i]->id().path, parsedMetadata.at_index<0>()[i]};
readings[i].metadata = readingMetadata.dump();
}
}
}
catch (const nlohmann::json::exception&)
{}
}