pseq: Find and parse configuaration file

Use the compatible systems information from entity manager to find the
correct system specific configuration file.  Then parse the rail and pin
data from the configuration file.

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: Ie7a13bece9c6cc1246cca733e2275b084bac95c8
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.cpp b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
index cd798d5..e001c43 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.cpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
@@ -21,15 +21,18 @@
 #include <fmt/format.h>
 #include <fmt/ranges.h>
 
+#include <nlohmann/json.hpp>
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/bus.hpp>
 
+#include <fstream>
 #include <map>
 #include <string>
 
 namespace phosphor::power::sequencer
 {
 
+using json = nlohmann::json;
 using namespace phosphor::logging;
 using namespace phosphor::power;
 
@@ -88,6 +91,7 @@
                                     compatibleSystemTypes)
                             .c_str());
                     // Use compatible systems information to find config file
+                    findConfigFile(compatibleSystemTypes);
                 }
             }
         }
@@ -98,10 +102,34 @@
     }
 }
 
+void UCD90320Monitor::findConfigFile(
+    const std::vector<std::string>& compatibleSystemTypes)
+{
+    // Expected config file path name:
+    // /usr/share/phosphor-power-sequencer/UCD90320Monitor_<systemType>.json
+
+    // Add possible file names based on compatible system types (if any)
+    for (const std::string& systemType : compatibleSystemTypes)
+    {
+        // Check if file exists
+        std::filesystem::path pathName{
+            "/usr/share/phosphor-power-sequencer/UCD90320Monitor_" +
+            systemType + ".json"};
+        if (std::filesystem::exists(pathName))
+        {
+            log<level::INFO>(
+                fmt::format("Config file path: {}", pathName.string()).c_str());
+            parseConfigFile(pathName);
+            break;
+        }
+    }
+}
+
 void UCD90320Monitor::interfacesAddedHandler(sdbusplus::message::message& msg)
 {
-    // Verify message is valid
-    if (!msg)
+    // Only continue if message is valid and rails / pins have not already been
+    // found
+    if (!msg || !rails.empty())
     {
         return;
     }
@@ -134,6 +162,7 @@
                             .c_str());
 
                     // Use compatible systems information to find config file
+                    findConfigFile(propValue);
                 }
             }
         }
@@ -144,4 +173,79 @@
     }
 }
 
+void UCD90320Monitor::parseConfigFile(const std::filesystem::path& pathName)
+{
+    try
+    {
+        std::ifstream file{pathName};
+        json rootElement = json::parse(file);
+
+        // Parse rail information from config file
+        auto railsIterator = rootElement.find("rails");
+        if (railsIterator != rootElement.end())
+        {
+            for (const auto& railElement : *railsIterator)
+            {
+                std::string rail = railElement.get<std::string>();
+                rails.emplace_back(std::move(rail));
+            }
+        }
+        else
+        {
+            log<level::ERR>(
+                fmt::format("No rails found in configuration file: {}",
+                            pathName.string())
+                    .c_str());
+        }
+        log<level::DEBUG>(fmt::format("Found rails: {}", rails).c_str());
+
+        // Parse pin information from config file
+        auto pinsIterator = rootElement.find("pins");
+        if (pinsIterator != rootElement.end())
+        {
+            for (const auto& pinElement : *pinsIterator)
+            {
+                auto nameIterator = pinElement.find("name");
+                auto lineIterator = pinElement.find("line");
+
+                if (nameIterator != pinElement.end() &&
+                    lineIterator != pinElement.end())
+                {
+                    std::string name = (*nameIterator).get<std::string>();
+                    int line = (*lineIterator).get<int>();
+
+                    Pin pin;
+                    pin.name = name;
+                    pin.line = line;
+                    pins.emplace_back(std::move(pin));
+                }
+                else
+                {
+                    log<level::ERR>(
+                        fmt::format(
+                            "No name or line found within pin in configuration file: {}",
+                            pathName.string())
+                            .c_str());
+                }
+            }
+        }
+        else
+        {
+            log<level::ERR>(
+                fmt::format("No pins found in configuration file: {}",
+                            pathName.string())
+                    .c_str());
+        }
+        log<level::DEBUG>(
+            fmt::format("Found number of pins: {}", rails.size()).c_str());
+    }
+    catch (const std::exception& e)
+    {
+        // Log error message in journal
+        log<level::ERR>(std::string("Exception parsing configuration file: " +
+                                    std::string(e.what()))
+                            .c_str());
+    }
+}
+
 } // namespace phosphor::power::sequencer