Add the restoreLedsAssert method for LED manager

- Logically, Since the physical LEDs states should not be operated
  during the lamp test, all the physical LEDs states before and
  during the lamp test should be saved and restored after the lamp
  test.

Tested:
- Store the state of all the physical LEDs before the lamp test,
  and keep the state of all the physical LEDs consistent after
  the lamp test.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I8b321bae2e38001ea33a80bda8badcf4c8a55040
diff --git a/lamptest.cpp b/lamptest.cpp
index 04db6fd..572ef68 100644
--- a/lamptest.cpp
+++ b/lamptest.cpp
@@ -9,8 +9,30 @@
 namespace led
 {
 
+using namespace phosphor::logging;
+
+bool LampTest::processLEDUpdates(const Manager::group& ledsAssert,
+                                 const Manager::group& ledsDeAssert)
+{
+    // If the physical LED status is updated during the lamp test, it should be
+    // saved to Queue, and the queue will be processed after the lamp test is
+    // stopped.
+    if (isLampTestRunning)
+    {
+        updatedLEDsDuringLampTest.emplace(
+            std::make_pair(ledsAssert, ledsDeAssert));
+        return true;
+    }
+    return false;
+}
+
 void LampTest::stop()
 {
+    if (!isLampTestRunning)
+    {
+        return;
+    }
+
     timer.setEnabled(false);
 
     // Set all the Physical action to Off
@@ -18,15 +40,93 @@
     {
         manager.drivePhysicalLED(path, Layout::Action::Off, 0, 0);
     }
+
+    isLampTestRunning = false;
+    restorePhysicalLedStates();
+}
+
+Layout::Action LampTest::getActionFromString(const std::string& str)
+{
+    Layout::Action action = Layout::Off;
+
+    if (str == "xyz.openbmc_project.Led.Physical.Action.On")
+    {
+        action = Layout::On;
+    }
+    else if (str == "xyz.openbmc_project.Led.Physical.Action.Blink")
+    {
+        action = Layout::Blink;
+    }
+
+    return action;
+}
+
+void LampTest::storePhysicalLEDsStates()
+{
+    physicalLEDStatesPriorToLampTest.clear();
+
+    for (const auto& path : physicalLEDPaths)
+    {
+        // Reverse intercept path, Get the name of each member of physical led
+        // e.g: path = /xyz/openbmc_project/led/physical/front_fan
+        //      name = front_fan
+        sdbusplus::message::object_path object_path(path);
+        auto name = object_path.filename();
+        if (name.empty())
+        {
+            log<level::ERR>(
+                "Failed to get the name of member of physical LED path",
+                entry("PATH=%s", path.c_str()), entry("NAME=%s", name.c_str()));
+            continue;
+        }
+
+        std::string state{};
+        uint16_t period{};
+        uint8_t dutyOn{};
+        try
+        {
+            auto properties = dBusHandler.getAllProperties(path, PHY_LED_IFACE);
+
+            state = std::get<std::string>(properties["State"]);
+            period = std::get<uint16_t>(properties["Period"]);
+            dutyOn = std::get<uint8_t>(properties["DutyOn"]);
+        }
+        catch (const sdbusplus::exception::SdBusError& e)
+        {
+            log<level::ERR>("Failed to get All properties",
+                            entry("ERROR=%s", e.what()),
+                            entry("PATH=%s", path.c_str()));
+            continue;
+        }
+
+        phosphor::led::Layout::Action action = getActionFromString(state);
+        if (action != phosphor::led::Layout::Off)
+        {
+            phosphor::led::Layout::LedAction ledAction{
+                name, action, dutyOn, period, phosphor::led::Layout::On};
+            physicalLEDStatesPriorToLampTest.emplace(ledAction);
+        }
+    }
 }
 
 void LampTest::start()
 {
+    if (isLampTestRunning)
+    {
+        // reset the timer and then return
+        timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
+        return;
+    }
+
     // Get paths of all the Physical LED objects
     physicalLEDPaths = dBusHandler.getSubTreePaths(PHY_LED_PATH, PHY_LED_IFACE);
 
+    // Get physical LEDs states before lamp test
+    storePhysicalLEDsStates();
+
     // restart lamp test, it contains initiate or reset the timer.
     timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
+    isLampTestRunning = true;
 
     // Set all the Physical action to On for lamp test
     for (const auto& path : physicalLEDPaths)
@@ -37,8 +137,6 @@
 
 void LampTest::timeOutHandler()
 {
-    using namespace phosphor::logging;
-
     // set the Asserted property of lamp test to false
     if (!groupObj)
     {
@@ -66,5 +164,21 @@
     }
 }
 
+void LampTest::restorePhysicalLedStates()
+{
+    // restore physical LEDs states before lamp test
+    Manager::group savedLEDStatesDeAssert{};
+    manager.driveLEDs(physicalLEDStatesPriorToLampTest, savedLEDStatesDeAssert);
+    physicalLEDStatesPriorToLampTest.clear();
+
+    // restore physical LEDs states during lamp test
+    while (!updatedLEDsDuringLampTest.empty())
+    {
+        auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front();
+        manager.driveLEDs(ledsAssert, ledsDeAssert);
+        updatedLEDsDuringLampTest.pop();
+    }
+}
+
 } // namespace led
 } // namespace phosphor