Add support for AppendLimit and ReportUpdates

Added 2 new properties for Report interface: AppendLimit and
ReportUpdates. They were also added as arguments to the future version
of AddReport method of ReportManager.

ReportUpdates property defines the report update behavior:
- Overwrite: Each report update overrides previous "Readings" property.
  "AppendLimit" set by user is not respected - "Readings" property size
  is equal to count of all sensor across all metrics defined in report.
- AppendWrapsWhenFull: New readings are appended until limit specified
  by "AppendLimit" is reached. Then oldest readings are overwritten by
  new ones.
- AppendStopsWhenFull: New readings are appended until limit specified
  by "AppendLimit" is reached. Then updates are stopped.
- NewReport: not supported yet and will be implemented in the future.

Please note that if ReportingType is set to OnRequest, those 2 new
properties are ignored, and Readings property will contain one reading
per defined sensor, across all metrics. They are still stored, which
means that if ReportingType will be changed in the runtime, those
properties will be respected.

Tested:
- Both new properties can be accessed from dbus.
- Both properties are reflected in Readings property.
- Old AddReport method is working as before the change.
- UTs are passing.

Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
Change-Id: I8a18f7e68215f0f6e5c403b533d2c4ff479df69e
diff --git a/.gitignore b/.gitignore
index 6ea24d5..d5e4365 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 build/
+.obj/
 xyz.openbmc_project.Telemetry.service
 telemetry
 __pycache__
diff --git a/src/interfaces/metric.hpp b/src/interfaces/metric.hpp
index 57a2b68..4e2fae3 100644
--- a/src/interfaces/metric.hpp
+++ b/src/interfaces/metric.hpp
@@ -19,6 +19,7 @@
     virtual void deinitialize() = 0;
     virtual std::vector<MetricValue> getReadings() const = 0;
     virtual LabeledMetricParameters dumpConfiguration() const = 0;
+    virtual uint64_t sensorCount() const = 0;
 };
 
 } // namespace interfaces
diff --git a/src/interfaces/report_factory.hpp b/src/interfaces/report_factory.hpp
index 970dd01..36fc589 100644
--- a/src/interfaces/report_factory.hpp
+++ b/src/interfaces/report_factory.hpp
@@ -26,7 +26,8 @@
     virtual std::unique_ptr<interfaces::Report>
         make(const std::string& name, const std::string& reportingType,
              bool emitsReadingsSignal, bool logToMetricReportsCollection,
-             Milliseconds period, ReportManager& reportManager,
+             Milliseconds period, uint64_t appendLimit,
+             const std::string& reportUpdates, ReportManager& reportManager,
              JsonStorage& reportStorage,
              std::vector<LabeledMetricParameters> labeledMetricParams,
              bool enabled) const = 0;
diff --git a/src/metric.cpp b/src/metric.cpp
index 4290293..d76f220 100644
--- a/src/metric.cpp
+++ b/src/metric.cpp
@@ -234,6 +234,11 @@
     return result;
 }
 
+uint64_t Metric::sensorCount() const
+{
+    return sensors.size();
+}
+
 void Metric::attemptUnpackJsonMetadata()
 {
     using MetricMetadata =
diff --git a/src/metric.hpp b/src/metric.hpp
index f053691..7929aac 100644
--- a/src/metric.hpp
+++ b/src/metric.hpp
@@ -22,6 +22,7 @@
     void sensorUpdated(interfaces::Sensor&, uint64_t) override;
     void sensorUpdated(interfaces::Sensor&, uint64_t, double value) override;
     LabeledMetricParameters dumpConfiguration() const override;
+    uint64_t sensorCount() const override;
 
   private:
     class CollectionData;
diff --git a/src/report.cpp b/src/report.cpp
index af19a91..8c01cf2 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -6,15 +6,17 @@
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/vtable.hpp>
 
+#include <limits>
 #include <numeric>
 
 Report::Report(boost::asio::io_context& ioc,
                const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
                const std::string& reportName,
-               const std::string& reportingTypeIn,
+               const ReportingType reportingTypeIn,
                const bool emitsReadingsUpdateIn,
                const bool logToMetricReportsCollectionIn,
-               const Milliseconds intervalIn,
+               const Milliseconds intervalIn, const uint64_t appendLimitIn,
+               const ReportUpdates reportUpdatesIn,
                interfaces::ReportManager& reportManager,
                interfaces::JsonStorage& reportStorageIn,
                std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
@@ -23,6 +25,10 @@
     path(reportDir + name), reportingType(reportingTypeIn),
     interval(intervalIn), emitsReadingsUpdate(emitsReadingsUpdateIn),
     logToMetricReportsCollection(logToMetricReportsCollectionIn),
+    sensorCount(getSensorCount(metricsIn)),
+    appendLimit(deduceAppendLimit(appendLimitIn)),
+    reportUpdates(reportUpdatesIn),
+    readingsBuffer(deduceBufferSize(reportUpdates, reportingType)),
     objServer(objServer), metrics(std::move(metricsIn)), timer(ioc),
     fileName(std::to_string(std::hash<std::string>{}(name))),
     reportStorage(reportStorageIn), enabled(enabledIn)
@@ -55,7 +61,7 @@
     persistency = storeConfiguration();
     reportIface = makeReportInterface();
 
-    if (reportingType == "Periodic")
+    if (reportingType == ReportingType::Periodic)
     {
         scheduleTimer(interval);
     }
@@ -69,6 +75,58 @@
     }
 }
 
