Add methods to driving the LEDs

This enables creating custom groups and participating LEDs so that
it can later be generated from MRW. For each of the group, a dbus object
is created which will announce LED actions. Corresponding groups are
asserted / de-asserted based on user input.

Change-Id: I7e64bea13767b8d083dd946f4cf3aeb37e62ff17
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/led-manager.cpp b/led-manager.cpp
index 8bc1751..bfbf426 100644
--- a/led-manager.cpp
+++ b/led-manager.cpp
@@ -1,17 +1,19 @@
 #include <iostream>
 #include <cstring>
+#include <algorithm>
 #include <sdbusplus/vtable.hpp>
 #include <sdbusplus/message.hpp>
 #include <sdbusplus/bus.hpp>
 #include "led-manager.hpp"
 #include "led-gen.hpp"
-
 namespace phosphor
 {
-
 namespace led
 {
 
+std::set<const Group::group*> Group::assertedGroups;
+Group::group Group::currentState;
+
 /** @brief Called when the group's property is read
  *         Signature as needed by sd_bus
  */
@@ -19,16 +21,10 @@
                   const char *property, sd_bus_message *reply,
                   void *data, sd_bus_error* error)
 {
-    auto group = strrchr(path, '/');
-    if (group)
-    {
-        // Removing the starting '/' in /group
-        group++;
-    }
+    auto ledMgr = static_cast<Group*>(data);
+    auto state = ledMgr->getGroupState(path);
 
-    //TODO : Need to see how to represent group specific asserted state
-    // May be a new tuple / map ?
-    sd_bus_message_append(reply, "b", 0);
+    sd_bus_message_append(reply, "b", state);
     return 0;
 }
 
@@ -40,49 +36,135 @@
                   void *data, sd_bus_error* error)
 {
     bool state {};
-    auto group = strrchr(path, '/');
-    if (group)
-    {
-        // Removing the starting '/' in /group
-        group++;
-    }
-
     auto msg = sdbusplus::message::message(value);
     sd_bus_message_ref(value);
     msg.read(state);
 
-    //TODO : Need to see how to represent group specific asserted state
-    // May be a new tuple / map ?
-    return 1;
+    auto ledMgr = static_cast<Group*>(data);
+    return ledMgr->setGroupState(path, state);
 }
 
