Add property changed signal handler

Enable the control application to handle property changed signals to set
or update a cached set of these properties

Change-Id: Ib84ffe1e801ee7dd85d17fdbb122d124d307dbd3
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/functor.hpp b/control/functor.hpp
new file mode 100644
index 0000000..f2f3a78
--- /dev/null
+++ b/control/functor.hpp
@@ -0,0 +1,109 @@
+#pragma once
+
+#include "types.hpp"
+#include <phosphor-logging/log.hpp>
+
+namespace phosphor
+{
+namespace fan
+{
+namespace control
+{
+class Zone;
+
+using namespace phosphor::logging;
+
+/**
+ * @brief Create a handler function object
+ *
+ * @param[in] handler - The handler being created
+ *
+ * @return - The created handler function object
+ */
+template <typename T>
+auto make_handler(T&& handler)
+{
+    return Handler(std::forward<T>(handler));
+}
+
+/**
+ * @struct Property Changed
+ * @brief A match filter functor for Dbus property value changed signals
+ *
+ * @tparam T - The type of the property value
+ * @tparam U - The type of the handler
+ */
+template <typename T, typename U>
+struct PropertyChanged
+{
+    PropertyChanged() = delete;
+    ~PropertyChanged() = default;
+    PropertyChanged(const PropertyChanged&) = default;
+    PropertyChanged& operator=(const PropertyChanged&) = default;
+    PropertyChanged(PropertyChanged&&) = default;
+    PropertyChanged& operator=(PropertyChanged&&) = default;
+    PropertyChanged(const char* iface,
+                    const char* property,
+                    U&& handler) :
+        _iface(iface),
+        _property(property),
+        _handler(std::forward<U>(handler)) { }
+
+    /** @brief Run signal handler function
+     *
+     * Extract the property from the PropertiesChanged
+     * message and run the handler function.
+     */
+    void operator()(sdbusplus::bus::bus&,
+                    sdbusplus::message::message& msg,
+                    Zone& zone) const
+    {
+        std::map<std::string, sdbusplus::message::variant<T>> properties;
+        const char* iface = nullptr;
+
+        msg.read(iface);
+        if (!iface || strcmp(iface, _iface))
+        {
+            return;
+        }
+
+        msg.read(properties);
+        auto it = properties.find(_property);
+        if (it == properties.cend())
+        {
+            log<level::ERR>("Unable to find property on interface",
+                            entry("PROPERTY=%s", _property),
+                            entry("INTERFACE=%s", _iface));
+            return;
+        }
+
+        _handler(zone, std::forward<T>(it->second.template get<T>()));
+    }
+
+private:
+    const char* _iface;
+    const char* _property;
+    U _handler;
+};
+
+/**
+ * @brief Used to process a Dbus property changed signal event
+ *
+ * @param[in] iface - Sensor value interface
+ * @param[in] property - Sensor value property
+ * @param[in] handler - Handler function to perform
+ *
+ * @tparam T - The type of the property
+ * @tparam U - The type of the handler
+ */
+template <typename T, typename U>
+auto propertySignal(const char* iface,
+                    const char* property,
+                    U&& handler)
+{
+    return PropertyChanged<T, U>(iface, property, std::forward<U>(handler));
+}
+
+} // namespace control
+} // namespace fan
+} // namespace phosphor