Add lampTest class for LED

- Add the framework of the lamp test class.

- For the lamp test path in led group, Initiate lamp test when the
  Asserted property is true, Stop the lamp test when the Asserted
  property is false or the timer expires.

Tested:
- stat lamp test:
  busctl set-property xyz.openbmc_project.LED.GroupManager
  /xyz/openbmc_project/led/groups/lamp_test
  xyz.openbmc_project.Led.Group  Asserted b true

- stop lamp test:
  busctl set-property xyz.openbmc_project.LED.GroupManager
  /xyz/openbmc_project/led/groups/lamp_test
  xyz.openbmc_project.Led.Group  Asserted b false

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I7e6fe0ffc4679288ab90050742d2680051e61905
diff --git a/Makefile.am b/Makefile.am
index 5128fda..6aba107 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,6 +26,10 @@
                               $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
                               $(SDEVENTPLUS_CFLAGS)
 
+if WANTS_LAMP_TEST
+phosphor_ledmanager_SOURCES += lamptest.cpp
+endif
+
 led_default_configdir    	 = 	${datadir}/phosphor-led-manager
 led_ibm_rainier_2u_configdir = 	${datadir}/phosphor-led-manager/ibm,rainier-2u
 led_ibm_rainier_4u_configdir = 	${datadir}/phosphor-led-manager/ibm,rainier-4u
diff --git a/configure.ac b/configure.ac
index 4a48e57..b0e7985 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,8 +88,23 @@
 AC_ARG_ENABLE([use-lamp-test],
     AS_HELP_STRING([--enable-use-lamp-test], [Enable lamp test configuration.]))
 AM_CONDITIONAL([WANTS_LAMP_TEST], [test "x$enable_use_lamp_test" == "xyes"])
+
+
+
 AS_IF([test "x$enable_use_lamp_test" == "xyes"],
     AC_DEFINE([USE_LAMP_TEST],[],[Enable lamp test configuration.])
+
+    # lamp test path
+    AC_ARG_VAR(LAMP_TEST_OBJECT, [The lamp test object])
+
+    # lamp test timeout secs
+    AC_ARG_VAR(LAMP_TEST_TIMEOUT_IN_SECS, [The lamp test timeout in seconds])
+
+    AS_IF([test "x$LAMP_TEST_OBJECT" == "x"], [LAMP_TEST_OBJECT="/xyz/openbmc_project/led/groups/lamp_test"])
+    AC_DEFINE_UNQUOTED([LAMP_TEST_OBJECT], ["$LAMP_TEST_OBJECT"], [The lamp test D-Bus object])
+
+    AS_IF([test "x$LAMP_TEST_TIMEOUT_IN_SECS" == "x"], [LAMP_TEST_TIMEOUT_IN_SECS=240])
+    AC_DEFINE_UNQUOTED([LAMP_TEST_TIMEOUT_IN_SECS], [$LAMP_TEST_TIMEOUT_IN_SECS], [The lamp test timeout in seconds])
 )
 
 # Path of file for storing the names of asserted groups
diff --git a/group.cpp b/group.cpp
index a6656d3..cffae1d 100644
--- a/group.cpp
+++ b/group.cpp
@@ -18,6 +18,15 @@
         return value;
     }
 
+    if (customCallBack != nullptr)
+    {
+        // Call the custom callback method
+        customCallBack(value);
+
+        return sdbusplus::xyz::openbmc_project::Led::server::Group::asserted(
+            value);
+    }
+
     // Introducing these to enable gtest.
     Manager::group ledsAssert{};
     Manager::group ledsDeAssert{};
diff --git a/group.hpp b/group.hpp
index 7bf97c6..bb21fb6 100644
--- a/group.hpp
+++ b/group.hpp
@@ -31,18 +31,21 @@
 
     /** @brief Constructs LED Group
      *
-     * @param[in] bus     - Handle to system dbus
-     * @param[in] objPath - The D-Bus path that hosts LED group
-     * @param[in] manager - Reference to Manager
+     * @param[in] bus       - Handle to system dbus
+     * @param[in] objPath   - The D-Bus path that hosts LED group
+     * @param[in] manager   - Reference to Manager
      * @param[in] serialize - Serialize object
+     * @param[in] callBack  - Custom callback when LED group is asserted
      */
     Group(sdbusplus::bus::bus& bus, const std::string& objPath,
-          Manager& manager, Serialize& serialize) :
+          Manager& manager, Serialize& serialize,
+          std::function<void(bool)> callBack = nullptr) :
 
         sdbusplus::server::object::object<
             sdbusplus::xyz::openbmc_project::Led::server::Group>(
             bus, objPath.c_str(), true),
