Implemented sensor class
Sensor class was introduced, it monitors
xyz.openbmc_project.Sensor.Value, for change and notifies all
listeners.
Tested:
- Unit tested with service stub that provides dbus interface
xyz.openbmc_project.Sensor.Value
- All changes are delivered to listeners
- All other unit tests are passing
Change-Id: I8c9d58cc986c1fe2a4d2386815d559814016efa6
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
diff --git a/src/interfaces/sensor.hpp b/src/interfaces/sensor.hpp
index dbeb158..07c7897 100644
--- a/src/interfaces/sensor.hpp
+++ b/src/interfaces/sensor.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <chrono>
+#include <memory>
#include <ostream>
#include <string>
#include <string_view>
@@ -8,6 +10,8 @@
namespace interfaces
{
+class SensorListener;
+
class Sensor
{
public:
@@ -28,11 +32,17 @@
return std::tie(type, service, path) <
std::tie(other.type, other.service, other.path);
}
+
+ inline std::string str() const
+ {
+ return type + ":" + service + ":" + path;
+ }
};
virtual ~Sensor() = default;
virtual Id id() const = 0;
+ virtual void registerForUpdates(const std::weak_ptr<SensorListener>&) = 0;
};
} // namespace interfaces
diff --git a/src/interfaces/sensor_listener.hpp b/src/interfaces/sensor_listener.hpp
new file mode 100644
index 0000000..9a2f0a2
--- /dev/null
+++ b/src/interfaces/sensor_listener.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <optional>
+
+namespace interfaces
+{
+
+class Sensor;
+
+class SensorListener
+{
+ public:
+ virtual ~SensorListener() = default;
+
+ virtual void sensorUpdated(interfaces::Sensor&, uint64_t) = 0;
+ virtual void sensorUpdated(interfaces::Sensor&, uint64_t, double) = 0;
+};
+
+} // namespace interfaces
diff --git a/src/sensor.cpp b/src/sensor.cpp
new file mode 100644
index 0000000..d38fbd9
--- /dev/null
+++ b/src/sensor.cpp
@@ -0,0 +1,168 @@
+#include "sensor.hpp"
+
+#include "utils/detached_timer.hpp"
+
+#include <boost/container/flat_map.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/asio/property.hpp>
+#include <sdbusplus/bus/match.hpp>
+
+#include <functional>
+#include <iostream>
+
+Sensor::Sensor(interfaces::Sensor::Id sensorId, boost::asio::io_context& ioc,
+ const std::shared_ptr<sdbusplus::asio::connection>& bus) :
+ sensorId(std::move(sensorId)),
+ ioc(ioc), bus(bus)
+{}
+
+Sensor::Id Sensor::makeId(std::string_view service, std::string_view path)
+{
+ return Id("Sensor", service, path);
+}
+
+Sensor::Id Sensor::id() const
+{
+ return sensorId;
+}
+
+void Sensor::async_read()
+{
+ uniqueCall([this](auto lock) { async_read(std::move(lock)); });
+}
+
+void Sensor::async_read(std::shared_ptr<utils::UniqueCall::Lock> lock)
+{
+ makeSignalMonitor();
+
+ sdbusplus::asio::getProperty<double>(
+ *bus, sensorId.service, sensorId.path,
+ "xyz.openbmc_project.Sensor.Value", "Value",
+ [lock, id = sensorId,
+ weakSelf = weak_from_this()](boost::system::error_code ec) {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "DBus 'GetProperty' call failed on Sensor Value",
+ phosphor::logging::entry("sensor=%s, ec=%lu", id.str().c_str(),
+ ec.value()));
+
+ if (auto self = weakSelf.lock())
+ {
+
+ constexpr auto retryIntervalAfterFailedRead =
+ std::chrono::seconds(30);
+ utils::makeDetachedTimer(
+ self->ioc, retryIntervalAfterFailedRead, [weakSelf] {
+ if (auto self = weakSelf.lock())
+ {
+ self->async_read();
+ }
+ });
+ }
+ },
+ [lock, weakSelf = weak_from_this()](double newValue) {
+ if (auto self = weakSelf.lock())
+ {
+ self->updateValue(newValue);
+ }
+ });
+}
+
+void Sensor::registerForUpdates(
+ const std::weak_ptr<interfaces::SensorListener>& weakListener)
+{
+ if (auto listener = weakListener.lock())
+ {
+ listeners.emplace_back(weakListener);
+
+ if (value)
+ {
+ listener->sensorUpdated(*this, timestamp, *value);
+ }
+ else
+ {
+ async_read();
+ }
+ }
+}
+
+void Sensor::updateValue(double newValue)
+{
+ timestamp = std::time(0);
+
+ if (value == newValue)
+ {
+ for (const auto& weakListener : listeners)
+ {
+ if (auto listener = weakListener.lock())
+ {
+ listener->sensorUpdated(*this, timestamp);
+ }
+ }
+ }
+ else
+ {
+ value = newValue;
+
+ for (const auto& weakListener : listeners)
+ {
+ if (auto listener = weakListener.lock())
+ {
+ listener->sensorUpdated(*this, timestamp, *value);
+ }
+ }
+ }
+}
+
+void Sensor::makeSignalMonitor()
+{
+ if (signalMonitor)
+ {
+ return;
+ }
+
+ using namespace std::string_literals;
+
+ const auto param = "type='signal',member='PropertiesChanged',path='"s +
+ sensorId.path +
+ "',arg0='xyz.openbmc_project.Sensor.Value'"s;
+
+ signalMonitor = std::make_unique<sdbusplus::bus::match::match>(
+ *bus, param,
+ [weakSelf = weak_from_this()](sdbusplus::message::message& message) {
+ signalProc(weakSelf, message);
+ });
+}
+
+void Sensor::signalProc(const std::weak_ptr<Sensor>& weakSelf,
+ sdbusplus::message::message& message)
+{
+ if (auto self = weakSelf.lock())
+ {
+ std::string iface;
+ boost::container::flat_map<std::string, ValueVariant>
+ changed_properties;
+ std::vector<std::string> invalidated_properties;
+
+ message.read(iface, changed_properties, invalidated_properties);
+
+ if (iface == "xyz.openbmc_project.Sensor.Value")
+ {
+ const auto it = changed_properties.find("Value");
+ if (it != changed_properties.end())
+ {
+ if (auto val = std::get_if<double>(&it->second))
+ {
+ self->updateValue(*val);
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to receive Value from Sensor "
+ "PropertiesChanged signal",
+ phosphor::logging::entry("sensor=%s",
+ self->sensorId.path.c_str()));
+ }
+ }
+ }
+ }
+}
diff --git a/src/sensor.hpp b/src/sensor.hpp
new file mode 100644
index 0000000..1374c54
--- /dev/null
+++ b/src/sensor.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "interfaces/sensor.hpp"
+#include "interfaces/sensor_listener.hpp"
+#include "utils/unique_call.hpp"
+
+#include <boost/asio/high_resolution_timer.hpp>
+#include <sdbusplus/asio/connection.hpp>
+
+#include <memory>
+
+class Sensor final :
+ public interfaces::Sensor,
+ public std::enable_shared_from_this<Sensor>
+{
+ using ValueVariant = std::variant<std::monostate, double>;
+
+ public:
+ Sensor(interfaces::Sensor::Id sensorId, boost::asio::io_context& ioc,
+ const std::shared_ptr<sdbusplus::asio::connection>& bus);
+
+ Sensor(const Sensor&) = delete;
+ Sensor& operator=(const Sensor&) = delete;
+
+ static Id makeId(std::string_view service, std::string_view path);
+
+ Id id() const override;
+ void registerForUpdates(
+ const std::weak_ptr<interfaces::SensorListener>& weakListener) override;
+
+ private:
+ static std::optional<double> readValue(const ValueVariant& v);
+ static void signalProc(const std::weak_ptr<Sensor>& weakSelf,
+ sdbusplus::message::message&);
+
+ void async_read();
+ void async_read(std::shared_ptr<utils::UniqueCall::Lock>);
+ void makeSignalMonitor();
+ void updateValue(double);
+
+ interfaces::Sensor::Id sensorId;
+ boost::asio::io_context& ioc;
+ std::shared_ptr<sdbusplus::asio::connection> bus;
+ std::chrono::milliseconds timerInterval = std::chrono::milliseconds(0);
+ std::optional<boost::asio::high_resolution_timer> timer;
+
+ utils::UniqueCall uniqueCall;
+ std::vector<std::weak_ptr<interfaces::SensorListener>> listeners;
+ uint64_t timestamp = 0;
+ std::optional<double> value;
+ std::unique_ptr<sdbusplus::bus::match::match> signalMonitor;
+};
diff --git a/src/utils/detached_timer.hpp b/src/utils/detached_timer.hpp
new file mode 100644
index 0000000..21343f9
--- /dev/null
+++ b/src/utils/detached_timer.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/spawn.hpp>
+#include <boost/asio/steady_timer.hpp>
+
+#include <chrono>
+
+namespace utils
+{
+
+template <class F>
+void makeDetachedTimer(boost::asio::io_context& ioc,
+ std::chrono::milliseconds delay, F&& fun)
+{
+ auto timer = std::make_unique<boost::asio::steady_timer>(ioc);
+ timer->expires_after(delay);
+ timer->async_wait([timer = std::move(timer),
+ fun = std::move(fun)](boost::system::error_code ec) {
+ if (ec)
+ {
+ return;
+ }
+ fun();
+ });
+}
+
+} // namespace utils
diff --git a/src/utils/unique_call.hpp b/src/utils/unique_call.hpp
new file mode 100644
index 0000000..db10786
--- /dev/null
+++ b/src/utils/unique_call.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <memory>
+#include <utility>
+
+namespace utils
+{
+
+class UniqueCall
+{
+ public:
+ struct Lock
+ {};
+
+ template <class Functor, class... Args>
+ void operator()(Functor&& functor, Args&&... args)
+ {
+ if (lock.expired())
+ {
+ auto l = std::make_shared<Lock>();
+ lock = l;
+ functor(std::move(l), std::forward<Args>(args)...);
+ }
+ }
+
+ private:
+ std::weak_ptr<Lock> lock;
+};
+
+} // namespace utils