control: Manage zones' dbus interfaces with a DBusZone object

Switch to using a DBusZone object to manage the dbus interfaces
configured on a zone. This allows the Zone objects' JSON configuration
to be reloaded using SIGHUP by separating the Zone object from its
associated dbus object.

Change-Id: I89e35282f96bfecffca8066682fb9f3506bfffa0
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/Makefile.am b/control/Makefile.am
index 80f5f80..45ffc96 100644
--- a/control/Makefile.am
+++ b/control/Makefile.am
@@ -31,6 +31,7 @@
 	json/profile.cpp \
 	json/fan.cpp \
 	json/zone.cpp \
+	json/dbus_zone.cpp \
 	json/group.cpp \
 	json/event.cpp \
 	json/triggers/timer.cpp \
diff --git a/control/json/zone.cpp b/control/json/zone.cpp
index 8053a16..434931a 100644
--- a/control/json/zone.cpp
+++ b/control/json/zone.cpp
@@ -13,26 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "config.h"
-
 #include "zone.hpp"
 
+#include "dbus_zone.hpp"
 #include "fan.hpp"
 #include "sdbusplus.hpp"
 
-#include <cereal/archives/json.hpp>
-#include <cereal/cereal.hpp>
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/log.hpp>
-#include <sdbusplus/bus.hpp>
 #include <sdeventplus/event.hpp>
 
 #include <algorithm>
 #include <chrono>
-#include <filesystem>
-#include <fstream>
 #include <iterator>
 #include <map>
+#include <memory>
 #include <numeric>
 #include <utility>
 #include <vector>
@@ -42,22 +37,20 @@
 
 using json = nlohmann::json;
 using namespace phosphor::logging;
-namespace fs = std::filesystem;
 
-const std::map<std::string,
-               std::map<std::string, std::function<std::function<void(Zone*)>(
-                                         const json&, bool)>>>
-    Zone::_intfPropHandlers = {{thermModeIntf,
-                                {{supportedProp, zone::property::supported},
-                                 {currentProp, zone::property::current}}}};
+const std::map<
+    std::string,
+    std::map<std::string, std::function<std::function<void(DBusZone&, Zone&)>(
+                              const json&, bool)>>>
+    Zone::_intfPropHandlers = {
+        {DBusZone::thermalModeIntf,
+         {{DBusZone::supportedProp, zone::property::supported},
+          {DBusZone::currentProp, zone::property::current}}}};
 
 Zone::Zone(const json& jsonObj, const sdeventplus::Event& event, Manager* mgr) :
