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
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.hpp b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
index ce6af97..d7b66ad 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.hpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
@@ -6,9 +6,18 @@
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/bus/match.hpp>
 
+#include <filesystem>
+#include <vector>
+
 namespace phosphor::power::sequencer
 {
 
+struct Pin
+{
+    std::string name;
+    int line;
+};
+
 /**
  * @class UCD90320Monitor
  * This class implements fault analysis for the UCD90320
@@ -51,16 +60,42 @@
     sdbusplus::bus::match_t match;
 
     /**
+     * List of pins
+     */
+    std::vector<Pin> pins;
+
+    /**
      * The read/write interface to this hardware
      */
     pmbus::PMBus pmbusInterface;
 
     /**
+     * List of rail names
+     */
+    std::vector<std::string> rails;
+
+    /**
      * Finds the list of compatible system types using D-Bus methods.
      * This list is used to find the correct JSON configuration file for the
      * current system.
      */
     void findCompatibleSystemTypes();
+
+    /**
+     * Finds the JSON configuration file.
+     * Looks for a configuration file based on the list of compatible system
+     * types.
+     * Throws an exception if an operating system error occurs while checking
+     * for the existance of a file.
+     * @param[in] compatibleSystemTypes List of compatible system types
+     */
+    void findConfigFile(const std::vector<std::string>& compatibleSystemTypes);
+
+    /**
+     * Parse the JSON configuration file.
+     * @param[in] pathName the path name
+     */
+    void parseConfigFile(const std::filesystem::path& pathName);
 };
 
 } // namespace phosphor::power::sequencer