+uint64_t Report::getSensorCount(
+    std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
+{
+    uint64_t sensorCount = 0;
+    for (auto& metric : metrics)
+    {
+        sensorCount += metric->sensorCount();
+    }
+    return sensorCount;
+}
+
+uint64_t Report::deduceAppendLimit(const uint64_t appendLimitIn) const
+{
+    if (appendLimitIn == std::numeric_limits<uint64_t>::max())
+    {
+        return sensorCount;
+    }
+    else
+    {
+        return appendLimitIn;
+    }
+}
+
+uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
+                                  const ReportingType reportingTypeIn) const
+{
+    if (reportUpdatesIn == ReportUpdates::Overwrite ||
+        reportingTypeIn == ReportingType::OnRequest)
+    {
+        return sensorCount;
+    }
+    else
+    {
+        return appendLimit;
+    }
+}
+
+void Report::setReportUpdates(const ReportUpdates newReportUpdates)
+{
+    if (reportUpdates != newReportUpdates)
+    {
+        if (reportingType != ReportingType::OnRequest &&
+            (reportUpdates == ReportUpdates::Overwrite ||
+             newReportUpdates == ReportUpdates::Overwrite))
+        {
+            readingsBuffer.clearAndResize(
+                deduceBufferSize(newReportUpdates, reportingType));
+        }
+        reportUpdates = newReportUpdates;
+    }
+}
+
 std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
 {
     auto dbusIface = objServer->add_unique_interface(path, reportIfaceName);
@@ -77,7 +135,7 @@
         [this](bool newVal, const auto&) {
             if (newVal != enabled)
             {
-                if (true == newVal && "Periodic" == reportingType)
+                if (true == newVal && ReportingType::Periodic == reportingType)
                 {
                     scheduleTimer(interval);
                 }
@@ -147,8 +205,8 @@
     dbusIface->register_property_r("Readings", readings, readingsFlag,
                                    [this](const auto&) { return readings; });
     dbusIface->register_property_r(
-        "ReportingType", reportingType, sdbusplus::vtable::property_::const_,
-        [this](const auto&) { return reportingType; });
+        "ReportingType", std::string(), sdbusplus::vtable::property_::const_,
+        [this](const auto&) { return reportingTypeToString(reportingType); });
     dbusIface->register_property_r(
         "ReadingParameters", readingParametersPastVersion,
         sdbusplus::vtable::property_::const_,
@@ -165,8 +223,21 @@
         "LogToMetricReportsCollection", logToMetricReportsCollection,
         sdbusplus::vtable::property_::const_,
         [this](const auto&) { return logToMetricReportsCollection; });
+    dbusIface->register_property_r("AppendLimit", appendLimit,
+                                   sdbusplus::vtable::property_::emits_change,
+                                   [this](const auto&) { return appendLimit; });
+    dbusIface->register_property_rw(
+        "ReportUpdates", std::string(),
+        sdbusplus::vtable::property_::emits_change,
+        [this](auto newVal, auto& oldVal) {
+            ReportManager::verifyReportUpdates(newVal);
+            setReportUpdates(stringToReportUpdates(newVal));
+            oldVal = newVal;
+            return true;
+        },
+        [this](const auto&) { return reportUpdatesToString(reportUpdates); });
     dbusIface->register_method("Update", [this] {
-        if (reportingType == "OnRequest")
+        if (reportingType == ReportingType::OnRequest)
         {
             updateReadings();
         }
@@ -201,20 +272,33 @@
         return;
     }
 
-    using ReadingsTimestamp = std::tuple_element_t<0, Readings>;
-    using ReadingsValue = std::tuple_element_t<1, Readings>;
+    if (reportUpdates == ReportUpdates::Overwrite ||
+        reportingType == ReportingType::OnRequest)
+    {
+        readingsBuffer.clear();
+    }
 
-    std::get<ReadingsValue>(cachedReadings).clear();
     for (const auto& metric : metrics)
     {
-        for (const auto& [id, meta, val, timestamp] : metric->getReadings())
+        for (const auto& [id, metadata, value, timestamp] :
+             metric->getReadings())
         {
-            std::get<ReadingsValue>(cachedReadings)
-                .emplace_back(id, meta, val, timestamp);
+            if (reportUpdates == ReportUpdates::AppendStopsWhenFull &&
+                readingsBuffer.isFull())
+            {
+                enabled = false;
+                for (auto& metric : metrics)
+                {
+                    metric->deinitialize();
+                }
+                break;
+            }
+            readingsBuffer.emplace(id, metadata, value, timestamp);
         }
     }
-    std::get<ReadingsTimestamp>(cachedReadings) = std::time(0);
-    std::swap(readings, cachedReadings);
+
+    readings = {std::time(0), std::vector<ReadingData>(readingsBuffer.begin(),
+                                                       readingsBuffer.end())};
 
     reportIface->signal_property("Readings");
 }
@@ -228,10 +312,12 @@
         data["Enabled"] = enabled;
         data["Version"] = reportVersion;
         data["Name"] = name;
-        data["ReportingType"] = reportingType;
+        data["ReportingType"] = reportingTypeToString(reportingType);
         data["EmitsReadingsUpdate"] = emitsReadingsUpdate;
         data["LogToMetricReportsCollection"] = logToMetricReportsCollection;
         data["Interval"] = interval.count();
+        data["AppendLimit"] = appendLimit;
+        data["ReportUpdates"] = reportUpdatesToString(reportUpdates);
         data["ReadingParameters"] =
             utils::transform(metrics, [](const auto& metric) {
                 return metric->dumpConfiguration();
diff --git a/src/report.hpp b/src/report.hpp
index 18574cf..c758988 100644
--- a/src/report.hpp
+++ b/src/report.hpp
@@ -5,6 +5,7 @@
 #include "interfaces/report.hpp"
 #include "interfaces/report_manager.hpp"
 #include "types/report_types.hpp"
+#include "utils/circular_vector.hpp"
 
 #include <boost/asio/io_context.hpp>
 #include <boost/asio/steady_timer.hpp>
@@ -18,9 +19,10 @@
   public:
     Report(boost::asio::io_context& ioc,
            const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
-           const std::string& reportName, const std::string& reportingType,
+           const std::string& reportName, const ReportingType reportingType,
            const bool emitsReadingsSignal,
            const bool logToMetricReportsCollection, const Milliseconds period,
+           const uint64_t appendLimitIn, const ReportUpdates reportUpdatesIn,
            interfaces::ReportManager& reportManager,
            interfaces::JsonStorage& reportStorage,
            std::vector<std::shared_ptr<interfaces::Metric>> metrics,
@@ -49,17 +51,26 @@
     std::unique_ptr<sdbusplus::asio::dbus_interface> makeReportInterface();
     static void timerProc(boost::system::error_code, Report& self);
     void scheduleTimer(Milliseconds interval);
+    uint64_t deduceAppendLimit(const uint64_t appendLimitIn) const;
+    uint64_t deduceBufferSize(const ReportUpdates reportUpdatesIn,
+                              const ReportingType reportingTypeIn) const;
+    void setReportUpdates(const ReportUpdates newReportUpdates);
+    static uint64_t getSensorCount(
+        std::vector<std::shared_ptr<interfaces::Metric>>& metrics);
 
     std::string name;
     std::string path;
-    std::string reportingType;
+    ReportingType reportingType;
     Milliseconds interval;
     bool emitsReadingsUpdate;
     bool logToMetricReportsCollection;
     ReadingParametersPastVersion readingParametersPastVersion;
     ReadingParameters readingParameters;
     bool persistency = false;
-    Readings cachedReadings = {};
+    uint64_t sensorCount;
+    uint64_t appendLimit;
+    ReportUpdates reportUpdates;
+    CircularVector<ReadingData> readingsBuffer;
     Readings readings = {};
     std::shared_ptr<sdbusplus::asio::object_server> objServer;
     std::unique_ptr<sdbusplus::asio::dbus_interface> reportIface;
diff --git a/src/report_factory.cpp b/src/report_factory.cpp
index 3d86521..6c61cbe 100644
--- a/src/report_factory.cpp
+++ b/src/report_factory.cpp
@@ -17,9 +17,11 @@
 {}
 
 std::unique_ptr<interfaces::Report> ReportFactory::make(
-    const std::string& name, const std::string& reportingType,
+    const std::string& name, const std::string& reportingTypeStr,
     bool emitsReadingsSignal, bool logToMetricReportsCollection,
-    Milliseconds period, interfaces::ReportManager& reportManager,
+    Milliseconds period, uint64_t appendLimit,
+    const std::string& reportUpdatesStr,
+    interfaces::ReportManager& reportManager,
     interfaces::JsonStorage& reportStorage,
     std::vector<LabeledMetricParameters> labeledMetricParams,
     bool enabled) const
@@ -39,10 +41,14 @@
                 std::make_unique<Clock>());
         });
 
-    return std::make_unique<Report>(
-        bus->get_io_context(), objServer, name, reportingType,
-        emitsReadingsSignal, logToMetricReportsCollection, period,
-        reportManager, reportStorage, std::move(metrics), enabled);
+    const ReportingType reportingType = stringToReportingType(reportingTypeStr);
+    const ReportUpdates reportUpdates = stringToReportUpdates(reportUpdatesStr);
+
+    return std::make_unique<Report>(bus->get_io_context(), objServer, name,
+                                    reportingType, emitsReadingsSignal,
+                                    logToMetricReportsCollection, period,
+                                    appendLimit, reportUpdates, reportManager,
+                                    reportStorage, std::move(metrics), enabled);
 }
 
 Sensors ReportFactory::getSensors(
diff --git a/src/report_factory.hpp b/src/report_factory.hpp
index 69f6349..9f68972 100644
--- a/src/report_factory.hpp
+++ b/src/report_factory.hpp
@@ -22,7 +22,9 @@
     std::unique_ptr<interfaces::Report>
         make(const std::string& name, const std::string& reportingType,
              bool emitsReadingsSignal, bool logToMetricReportsCollection,
-             Milliseconds period, interfaces::ReportManager& reportManager,
+             Milliseconds period, uint64_t appendLimitIn,
+             const std::string& reportUpdatesIn,
+             interfaces::ReportManager& reportManager,
              interfaces::JsonStorage& reportStorage,
              std::vector<LabeledMetricParameters> labeledMetricParams,
              bool enabled) const override;
diff --git a/src/report_manager.cpp b/src/report_manager.cpp
index 64c587e..21a9736 100644
--- a/src/report_manager.cpp
+++ b/src/report_manager.cpp
@@ -57,31 +57,39 @@
                                     const uint64_t interval,
                                     ReadingParametersPastVersion metricParams) {
                     constexpr auto enabledDefault = true;
-                    return addReport(yield, reportName, reportingType,
-                                     emitsReadingsUpdate,
-                                     logToMetricReportsCollection,
-                                     Milliseconds(interval),
-                                     convertToReadingParameters(
-                                         std::move(metricParams)),
-                                     enabledDefault)
+                    constexpr uint64_t appendLimitDefault = 0;
+                    constexpr ReportUpdates reportUpdatesDefault =
+                        ReportUpdates::Overwrite;
+                    return addReport(
+                               yield, reportName, reportingType,
+                               emitsReadingsUpdate,
+                               logToMetricReportsCollection,
+                               Milliseconds(interval), appendLimitDefault,
+                               reportUpdatesToString(reportUpdatesDefault),
+                               convertToReadingParameters(
+                                   std::move(metricParams)),
+                               enabledDefault)
                         .getPath();
                 });
 
             dbusIface.register_method(
                 "AddReportFutureVersion",
-                [this](boost::asio::yield_context& yield,
-                       const std::string& reportName,
-                       const std::string& reportingType,
-                       const bool emitsReadingsUpdate,
-                       const bool logToMetricReportsCollection,
-                       const uint64_t interval,
-                       ReadingParameters metricParams) {
+
+                [this](
+                    boost::asio::yield_context& yield,
+                    const std::string& reportName,
+                    const std::string& reportingType,
+                    const std::string& reportUpdates,
+                    const uint64_t appendLimit, const bool emitsReadingsUpdate,
+                    const bool logToMetricReportsCollection,
+                    const uint64_t interval, ReadingParameters metricParams) {
                     constexpr auto enabledDefault = true;
                     return addReport(yield, reportName, reportingType,
                                      emitsReadingsUpdate,
                                      logToMetricReportsCollection,
-                                     Milliseconds(interval),
-                                     std::move(metricParams), enabledDefault)
+                                     Milliseconds(interval), appendLimit,
+                                     reportUpdates, std::move(metricParams),
+                                     enabledDefault)
                         .getPath();
                 });
         });