-    ConfigBase(jsonObj),
-    ThermalObject(util::SDBusPlus::getBus(),
-                  (fs::path{CONTROL_OBJPATH} /= getName()).c_str(), true),
-    _manager(mgr), _incDelay(0), _floor(0), _target(0), _incDelta(0),
-    _decDelta(0), _requestTargetBase(0), _isActive(true),
-    _incTimer(event, std::bind(&Zone::incTimerExpired, this)),
+    ConfigBase(jsonObj), _dbusZone{}, _manager(mgr), _incDelay(0), _floor(0),
+    _target(0), _incDelta(0), _decDelta(0), _requestTargetBase(0),
+    _isActive(true), _incTimer(event, std::bind(&Zone::incTimerExpired, this)),
     _decTimer(event, std::bind(&Zone::decTimerExpired, this))
 {
     // Increase delay is optional, defaults to 0
@@ -78,11 +71,25 @@
 
 void Zone::enable()
 {
-    // Restore thermal control current mode state
-    restoreCurrentMode();
+    // Create thermal control dbus object
+    _dbusZone = std::make_unique<DBusZone>(*this);
 
-    // Emit object added for this zone dbus object
-    this->emit_object_added();
+    // Init all configured dbus interfaces' property states
+    for (const auto& func : _propInitFunctions)
+    {
+        // Only call non-null init property functions
+        if (func)
+        {
+            func(*_dbusZone, *this);
+        }
+    }
+
+    // TODO - Restore any persisted properties in init function
+    // Restore thermal control current mode state, if exists
+    _dbusZone->restoreCurrentMode();
+
+    // Emit object added for this zone's associated dbus object
+    _dbusZone->emit_object_added();
 
     // Start timer for fan target decreases
     _decTimer.restart(_decInterval);
@@ -208,7 +215,7 @@
 void Zone::setPersisted(const std::string& intf, const std::string& prop)
 {
     if (std::find_if(_propsPersisted[intf].begin(), _propsPersisted[intf].end(),
-                     [&prop](const auto& p) { return prop == p; }) !=
+                     [&prop](const auto& p) { return prop == p; }) ==
         _propsPersisted[intf].end())
     {
         _propsPersisted[intf].emplace_back(prop);
@@ -227,30 +234,6 @@
                        [&prop](const auto& p) { return prop == p; });
 }
 
-std::string Zone::current(std::string value)
-{
-    auto current = ThermalObject::current();
-    std::transform(value.begin(), value.end(), value.begin(), toupper);
-
-    auto supported = ThermalObject::supported();
-    auto isSupported =
-        std::any_of(supported.begin(), supported.end(), [&value](auto& s) {
-            std::transform(s.begin(), s.end(), s.begin(), toupper);
-            return value == s;
-        });
-
-    if (value != current && isSupported)
-    {
-        current = ThermalObject::current(value);
-        if (isPersisted(thermModeIntf, currentProp))
-        {
-            saveCurrentMode();
-        }
-    }
-
-    return current;
-}
-
 void Zone::setDefaultCeiling(const json& jsonObj)
 {
     // TODO Remove "full_speed" after configs replaced with "default_ceiling"
@@ -361,54 +344,13 @@
                 throw std::runtime_error(
                     "Configured property function not available");
             }
-            auto propHandler = propFunc->second(property, persist);
-            // Only call non-null set property handler functions
-            if (propHandler)
-            {
-                propHandler(this);
-            }
+
+            _propInitFunctions.emplace_back(
+                propFunc->second(property, persist));
         }
     }
 }
 
