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/Makefile.am b/Makefile.am
index c93a550..e146a74 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,17 +1,16 @@
 AM_DEFAULT_SOURCE_EXT = .cpp
-AM_CPPFLAGS = -I$(top_srcdir)/../sdbusplus
+sbin_PROGRAMS = phosphor-ledmanager
+
+phosphor_ledmanager_SOURCES = \
+                led-main.cpp  \
+                led-manager.cpp
 
 BUILT_SOURCES = led-gen.hpp
 CLEANFILES = led-gen.hpp
+
 PYTHON="/usr/bin/python"
 led-gen.hpp: ${srcdir}/parse_led.py
 	${AM_V_at}${PYTHON} $^ > $@
 
-sbin_PROGRAMS = \
-	ledmanager
-
-ledmanager_SOURCES = \
-	led-manager.cpp \
-	led-main.cpp
-
-ledmanager_LDFLAGS = $(SYSTEMD_LIBS)
+phosphor_ledmanager_LDFLAGS = $(SYSTEMD_LIBS)
+phosphor_ledmanager_CFLAGS = $(SYSTEMD_CFLAGS)
diff --git a/configure.ac b/configure.ac
index 059f612..600571e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,17 +1,19 @@
 # Initialization
 AC_PREREQ([2.69])
 AC_INIT([phosphor-led-manager], [1.0], [https://github.com/openbmc/phosphor-led-manager/issues])
+AC_LANG([C++])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
 AM_SILENT_RULES([yes])
 
 # Checks for programs.
 AC_PROG_CXX
-AX_CXX_COMPILE_STDCXX_14([noext])
-AM_PROG_AR
 AC_PROG_INSTALL
 AC_PROG_MAKE_SET
-LT_INIT
+
+# Checks for typedefs, structures, and compiler characteristics.
+AX_CXX_COMPILE_STDCXX_14([noext])
+AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
 
 # Python
 AM_PATH_PYTHON([2.7], [AC_SUBST([PYTHON], [echo "$PYTHON"])], [AC_MSG_ERROR([Could not find python-2.7 installed...python-2.7 is required])])
@@ -19,22 +21,26 @@
 # Checks for libraries.
 PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
 
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
 # Checks for header files.
 AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])])
-#AC_CHECK_HEADER(sdbusplus/vtable.hpp, ,[AC_MSG_ERROR([Could not find vtable.hpp...openbmc/sdbusplus package required])])
-#AC_CHECK_HEADER(dbusplus/message.hpp, ,[AC_MSG_ERROR([Could not find messsage.hpp...openbmc/sdbusplus package required])])
-#AC_CHECK_HEADER(sdbusplus/bus.hpp, ,[AC_MSG_ERROR([Could not find bus.hpp...openbmc/sdbusplus package required])])
+AC_CHECK_HEADER(sdbusplus/server.hpp, ,[AC_MSG_ERROR([Could not find sdbusplus/server.hpp...openbmc/sdbusplus package required])])
 
-# Checks for typedefs, structures, and compiler characteristics.
-AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+# Dbus service name
 AC_ARG_VAR(BUSNAME, [The Dbus busname to own])
-AC_ARG_VAR(OBJPATH, [The Ledmanager Dbus root])
-AC_ARG_VAR(INTERFACE, [The Ledmanager Dbus interface])
 AS_IF([test "x$BUSNAME" == "x"], [BUSNAME="xyz.openbmc_project.ledmanager"])
+AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The Dbus busname to own])
+
+# Service dbus root
+AC_ARG_VAR(OBJPATH, [The Ledmanager Dbus root])
 AS_IF([test "x$OBJPATH" == "x"], [OBJPATH="/xyz/openbmc_project/ledmanager/groups"])
-AS_IF([test "x$INTERFACE" == "x"], [INTERFACE="xyz.openbmc_project.Ledmanager"])
-AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The DBus busname to own])
 AC_DEFINE_UNQUOTED([OBJPATH], ["$OBJPATH"], [The Ledmanager Dbus root])
