Add utility for D-Bus

Putting all D-Bus related operation to the utils file.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I934e519bca43b73da01ed09fc73f6c7cdc795e1f
diff --git a/Makefile.am b/Makefile.am
index 5edc8f9..4b91f58 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,8 @@
                 led-main.cpp  \
                 manager.cpp \
                 group.cpp \
-                serialize.cpp
+                serialize.cpp \
+                utils.cpp
 
 if !WANTS_JSON
 BUILT_SOURCES = led-gen.hpp
diff --git a/led-main.cpp b/led-main.cpp
index 0b060dc..7dc0488 100644
--- a/led-main.cpp
+++ b/led-main.cpp
@@ -9,13 +9,14 @@
 #include "ledlayout.hpp"
 #include "manager.hpp"
 #include "serialize.hpp"
+#include "utils.hpp"
 
 #include <iostream>
 
 int main(void)
 {
     /** @brief Dbus constructs used by LED Group manager */
-    sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+    auto& bus = phosphor::led::utils::DBusHandler::getBus();
 
 #ifdef LED_USE_JSON
     auto systemLedMap = loadJsonConfig(LED_JSON_FILE);
diff --git a/manager.cpp b/manager.cpp
index 7741933..d19eeae 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -12,6 +12,11 @@
 namespace led
 {
 
+using namespace phosphor::logging;
+
+static constexpr auto PHY_LED_PATH = "/xyz/openbmc_project/led/physical/";
+static constexpr auto PHY_LED_IFACE = "xyz.openbmc_project.Led.Physical";
+
 // Assert -or- De-assert
 bool Manager::setGroupState(const std::string& path, bool assert,
                             group& ledsAssert, group& ledsDeAssert)
@@ -101,16 +106,6 @@
 /** @brief Run through the map and apply action on the LEDs */
 void Manager::driveLEDs(group& ledsAssert, group& ledsDeAssert)
 {
-    using namespace phosphor::logging;
-    // Map of physical LED dbus paths to their Service providers
-    populateObjectMap();
-
-    if (phyLeds.empty())
-    {
-        // Error message is inside the map construction logic.
-        return;
-    }
-
     // This order of LED operation is important.
     if (ledsDeAssert.size())
     {
@@ -142,24 +137,30 @@
                                Layout::Action action, uint8_t dutyOn,
                                const uint16_t period)
 {
-    using namespace phosphor::logging;
-
-    auto service = phyLeds.find(objPath);
-    if (service == phyLeds.end() || service->second.empty())
+    try
     {
-        log<level::ERR>("No service providers for physical LED",
-                        entry("PATH=%s", objPath.c_str()));
-        return;
+        // If Blink, set its property
+        if (action == Layout::Action::Blink)
+        {
+            PropertyValue dutyOnValue{dutyOn};
+            PropertyValue periodValue{period};
+
+            dBusHandler.setProperty(objPath, PHY_LED_IFACE, "DutyOn",
+                                    dutyOnValue);
+            dBusHandler.setProperty(objPath, PHY_LED_IFACE, "Period",
+                                    periodValue);
+        }
+
+        PropertyValue actionValue{getPhysicalAction(action)};
+        dBusHandler.setProperty(objPath, PHY_LED_IFACE, "State", actionValue);
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Error setting property for physical LED",
+                        entry("ERROR=%s", e.what()),
+                        entry("OBJECT_PATH=%s", objPath.c_str()));
     }
 
-    // If Blink, set its property
-    if (action == Layout::Action::Blink)
-    {
-        drivePhysicalLED(service->second, objPath, "DutyOn", dutyOn);
-        drivePhysicalLED(service->second, objPath, "Period", period);
-    }
-    drivePhysicalLED(service->second, objPath, "State",
-                     getPhysicalAction(action));
     return;
 }
 
@@ -185,69 +186,5 @@
     }
 }
 
