added support for onChange report

Report is now notified when metric changes and updates reading values.

Tested:
  - Added new unit tests
  - OnChange report updates Readings when metric values changes

Change-Id: I3be9ef7aa0486cb15bac627aa1de5cc632613b3b
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
diff --git a/src/metrics/collection_data.cpp b/src/metrics/collection_data.cpp
new file mode 100644
index 0000000..9512252
--- /dev/null
+++ b/src/metrics/collection_data.cpp
@@ -0,0 +1,162 @@
+#include "metrics/collection_data.hpp"
+
+#include "metrics/collection_function.hpp"
+
+namespace metrics
+{
+
+bool CollectionData::updateLastValue(double value)
+{
+    const bool changed = lastValue != value;
+    lastValue = value;
+    return changed;
+}
+
+class DataPoint : public CollectionData
+{
+  public:
+    std::optional<double> update(Milliseconds) override
+    {
+        return lastReading;
+    }
+
+    double update(Milliseconds, double reading) override
+    {
+        lastReading = reading;
+        return reading;
+    }
+
+  private:
+    std::optional<double> lastReading;
+};
+
+class DataInterval : public CollectionData
+{
+  public:
+    DataInterval(std::shared_ptr<CollectionFunction> function,
+                 CollectionDuration duration) :
+        function(std::move(function)),
+        duration(duration)
+    {
+        if (duration.t.count() == 0)
+        {
+            throw sdbusplus::exception::SdBusError(
+                static_cast<int>(std::errc::invalid_argument),
+                "Invalid CollectionDuration");
+        }
+    }
+
+    std::optional<double> update(Milliseconds timestamp) override
+    {
+        if (readings.empty())
+        {
+            return std::nullopt;
+        }
+
+        cleanup(timestamp);
+
+        return function->calculate(readings, timestamp);
+    }
+
+    double update(Milliseconds timestamp, double reading) override
+    {
+        readings.emplace_back(timestamp, reading);
+
+        cleanup(timestamp);
+
+        return function->calculate(readings, timestamp);
+    }
+
+  private:
+    void cleanup(Milliseconds timestamp)
+    {
+        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 &&
+                timestamp - nextItemTimestamp > duration.t)
+            {
+                it = kt.base();
+                break;
+            }
+        }
+        readings.erase(readings.begin(), it);
+
+        if (timestamp > duration.t)
+        {
+            readings.front().first =
+                std::max(readings.front().first, timestamp - duration.t);
+        }
+    }
+
+    std::shared_ptr<CollectionFunction> function;
+    std::vector<ReadingItem> readings;
+    CollectionDuration duration;
+};
+
+class DataStartup : public CollectionData
+{
+  public:
+    explicit DataStartup(std::shared_ptr<CollectionFunction> function) :
+        function(std::move(function))
+    {}
+
+    std::optional<double> update(Milliseconds timestamp) override
+    {
+        if (readings.empty())
+        {
+            return std::nullopt;
+        }
+
+        return function->calculateForStartupInterval(readings, timestamp);
+    }
+
+    double update(Milliseconds timestamp, double reading) override
+    {
+        readings.emplace_back(timestamp, reading);
+        return function->calculateForStartupInterval(readings, timestamp);
+    }
+
+  private:
+    std::shared_ptr<CollectionFunction> function;
+    std::vector<ReadingItem> readings;
+};
+
+std::vector<std::unique_ptr<CollectionData>>
+    makeCollectionData(size_t size, OperationType op,
+                       CollectionTimeScope timeScope,
+                       CollectionDuration duration)
+{
+    using namespace std::string_literals;
+
+    std::vector<std::unique_ptr<CollectionData>> result;
+
+    result.reserve(size);
+
+    switch (timeScope)
+    {
+        case CollectionTimeScope::interval:
+            std::generate_n(std::back_inserter(result), size,
+                            [cf = 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 = makeCollectionFunction(op)] {
+                                return std::make_unique<DataStartup>(cf);
+                            });
+            break;
+    }
+
+    return result;
+}
+
+} // namespace metrics