move dbus helper interface into its own file

This is step 1 to improving the interface to be more generic and drop
requirements on dbus.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I7835ea8242bfb7189cabc1a4084e5f84143d40f6
diff --git a/dbus/dbusactiveread.cpp b/dbus/dbusactiveread.cpp
index 21f6c4d..b21922c 100644
--- a/dbus/dbusactiveread.cpp
+++ b/dbus/dbusactiveread.cpp
@@ -16,6 +16,7 @@
 
 #include "dbusactiveread.hpp"
 
+#include "dbushelper_interface.hpp"
 #include "util.hpp"
 
 #include <chrono>
diff --git a/dbus/dbusactiveread.hpp b/dbus/dbusactiveread.hpp
index adf091d..39640cc 100644
--- a/dbus/dbusactiveread.hpp
+++ b/dbus/dbusactiveread.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "dbushelper_interface.hpp"
 #include "interfaces.hpp"
 #include "util.hpp"
 
diff --git a/dbus/util.cpp b/dbus/dbushelper.cpp
similarity index 77%
rename from dbus/util.cpp
rename to dbus/dbushelper.cpp
index a464458..cb847e6 100644
--- a/dbus/util.cpp
+++ b/dbus/dbushelper.cpp
@@ -1,19 +1,39 @@
-#include "util.hpp"
+/**
+ * Copyright 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dbushelper.hpp"
+
+#include "dbushelper_interface.hpp"
+#include "dbusutil.hpp"
 
 #include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
 
-#include <cmath>
-#include <iostream>
-#include <set>
+#include <map>
+#include <string>
 #include <variant>
+#include <vector>
+
+namespace pid_control
+{
 
 using Property = std::string;
 using Value = std::variant<int64_t, double, std::string, bool>;
 using PropertyMap = std::map<Property, Value>;
 
-namespace pid_control
-{
-
 using namespace phosphor::logging;
 
 /* TODO(venture): Basically all phosphor apps need this, maybe it should be a
@@ -60,7 +80,7 @@
                                struct SensorProperties* prop)
 {
     auto pimMsg = bus.new_method_call(service.c_str(), path.c_str(),
-                                      propertiesintf.c_str(), "GetAll");
+                                      propertiesintf, "GetAll");
 
     pimMsg.append(sensorintf);
 
@@ -122,7 +142,7 @@
 {
 
     auto critical = bus.new_method_call(service.c_str(), path.c_str(),
-                                        propertiesintf.c_str(), "GetAll");
+                                        propertiesintf, "GetAll");
     critical.append(criticalThreshInf);
     PropertyMap criticalMap;
 
@@ -155,47 +175,4 @@
     return asserted;
 }
 
-std::string getSensorPath(const std::string& type, const std::string& id)
-{
-    std::string layer = type;
-    if (type == "fan")
-    {
-        layer = "fan_tach";
-    }
-    else if (type == "temp")
-    {
-        layer = "temperature";
-    }
-    else
-    {
-        layer = "unknown"; // TODO(venture): Need to handle.
-    }
-
-    return std::string("/xyz/openbmc_project/sensors/" + layer + "/" + id);
-}
-
-std::string getMatch(const std::string& type, const std::string& id)
-{
-    return std::string("type='signal',"
-                       "interface='org.freedesktop.DBus.Properties',"
-                       "member='PropertiesChanged',"
-                       "path='" +
-                       getSensorPath(type, id) + "'");
-}
-
-bool validType(const std::string& type)
-{
-    static std::set<std::string> valid = {"fan", "temp"};
-    return (valid.find(type) != valid.end());
-}
-
-void scaleSensorReading(const double min, const double max, double& value)
-{
-    if (max <= 0 || max <= min)
-    {
-        return;
-    }
-    value /= (max - min);
-}
-
 } // namespace pid_control
diff --git a/dbus/dbushelper.hpp b/dbus/dbushelper.hpp
new file mode 100644
index 0000000..7ae60f1
--- /dev/null
+++ b/dbus/dbushelper.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "dbushelper_interface.hpp"
+
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <string>
+#include <variant>
+
+namespace pid_control
+{
+
+class DbusHelper : public DbusHelperInterface
+{
+  public:
+    static constexpr char sensorintf[] = "xyz.openbmc_project.Sensor.Value";
+    static constexpr char propertiesintf[] = "org.freedesktop.DBus.Properties";
+    static constexpr char criticalThreshInf[] =
+        "xyz.openbmc_project.Sensor.Threshold.Critical";
+
+    DbusHelper() = default;
+    ~DbusHelper() = default;
+    DbusHelper(const DbusHelper&) = default;
+    DbusHelper& operator=(const DbusHelper&) = default;
+    DbusHelper(DbusHelper&&) = default;
+    DbusHelper& operator=(DbusHelper&&) = default;
+
+    std::string getService(sdbusplus::bus::bus& bus, const std::string& intf,
+                           const std::string& path) override;
+
+    void getProperties(sdbusplus::bus::bus& bus, const std::string& service,
+                       const std::string& path,
+                       struct SensorProperties* prop) override;
+
+    bool thresholdsAsserted(sdbusplus::bus::bus& bus,
+                            const std::string& service,
+                            const std::string& path) override;
+
+    template <typename T>
+    void getProperty(sdbusplus::bus::bus& bus, const std::string& service,
+                     const std::string& path, const std::string& interface,
+                     const std::string& propertyName, T& prop)
+    {
+        namespace log = phosphor::logging;
+
+        auto msg = bus.new_method_call(service.c_str(), path.c_str(),
+                                       propertiesintf, "Get");
+
+        msg.append(interface, propertyName);
+
+        std::variant<T> result;
+        try
+        {
+            auto valueResponseMsg = bus.call(msg);
+            valueResponseMsg.read(result);
+        }
+        catch (const sdbusplus::exception::SdBusError& ex)
+        {
+            log::log<log::level::ERR>("Get Property Failed",
+                                      log::entry("WHAT=%s", ex.what()));
+            throw;
+        }
+
+        prop = std::get<T>(result);
+    }
+};
+
+} // namespace pid_control
diff --git a/dbus/dbushelper_interface.hpp b/dbus/dbushelper_interface.hpp
new file mode 100644
index 0000000..c7c2c90
--- /dev/null
+++ b/dbus/dbushelper_interface.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+
+#include <cstdint>
+#include <string>
+
+namespace pid_control
+{
+
+struct SensorProperties
+{
+    int64_t scale;
+    double value;
+    double min;
+    double max;
+    std::string unit;
+};
+
+class DbusHelperInterface
+{
+  public:
+    virtual ~DbusHelperInterface() = default;
+
+    /** @brief Get the service providing the interface for the path.
+     *
+     * @warning Throws exception on dbus failure.
+     */
+    virtual std::string getService(sdbusplus::bus::bus& bus,
+                                   const std::string& intf,
+                                   const std::string& path) = 0;
+
+    /** @brief Get all Sensor.Value properties for a service and path.
+     *
+     * @param[in] bus - A bus to use for the call.
+     * @param[in] service - The service providing the interface.
+     * @param[in] path - The dbus path.
+     * @param[out] prop - A pointer to a properties struct to fill out.
+     *
+     * @warning Throws exception on dbus failure.
+     */
+    virtual void getProperties(sdbusplus::bus::bus& bus,
+                               const std::string& service,
+                               const std::string& path,
+                               struct SensorProperties* prop) = 0;
+
+    /** @brief Get Critical Threshold current assert status
+     *
+     * @param[in] bus - A bus to use for the call.
+     * @param[in] service - The service providing the interface.
+     * @param[in] path - The dbus path.
+     */
+    virtual bool thresholdsAsserted(sdbusplus::bus::bus& bus,
+                                    const std::string& service,
+                                    const std::string& path) = 0;
+};
+
+} // namespace pid_control
diff --git a/dbus/dbuspassive.cpp b/dbus/dbuspassive.cpp
index 815ed49..27255e7 100644
--- a/dbus/dbuspassive.cpp
+++ b/dbus/dbuspassive.cpp
@@ -15,7 +15,9 @@
  */
 #include "dbuspassive.hpp"
 