-/** Populates a map with physical LED paths to its service providers */
-void Manager::populateObjectMap()
-{
-    using namespace phosphor::logging;
-
-    // Mapper dbus constructs
-    constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
-    constexpr auto MAPPER_OBJ_PATH = "/xyz/openbmc_project/object_mapper";
-    constexpr auto MAPPER_IFACE = "xyz.openbmc_project.ObjectMapper";
-
-    // Needed to be passed to get the SubTree level
-    auto depth = 0;
-
-    // Clean start
-    phyLeds.clear();
-
-    // Make a mapper call
-    auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
-                                          MAPPER_IFACE, "GetSubTree");
-    // Cook rest of the things.
-    mapperCall.append(PHY_LED_PATH);
-    mapperCall.append(depth);
-    mapperCall.append(std::vector<std::string>({PHY_LED_IFACE}));
-
-    auto reply = bus.call(mapperCall);
-    if (reply.is_method_error())
-    {
-        // Its okay if we do not see a corresponding physical LED.
-        log<level::INFO>("Error looking up Physical LED services",
-                         entry("PATH=%s", PHY_LED_PATH));
-        return;
-    }
-
-    // Response by mapper in the case of success
-    std::map<std::string, std::map<std::string, std::vector<std::string>>>
-        objectTree;
-
-    // This is the dict of object paths - service names - interfaces
-    try
-    {
-        reply.read(objectTree);
-    }
-    catch (const sdbusplus::exception::SdBusError& e)
-    {
-        log<level::ERR>("Failed to parse Physical LED service lookup",
-                        entry("ERROR=%s", e.what()),
-                        entry("REPLY_SIG=%s", reply.get_signature()));
-        return;
-    }
-    if (objectTree.empty())
-    {
-        log<level::INFO>("Physical LED lookup did not return any services",
-                         entry("PATH=%s", PHY_LED_PATH));
-        return;
-    }
-
-    // Now construct our path -> Service name map.
-    for (const auto& iter : objectTree)
-    {
-        phyLeds.emplace(iter.first, iter.second.begin()->first);
-    }
-    return;
-}
-
 } // namespace led
 } // namespace phosphor
diff --git a/manager.hpp b/manager.hpp
index 0cc5cf6..390c8fe 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -1,8 +1,7 @@
 #pragma once
 
 #include "ledlayout.hpp"
-
-#include <sdbusplus/bus.hpp>
+#include "utils.hpp"
 
 #include <map>
 #include <set>
@@ -12,11 +11,7 @@
 {
 namespace led
 {
-
-/** @brief Physical LED dbus constructs */
-constexpr auto PHY_LED_PATH = "/xyz/openbmc_project/led/physical/";
-constexpr auto PHY_LED_IFACE = "xyz.openbmc_project.Led.Physical";
-constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
+using namespace phosphor::led::utils;
 
 /** @class Manager
  *  @brief Manages group of LEDs and applies action on the elements of group
@@ -117,6 +112,9 @@
     /** Map of physical LED path to service name */
     std::map<std::string, std::string> phyLeds{};
 
+    /** DBusHandler class handles the D-Bus operations */
+    DBusHandler dBusHandler;
+
     /** @brief Pointers to groups that are in asserted state */
     std::set<const group*> assertedGroups;
 
@@ -146,36 +144,6 @@
      */
     void drivePhysicalLED(const std::string& objPath, Layout::Action action,
                           uint8_t dutyOn, const uint16_t period);
-
-    /** @brief Makes a dbus call to a passed in service name.
-     *  This is now the physical LED controller
-     *
-     *  @param[in]  service   -  dbus service name
-     *  @param[in]  objPath   -  dbus object path
-     *  @param[in]  property  -  property to be written to
-     *  @param[in]  value     -  Value to write
-     */
-    template <typename T>
-    void drivePhysicalLED(const std::string& service,
-                          const std::string& objPath,
-                          const std::string& property, const T& value)
-    {
-        std::variant<T> data = value;
-
-        auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
-                                          DBUS_PROPERTY_IFACE, "Set");
-        method.append(PHY_LED_IFACE);
-        method.append(property);
-        method.append(data);
-
-        // There will be exceptions going forward and hence don't need a
-        // response
-        bus.call_noreply(method);
-        return;
-    }
-
-    /** @brief Populates map of Physical LED paths to service name */
-    void populateObjectMap();
 };
 
 } // namespace led
diff --git a/test/Makefile.am b/test/Makefile.am
index 3941c5c..0a6ef3a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -9,4 +9,4 @@
 utest_CXXFLAGS = $(PTHREAD_CFLAGS)
 utest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS) $(SYSTEMD_LIBS) $(PHOSPHOR_LOGGING_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS)
 utest_SOURCES = utest.cpp utest-led-json.cpp utest-serialize.cpp