+
+AC_ARG_VAR(INTERFACE, [The Ledmanager Dbus interface])
+AS_IF([test "x$INTERFACE" == "x"], [INTERFACE="xyz.openbmc_project.ledmanager"])
 AC_DEFINE_UNQUOTED([INTERFACE], ["$INTERFACE"], [The Ledmanager Dbus interface])
 
 # Create configured output
diff --git a/led-main.cpp b/led-main.cpp
index e10c2fe..90bba1e 100644
--- a/led-main.cpp
+++ b/led-main.cpp
@@ -3,7 +3,7 @@
 
 int main(void)
 {
-    phosphor::led::Manager ledMgr(BUSNAME, OBJPATH, INTERFACE);
+    phosphor::led::Group ledMgr(BUSNAME, OBJPATH, INTERFACE);
     ledMgr.run();
     return 0;
 }
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)
         {
diff --git a/led-manager.hpp b/led-manager.hpp
index 1ff869b..32306bd 100644
--- a/led-manager.hpp
+++ b/led-manager.hpp
@@ -6,92 +6,112 @@
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/server/interface.hpp>
 #include <sdbusplus/server/manager.hpp>
-
 namespace phosphor
 {
-
 namespace led
 {
 
-/** @class Manager
+/** @class Group
  *  @brief Manages group of LEDs and applies action on the elements of group
  */
 
-class Manager
+class Group
 {
-private:
-
-    /** @brief Define possible actions on a given LED.
-     *  For the BLINK operation, follow 50-50 duty cycle
-     */
-    enum Action
-    {
-        OFF,
-        ON,
-        BLINK,
-    };
-
-    /** @brief Dbus constructs used by LED manager */
-    sdbusplus::bus::bus iv_bus;
-
-public:
-    Manager() = delete;
-    ~Manager() = default;
-    Manager(const Manager&) = delete;
-    Manager& operator=(const Manager&) = delete;
-    Manager(Manager&&) = default;
-    Manager& operator=(Manager&&) = default;
-
-    /** @brief Constructs LED manager
-     *
-     * @param[in] busName   - The Dbus name to own
-     * @param[in] objPath   - The Dbus path that hosts LED manager
-     * @param[in] intfName  - The Dbus interface
-     */
-    Manager(const char* busName, const char* objPath, const char* intfName);
-
-    /** @brief Name of the LED and it's proposed action.
-     *  This structure is supplied as configuration at build time
-     */
-    struct LedAction
-    {
-        std::string name;
-        Action action;
-
-        // Needed for inserting elements into sets
-        bool operator<(const LedAction& right) const
+    public:
+        /** @brief Define possible actions on a given LED.
+         *  For the BLINK operation, follow 50-50 duty cycle
+         */
+        enum Action
         {
-            if (name == right.name)
+            OFF,
+            ON,
+            BLINK,
+        };
+
+        Group() = delete;
+        ~Group() = default;
+        Group(const Group&) = delete;
+        Group& operator=(const Group&) = delete;
+        Group(Group&&) = delete;
+        Group& operator=(Group&&) = delete;
+
+        /** @brief Constructs LED manager
+         *
+         * @param[in] busName   - The Dbus name to own
+         * @param[in] objPath   - The Dbus path that hosts LED manager
+         * @param[in] intfName  - The Dbus interface
+         */
+        Group(const char* busName, const char* objPath, const char* intfName);
+
+        /** @brief Name of the LED and it's proposed action.
+         *  This structure is supplied as configuration at build time
+         */
+        struct LedAction
+        {
+            std::string name;
+            Action action;
+
+            // Needed for inserting elements into sets
+            bool operator<(const LedAction& right) const
             {
-                return action < right.action;
+                if (name == right.name)
+                {
+                    return action < right.action;
+                }
+                return name < right.name;
             }
-            return name < right.name;
+        };
+
+        /** @brief For finding intersection */
+        static bool ledComp(const LedAction& left, const LedAction& right)
+        {
+            return left.name < right.name;
         }
 
-        // Needed for set union / intersect
-        bool operator==(const LedAction& right) const
-        {
-            // Only if name and action are same, consider equal
-            if (name == right.name &&
-                    action == right.action)
-            {
-                return true;
-            }
-            return false;
-        }
-    };
+        using group = std::set<LedAction>;
 
-    /** @brief static global map constructed at compile time */
-    static const std::map<std::string,
-           std::set<LedAction>> cv_LedMap;
+        /** @brief static global map constructed at compile time */
+        static const std::map<std::string, group> ledMap;
 
-    /** @brief sd_bus object manager */
-    sdbusplus::server::manager::manager objManager;
+        /** @brief Dbus constructs used by LED manager */
+        sdbusplus::bus::bus bus;
 
-    /** @brief Individual objects */
-    std::vector<sdbusplus::server::interface::interface> intfContainer;
+        /** @brief sd_bus object manager */
+        sdbusplus::server::manager::manager objManager;
 
-    void run();
+        /** @brief Individual objects */
+        std::vector<sdbusplus::server::interface::interface> intfContainer;
+
+        /** @brief Pointers to groups that are in asserted state */
+        static std::set<const group*> assertedGroups;
+
+        /** @brief Contains the LEDs that are in asserted state */
+        static group currentState;
+
+        /** @brief Waits on the client request and processes them */
+        void run();
+
+        /** @brief Given a group name, tells if its in asserted state or not.
+         *
+         *  @param[in]  name    -  Group name
+         *  @return             -  Whether in asserted state or not
+         */
+        bool getGroupState(const std::string& name);
+
+        /** @brief Given a group name, applies the action on the group
+         *
+         *  @param[in]  name    -  Group name
+         *  @param[in]  assert  -  Could be 0 or 1
+         *  @return             -  Success or exception thrown
+         */
+        int setGroupState(const std::string& name, bool assert);
+
+    private:
+        /** @brief Finds the set of LEDs to operate on and executes action
+         *
+         *  @return: Returns '0' for now.
+         */
+        int driveLEDs();
 };
 
 } // namespace led
diff --git a/parse_led.py b/parse_led.py
index 96bcd5e..7f212ea 100755
--- a/parse_led.py
+++ b/parse_led.py
@@ -1,21 +1,23 @@
 #!/usr/bin/env python
 import yaml
+import os
 
 if __name__ == '__main__':
-    with open('led.yaml', 'r') as f:
+    script_dir = os.path.dirname(os.path.realpath(__file__))
+    with open(os.path.join(script_dir, 'led.yaml'), 'r') as f:
         ifile = yaml.safe_load(f)
 
-    with open('led-gen.hpp', 'w') as ofile:
+    with open(os.path.join(script_dir, 'led-gen.hpp'), 'w') as ofile:
         ofile.write('/* !!! WARNING: This is a GENERATED Code..')
         ofile.write('Please do NOT Edit !!! */\n\n')
 
         ofile.write('const std::map<std::string,')
-        ofile.write(' std::set<phosphor::led::Manager::LedAction>>')
-        ofile.write(' phosphor::led::Manager::cv_LedMap = {\n\n')
+        ofile.write(' std::set<phosphor::led::Group::LedAction>>')
+        ofile.write(' phosphor::led::Group::ledMap = {\n\n')
         for group in ifile.iterkeys():
             # Value of this group is a std::set<string, led structure>
             ledset = ifile[group]
-            ofile.write('   {\"' + group + '\",{\n')
+            ofile.write('   {\"' + "/xyz/openbmc_project/ledmanager/groups/" + group + '\",{\n')
 
             for led_dict, list_dict in ledset.iteritems():
                 for name, value in list_dict.iteritems():