@@ -107,7 +115,7 @@
 
 void ReportManager::verifyAddReport(
     const std::string& reportName, const std::string& reportingType,
-    Milliseconds interval,
+    Milliseconds interval, const std::string& reportUpdates,
     const std::vector<LabeledMetricParameters>& readingParams)
 {
     if (reports.size() >= maxReports)
@@ -128,15 +136,16 @@
         }
     }
 
-    auto found = std::find(supportedReportingType.begin(),
-                           supportedReportingType.end(), reportingType);
-    if (found == supportedReportingType.end())
+    if (std::find(supportedReportingType.begin(), supportedReportingType.end(),
+                  reportingType) == supportedReportingType.end())
     {
         throw sdbusplus::exception::SdBusError(
             static_cast<int>(std::errc::invalid_argument),
             "Invalid reportingType");
     }
 
+    verifyReportUpdates(reportUpdates);
+
     if (reportingType == "Periodic" && interval < minInterval)
     {
         throw sdbusplus::exception::SdBusError(
@@ -172,29 +181,32 @@
     boost::asio::yield_context& yield, const std::string& reportName,
     const std::string& reportingType, const bool emitsReadingsUpdate,
     const bool logToMetricReportsCollection, Milliseconds interval,
+    const uint64_t appendLimit, const std::string& reportUpdates,
     ReadingParameters metricParams, const bool enabled)
 {
     auto labeledMetricParams =
         reportFactory->convertMetricParams(yield, metricParams);
 
     return addReport(reportName, reportingType, emitsReadingsUpdate,
-                     logToMetricReportsCollection, interval,
-                     std::move(labeledMetricParams), enabled);
+                     logToMetricReportsCollection, interval, appendLimit,
+                     reportUpdates, std::move(labeledMetricParams), enabled);
 }
 
 interfaces::Report& ReportManager::addReport(
     const std::string& reportName, const std::string& reportingType,
     const bool emitsReadingsUpdate, const bool logToMetricReportsCollection,
-    Milliseconds interval,
+    Milliseconds interval, const uint64_t appendLimit,
+    const std::string& reportUpdates,
     std::vector<LabeledMetricParameters> labeledMetricParams,
     const bool enabled)
 {
-    verifyAddReport(reportName, reportingType, interval, labeledMetricParams);
+    verifyAddReport(reportName, reportingType, interval, reportUpdates,
+                    labeledMetricParams);
 
-    reports.emplace_back(
-        reportFactory->make(reportName, reportingType, emitsReadingsUpdate,
-                            logToMetricReportsCollection, interval, *this,
-                            *reportStorage, labeledMetricParams, enabled));
+    reports.emplace_back(reportFactory->make(
+        reportName, reportingType, emitsReadingsUpdate,
+        logToMetricReportsCollection, interval, appendLimit, reportUpdates,
+        *this, *reportStorage, labeledMetricParams, enabled));
     return *reports.back();
 }
 