-utest_LDADD = $(top_builddir)/manager.o $(top_builddir)/serialize.o
+utest_LDADD = $(top_builddir)/manager.o $(top_builddir)/serialize.o $(top_builddir)/utils.o
diff --git a/utils.cpp b/utils.cpp
new file mode 100644
index 0000000..136d20f
--- /dev/null
+++ b/utils.cpp
@@ -0,0 +1,72 @@
+#include "utils.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+namespace phosphor
+{
+namespace led
+{
+namespace utils
+{
+
+using namespace phosphor::logging;
+
+// Get service name
+const std::string DBusHandler::getService(const std::string& path,
+                                          const std::string& interface) const
+{
+
+    using InterfaceList = std::vector<std::string>;
+    std::map<std::string, std::vector<std::string>> mapperResponse;
+
+    auto& bus = DBusHandler::getBus();
+
+    auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
+                                      MAPPER_IFACE, "GetObject");
+    mapper.append(path, InterfaceList({interface}));
+
+    auto mapperResponseMsg = bus.call(mapper);
+    if (mapperResponseMsg.is_method_error())
+    {
+        log<level::ERR>("Failed to invoke ObjectMapper method",
+                        entry("OBJECT_PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", interface.c_str()));
+        return "";
+    }
+
+    mapperResponseMsg.read(mapperResponse);
+    if (mapperResponse.empty())
+    {
+        log<level::ERR>("Failed to read getService mapper response",
+                        entry("OBJECT_PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", interface.c_str()));
+        return "";
+    }
+
+    // the value here will be the service name
+    return mapperResponse.cbegin()->first;
+}
+
+// Set property
+void DBusHandler::setProperty(const std::string& objectPath,
+                              const std::string& interface,
+                              const std::string& propertyName,
+                              const PropertyValue& value) const
+{
+    auto& bus = DBusHandler::getBus();
+    auto service = getService(objectPath, interface);
+    if (service.empty())
+    {
+        return;
+    }
+
+    auto method = bus.new_method_call(service.c_str(), objectPath.c_str(),
+                                      DBUS_PROPERTY_IFACE, "Set");
+    method.append(interface.c_str(), propertyName.c_str(), value);
+
+    bus.call_noreply(method);
+}
+
+} // namespace utils
+} // namespace led
+} // namespace phosphor
\ No newline at end of file
diff --git a/utils.hpp b/utils.hpp
new file mode 100644
index 0000000..edb0315
--- /dev/null
+++ b/utils.hpp
@@ -0,0 +1,67 @@
+#pragma once
+#include <sdbusplus/server.hpp>
+
+#include <map>
+#include <vector>
+namespace phosphor
+{
+namespace led
+{
+namespace utils
+{
+constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
+constexpr auto MAPPER_OBJ_PATH = "/xyz/openbmc_project/object_mapper";
+constexpr auto MAPPER_IFACE = "xyz.openbmc_project.ObjectMapper";
+constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
+
+// The value of the property(type: variant, contains some basic types)
+// Eg: uint8_t : dutyOn, uint16_t : Period, std::string : Name
+using PropertyValue = std::variant<uint8_t, uint16_t, std::string>;
+
+/**
+ *  @class DBusHandler
+ *
+ *  Wrapper class to handle the D-Bus calls
+ *
+ *  This class contains the APIs to handle the D-Bus calls.
+ */
+class DBusHandler
+{
+  public:
+    /** @brief Get the bus connection. */
+    static auto& getBus()
+    {
+        static auto bus = sdbusplus::bus::new_default();
+        return bus;
+    }
+
+    /**
+     *  @brief Get service name by the path and interface of the DBus.
+     *
+     *  @param[in] path      -  D-Bus object path
+     *  @param[in] interface -  D-Bus Interface
+     *
+     *  @return std::string  -  the D-Bus service name
+     *
+     */
+    const std::string getService(const std::string& path,
+                                 const std::string& interface) const;
+
+    /** @brief Set D-Bus property
+     *
+     *  @param[in] objectPath       -   D-Bus object path
+     *  @param[in] interface        -   D-Bus interface
+     *  @param[in] propertyName     -   D-Bus property name
+     *  @param[in] value            -   The value to be set
+     *
+     *  @throw sdbusplus::exception::SdBusError when it fails
+     */
+    void setProperty(const std::string& objectPath,
+                     const std::string& interface,
+                     const std::string& propertyName,
+                     const PropertyValue& value) const;
+};
+
+} // namespace utils
+} // namespace led
+} // namespace phosphor