lamp test: Add vritual lamp test function

- This feature adds the functionality that enables force updating
  certain physical LEDs and skip updating certain physical LEDs as
  part of lamp test.

- This is a generic feature that gives more control over handing
  physical LEDs.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I6f06ed103b1ceefdaa774b7a74a4a392609000ed
diff --git a/lamptest.cpp b/lamptest.cpp
index aaf73de..8220f96 100644
--- a/lamptest.cpp
+++ b/lamptest.cpp
@@ -1,5 +1,3 @@
-#include "config.h"
-
 #include "lamptest.hpp"
 
 #include <phosphor-logging/log.hpp>
@@ -10,6 +8,7 @@
 {
 
 using namespace phosphor::logging;
+using Json = nlohmann::json;
 
 bool LampTest::processLEDUpdates(const Manager::group& ledsAssert,
                                  const Manager::group& ledsDeAssert)
@@ -19,6 +18,34 @@
     // stopped.
     if (isLampTestRunning)
     {
+        // Physical LEDs will be updated during lamp test
+        for (const auto& it : ledsDeAssert)
+        {
+            std::string path = std::string(PHY_LED_PATH) + it.name;
+            auto iter = std::find_if(
+                forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
+                [&path](const auto& name) { return name == path; });
+
+            if (iter != forceUpdateLEDs.end())
+            {
+                manager.drivePhysicalLED(path, Layout::Action::Off, it.dutyOn,
+                                         it.period);
+            }
+        }
+
+        for (const auto& it : ledsAssert)
+        {
+            std::string path = std::string(PHY_LED_PATH) + it.name;
+            auto iter = std::find_if(
+                forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
+                [&path](const auto& name) { return name == path; });
+
+            if (iter != forceUpdateLEDs.end())
+            {
+                manager.drivePhysicalLED(path, it.action, it.dutyOn, it.period);
+            }
+        }
+
         updatedLEDsDuringLampTest.emplace(
             std::make_pair(ledsAssert, ledsDeAssert));
         return true;
@@ -41,6 +68,16 @@
     // Set all the Physical action to Off
     for (const auto& path : physicalLEDPaths)
     {
+        auto iter =
+            std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
+                         [&path](const auto& skip) { return skip == path; });
+
+        if (iter != skipUpdateLEDs.end())
+        {
+            // Skip update physical path
+            continue;
+        }
+
         manager.drivePhysicalLED(path, Layout::Action::Off, 0, 0);
     }
 
@@ -70,6 +107,16 @@
 
     for (const auto& path : physicalLEDPaths)
     {
+        auto iter = std::find_if(
+            skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
+            [&path](const auto& skipLed) { return skipLed == path; });
+
+        if (iter != skipUpdateLEDs.end())
+        {
+            // Physical LEDs will be skipped
+            continue;
+        }
+
         // 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
@@ -137,6 +184,16 @@
     // Set all the Physical action to On for lamp test
     for (const auto& path : physicalLEDPaths)
     {
+        auto iter =
+            std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
+                         [&path](const auto& skip) { return skip == path; });
+
+        if (iter != skipUpdateLEDs.end())
+        {
+            // Skip update physical path
+            continue;
+        }
+
         manager.drivePhysicalLED(path, Layout::Action::On, 0, 0);
     }
 }
@@ -173,8 +230,8 @@
 void LampTest::restorePhysicalLedStates()
 {
     // restore physical LEDs states before lamp test
-    Manager::group savedLEDStatesDeAssert{};
-    manager.driveLEDs(physicalLEDStatesPriorToLampTest, savedLEDStatesDeAssert);
+    Manager::group ledsDeAssert{};
+    manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert);
     physicalLEDStatesPriorToLampTest.clear();
 
     // restore physical LEDs states during lamp test
@@ -203,5 +260,42 @@
     }
 }
 
+void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path)
+{
+    if (!fs::exists(path) || fs::is_empty(path))
+    {
+        log<level::INFO>("The file does not exist or is empty",
+                         entry("FILE_PATH=%s", path.c_str()));
+        return;
+    }
+
+    try
+    {
+        std::ifstream jsonFile(path);
+        auto json = Json::parse(jsonFile);
+
+        // define the default JSON as empty
+        const std::vector<std::string> empty{};
+        auto forceLEDs = json.value("forceLEDs", empty);
+        for (auto& member : forceLEDs)
+        {
+            forceUpdateLEDs.push_back(PHY_LED_PATH + member);
+        }
+
+        auto skipLEDs = json.value("skipLEDs", empty);
+        for (auto& member : skipLEDs)
+        {
+            skipUpdateLEDs.push_back(PHY_LED_PATH + member);
+        }
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Failed to parse config file",
+                        entry("ERROR=%s", e.what()),
+                        entry("FILE_PATH=%s", path.c_str()));
+    }
+    return;
+}
+
 } // namespace led
 } // namespace phosphor