Add the method to set Functional property based on Group state

When a particular LED Group asserted property is set to true, then the
functional state of that FRU is false and vice-versa. This commit adds
that behavior. Functional status is defined in
xyz.openbmc_project.State.Decorator.OperationalStatus interface.

Tested: When manually set the Asserted property to true by the D-Bus,
we can see that the Functional is updated to false.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I6fa7a4c41363263d345b6b5335056fad15efb78c
diff --git a/group.cpp b/group.cpp
index 9292770..a6656d3 100644
--- a/group.cpp
+++ b/group.cpp
@@ -30,6 +30,11 @@
     // Store asserted state
     serialize.storeGroups(path, result);
 
+    // Set OperationalStatus according to the status of asserted property.
+    // If the group is asserted, then the functional status is false and
+    // vice-versa.
+    manager.setOperationalStatus(path, !value);
+
     // If something does not go right here, then there should be an sdbusplus
     // exception thrown.
     manager.driveLEDs(ledsAssert, ledsDeAssert);
diff --git a/manager.cpp b/manager.cpp
index d19eeae..a1c4175 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -186,5 +186,57 @@
     }
 }
 
+// Set OperationalStatus functional according to the asserted state of the group
+void Manager::setOperationalStatus(const std::string& path, bool value) const
+{
+    using namespace phosphor::logging;
+
+    // Get endpoints from the rType
+    std::string fru = path + "/fru_fault";
+
+    // endpoint contains the vector of strings, where each string is a Inventory
+    // D-Bus object that this, associated with this LED Group D-Bus object
+    // pointed to by fru_fault
+    PropertyValue endpoint{};
+
+    try
+    {
+        endpoint = dBusHandler.getProperty(
+            fru, "xyz.openbmc_project.Association", "endpoints");
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("Failed to get endpoints property",
+                        entry("ERROR=%s", e.what()),
+                        entry("PATH=%s", fru.c_str()));
+        return;
+    }
+
+    auto& endpoints = std::get<std::vector<std::string>>(endpoint);
+    if (endpoints.empty())
+    {
+        return;
+    }
+
+    for (const auto& fruInstancePath : endpoints)
+    {
+        // Set OperationalStatus by fru instance path
+        try
+        {
+            PropertyValue functionalValue{value};
+            dBusHandler.setProperty(
+                fruInstancePath,
+                "xyz.openbmc_project.State.Decorator.OperationalStatus",
+                "Functional", functionalValue);
+        }
+        catch (const sdbusplus::exception::SdBusError& e)
+        {
+            log<level::ERR>("Failed to set Functional property",
+                            entry("ERROR=%s", e.what()),
+                            entry("PATH=%s", fruInstancePath.c_str()));
+        }
+    }
+}
+
 } // namespace led
 } // namespace phosphor
diff --git a/manager.hpp b/manager.hpp
index 390c8fe..91f8b53 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -105,6 +105,16 @@
      */
     void driveLEDs(group& ledsAssert, group& ledsDeAssert);
 
+    /** @brief Set OperationalStatus according to the status of asserted
+     *         property
+     *
+     *  @param[in]  path          -  D-Bus path of group
+     *  @param[in]  value         -  Could be true or false
+     *
+     *  @return: None
+     */
+    void setOperationalStatus(const std::string& path, bool value) const;
+
   private:
     /** @brief sdbusplus handler */
     sdbusplus::bus::bus& bus;
diff --git a/utils.cpp b/utils.cpp
index 136d20f..c9a21e5 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -47,6 +47,31 @@
     return mapperResponse.cbegin()->first;
 }
 
+// Get the property name
+const PropertyValue
+    DBusHandler::getProperty(const std::string& objectPath,
+                             const std::string& interface,
+                             const std::string& propertyName) const
+{
+    PropertyValue value{};
+
+    auto& bus = DBusHandler::getBus();
+    auto service = getService(objectPath, interface);
+    if (service.empty())
+    {
+        return value;
+    }
+
+    auto method = bus.new_method_call(service.c_str(), objectPath.c_str(),
+                                      DBUS_PROPERTY_IFACE, "Get");
+    method.append(interface, propertyName);
+
+    auto reply = bus.call(method);
+    reply.read(value);
+
+    return value;
+}
+
 // Set property
 void DBusHandler::setProperty(const std::string& objectPath,
                               const std::string& interface,
diff --git a/utils.hpp b/utils.hpp
index edb0315..68a616a 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -15,8 +15,10 @@
 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>;
+// Eg: uint8_t : dutyOn, uint16_t : Period, std::string : Name,
+// std::vector<std::string> : endpoints, bool : Functional
+using PropertyValue = std::variant<uint8_t, uint16_t, std::string,
+                                   std::vector<std::string>, bool>;
 
 /**
  *  @class DBusHandler
@@ -47,6 +49,20 @@
     const std::string getService(const std::string& path,
                                  const std::string& interface) const;
 
+    /** @brief Get property(type: variant)
+     *
+     *  @param[in] objectPath       -   D-Bus object path
+     *  @param[in] interface        -   D-Bus interface
+     *  @param[in] propertyName     -   D-Bus property name
+     *
+     *  @return The value of the property(type: variant)
+     *
+     *  @throw sdbusplus::exception::SdBusError when it fails
+     */
+    const PropertyValue getProperty(const std::string& objectPath,
+                                    const std::string& interface,
+                                    const std::string& propertyName) const;
+
     /** @brief Set D-Bus property
      *
      *  @param[in] objectPath       -   D-Bus object path