-void Zone::saveCurrentMode()
-{
-    fs::path path{CONTROL_PERSIST_ROOT_PATH};
-    // Append zone name and property description
-    path /= getName();
-    path /= "CurrentMode";
-    std::ofstream ofs(path.c_str(), std::ios::binary);
-    cereal::JSONOutputArchive oArch(ofs);
-    oArch(ThermalObject::current());
-}
-
-void Zone::restoreCurrentMode()
-{
-    auto current = ThermalObject::current();
-    fs::path path{CONTROL_PERSIST_ROOT_PATH};
-    path /= getName();
-    path /= "CurrentMode";
-    fs::create_directories(path.parent_path());
-
-    try
-    {
-        if (fs::exists(path))
-        {
-            std::ifstream ifs(path.c_str(), std::ios::in | std::ios::binary);
-            cereal::JSONInputArchive iArch(ifs);
-            iArch(current);
-        }
-    }
-    catch (std::exception& e)
-    {
-        log<level::ERR>(e.what());
-        fs::remove(path);
-        current = ThermalObject::current();
-    }
-
-    this->current(current);
-}
-
 /**
  * Properties of interfaces supported by the zone configuration that return
  * a handler function that sets the zone's property value(s) and persist state.
@@ -417,7 +359,8 @@
 {
 // Get a set property handler function for the configured values of the
 // "Supported" property
-std::function<void(Zone*)> supported(const json& jsonObj, bool persist)
+std::function<void(DBusZone&, Zone&)> supported(const json& jsonObj,
+                                                bool persist)
 {
     std::vector<std::string> values;
     if (!jsonObj.contains("values"))
@@ -444,13 +387,13 @@
     }
 
     return Zone::setProperty<std::vector<std::string>>(
-        Zone::thermModeIntf, Zone::supportedProp, &Zone::supported,
-        std::move(values), persist);
+        DBusZone::thermalModeIntf, DBusZone::supportedProp,
+        &DBusZone::supported, std::move(values), persist);
 }
 
 // Get a set property handler function for a configured value of the "Current"
 // property
-std::function<void(Zone*)> current(const json& jsonObj, bool persist)
+std::function<void(DBusZone&, Zone&)> current(const json& jsonObj, bool persist)
 {
     // Use default value for "Current" property if no "value" entry given
     if (!jsonObj.contains("value"))
@@ -459,12 +402,12 @@
                          "using default",
                          entry("JSON=%s", jsonObj.dump().c_str()));
         // Set persist state of property
-        return Zone::setPropertyPersist(Zone::thermModeIntf, Zone::currentProp,
-                                        persist);
+        return Zone::setPropertyPersist(DBusZone::thermalModeIntf,
+                                        DBusZone::currentProp, persist);
     }
 
     return Zone::setProperty<std::string>(
-        Zone::thermModeIntf, Zone::currentProp, &Zone::current,
+        DBusZone::thermalModeIntf, DBusZone::currentProp, &DBusZone::current,
         jsonObj["value"].get<std::string>(), persist);
 }
 
diff --git a/control/json/zone.hpp b/control/json/zone.hpp
index 5176c4b..c4d90d7 100644
--- a/control/json/zone.hpp
+++ b/control/json/zone.hpp
@@ -16,11 +16,10 @@
 #pragma once
 
 #include "config_base.hpp"
+#include "dbus_zone.hpp"
 #include "fan.hpp"
-#include "xyz/openbmc_project/Control/ThermalMode/server.hpp"
 
 #include <nlohmann/json.hpp>
-#include <sdbusplus/bus.hpp>
 #include <sdeventplus/event.hpp>
 #include <sdeventplus/utility/timer.hpp>
 
@@ -28,6 +27,7 @@
 #include <chrono>
 #include <functional>
 #include <map>
+#include <memory>
 #include <tuple>
 
 namespace phosphor::fan::control::json
@@ -37,10 +37,6 @@
 
 using json = nlohmann::json;
 
-/* Extend the Control::ThermalMode interface */
-using ThermalObject = sdbusplus::server::object::object<
-    sdbusplus::xyz::openbmc_project::Control::server::ThermalMode>;
-
 /* Dbus event timer */
 using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
 
@@ -56,15 +52,11 @@
  * (When no profile for a zone is given, the zone defaults to always exist)
  *
  */