@@ -222,13 +234,17 @@
             bool logToMetricReportsCollection =
                 data->at("LogToMetricReportsCollection").get<bool>();
             uint64_t interval = data->at("Interval").get<uint64_t>();
+            uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
+            std::string reportUpdates =
+                data->at("ReportUpdates").get<std::string>();
             auto readingParameters =
                 data->at("ReadingParameters")
                     .get<std::vector<LabeledMetricParameters>>();
 
             addReport(name, reportingType, emitsReadingsSignal,
                       logToMetricReportsCollection, Milliseconds(interval),
-                      std::move(readingParameters), enabled);
+                      appendLimit, reportUpdates, std::move(readingParameters),
+                      enabled);
         }
         catch (const std::exception& e)
         {
@@ -254,3 +270,14 @@
         }
     }
 }
+
+void ReportManager::verifyReportUpdates(const std::string& reportUpdates)
+{
+    if (std::find(supportedReportUpdates.begin(), supportedReportUpdates.end(),
+                  reportUpdates) == supportedReportUpdates.end())
+    {
+        throw sdbusplus::exception::SdBusError(
+            static_cast<int>(std::errc::invalid_argument),
+            "Invalid ReportUpdates");
+    }
+}
diff --git a/src/report_manager.hpp b/src/report_manager.hpp
index 9c21c5d..32118c2 100644
--- a/src/report_manager.hpp
+++ b/src/report_manager.hpp
@@ -42,17 +42,19 @@
     void verifyReportNameLength(const std::string& reportName);
     void verifyAddReport(
         const std::string& reportName, const std::string& reportingType,
-        Milliseconds interval,
+        Milliseconds interval, const std::string& reportUpdates,
         const std::vector<LabeledMetricParameters>& readingParams);
     interfaces::Report& addReport(
         boost::asio::yield_context& yield, const std::string& reportName,
         const std::string& reportingType, const bool emitsReadingsUpdate,
         const bool logToMetricReportsCollection, Milliseconds interval,
+        const uint64_t appendLimit, const std::string& reportUpdates,
         ReadingParameters metricParams, const bool enabled);
     interfaces::Report& addReport(
         const std::string& reportName, const std::string& reportingType,
         const bool emitsReadingsUpdate, const bool logToMetricReportsCollection,
-        Milliseconds interval,
+        Milliseconds interval, const uint64_t appendLimit,
+        const std::string& reportUpdates,
         std::vector<LabeledMetricParameters> metricParams, const bool enabled);
     void loadFromPersistent();
 
