added support for Collection Functions
new supported operations: min,max,sum,avg
new supported time scopes: interval,startup
added unit test to verify that each collection function returns correct
timestamp and value
Tested:
- POST/GET on telemetry features in bmcweb, no regression detected
- Using dbus API metric with collection function works as expected
Change-Id: Ib364c433915e07fd7a102f00109525362c40ab8a
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
diff --git a/src/details/collection_function.cpp b/src/details/collection_function.cpp
new file mode 100644
index 0000000..d92fdf3
--- /dev/null
+++ b/src/details/collection_function.cpp
@@ -0,0 +1,172 @@
+#include "collection_function.hpp"
+
+#include <cmath>
+
+namespace details
+{
+
+class FunctionSingle : public CollectionFunction
+{
+ public:
+ ReadingItem calculate(const std::vector<ReadingItem>& readings,
+ uint64_t) const override
+ {
+ return readings.back();
+ }
+
+ ReadingItem calculateForStartupInterval(std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const override
+ {
+ readings.assign({readings.back()});
+ return readings.back();
+ }
+};
+
+class FunctionMinimum : public CollectionFunction
+{
+ public:
+ ReadingItem calculate(const std::vector<ReadingItem>& readings,
+ uint64_t) const override
+ {
+ return *std::min_element(
+ readings.begin(), readings.end(),
+ [](const auto& left, const auto& right) {
+ return std::make_tuple(!std::isfinite(left.second),
+ left.second) <
+ std::make_tuple(!std::isfinite(right.second),
+ right.second);
+ });
+ }
+
+ ReadingItem calculateForStartupInterval(std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const override
+ {
+ readings.assign({ReadingItem(calculate(readings, timestamp))});
+ return readings.back();
+ }
+};
+
+class FunctionMaximum : public CollectionFunction
+{
+ public:
+ ReadingItem calculate(const std::vector<ReadingItem>& readings,
+ uint64_t) const override
+ {
+ return *std::max_element(
+ readings.begin(), readings.end(),
+ [](const auto& left, const auto& right) {
+ return std::make_tuple(std::isfinite(left.second),
+ left.second) <
+ std::make_tuple(std::isfinite(right.second),
+ right.second);
+ });
+ }
+
+ ReadingItem calculateForStartupInterval(std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const override
+ {
+ readings.assign({ReadingItem(calculate(readings, timestamp))});
+ return readings.back();
+ }
+};
+
+class FunctionAverage : public CollectionFunction
+{
+ public:
+ ReadingItem calculate(const std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const override
+ {
+ auto valueSum = 0.0;
+ auto timeSum = uint64_t{0};
+ for (auto it = readings.begin(); it != std::prev(readings.end()); ++it)
+ {
+ if (std::isfinite(it->second))
+ {
+ const auto kt = std::next(it);
+ const auto duration = kt->first - it->first;
+ valueSum += it->second * duration;
+ timeSum += duration;
+ }
+ }
+
+ const auto duration = timestamp - readings.back().first;
+ valueSum += readings.back().second * duration;
+ timeSum += duration;
+
+ return ReadingItem{timestamp, valueSum / timeSum};
+ }
+
+ ReadingItem calculateForStartupInterval(std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const override
+ {
+ auto result = calculate(readings, timestamp);
+ if (std::isfinite(result.second))
+ {
+ readings.assign({ReadingItem(readings.front().first, result.second),
+ ReadingItem(timestamp, readings.back().second)});
+ }
+ return result;
+ }
+};
+
+class FunctionSummation : public CollectionFunction
+{
+ public:
+ ReadingItem calculate(const std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const override
+ {
+ auto valueSum = 0.0;
+ for (auto it = readings.begin(); it != std::prev(readings.end()); ++it)
+ {
+ if (std::isfinite(it->second))
+ {
+ const auto kt = std::next(it);
+ const auto duration = kt->first - it->first;
+ valueSum += it->second * duration;
+ }
+ }
+
+ const auto duration = timestamp - readings.back().first;
+ valueSum += readings.back().second * duration;
+
+ return ReadingItem{timestamp, valueSum};
+ }
+
+ ReadingItem calculateForStartupInterval(std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const override
+ {
+ auto result = calculate(readings, timestamp);
+ if (std::isfinite(result.second) && timestamp > 0u)
+ {
+ readings.assign({ReadingItem(timestamp - 1u, result.second),
+ ReadingItem(timestamp, readings.back().second)});
+ }
+ return result;
+ }
+};
+
+std::shared_ptr<CollectionFunction>
+ makeCollectionFunction(OperationType operationType)
+{
+ using namespace std::string_literals;
+
+ switch (operationType)
+ {
+ case OperationType::single:
+ return std::make_shared<FunctionSingle>();
+ case OperationType::min:
+ return std::make_shared<FunctionMinimum>();
+ case OperationType::max:
+ return std::make_shared<FunctionMaximum>();
+ case OperationType::avg:
+ return std::make_shared<FunctionAverage>();
+ case OperationType::sum:
+ return std::make_shared<FunctionSummation>();
+ default:
+ throw std::runtime_error("op: "s +
+ utils::enumToString(operationType) +
+ " is not supported"s);
+ }
+}
+
+} // namespace details
diff --git a/src/details/collection_function.hpp b/src/details/collection_function.hpp
new file mode 100644
index 0000000..a8708e7
--- /dev/null
+++ b/src/details/collection_function.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "types/operation_type.hpp"
+
+#include <cstdint>
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace details
+{
+
+using ReadingItem = std::pair<uint64_t, double>;
+
+class CollectionFunction
+{
+ public:
+ virtual ~CollectionFunction() = default;
+
+ virtual ReadingItem calculate(const std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const = 0;
+ virtual ReadingItem
+ calculateForStartupInterval(std::vector<ReadingItem>& readings,
+ uint64_t timestamp) const = 0;
+};
+
+std::shared_ptr<CollectionFunction> makeCollectionFunction(OperationType);
+
+} // namespace details