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
diff --git a/control/handlers.hpp b/control/handlers.hpp
new file mode 100644
index 0000000..0503c53
--- /dev/null
+++ b/control/handlers.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+namespace phosphor
+{
+namespace fan
+{
+namespace control
+{
+namespace handler
+{
+
+/**
+ * @brief A handler function to set/update a property
+ * @details Sets or updates a property's value determined by a combination of
+ * an object's path and property names
+ *
+ * @param[in] path - Object's path name
+ * @param[in] property - Object's property name
+ *
+ * @return Lambda function
+ *     A lambda function to set/update the property value
+ */
+template <typename T>
+auto setProperty(const char* path, const char* property)
+{
+    return [=](auto& zone, T&& arg)
+    {
+        zone.setPropertyValue(path, property, std::forward<T>(arg));
+    };
+}
+
+} // namespace handler
+} // namespace control
+} // namespace fan
+} // namespace phosphor
diff --git a/control/types.hpp b/control/types.hpp
index e4c3725..bbbac61 100644
--- a/control/types.hpp
+++ b/control/types.hpp
@@ -10,6 +10,8 @@
 namespace control
 {
 
+class Zone;
+
 //Placeholder. Conditions are completely TBD.
 using Condition = bool;
 
@@ -17,12 +19,26 @@
 constexpr auto sensorListPos = 1;
 using FanDefinition = std::tuple<std::string, std::vector<std::string>>;
 
+using Handler = std::function<void(sdbusplus::bus::bus&,
+                                   sdbusplus::message::message&,
+                                   Zone&)>;
+
+constexpr auto signaturePos = 0;
+constexpr auto handlerObjPos = 1;
+using PropertyChange = std::tuple<std::string, Handler>;
+using SetSpeedEvent = std::vector<PropertyChange>;
+
+constexpr auto zoneObjPos = 0;
+using SignalEvent = std::tuple<Zone*, Handler>;
+
 constexpr auto zoneNumPos = 0;
 constexpr auto fullSpeedPos = 1;
 constexpr auto fanListPos = 2;
+constexpr auto setSpeedEventsPos = 3;
 using ZoneDefinition = std::tuple<size_t,
                                   unsigned int,
-                                  std::vector<FanDefinition>>;
+                                  std::vector<FanDefinition>,
+                                  std::vector<SetSpeedEvent>>;
 
 constexpr auto conditionListPos = 0;
 constexpr auto zoneListPos = 1;
diff --git a/control/zone.cpp b/control/zone.cpp
index 1e50060..28b81f9 100644
--- a/control/zone.cpp
+++ b/control/zone.cpp
@@ -35,6 +35,22 @@
     {
         _fans.emplace_back(std::make_unique<Fan>(bus, def));
     }
+
+    // Setup signal trigger for property changes

+    for (auto& event : std::get<setSpeedEventsPos>(def))

+    {

+        for (auto& prop : event)

+        {

+            _signalEvents.emplace_back(

+                std::make_unique<SignalEvent>(this,

+                                              std::get<handlerObjPos>(prop)

+                                              ));

+            _matches.emplace_back(bus,

+                                  std::get<signaturePos>(prop).c_str(),

+                                  signalHandler,

+                                  _signalEvents.back().get());

+        }

+    }
 }
 
 
@@ -46,6 +62,26 @@
     }
 }
 
+int Zone::signalHandler(sd_bus_message* msg,
+                        void* data,
+                        sd_bus_error* err)
+{
+    auto sdbpMsg = sdbusplus::message::message(msg);
+    auto& signalEvent = *static_cast<SignalEvent*>(data);
+    std::get<zoneObjPos>(signalEvent)->handleEvent(
+        sdbpMsg,
+        std::get<handlerObjPos>(signalEvent));
+    return 0;
+}
+
+void Zone::handleEvent(sdbusplus::message::message& msg,
+                       const Handler& handler)
+{
+    // Handle the callback
+    handler(_bus, msg, *this);
+}
+
+
 }
 }
 }
diff --git a/control/zone.hpp b/control/zone.hpp
index 985a46d..601c002 100644
--- a/control/zone.hpp
+++ b/control/zone.hpp
@@ -1,6 +1,7 @@
 #pragma once
 #include <vector>
 #include <sdbusplus/bus.hpp>
+#include <sdbusplus/server.hpp>
 #include "fan.hpp"
 #include "types.hpp"
 
@@ -56,6 +57,20 @@
             }
         }
 
+        /**
+         * @brief Sets a given object's property value
+         *
+         * @param[in] object - Name of the object containing the property
+         * @param[in] property - Property name
+         * @param[in] value - Property value
+         */
+        void setPropertyValue(const char* object,
+                              const char* property,
+                              bool value)
+        {
+            _properties[object][property] = value;
+        };
+
     private:
 
         /**
@@ -77,6 +92,41 @@
          * The vector of fans in this zone
          */
         std::vector<std::unique_ptr<Fan>> _fans;
+
+        /**
+         * @brief Map of object property values
+         */
+        std::map<std::string, std::map<std::string, bool>> _properties;
+
+        /**
+         * @brief List of signal event arguments
+         */
+        std::vector<std::unique_ptr<SignalEvent>> _signalEvents;
+
+        /**
+         * @brief list of Dbus matches for callbacks
+         */
+        std::vector<sdbusplus::server::match::match> _matches;
+
+        /**
+         * @brief Dbus signal change handler
+         *
+         * @param[in] msg - Data associated with the subscribed signal
+         * @param[in] data - Pointer to the event sensor's data
+         * @param[in] err - Contains any sdbus error reference if occurred
+         */
+        static int signalHandler(sd_bus_message* msg,
+                                 void* data,
+                                 sd_bus_error* err);
+
+         /**
+          * @brief Envokes the assigned handler and action
+          *
+          * @param[in] msg - Expanded sdbusplus message data
+          * @param[in] eventData - The event's data
+          */
+         void handleEvent(sdbusplus::message::message& msg,
+                          const Handler& handler);
 };
 
 }