@@ -69,4 +71,8 @@
         "/xyz/openbmc_project/Telemetry/Reports";
     static constexpr std::array<std::string_view, 2> supportedReportingType = {
         "Periodic", "OnRequest"};
+    static constexpr std::array<std::string_view, 3> supportedReportUpdates = {
+        "Overwrite", "AppendStopsWhenFull", "AppendWrapsWhenFull"};
+
+    static void verifyReportUpdates(const std::string& reportUpdates);
 };
diff --git a/src/sensor_cache.hpp b/src/sensor_cache.hpp
index 3e19721..bebc4c6 100644
--- a/src/sensor_cache.hpp
+++ b/src/sensor_cache.hpp
@@ -5,7 +5,6 @@
 #include <boost/container/flat_map.hpp>
 #include <boost/system/error_code.hpp>
 
-#include <iostream>
 #include <memory>
 #include <string_view>
 
diff --git a/src/types/report_types.hpp b/src/types/report_types.hpp
index 8046df0..b0b1a69 100644
--- a/src/types/report_types.hpp
+++ b/src/types/report_types.hpp
@@ -33,9 +33,67 @@
     utils::tstring::Id, utils::tstring::MetricMetadata,
     utils::tstring::CollectionTimeScope, utils::tstring::CollectionDuration>;
 