+#include "dbushelper_interface.hpp"
 #include "dbuspassiveredundancy.hpp"
+#include "dbusutil.hpp"
 #include "util.hpp"
 
 #include <sdbusplus/bus.hpp>
diff --git a/dbus/dbuspassive.hpp b/dbus/dbuspassive.hpp
index 74db80b..07a6ab2 100644
--- a/dbus/dbuspassive.hpp
+++ b/dbus/dbuspassive.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "conf.hpp"
+#include "dbushelper_interface.hpp"
 #include "dbuspassiveredundancy.hpp"
 #include "interfaces.hpp"
 #include "util.hpp"
diff --git a/dbus/dbusutil.cpp b/dbus/dbusutil.cpp
new file mode 100644
index 0000000..fc67439
--- /dev/null
+++ b/dbus/dbusutil.cpp
@@ -0,0 +1,58 @@
+#include "util.hpp"
+
+#include <cmath>
+#include <iostream>
+#include <set>
+#include <variant>
+
+using Property = std::string;
+using Value = std::variant<int64_t, double, std::string, bool>;
+using PropertyMap = std::map<Property, Value>;
+
+namespace pid_control
+{
+
+std::string getSensorPath(const std::string& type, const std::string& id)
+{
+    std::string layer = type;
+    if (type == "fan")
+    {
+        layer = "fan_tach";
+    }
+    else if (type == "temp")
+    {
+        layer = "temperature";
+    }
+    else
+    {
+        layer = "unknown"; // TODO(venture): Need to handle.
+    }
+
+    return std::string("/xyz/openbmc_project/sensors/" + layer + "/" + id);
+}
+
+std::string getMatch(const std::string& type, const std::string& id)
+{
+    return std::string("type='signal',"
+                       "interface='org.freedesktop.DBus.Properties',"
+                       "member='PropertiesChanged',"
+                       "path='" +
+                       getSensorPath(type, id) + "'");
+}
+
+bool validType(const std::string& type)
+{
+    static std::set<std::string> valid = {"fan", "temp"};
+    return (valid.find(type) != valid.end());
+}
+
+void scaleSensorReading(const double min, const double max, double& value)
+{
+    if (max <= 0 || max <= min)
+    {
+        return;
+    }
+    value /= (max - min);
+}
+
+} // namespace pid_control
diff --git a/dbus/dbusutil.hpp b/dbus/dbusutil.hpp
new file mode 100644
index 0000000..a7b8d3b
--- /dev/null
+++ b/dbus/dbusutil.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <exception>
+
+namespace pid_control
+{
+
+struct VariantToDoubleVisitor
+{
+    template <typename T>
+    std::enable_if_t<std::is_arithmetic<T>::value, double>
+        operator()(const T& t) const
+    {
+        return static_cast<double>(t);
+    }
+
+    template <typename T>
+    std::enable_if_t<!std::is_arithmetic<T>::value, double>
+        operator()(const T& t) const
+    {
+        throw std::invalid_argument("Cannot translate type to double");
+    }
+};
+
+} // namespace pid_control
diff --git a/dbus/dbuswrite.cpp b/dbus/dbuswrite.cpp
index 0b02872..83e5cd1 100644
--- a/dbus/dbuswrite.cpp
+++ b/dbus/dbuswrite.cpp
@@ -14,7 +14,9 @@
 // limitations under the License.
 */
 
-#include "dbus/dbuswrite.hpp"
+#include "dbuswrite.hpp"
+
+#include "dbushelper_interface.hpp"
 
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/bus.hpp>
diff --git a/dbus/dbuswrite.hpp b/dbus/dbuswrite.hpp
index 5cc2d49..06b7dcc 100644
--- a/dbus/dbuswrite.hpp
+++ b/dbus/dbuswrite.hpp
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "dbushelper_interface.hpp"
 #include "interfaces.hpp"
 #include "util.hpp"