-/** @brief Users having to assert a group will just turn this property to 1
- *  similarly, setting this property to 0 will deassert the group
+// Get the asserted state
+bool Group::getGroupState(const std::string& path)
+{
+    return assertedGroups.find(&ledMap.at(path)) != assertedGroups.end();
+}
+
+// Assert -or- De-assert
+int Group::setGroupState(const std::string& path, bool assert)
+{
+    if (assert)
+    {
+        assertedGroups.insert(&ledMap.at(path));
+    }
+    else
+    {
+        auto search = assertedGroups.find(&ledMap.at(path));
+        if (search != assertedGroups.end())
+        {
+            assertedGroups.erase(&ledMap.at(path));
+        }
+        else
+        {
+            std::cout << "Group [ " << path << " ] Not present\n";
+        }
+    }
+    return driveLEDs();
+}
+
+// Run through the map and apply action
+int Group::driveLEDs()
+{
+    // This will contain the union of what's already in the asserted group
+    group desiredState {};
+    for(const auto& grp : assertedGroups)
+    {
+        desiredState.insert(grp->cbegin(), grp->cend());
+    }
+
+    // Always Do execute Turn Off and then Turn on since we have the Blink
+    // taking priority over -on-
+    group ledsToDeAssert {};
+
+    std::set_difference(currentState.begin(), currentState.end(),
+                        desiredState.begin(), desiredState.end(),
+                        std::inserter(ledsToDeAssert, ledsToDeAssert.begin()));
+    if(ledsToDeAssert.size())
+    {
+        // We really do not want the Group Manager to know how a particular LED
+        // transitions from State-A --> State-B and all this must be handled by
+        // the physical LED controller implementation.
+        // So in this case, Group Manager really does not want to turn off the
+        // LEDs and then turning it back on and let the physical LED controller
+        // handle that.
+
+        // If we previously had a FRU in ON state , and then if there was a
+        // request to make it blink, the end state would now be blink.
+        // If we either turn off blink / fault, then we need to go back to its
+        // previous state.
+        group ledsToReAssert {};
+        std::set_intersection(desiredState.begin(), desiredState.end(),
+                              ledsToDeAssert.begin(), ledsToDeAssert.end(),
+                              std::inserter(ledsToReAssert, ledsToReAssert.begin()),
+                              ledComp);
+
+        if (ledsToReAssert.size())
+        {
+            std::cout << "Asserting LEDs again" << std::endl;
+            for (const auto& it: ledsToReAssert)
+            {
+                std::cout << "\t{" << it.name << "::" << it.action << "}"
+                          << std::endl;
+            }
+        }
+    }
+
+    // Turn on these
+    group ledsToAssert {};
+    std::set_difference(desiredState.begin(), desiredState.end(),
+                        currentState.begin(), currentState.end(),
+                        std::inserter(ledsToAssert, ledsToAssert.begin()));
+
+    if(ledsToAssert.size())
+    {
+        std::cout << "Asserting LEDs" << std::endl;
+        for (const auto& it: ledsToAssert)
+        {
+            std::cout << "\t{" << it.name << "::" << it.action << "}"
+                      << std::endl;
+        }
+    }
+
+    // Done.. Save the latest and greatest.
+    currentState = std::move(desiredState);
+
+    return 0;
+}
+
+/** @brief Users having to assert a group will just turn this property to TRUE
+ *  similarly, setting this property to FALSE will deassert the group
  */
 constexpr sdbusplus::vtable::vtable_t led_vtable[] =
 {
     sdbusplus::vtable::start(),
-    sdbusplus::vtable::property("Assert", "b",
-    getGroupState, setGroupState, sdbusplus::vtable::property_::emits_change),
-    sdbusplus::vtable::end()
+    sdbusplus::vtable::property("Asserted", "b",
+                                getGroupState, setGroupState,
+                                sdbusplus::vtable::property_::emits_change),
+                                sdbusplus::vtable::end()
 };
 
 /** @brief Initialize the bus and announce services */
-Manager::Manager(const char* busName,
-                 const char* objPath,
-                 const char* intfName) :
-    iv_bus(sdbusplus::bus::new_system()),
-    objManager(iv_bus, objPath)
+Group::Group(const char* busName,
+             const char* objPath,
+             const char* intfName) :
+    bus(sdbusplus::bus::new_system()),
+    objManager(bus, objPath)
 {
-    // Like /org/openbmc/ledmanager/groups/
-    auto path = std::string(objPath) + "/";
-
     /** Now create so many dbus objects as there are groups */
-    for (auto &grp: Manager::cv_LedMap)
+    for (auto &grp: Group::ledMap)
     {
-        auto grpPath = path + grp.first;
-        intfContainer.emplace_back(sdbusplus::server::interface::interface(
-                      iv_bus, grpPath.c_str(), intfName, led_vtable, this));
+        intfContainer.emplace_back(bus, grp.first.c_str(),
+                                   intfName, led_vtable, this);
 
         // These are now set of structs having LED name and the action. Do not
         // have anything to be done here at the moment but need to create a
@@ -94,18 +176,18 @@
     }
     // Once done, claim the bus and systemd will
     // consider this service started
-    iv_bus.request_name(busName);
+    bus.request_name(busName);
 }
 
 /** @brief Wait for client requests */
-void Manager::run()
+void Group::run()
 {
     while(true)
     {
         try
         {
-            iv_bus.process_discard();
-            iv_bus.wait();
+            bus.process_discard();
+            bus.wait();
         }
         catch (std::exception &e)
         {