-using Readings = std::tuple<
-    uint64_t,
-    std::vector<std::tuple<std::string, std::string, double, uint64_t>>>;
+using ReadingData = std::tuple<std::string, std::string, double, uint64_t>;
+
+using Readings = std::tuple<uint64_t, std::vector<ReadingData>>;
 
 ReadingParameters
     toReadingParameters(const std::vector<LabeledMetricParameters>& labeled);
+
+enum class ReportUpdates
+{
+    Overwrite = 0,
+    AppendStopsWhenFull,
+    AppendWrapsWhenFull,
+    NewReport
+};
+
+namespace details
+{
+constexpr std::array<std::pair<std::string_view, ReportUpdates>, 5>
+    convDataReportUpdates = {
+        std::make_pair("Overwrite", ReportUpdates::Overwrite),
+        std::make_pair("AppendStopsWhenFull",
+                       ReportUpdates::AppendStopsWhenFull),
+        std::make_pair("AppendWrapsWhenFull",
+                       ReportUpdates::AppendWrapsWhenFull),
+        std::make_pair("NewReport", ReportUpdates::NewReport)};
+
+} // namespace details
+
+inline ReportUpdates stringToReportUpdates(const std::string& str)
+{
+    return utils::stringToEnum(details::convDataReportUpdates, str);
+}
+
+inline std::string reportUpdatesToString(ReportUpdates v)
+{
+    return std::string(utils::enumToString(details::convDataReportUpdates, v));
+}
+
+enum class ReportingType
+{
+    OnChange = 0,
+    OnRequest,
+    Periodic
+};
+
+namespace details
+{
+constexpr std::array<std::pair<std::string_view, ReportingType>, 3>
+    convDataReportingType = {
+        std::make_pair("OnChange", ReportingType::OnChange),
+        std::make_pair("OnRequest", ReportingType::OnRequest),
+        std::make_pair("Periodic", ReportingType::Periodic)};
+
+} // namespace details
+
+inline ReportingType stringToReportingType(const std::string& str)
+{
+    return utils::stringToEnum(details::convDataReportingType, str);
+}
+
+inline std::string reportingTypeToString(ReportingType v)
+{
+    return std::string(utils::enumToString(details::convDataReportingType, v));
+}
diff --git a/src/utils/circular_vector.hpp b/src/utils/circular_vector.hpp
new file mode 100644
index 0000000..55d3f29
--- /dev/null
+++ b/src/utils/circular_vector.hpp
@@ -0,0 +1,59 @@
+#include <vector>
+
+template <class T>
+class CircularVector
+{
+  public:
+    CircularVector(size_t maxSizeIn) : maxSize(maxSizeIn)
+    {}
+
+    template <class... Args>
+    void emplace(Args&&... args)
+    {
+        if (maxSize == 0)
+        {
+            return;
+        }
+        if (vec.size() == maxSize)
+        {
+            vec.at(idx) = T(std::forward<Args>(args)...);
+        }
+        else
+        {
+            vec.emplace_back(std::forward<Args>(args)...);
+        }
+        idx = (idx + 1) == maxSize ? 0 : (idx + 1);
+    }
+
+    void clear()
+    {
+        vec.clear();
+        idx = 0;
+    }
+
+    void clearAndResize(size_t newMaxSize)
+    {
+        clear();
+        maxSize = newMaxSize;
+    }
+
+    bool isFull() const
+    {
+        return vec.size() == maxSize;
+    }
+
+    typename std::vector<T>::const_iterator begin() const
+    {
+        return vec.begin();
+    }
+
+    typename std::vector<T>::const_iterator end() const
+    {
+        return vec.end();
+    }
+
+  private:
+    size_t maxSize = 0;
+    size_t idx = 0;
+    std::vector<T> vec;
+};
diff --git a/tests/src/mocks/metric_mock.hpp b/tests/src/mocks/metric_mock.hpp
index a105aa0..268b681 100644
--- a/tests/src/mocks/metric_mock.hpp
+++ b/tests/src/mocks/metric_mock.hpp
@@ -20,4 +20,9 @@
     MOCK_METHOD(std::vector<MetricValue>, getReadings, (), (const, override));
     MOCK_METHOD(LabeledMetricParameters, dumpConfiguration, (),
                 (const, override));
