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/discrete_threshold.cpp b/src/discrete_threshold.cpp
index a2073b7..d7640f6 100644
--- a/src/discrete_threshold.cpp
+++ b/src/discrete_threshold.cpp
@@ -40,10 +40,6 @@
}
void DiscreteThreshold::sensorUpdated(interfaces::Sensor& sensor,
- Milliseconds timestamp)
-{}
-
-void DiscreteThreshold::sensorUpdated(interfaces::Sensor& sensor,
Milliseconds timestamp, double value)
{
auto& details = getDetails(sensor);
diff --git a/src/discrete_threshold.hpp b/src/discrete_threshold.hpp
index 1f145f4..2a51a1a 100644
--- a/src/discrete_threshold.hpp
+++ b/src/discrete_threshold.hpp
@@ -30,7 +30,6 @@
DiscreteThreshold(DiscreteThreshold&&) = delete;
void initialize() override;
- void sensorUpdated(interfaces::Sensor&, Milliseconds) override;
void sensorUpdated(interfaces::Sensor&, Milliseconds, double) override;
LabeledThresholdParam getThresholdParam() const override;
void updateSensors(Sensors newSensors) override;
diff --git a/src/interfaces/metric.hpp b/src/interfaces/metric.hpp
index 4e2fae3..c40e960 100644
--- a/src/interfaces/metric.hpp
+++ b/src/interfaces/metric.hpp
@@ -1,6 +1,8 @@
#pragma once
+#include "interfaces/metric_listener.hpp"
#include "metric_value.hpp"
+#include "types/duration_types.hpp"
#include "types/report_types.hpp"
#include <nlohmann/json.hpp>
@@ -20,6 +22,11 @@
virtual std::vector<MetricValue> getReadings() const = 0;
virtual LabeledMetricParameters dumpConfiguration() const = 0;
virtual uint64_t sensorCount() const = 0;
+ virtual void registerForUpdates(interfaces::MetricListener& listener) = 0;
+ virtual void
+ unregisterFromUpdates(interfaces::MetricListener& listener) = 0;
+ virtual void updateReadings(Milliseconds) = 0;
+ virtual bool isTimerRequired() const = 0;
};
} // namespace interfaces
diff --git a/src/interfaces/metric_listener.hpp b/src/interfaces/metric_listener.hpp
new file mode 100644
index 0000000..3006a09
--- /dev/null
+++ b/src/interfaces/metric_listener.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+namespace interfaces
+{
+
+class MetricListener
+{
+ public:
+ virtual ~MetricListener() = default;
+
+ virtual void metricUpdated() = 0;
+};
+
+} // namespace interfaces
diff --git a/src/interfaces/sensor_listener.hpp b/src/interfaces/sensor_listener.hpp
index a47c8d4..3f35c38 100644
--- a/src/interfaces/sensor_listener.hpp
+++ b/src/interfaces/sensor_listener.hpp
@@ -15,7 +15,6 @@
public:
virtual ~SensorListener() = default;
- virtual void sensorUpdated(interfaces::Sensor&, Milliseconds) = 0;
virtual void sensorUpdated(interfaces::Sensor&, Milliseconds, double) = 0;
};
diff --git a/src/metric.cpp b/src/metric.cpp
index 3e450d6..a9cf45e 100644
--- a/src/metric.cpp
+++ b/src/metric.cpp
@@ -1,6 +1,6 @@
#include "metric.hpp"
-#include "details/collection_function.hpp"
+#include "metrics/collection_data.hpp"
#include "types/report_types.hpp"
#include "types/sensor_types.hpp"
#include "utils/labeled_tuple.hpp"
@@ -10,130 +10,6 @@
#include <algorithm>
-class Metric::CollectionData
-{
- public:
- using ReadingItem = details::ReadingItem;
-
- virtual ~CollectionData() = default;
-
- virtual std::optional<double> update(Milliseconds timestamp) = 0;
- virtual double update(Milliseconds timestamp, double value) = 0;
-};
-
-class Metric::DataPoint : public Metric::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 Metric::DataInterval : public Metric::CollectionData
-{
- public:
- DataInterval(std::shared_ptr<details::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<details::CollectionFunction> function;
- std::vector<ReadingItem> readings;
- CollectionDuration duration;
-};
-
-class Metric::DataStartup : public Metric::CollectionData
-{
- public:
- explicit DataStartup(
- std::shared_ptr<details::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<details::CollectionFunction> function;
- std::vector<ReadingItem> readings;
-};
-
Metric::Metric(Sensors sensorsIn, OperationType operationTypeIn,
std::string idIn, CollectionTimeScope timeScopeIn,
CollectionDuration collectionDurationIn,
@@ -141,9 +17,9 @@
id(std::move(idIn)),
sensors(std::move(sensorsIn)), operationType(operationTypeIn),
collectionTimeScope(timeScopeIn), collectionDuration(collectionDurationIn),
- collectionAlgorithms(makeCollectionData(sensors.size(), operationType,
- collectionTimeScope,
- collectionDuration)),
+ collectionAlgorithms(
+ metrics::makeCollectionData(sensors.size(), operationType,
+ collectionTimeScope, collectionDuration)),
clock(std::move(clockIn))
{
readings = utils::transform(sensors, [this](const auto& sensor) {
@@ -151,7 +27,20 @@
});
}
-Metric::~Metric() = default;
+void Metric::registerForUpdates(interfaces::MetricListener& listener)
+{
+ listeners.emplace_back(listener);
+}
+
+void Metric::unregisterFromUpdates(interfaces::MetricListener& listener)
+{
+ listeners.erase(
+ std::remove_if(listeners.begin(), listeners.end(),
+ [&listener](const interfaces::MetricListener& item) {
+ return &item == &listener;
+ }),
+ listeners.end());
+}
void Metric::initialize()
{
@@ -190,18 +79,22 @@
return resultReadings;
}
-void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp)
-{
- findAssociatedData(notifier).update(timestamp);
-}
-
void Metric::sensorUpdated(interfaces::Sensor& notifier, Milliseconds timestamp,
double value)
{
- findAssociatedData(notifier).update(timestamp, value);
+ auto& data = findAssociatedData(notifier);
+ double newValue = data.update(timestamp, value);
+
+ if (data.updateLastValue(newValue))
+ {
+ for (interfaces::MetricListener& listener : listeners)
+ {
+ listener.metricUpdated();
+ }
+ }
}
-Metric::CollectionData&
+metrics::CollectionData&
Metric::findAssociatedData(const interfaces::Sensor& notifier)
{
auto it = std::find_if(
@@ -222,46 +115,42 @@
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;
-}
-
uint64_t Metric::sensorCount() const
{
return sensors.size();
}
+
+void Metric::updateReadings(Milliseconds timestamp)
+{
+ for (auto& data : collectionAlgorithms)
+ {
+ if (std::optional<double> newValue = data->update(timestamp))
+ {
+ if (data->updateLastValue(*newValue))
+ {
+ for (interfaces::MetricListener& listener : listeners)
+ {
+ listener.metricUpdated();
+ }
+ return;
+ }
+ }
+ }
+}
+
+bool Metric::isTimerRequired() const
+{
+ if (collectionTimeScope == CollectionTimeScope::point)
+ {
+ return false;
+ }
+
+ if (collectionTimeScope == CollectionTimeScope::startup &&
+ (operationType == OperationType::min ||
+ operationType == OperationType::max))
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/metric.hpp b/src/metric.hpp
index 859315e..b627b4e 100644
--- a/src/metric.hpp
+++ b/src/metric.hpp
@@ -2,8 +2,11 @@
#include "interfaces/clock.hpp"
#include "interfaces/metric.hpp"
+#include "interfaces/metric_listener.hpp"
#include "interfaces/sensor.hpp"
#include "interfaces/sensor_listener.hpp"
+#include "metrics/collection_data.hpp"
+#include "types/collection_duration.hpp"
class Metric :
public interfaces::Metric,
@@ -14,28 +17,22 @@
Metric(Sensors sensors, OperationType operationType, std::string id,
CollectionTimeScope, CollectionDuration,
std::unique_ptr<interfaces::Clock>);
- ~Metric();
void initialize() override;
void deinitialize() override;
std::vector<MetricValue> getReadings() const override;
- void sensorUpdated(interfaces::Sensor&, Milliseconds) override;
void sensorUpdated(interfaces::Sensor&, Milliseconds,
double value) override;
LabeledMetricParameters dumpConfiguration() const override;
uint64_t sensorCount() const override;
+ void registerForUpdates(interfaces::MetricListener& listener) override;
+ void unregisterFromUpdates(interfaces::MetricListener& listener) override;
+ void updateReadings(Milliseconds) override;
+ bool isTimerRequired() const override;
private:
- class CollectionData;
- class DataPoint;
- class DataInterval;
- class DataStartup;
-
- static std::vector<std::unique_ptr<CollectionData>>
- makeCollectionData(size_t size, OperationType, CollectionTimeScope,
- CollectionDuration);
-
- CollectionData& findAssociatedData(const interfaces::Sensor& notifier);
+ metrics::CollectionData&
+ findAssociatedData(const interfaces::Sensor& notifier);
std::string id;
std::vector<MetricValue> readings;
@@ -43,6 +40,7 @@
OperationType operationType;
CollectionTimeScope collectionTimeScope;
CollectionDuration collectionDuration;
- std::vector<std::unique_ptr<CollectionData>> collectionAlgorithms;
+ std::vector<std::unique_ptr<metrics::CollectionData>> collectionAlgorithms;
std::unique_ptr<interfaces::Clock> clock;
+ std::vector<std::reference_wrapper<interfaces::MetricListener>> listeners;
};
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
diff --git a/src/metrics/collection_data.hpp b/src/metrics/collection_data.hpp
new file mode 100644
index 0000000..251e704
--- /dev/null
+++ b/src/metrics/collection_data.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "types/collection_duration.hpp"
+#include "types/collection_time_scope.hpp"
+#include "types/duration_types.hpp"
+#include "types/operation_type.hpp"
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+namespace metrics
+{
+
+class CollectionData
+{
+ public:
+ virtual ~CollectionData() = default;
+
+ virtual std::optional<double> update(Milliseconds timestamp) = 0;
+ virtual double update(Milliseconds timestamp, double value) = 0;
+ bool updateLastValue(double value);
+
+ private:
+ std::optional<double> lastValue;
+};
+
+std::vector<std::unique_ptr<CollectionData>>
+ makeCollectionData(size_t size, OperationType, CollectionTimeScope,
+ CollectionDuration);
+
+} // namespace metrics
diff --git a/src/details/collection_function.cpp b/src/metrics/collection_function.cpp
similarity index 98%
rename from src/details/collection_function.cpp
rename to src/metrics/collection_function.cpp
index 5134b0e..717bb68 100644
--- a/src/details/collection_function.cpp
+++ b/src/metrics/collection_function.cpp
@@ -1,8 +1,8 @@
-#include "collection_function.hpp"
+#include "metrics/collection_function.hpp"
#include <cmath>
-namespace details
+namespace metrics
{
class FunctionMinimum : public CollectionFunction
@@ -173,4 +173,4 @@
}
}
-} // namespace details
+} // namespace metrics
diff --git a/src/details/collection_function.hpp b/src/metrics/collection_function.hpp
similarity index 93%
rename from src/details/collection_function.hpp
rename to src/metrics/collection_function.hpp
index ca653a1..610f015 100644
--- a/src/details/collection_function.hpp
+++ b/src/metrics/collection_function.hpp
@@ -8,7 +8,7 @@
#include <utility>
#include <vector>
-namespace details
+namespace metrics
{
using ReadingItem = std::pair<Milliseconds, double>;
@@ -27,4 +27,4 @@
std::shared_ptr<CollectionFunction> makeCollectionFunction(OperationType);
-} // namespace details
+} // namespace metrics
diff --git a/src/numeric_threshold.cpp b/src/numeric_threshold.cpp
index f152e76..cb6dbdd 100644
--- a/src/numeric_threshold.cpp
+++ b/src/numeric_threshold.cpp
@@ -41,10 +41,6 @@
}
void NumericThreshold::sensorUpdated(interfaces::Sensor& sensor,
- Milliseconds timestamp)
-{}
-
-void NumericThreshold::sensorUpdated(interfaces::Sensor& sensor,
Milliseconds timestamp, double value)
{
auto& details = getDetails(sensor);
diff --git a/src/numeric_threshold.hpp b/src/numeric_threshold.hpp
index 02480a4..ecf3d6a 100644
--- a/src/numeric_threshold.hpp
+++ b/src/numeric_threshold.hpp
@@ -30,7 +30,6 @@
{}
void initialize() override;
- void sensorUpdated(interfaces::Sensor&, Milliseconds) override;
void sensorUpdated(interfaces::Sensor&, Milliseconds, double) override;
LabeledThresholdParam getThresholdParam() const override;
void updateSensors(Sensors newSensors) override;
diff --git a/src/on_change_threshold.cpp b/src/on_change_threshold.cpp
index fe3d1dd..72aedc5 100644
--- a/src/on_change_threshold.cpp
+++ b/src/on_change_threshold.cpp
@@ -51,10 +51,6 @@
}
void OnChangeThreshold::sensorUpdated(interfaces::Sensor& sensor,
- Milliseconds timestamp)
-{}
-
-void OnChangeThreshold::sensorUpdated(interfaces::Sensor& sensor,
Milliseconds timestamp, double value)
{
commit(sensor.getName(), timestamp, value);
diff --git a/src/on_change_threshold.hpp b/src/on_change_threshold.hpp
index 32a5f65..80876a2 100644
--- a/src/on_change_threshold.hpp
+++ b/src/on_change_threshold.hpp
@@ -25,7 +25,6 @@
{}
void initialize() override;
- void sensorUpdated(interfaces::Sensor&, Milliseconds) override;
void sensorUpdated(interfaces::Sensor&, Milliseconds, double) override;
LabeledThresholdParam getThresholdParam() const override;
void updateSensors(Sensors newSensors) override;
diff --git a/src/report.cpp b/src/report.cpp
index 12d88c7..d9d89b8 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -6,6 +6,7 @@
#include "report_manager.hpp"
#include "utils/clock.hpp"
#include "utils/contains.hpp"
+#include "utils/ensure.hpp"
#include "utils/transform.hpp"
#include <phosphor-logging/log.hpp>
@@ -13,6 +14,7 @@
#include <limits>
#include <numeric>
+#include <optional>
Report::Report(boost::asio::io_context& ioc,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
@@ -71,10 +73,7 @@
persistency = storeConfiguration();
reportIface = makeReportInterface(reportFactory);
- if (reportingType == ReportingType::periodic)
- {
- scheduleTimer(interval);
- }
+ updateReportingType(reportingType);
if (enabled)
{
@@ -119,7 +118,7 @@
}
uint64_t Report::getSensorCount(
- std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
+ const std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
{
uint64_t sensorCount = 0;
for (auto& metric : metrics)
@@ -188,7 +187,7 @@
{
if (true == newVal && ReportingType::periodic == reportingType)
{
- scheduleTimer(interval);
+ scheduleTimerForPeriodicReport(interval);
}
if (newVal)
{
@@ -260,12 +259,6 @@
ReportingType tmp = utils::toReportingType(newVal);
if (tmp != reportingType)
{
- if (tmp == ReportingType::onChange)
- {
- throw sdbusplus::exception::SdBusError(
- static_cast<int>(std::errc::invalid_argument),
- "Invalid reportingType");
- }
if (tmp == ReportingType::periodic)
{
if (interval < ReportManager::minInterval)
@@ -274,20 +267,13 @@
static_cast<int>(std::errc::invalid_argument),
"Invalid interval");
}
- if (enabled == true)
- {
- scheduleTimer(interval);
- }
}
- else
- {
- timer.cancel();
- }
- reportingType = tmp;
+
+ updateReportingType(tmp);
setReadingBuffer(reportUpdates);
persistency = storeConfiguration();
+ oldVal = std::move(newVal);
}
- oldVal = std::move(newVal);
return 1;
},
[this](const auto&) { return utils::enumToString(reportingType); });
@@ -377,7 +363,8 @@
return dbusIface;
}
-void Report::timerProc(boost::system::error_code ec, Report& self)
+void Report::timerProcForPeriodicReport(boost::system::error_code ec,
+ Report& self)
{
if (ec)
{
@@ -385,14 +372,58 @@
}
self.updateReadings();
- self.scheduleTimer(self.interval);
+ self.scheduleTimerForPeriodicReport(self.interval);
}
-void Report::scheduleTimer(Milliseconds timerInterval)
+void Report::timerProcForOnChangeReport(boost::system::error_code ec,
+ Report& self)
{
+ if (ec)
+ {
+ return;
+ }
+
+ const auto ensure =
+ utils::Ensure{[&self] { self.onChangeContext = std::nullopt; }};
+
+ self.onChangeContext.emplace(self);
+
+ const auto steadyTimestamp = self.clock->steadyTimestamp();
+
+ for (auto& metric : self.metrics)
+ {
+ metric->updateReadings(steadyTimestamp);
+ }
+
+ self.scheduleTimerForOnChangeReport();
+}
+
+void Report::scheduleTimerForPeriodicReport(Milliseconds timerInterval)
+{
+ if (!enabled)
+ {
+ return;
+ }
+
timer.expires_after(timerInterval);
- timer.async_wait(
- [this](boost::system::error_code ec) { timerProc(ec, *this); });
+ timer.async_wait([this](boost::system::error_code ec) {
+ timerProcForPeriodicReport(ec, *this);
+ });
+}
+
+void Report::scheduleTimerForOnChangeReport()
+{
+ if (!enabled)
+ {
+ return;
+ }
+
+ constexpr Milliseconds timerInterval{100};
+
+ timer.expires_after(timerInterval);
+ timer.async_wait([this](boost::system::error_code ec) {
+ timerProcForOnChangeReport(ec, *this);
+ });
}
void Report::updateReadings()
@@ -495,3 +526,62 @@
return result;
}
+
+void Report::metricUpdated()
+{
+ if (onChangeContext)
+ {
+ onChangeContext->metricUpdated();
+ return;
+ }
+
+ updateReadings();
+}
+
+void Report::updateReportingType(ReportingType newReportingType)
+{
+ if (reportingType != newReportingType)
+ {
+ timer.cancel();
+ unregisterFromMetrics = nullptr;
+ }
+
+ reportingType = newReportingType;
+
+ switch (reportingType)
+ {
+ case ReportingType::periodic:
+ {
+ scheduleTimerForPeriodicReport(interval);
+ break;
+ }
+ case ReportingType::onChange:
+ {
+ unregisterFromMetrics = [this] {
+ for (auto& metric : metrics)
+ {
+ metric->unregisterFromUpdates(*this);
+ }
+ };
+
+ bool isTimerRequired = false;
+
+ for (auto& metric : metrics)
+ {
+ metric->registerForUpdates(*this);
+ if (metric->isTimerRequired())
+ {
+ isTimerRequired = true;
+ }
+ }
+
+ if (isTimerRequired)
+ {
+ scheduleTimerForOnChangeReport();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
diff --git a/src/report.hpp b/src/report.hpp
index 3b8b451..213265c 100644
--- a/src/report.hpp
+++ b/src/report.hpp
@@ -3,6 +3,7 @@
#include "interfaces/clock.hpp"
#include "interfaces/json_storage.hpp"
#include "interfaces/metric.hpp"
+#include "interfaces/metric_listener.hpp"
#include "interfaces/report.hpp"
#include "interfaces/report_factory.hpp"
#include "interfaces/report_manager.hpp"
@@ -11,6 +12,7 @@
#include "types/report_updates.hpp"
#include "types/reporting_type.hpp"
#include "utils/circular_vector.hpp"
+#include "utils/ensure.hpp"
#include "utils/messanger.hpp"
#include <boost/asio/io_context.hpp>
@@ -21,8 +23,32 @@
#include <memory>
#include <unordered_set>
-class Report : public interfaces::Report
+class Report : public interfaces::Report, public interfaces::MetricListener
{
+ class OnChangeContext
+ {
+ public:
+ OnChangeContext(Report& report) : report(report)
+ {}
+
+ ~OnChangeContext()
+ {
+ if (updated)
+ {
+ report.updateReadings();
+ }
+ }
+
+ void metricUpdated()
+ {
+ updated = true;
+ }
+
+ private:
+ Report& report;
+ bool updated = false;
+ };
+
public:
Report(boost::asio::io_context& ioc,
const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
@@ -51,11 +77,17 @@
return reportDir + id;
}
+ void metricUpdated() override;
+
private:
std::unique_ptr<sdbusplus::asio::dbus_interface>
makeReportInterface(const interfaces::ReportFactory& reportFactory);
- static void timerProc(boost::system::error_code, Report& self);
- void scheduleTimer(Milliseconds interval);
+ static void timerProcForPeriodicReport(boost::system::error_code,
+ Report& self);
+ static void timerProcForOnChangeReport(boost::system::error_code,
+ Report& self);
+ void scheduleTimerForPeriodicReport(Milliseconds interval);
+ void scheduleTimerForOnChangeReport();
std::optional<uint64_t>
deduceAppendLimit(const uint64_t appendLimitIn) const;
uint64_t deduceBufferSize(const ReportUpdates reportUpdatesIn,
@@ -63,12 +95,13 @@
void setReadingBuffer(const ReportUpdates newReportUpdates);
void setReportUpdates(const ReportUpdates newReportUpdates);
static uint64_t getSensorCount(
- std::vector<std::shared_ptr<interfaces::Metric>>& metrics);
+ const std::vector<std::shared_ptr<interfaces::Metric>>& metrics);
interfaces::JsonStorage::FilePath fileName() const;
std::unordered_set<std::string>
collectTriggerIds(boost::asio::io_context& ioc) const;
bool storeConfiguration() const;
void updateReadings();
+ void updateReportingType(ReportingType);
std::string id;
std::string name;
@@ -94,6 +127,8 @@
bool enabled;
std::unique_ptr<interfaces::Clock> clock;
utils::Messanger messanger;
+ std::optional<OnChangeContext> onChangeContext;
+ utils::Ensure<std::function<void()>> unregisterFromMetrics;
public:
static constexpr const char* reportIfaceName =
diff --git a/src/report_factory.cpp b/src/report_factory.cpp
index 039846e..0d7f011 100644
--- a/src/report_factory.cpp
+++ b/src/report_factory.cpp
@@ -26,7 +26,7 @@
std::vector<LabeledMetricParameters> labeledMetricParams,
bool enabled) const
{
- std::vector<std::shared_ptr<interfaces::Metric>> metrics = utils::transform(
+ auto metrics = utils::transform(
labeledMetricParams,
[this](const LabeledMetricParameters& param)
-> std::shared_ptr<interfaces::Metric> {
diff --git a/src/report_manager.cpp b/src/report_manager.cpp
index 10035a1..7600088 100644
--- a/src/report_manager.cpp
+++ b/src/report_manager.cpp
@@ -139,13 +139,6 @@
const ReportUpdates reportUpdates, const uint64_t appendLimit,
const std::vector<LabeledMetricParameters>& readingParams)
{
- if (reportingType == ReportingType::onChange)
- {
- throw sdbusplus::exception::SdBusError(
- static_cast<int>(std::errc::invalid_argument),
- "Invalid reportingType");
- }
-
if (reports.size() >= maxReports)
{
throw sdbusplus::exception::SdBusError(
diff --git a/src/sensor.cpp b/src/sensor.cpp
index 2551562..387d0d5 100644
--- a/src/sensor.cpp
+++ b/src/sensor.cpp
@@ -107,17 +107,7 @@
{
timestamp = Clock().steadyTimestamp();
- if (value == newValue)
- {
- for (const auto& weakListener : listeners)
- {
- if (auto listener = weakListener.lock())
- {
- listener->sensorUpdated(*this, timestamp);
- }
- }
- }
- else
+ if (value != newValue)
{
value = newValue;
diff --git a/src/utils/ensure.hpp b/src/utils/ensure.hpp
new file mode 100644
index 0000000..cbe69c5
--- /dev/null
+++ b/src/utils/ensure.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <optional>
+#include <utility>
+
+namespace utils
+{
+
+template <class F>
+struct Ensure
+{
+ Ensure() = default;
+
+ template <class U>
+ Ensure(U&& functor) : functor(std::forward<U>(functor))
+ {}
+
+ Ensure(F functor) : functor(std::move(functor))
+ {}
+
+ Ensure(Ensure&& other) : functor(std::move(other.functor))
+ {
+ other.functor = std::nullopt;
+ }
+
+ Ensure(const Ensure&) = delete;
+
+ ~Ensure()
+ {
+ clear();
+ }
+
+ template <class U>
+ Ensure& operator=(U&& other)
+ {
+ clear();
+ functor = std::move(other);
+ return *this;
+ }
+
+ Ensure& operator=(Ensure&& other)
+ {
+ clear();
+ std::swap(functor, other.functor);
+ return *this;
+ }
+
+ Ensure& operator=(std::nullptr_t)
+ {
+ clear();
+ return *this;
+ }
+
+ Ensure& operator=(const Ensure&) = delete;
+
+ private:
+ void clear()
+ {
+ if (functor)
+ {
+ (*functor)();
+ functor = std::nullopt;
+ }
+ }
+
+ std::optional<F> functor;
+};
+
+} // namespace utils