-        path(objPath), manager(manager), serialize(serialize)
+        path(objPath), manager(manager), serialize(serialize),
+        customCallBack(callBack)
     {
         // Initialize Asserted property value
         if (serialize.getGroupSavedState(objPath))
@@ -70,6 +73,10 @@
 
     /** @brief The serialize class for storing and restoring groups of LEDs */
     Serialize& serialize;
+
+    /** @brief Custom callback when LED group is asserted
+     */
+    std::function<void(bool)> customCallBack;
 };
 
 } // namespace led
diff --git a/lamptest.cpp b/lamptest.cpp
new file mode 100644
index 0000000..743bf29
--- /dev/null
+++ b/lamptest.cpp
@@ -0,0 +1,39 @@
+#include "config.h"
+
+#include "lamptest.hpp"
+
+namespace phosphor
+{
+namespace led
+{
+
+void LampTest::stop()
+{
+    timer.setEnabled(false);
+}
+
+void LampTest::start()
+{
+    // restart lamp test, it contains initiate or reset the timer.
+    timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
+}
+
+void LampTest::timeOutHandler()
+{
+    // set the Asserted property of lamp test to false
+}
+
+void LampTest::requestHandler(bool value)
+{
+    if (value)
+    {
+        start();
+    }
+    else
+    {
+        stop();
+    }
+}
+
+} // namespace led
+} // namespace phosphor
diff --git a/lamptest.hpp b/lamptest.hpp
new file mode 100644
index 0000000..ab33887
--- /dev/null
+++ b/lamptest.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "manager.hpp"
+
+#include <sdeventplus/utility/timer.hpp>
+
+#include <vector>
+
+namespace phosphor
+{
+namespace led
+{
+
+/** @class LampTest
+ *  @brief Manager LampTest feature
+ */
+class LampTest
+{
+  public:
+    LampTest() = delete;
+    ~LampTest() = default;
+    LampTest(const LampTest&) = delete;
+    LampTest& operator=(const LampTest&) = delete;
+    LampTest(LampTest&&) = default;
+    LampTest& operator=(LampTest&&) = default;
+
+    /** @brief Constructs LED LampTest
+     *
+     * Constructs timer and when the timeout occurs, the stop method is called
+     * back to stop timer and also end the lamp test.
+     *
+     * @param[in] event   - sd event handler
+     * @param[in] manager - reference to manager instance
+     */
+    LampTest(const sdeventplus::Event& event, Manager& manager) :
+        timer(event, std::bind(std::mem_fn(&LampTest::timeOutHandler), this)),
+        manager(manager)
+    {}
+
+    /** @brief the lamp test request handler
+     *
+     *  @param[in]  value    -  true: start lamptest
+     *                          false: stop lamptest
+     *  @return
+     */
+    void requestHandler(bool value);
+
+  private:
+    /** @brief Timer used for LEDs lamp test period */
+    sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;
+
+    /** @brief Reference to Manager object */
+    Manager& manager;
+
+    /** @brief Start and restart lamp test depending on what is the current
+     *         state. */
+    void start();
+
+    /** @brief Stop lamp test. */
+    void stop();
+
+    /** @brief This method gets called when the lamp test procedure is done as
+     *         part of timeout. */
+    void timeOutHandler();
+};
+
+} // namespace led
+} // namespace phosphor
diff --git a/led-main.cpp b/led-main.cpp
index 8dcfff6..7857e91 100644
--- a/led-main.cpp
+++ b/led-main.cpp
@@ -10,11 +10,19 @@
 #include "manager.hpp"
 #include "serialize.hpp"
 #include "utils.hpp"
+#ifdef USE_LAMP_TEST
+#include "lamptest.hpp"
+#endif
+
+#include <sdeventplus/event.hpp>
 
 #include <iostream>
 
 int main(void)
 {
+    // Get a default event loop
+    auto event = sdeventplus::Event::get_default();
+
     /** @brief Dbus constructs used by LED Group manager */
     auto& bus = phosphor::led::utils::DBusHandler::getBus();
 
@@ -34,6 +42,15 @@
     /** @brief store and re-store Group */
     phosphor::led::Serialize serialize(SAVED_GROUPS_FILE);
 
+#ifdef USE_LAMP_TEST
+    phosphor::led::LampTest lampTest(event, manager);
+
+    groups.emplace_back(std::make_unique<phosphor::led::Group>(
+        bus, LAMP_TEST_OBJECT, manager, serialize,
+        std::bind(std::mem_fn(&phosphor::led::LampTest::requestHandler),
+                  &lampTest, std::placeholders::_1)));
+#endif
+
     /** Now create so many dbus objects as there are groups */
     for (auto& grp : systemLedMap)
     {
@@ -41,15 +58,12 @@
             bus, grp.first, manager, serialize));
     }
 
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+
     /** @brief Claim the bus */
     bus.request_name(BUSNAME);
+    event.loop();
 
-    /** @brief Wait for client requests */
-    while (true)
-    {
-        /** @brief process dbus calls / signals discarding unhandled */
-        bus.process_discard();
-        bus.wait();
-    }
     return 0;
 }