-class Zone : public ConfigBase, public ThermalObject
+class Zone : public ConfigBase
 {
   public:
     /* JSON file name for zones */
     static constexpr auto confFileName = "zones.json";
-    static constexpr auto thermModeIntf =
-        "xyz.openbmc_project.Control.ThermalMode";
-    static constexpr auto supportedProp = "Supported";
-    static constexpr auto currentProp = "Current";
 
     Zone() = delete;
     Zone(const Zone&) = delete;
@@ -306,37 +298,29 @@
     bool isPersisted(const std::string& intf, const std::string& prop) const;
 
     /**
-     * @brief Overridden thermal object's set 'Current' property function
-     *
-     * @param[in] value - Value to set 'Current' to
-     *
-     * @return - The updated value of the 'Current' property
-     */
-    std::string current(std::string value) override;
-
-    /**
      * @brief A handler function to set/update a property on a zone
      * @details Sets or updates a zone's dbus property to the given value using
      * the provided base dbus object's set property function
      *
      * @param[in] intf - Interface on zone object
      * @param[in] prop - Property on interface
-     * @param[in] func - Zone object's set property function pointer
+     * @param[in] func - Zone dbus object's set property function pointer
      * @param[in] value - Value to set property to
      * @param[in] persist - Persist property value or not
      *
      * @return Lambda function
-     *     A lambda function to set/update the zone's dbus property
+     *     A lambda function to set/update the zone dbus object's property
      */
     template <typename T>
     static auto setProperty(const char* intf, const char* prop,
-                            T (Zone::*func)(T), T&& value, bool persist)
+                            T (DBusZone::*func)(T), T&& value, bool persist)
     {
-        return [=, value = std::forward<T>(value)](Zone* zone) {
-            (zone->*func)(value);
+        return [=, value = std::forward<T>(value)](DBusZone& dbusZone,
+                                                   Zone& zone) {
+            (dbusZone.*func)(value);
             if (persist)
             {
-                zone->setPersisted(intf, prop);
+                zone.setPersisted(intf, prop);
             }
         };
     }
@@ -352,21 +336,24 @@
      * @param[in] persist - Persist property value or not
      *
      * @return Lambda function
-     *     A lambda function to set/update the zone's dbus property's persist
-     * state
+     *     A lambda function to set/update the zone's dbus object's property's
+     * persist state
      */
     static auto setPropertyPersist(const char* intf, const char* prop,
                                    bool persist)
     {
-        return [=](Zone* zone) {
+        return [=](DBusZone&, Zone& zone) {
             if (persist)
             {
-                zone->setPersisted(intf, prop);
+                zone.setPersisted(intf, prop);
             }
         };
     }
 
   private:
+    /* The zone's associated dbus object */
+    std::unique_ptr<DBusZone> _dbusZone;
+
     /* The zone's manager */
     Manager* _manager;
 
@@ -425,13 +412,16 @@
      * function */
     static const std::map<
         std::string,
-        std::map<std::string,
-                 std::function<std::function<void(Zone*)>(const json&, bool)>>>
+        std::map<std::string, std::function<std::function<void(
+                                  DBusZone&, Zone&)>(const json&, bool)>>>
         _intfPropHandlers;
 
     /* List of fans included in this zone */
     std::vector<std::unique_ptr<Fan>> _fans;
 
+    /* List of configured interface set property functions */
+    std::vector<std::function<void(DBusZone&, Zone&)>> _propInitFunctions;
+
     /**
      * @brief Parse and set the zone's default ceiling value
      *
@@ -475,18 +465,6 @@
     void setInterfaces(const json& jsonObj);
 
     /**
-     * @brief Save the thermal control current mode property to persisted
-     * storage
-     */
-    void saveCurrentMode();
-
-    /**
-     * @brief Restore persisted thermal control current mode property
-     * value, setting the mode to "Default" otherwise
-     */
-    void restoreCurrentMode();
-
-    /**
      * @brief Get the request target base if defined, otherwise the the current
      * target is returned
      *
@@ -506,29 +484,29 @@
 
 /**
  * @brief "Supported" property on the "xyz.openbmc_project.Control.ThermalMode"
- * interface parser. Also creates the handler function for the Zone object to
- * initialize the property according to what's parsed from the configuration.
+ * interface parser. Also creates the handler function for the Zone dbus object
+ * to initialize the property according to what's parsed from the configuration.
  *
  * @param[in] jsonObj - JSON object for the "Supported" property
  * @param[in] persist - Whether to persist the value or not
  *
- * @return Zone interface set property handler function for the "Supported"
- * property
+ * @return Zone dbus object's set property function for the "Supported" property
  */
-std::function<void(Zone*)> supported(const json& jsonObj, bool persist);
+std::function<void(DBusZone&, Zone&)> supported(const json& jsonObj,
+                                                bool persist);
 
 /**
  * @brief "Current" property on the "xyz.openbmc_project.Control.ThermalMode"
- * interface parser. Also creates the handler function for the Zone object to
- * initialize the property according to what's parsed from the configuration.
+ * interface parser. Also creates the handler function for the Zone dbus object
+ * to initialize the property according to what's parsed from the configuration.
  *
  * @param[in] jsonObj - JSON object for the "Current" property
  * @param[in] persist - Whether to persist the value or not
  *
- * @return Zone interface set property handler function for the "Current"
- * property
+ * @return Zone dbus object's set property function for the "Current" property
  */
-std::function<void(Zone*)> current(const json& jsonObj, bool persist);
+std::function<void(DBusZone&, Zone&)> current(const json& jsonObj,
+                                              bool persist);
 
 } // namespace zone::property