+
+    uint64_t sensorCount() const override
+    {
+        return getReadings().size();
+    }
 };
diff --git a/tests/src/mocks/report_factory_mock.hpp b/tests/src/mocks/report_factory_mock.hpp
index e456429..f593883 100644
--- a/tests/src/mocks/report_factory_mock.hpp
+++ b/tests/src/mocks/report_factory_mock.hpp
@@ -35,7 +35,8 @@
             .WillByDefault(
                 WithArgs<1>(Invoke(&ReportFactoryMock::convertToLabeled)));
 
-        ON_CALL(*this, make(A<const std::string&>(), _, _, _, _, _, _, _, _))
+        ON_CALL(*this,
+                make(A<const std::string&>(), _, _, _, _, _, _, _, _, _, _))
             .WillByDefault(WithArgs<0>(Invoke([](const std::string& name) {
                 return std::make_unique<NiceMock<ReportMock>>(name);
             })));
@@ -47,9 +48,9 @@
 
     MOCK_METHOD(std::unique_ptr<interfaces::Report>, make,
                 (const std::string&, const std::string&, bool, bool,
-                 Milliseconds, interfaces::ReportManager&,
-                 interfaces::JsonStorage&, std::vector<LabeledMetricParameters>,
-                 bool),
+                 Milliseconds, uint64_t, const std::string&,
+                 interfaces::ReportManager&, interfaces::JsonStorage&,
+                 std::vector<LabeledMetricParameters>, bool),
                 (const, override));
 
     auto& expectMake(
@@ -65,12 +66,13 @@
                 make(params.reportName(), params.reportingType(),
                      params.emitReadingUpdate(),
                      params.logToMetricReportCollection(), params.interval(),
-                     rm, js, params.metricParameters(), params.enabled()));
+                     params.appendLimit(), params.reportUpdates(), rm, js,
+                     params.metricParameters(), params.enabled()));
         }
         else
         {
             using testing::_;
-            return EXPECT_CALL(*this, make(_, _, _, _, _, rm, js, _, _));
+            return EXPECT_CALL(*this, make(_, _, _, _, _, _, _, rm, js, _, _));
         }
     }
 };
diff --git a/tests/src/params/report_params.hpp b/tests/src/params/report_params.hpp
index cc71ce3..d68f3fb 100644
--- a/tests/src/params/report_params.hpp
+++ b/tests/src/params/report_params.hpp
@@ -75,6 +75,28 @@
         return enabledProperty;
     }
 
+    ReportParams& appendLimit(uint64_t val)
+    {
+        appendLimitProperty = val;
+        return *this;
+    }
+
+    uint64_t appendLimit() const
+    {
+        return appendLimitProperty;
+    }
+
+    ReportParams& reportUpdates(std::string val)
+    {
+        reportUpdatesProperty = val;
+        return *this;
+    }
+
+    std::string reportUpdates() const
+    {
+        return reportUpdatesProperty;
+    }
+
     ReportParams& metricParameters(std::vector<LabeledMetricParameters> val)
     {
         metricParametersProperty = std::move(val);
@@ -92,6 +114,8 @@
     bool emitReadingUpdateProperty = true;
     bool logToMetricReportCollectionProperty = true;
     Milliseconds intervalProperty = ReportManager::minInterval;
+    uint64_t appendLimitProperty = 123;
+    std::string reportUpdatesProperty = "Overwrite";
     std::vector<LabeledMetricParameters> metricParametersProperty{
         {LabeledMetricParameters{
              {LabeledSensorParameters{"Service",
diff --git a/tests/src/test_report.cpp b/tests/src/test_report.cpp
index ccdcce8..f09ddef 100644
--- a/tests/src/test_report.cpp
+++ b/tests/src/test_report.cpp
@@ -68,9 +68,11 @@
 
         return std::make_unique<Report>(
             DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(),
-            params.reportName(), params.reportingType(),
+            params.reportName(), stringToReportingType(params.reportingType()),
             params.emitReadingUpdate(), params.logToMetricReportCollection(),
-            params.interval(), *reportManagerMock, storageMock,
+            params.interval(), params.appendLimit(),
+            stringToReportUpdates(params.reportUpdates()), *reportManagerMock,
+            storageMock,
             utils::convContainer<std::shared_ptr<interfaces::Metric>>(
                 metricMocks),
             params.enabled());
@@ -125,6 +127,10 @@
     EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"), Eq(true));
     EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"),
                 Eq(defaultParams.emitReadingUpdate()));
+    EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "AppendLimit"),
+                Eq(defaultParams.appendLimit()));
+    EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportUpdates"),
+                Eq(defaultParams.reportUpdates()));
     EXPECT_THAT(
         getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"),
         Eq(defaultParams.logToMetricReportCollection()));
@@ -503,6 +509,113 @@
                             std::make_tuple("aa"s, "bb"s, 42.0, 74u)));
 }
 
