pseq: Add presence checking

As a circumvention for hardware problems, it can be necessary to check
for device presence in order to prevent calling out not present
components. Add the ability to configure presence checking in the PSEQ
configuration files and use presence, when configured, in problem
determination.

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: Ia9d8ee57f2d643f61ece45d9c03d523b891e98b8
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.cpp b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
index 3f431fb..050fcdc 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.cpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
@@ -16,6 +16,7 @@
 
 #include "ucd90320_monitor.hpp"
 
+#include "types.hpp"
 #include "utility.hpp"
 
 #include <fmt/format.h>
@@ -179,6 +180,35 @@
     }
 }
 
+bool UCD90320Monitor::isPresent(const std::string& inventoryPath)
+{
+    // Empty path indicates no presence check is needed
+    if (inventoryPath.empty())
+    {
+        return true;
+    }
+
+    // Get presence from D-Bus interface/property
+    try
+    {
+        bool present{true};
+        util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
+                          INVENTORY_MGR_IFACE, bus, present);
+        log<level::INFO>(
+            fmt::format("Presence, path: {}, value: {}", inventoryPath, present)
+                .c_str());
+        return present;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::INFO>(
+            fmt::format("Error getting presence property, path: {}, error: {}",
+                        inventoryPath, e.what())
+                .c_str());
+        return false;
+    }
+}
+
 void UCD90320Monitor::parseConfigFile(const std::filesystem::path& pathName)
 {
     try
@@ -192,8 +222,34 @@
         {
             for (const auto& railElement : *railsIterator)
             {
-                std::string rail = railElement.get<std::string>();
-                rails.emplace_back(std::move(rail));
+                auto nameIterator = railElement.find("name");
+
+                if (nameIterator != railElement.end())
+                {
+                    Rail rail;
+                    rail.name = (*nameIterator).get<std::string>();
+
+                    // Presence element is optional
+                    auto presenceIterator = railElement.find("presence");
+                    if (presenceIterator != railElement.end())
+                    {
+                        rail.presence = (*presenceIterator).get<std::string>();
+                    }
+
+                    log<level::DEBUG>(
+                        fmt::format("Adding rail, name: {}, presence: {}",
+                                    rail.name, rail.presence)
+                            .c_str());
+                    rails.emplace_back(std::move(rail));
+                }
+                else
+                {
+                    log<level::ERR>(
+                        fmt::format(
+                            "No name found within rail in configuration file: {}",
+                            pathName.string())
+                            .c_str());
+                }
             }
         }
         else
@@ -203,7 +259,8 @@
                             pathName.string())
                     .c_str());
         }
-        log<level::DEBUG>(fmt::format("Found rails: {}", rails).c_str());
+        log<level::DEBUG>(
+            fmt::format("Found number of rails: {}", rails.size()).c_str());
 
         // Parse pin information from config file
         auto pinsIterator = rootElement.find("pins");
@@ -217,12 +274,22 @@
                 if (nameIterator != pinElement.end() &&
                     lineIterator != pinElement.end())
                 {
-                    std::string name = (*nameIterator).get<std::string>();
-                    unsigned int line = (*lineIterator).get<unsigned int>();
-
                     Pin pin;
-                    pin.name = name;
-                    pin.line = line;
+                    pin.name = (*nameIterator).get<std::string>();
+                    pin.line = (*lineIterator).get<unsigned int>();
+
+                    // Presence element is optional
+                    auto presenceIterator = pinElement.find("presence");
+                    if (presenceIterator != pinElement.end())
+                    {
+                        pin.presence = (*presenceIterator).get<std::string>();
+                    }
+
+                    log<level::DEBUG>(
+                        fmt::format(
+                            "Adding pin, name: {}, line: {}, presence: {}",
+                            pin.name, pin.line, pin.presence)
+                            .c_str());
                     pins.emplace_back(std::move(pin));
                 }
                 else
@@ -247,10 +314,9 @@
     }
     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());
+        log<level::ERR>(
+            fmt::format("Error parsing configuration file, error: {}", e.what())
+                .c_str());
     }
 }
 
@@ -269,12 +335,12 @@
         onFailureCheckRails(message, additionalData, powerSupplyError);
         onFailureCheckPins(message, additionalData);
     }
-    catch (device_error::ReadFailure& e)
+    catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("ReadFailure when collecting metadata, error {}",
-                        e.what())
+            fmt::format("Error when collecting metadata, error: {}", e.what())
                 .c_str());
+        additionalData.emplace("ERROR", e.what());
     }
 
     if (message.empty())
@@ -319,14 +385,14 @@
     catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("Error reading device GPIOs, error {}", e.what())
+            fmt::format("Error reading device GPIOs, error: {}", e.what())
                 .c_str());
         additionalData.emplace("GPIO_ERROR", e.what());
     }
 
     // Add GPIO values to additional data, device has 84 GPIO pins so that value
     // is expected
-    if (numberLines == 84)
+    if (numberLines == 84 && values.size() >= 84)
     {
         log<level::INFO>(fmt::format("MAR01-24 GPIO values: {}",
                                      std::span{values}.subspan(0, 24))
@@ -374,7 +440,8 @@
             if (line < values.size())
             {
                 int value = values[line];
-                if (value == 0)
+
+                if ((value == 0) && isPresent(pins[pin].presence))
                 {
                     additionalData.emplace("INPUT_NUM",
                                            fmt::format("{}", line));
@@ -399,12 +466,12 @@
         additionalData.emplace("MFR_STATUS",
                                fmt::format("{:#014x}", readMFRStatus()));
     }
-    catch (device_error::ReadFailure& e)
+    catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("ReadFailure when collecting MFR_STATUS, error {}",
-                        e.what())
+            fmt::format("Error when collecting MFR_STATUS, error: {}", e.what())
                 .c_str());
+        additionalData.emplace("ERROR", e.what());
     }
 
     // The status_word register has a summary bit to tell us if each page even
@@ -434,14 +501,13 @@
                             fmt::format("STATUS{}_VOUT", page),
                             fmt::format("{:#04x}", vout));
 
-                        // Base the callouts on the first vout failure found
-                        if (message.empty())
+                        // Base the callouts on the first present vout failure
+                        // found
+                        if (message.empty() && (page < rails.size()) &&
+                            isPresent(rails[page].presence))
                         {
-                            if (page < rails.size())
-                            {
-                                additionalData.emplace("RAIL_NAME",
-                                                       rails[page]);
-                            }
+                            additionalData.emplace("RAIL_NAME",
+                                                   rails[page].name);
 
                             // Use power supply error if set and 12v rail has
                             // failed, else use voltage error