+struct ReportUpdatesReportParams
+{
+    ReportParams reportParams;
+    std::vector<ReadingData> expectedReadings;
+    bool expectedEnabled;
+};
+
+class TestReportWithReportUpdatesAndLimit :
+    public TestReport,
+    public WithParamInterface<ReportUpdatesReportParams>
+{
+    void SetUp() override
+    {
+        sut = makeReport(ReportParams(GetParam().reportParams)
+                             .reportingType("Periodic")
+                             .interval(std::chrono::hours(1000)));
+    }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    _, TestReportWithReportUpdatesAndLimit,
+    Values(
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("AppendWrapsWhenFull").appendLimit(5),
+            std::vector<ReadingData>{{std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                                      std::make_tuple("a"s, "b"s, 17.1, 114u),
+                                      std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                                      std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                                      std::make_tuple("a"s, "b"s, 17.1, 114u)}},
+            true},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("AppendWrapsWhenFull").appendLimit(4),
+            std::vector<ReadingData>{
+                {std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                 std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}},
+            true},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("AppendWrapsWhenFull").appendLimit(0),
+            std::vector<ReadingData>{}, true},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("AppendStopsWhenFull").appendLimit(10),
+            std::vector<ReadingData>{
+                {std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                 std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                 std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                 std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}},
+            true},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("AppendStopsWhenFull").appendLimit(5),
+            std::vector<ReadingData>{{std::make_tuple("a"s, "b"s, 17.1, 114u),
+                                      std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                                      std::make_tuple("a"s, "b"s, 17.1, 114u),
+                                      std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                                      std::make_tuple("a"s, "b"s, 17.1, 114u)}},
+            false},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("AppendStopsWhenFull").appendLimit(4),
+            std::vector<ReadingData>{
+                {std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u),
+                 std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}},
+            false},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("AppendStopsWhenFull").appendLimit(0),
+            std::vector<ReadingData>{}, false},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("Overwrite").appendLimit(500),
+            std::vector<ReadingData>{
+                {std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}},
+            true},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("Overwrite").appendLimit(1),
+            std::vector<ReadingData>{
+                {std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}},
+            true},
+        ReportUpdatesReportParams{
+            ReportParams().reportUpdates("Overwrite").appendLimit(0),
+            std::vector<ReadingData>{
+                {std::make_tuple("a"s, "b"s, 17.1, 114u),
+                 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}},
+            true}));
+
+TEST_P(TestReportWithReportUpdatesAndLimit,
+       readingsAreUpdatedAfterIntervalExpires)
+{
+    for (int i = 0; i < 4; i++)
+    {
+        sut->updateReadings();
+    }
+
+    const auto [timestamp, readings] =
+        getProperty<Readings>(sut->getPath(), "Readings");
+    const auto enabled = getProperty<bool>(sut->getPath(), "Enabled");
+
+    EXPECT_THAT(readings, ElementsAreArray(GetParam().expectedReadings));
+    EXPECT_EQ(enabled, GetParam().expectedEnabled);
+}
+
 class TestReportInitialization : public TestReport
 {
   public:
@@ -593,3 +706,11 @@
     makeMonitor();
     DbusEnvironment::sleepFor(defaultParams.interval() * 2);
 }
+
+TEST_F(TestReportInitialization, appendLimitDeducedProperly)
+{
+    sut = makeReport(
+        ReportParams().appendLimit(std::numeric_limits<uint64_t>::max()));
+    auto appendLimit = getProperty<uint64_t>(sut->getPath(), "AppendLimit");
+    EXPECT_EQ(appendLimit, 2ull);
+}
diff --git a/tests/src/test_report_manager.cpp b/tests/src/test_report_manager.cpp
index eeedd96..a740d95 100644
--- a/tests/src/test_report_manager.cpp
+++ b/tests/src/test_report_manager.cpp
@@ -60,9 +60,9 @@
             },
             DbusEnvironment::serviceName(), ReportManager::reportManagerPath,
             ReportManager::reportManagerIfaceName, "AddReportFutureVersion",
-            params.reportName(), params.reportingType(),
-            params.emitReadingUpdate(), params.logToMetricReportCollection(),
-            params.interval().count(),
+            params.reportName(), params.reportingType(), params.reportUpdates(),
+            params.appendLimit(), params.emitReadingUpdate(),
+            params.logToMetricReportCollection(), params.interval().count(),
             toReadingParameters(params.metricParameters()));
         return DbusEnvironment::waitForFuture(addReportPromise.get_future());
     }
@@ -336,6 +336,8 @@
         {"LogToMetricReportsCollection",
          reportParams.logToMetricReportCollection()},
         {"Interval", reportParams.interval().count()},
+        {"ReportUpdates", reportParams.reportUpdates()},
+        {"AppendLimit", reportParams.appendLimit()},
         {"ReadingParameters", reportParams.metricParameters()}};
 };