pseq: Add generic failure handling to base class

Use the power sequencer base class to handle failure cases where the
device is not found or a device specific failure can not determined. Add
a error logging convience method. Move the D-Bus object to the base
class.

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: I6f7c2951e83cb892ec0c63018d65d5e241ebe6f0
diff --git a/phosphor-power-sequencer/src/meson.build b/phosphor-power-sequencer/src/meson.build
index 4ea4b9c..590e10e 100644
--- a/phosphor-power-sequencer/src/meson.build
+++ b/phosphor-power-sequencer/src/meson.build
@@ -8,6 +8,7 @@
     'power_control_main.cpp',
     'power_control.cpp',
     'power_interface.cpp',
+    'power_sequencer_monitor.cpp',
     'ucd90320_monitor.cpp',
     dependencies: [
         libgpiodcxx,
diff --git a/phosphor-power-sequencer/src/power_control.cpp b/phosphor-power-sequencer/src/power_control.cpp
index 9e8b1b4..aa6a8a3 100644
--- a/phosphor-power-sequencer/src/power_control.cpp
+++ b/phosphor-power-sequencer/src/power_control.cpp
@@ -20,8 +20,6 @@
 #include "ucd90320_monitor.hpp"
 
 #include <fmt/format.h>
-#include <sys/types.h>
-#include <unistd.h>
 
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/elog.hpp>
@@ -44,12 +42,13 @@
 PowerControl::PowerControl(sdbusplus::bus::bus& bus,
                            const sdeventplus::Event& event) :
     PowerObject{bus, POWER_OBJ_PATH, true},
-    bus{bus}, match{bus,
-                    sdbusplus::bus::match::rules::interfacesAdded() +
-                        sdbusplus::bus::match::rules::sender(
-                            "xyz.openbmc_project.EntityManager"),
-                    std::bind(&PowerControl::interfacesAddedHandler, this,
-                              std::placeholders::_1)},
+    bus{bus}, device{std::make_unique<PowerSequencerMonitor>(bus)},
+    match{bus,
+          sdbusplus::bus::match::rules::interfacesAdded() +
+              sdbusplus::bus::match::rules::sender(
+                  "xyz.openbmc_project.EntityManager"),
+          std::bind(&PowerControl::interfacesAddedHandler, this,
+                    std::placeholders::_1)},
     timer{event, std::bind(&PowerControl::pollPgood, this), pollInterval}
 {
     // Obtain dbus service name
@@ -157,32 +156,18 @@
             log<level::ERR>("ERROR PowerControl: Pgood poll timeout");
             inStateTransition = false;
 
-            try
+            if (state)
             {
-                auto method = bus.new_method_call(
-                    "xyz.openbmc_project.Logging",
-                    "/xyz/openbmc_project/logging",
-                    "xyz.openbmc_project.Logging.Create", "Create");
-
-                std::map<std::string, std::string> additionalData;
-                // Add PID to AdditionalData
-                additionalData.emplace("_PID", std::to_string(getpid()));
-
-                method.append(
-                    state ? "xyz.openbmc_project.Power.Error.PowerOnTimeout"
-                          : "xyz.openbmc_project.Power.Error.PowerOffTimeout",
-                    sdbusplus::xyz::openbmc_project::Logging::server::Entry::
-                        Level::Critical,
-                    additionalData);
-                bus.call_noreply(method);
+                // Time out powering on
+                device->onFailure(true, powerSupplyError);
             }
-            catch (const std::exception& e)
+            else
             {
-                log<level::ERR>(
-                    fmt::format(
-                        "Unable to log timeout error, state: {}, error {}",
-                        state, e.what())
-                        .c_str());
+                // Time out powering off
+                std::map<std::string, std::string> additionalData{};
+                device->logError(
+                    "xyz.openbmc_project.Power.Error.PowerOffTimeout",
+                    additionalData);
             }
 
             return;
@@ -212,6 +197,7 @@
     else if (!inStateTransition && (pgoodState == 0))
     {
         // Not in power off state, not changing state, and power good is off
+        device->onFailure(false, powerSupplyError);
         // Power good has failed, call for chassis hard power off
         log<level::ERR>("Chassis pgood failure");
 
diff --git a/phosphor-power-sequencer/src/power_control.hpp b/phosphor-power-sequencer/src/power_control.hpp
index 1bac68c..5bd5e47 100644
--- a/phosphor-power-sequencer/src/power_control.hpp
+++ b/phosphor-power-sequencer/src/power_control.hpp
@@ -75,8 +75,7 @@
     /**
      * The power sequencer device to monitor.
      */
-    std::unique_ptr<PowerSequencerMonitor> device{
-        std::make_unique<PowerSequencerMonitor>()};
+    std::unique_ptr<PowerSequencerMonitor> device;
 
     /**
      * Indicates if a specific power sequencer device has already been found.
diff --git a/phosphor-power-sequencer/src/power_sequencer_monitor.cpp b/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
new file mode 100644
index 0000000..d56c38d
--- /dev/null
+++ b/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
@@ -0,0 +1,86 @@
+/**
+ * Copyright © 2021 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "power_sequencer_monitor.hpp"
+
+#include <fmt/format.h>
+
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include <exception>
+#include <map>
+
+namespace phosphor::power::sequencer
+{
+
+using namespace phosphor::logging;
+
+PowerSequencerMonitor::PowerSequencerMonitor(sdbusplus::bus::bus& bus) :
+    bus(bus)
+{}
+
+void PowerSequencerMonitor::logError(
+    const std::string& message,
+    std::map<std::string, std::string>& additionalData)
+{
+    try
+    {
+        auto method = bus.new_method_call(
+            "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
+            "xyz.openbmc_project.Logging.Create", "Create");
+
+        // Add PID to AdditionalData
+        additionalData.emplace("_PID", std::to_string(getpid()));
+
+        method.append(message,
+                      sdbusplus::xyz::openbmc_project::Logging::server::Entry::
+                          Level::Critical,
+                      additionalData);
+        bus.call_noreply(method);
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(
+            fmt::format("Unable to log error, message: {}, error {}", message,
+                        e.what())
+                .c_str());
+    }
+}
+
+void PowerSequencerMonitor::onFailure(bool timeout,
+                                      const std::string& powerSupplyError)
+{
+    std::map<std::string, std::string> additionalData{};
+    if (!powerSupplyError.empty())
+    {
+        // Default to power supply error
+        logError(powerSupplyError, additionalData);
+    }
+    else if (timeout)
+    {
+        // Default to timeout error
+        logError("xyz.openbmc_project.Power.Error.PowerOnTimeout",
+                 additionalData);
+    }
+    else
+    {
+        // Default to generic pgood error
+        logError("xyz.openbmc_project.Power.Error.Shutdown", additionalData);
+    }
+}
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/power_sequencer_monitor.hpp b/phosphor-power-sequencer/src/power_sequencer_monitor.hpp
index e714d08..944e7ae 100644
--- a/phosphor-power-sequencer/src/power_sequencer_monitor.hpp
+++ b/phosphor-power-sequencer/src/power_sequencer_monitor.hpp
@@ -1,5 +1,10 @@
 #pragma once
 
+#include <sdbusplus/bus.hpp>
+
+#include <map>
+#include <string>
+
 namespace phosphor::power::sequencer
 {
 
@@ -10,12 +15,42 @@
 class PowerSequencerMonitor
 {
   public:
-    PowerSequencerMonitor() = default;
+    PowerSequencerMonitor() = delete;
     PowerSequencerMonitor(const PowerSequencerMonitor&) = delete;
     PowerSequencerMonitor& operator=(const PowerSequencerMonitor&) = delete;
     PowerSequencerMonitor(PowerSequencerMonitor&&) = delete;
     PowerSequencerMonitor& operator=(PowerSequencerMonitor&&) = delete;
     virtual ~PowerSequencerMonitor() = default;
+
+    /**
+     * Create a base device object for power sequence monitoring.
+     * @param[in] bus D-Bus bus object
+     */
+    explicit PowerSequencerMonitor(sdbusplus::bus::bus& bus);
+
+    /**
+     * Logs an error using the D-Bus Create method.
+     * @param[in] message Message property of the error log entry
+     * @param[in] additionalData AdditionalData property of the error log entry
+     */
+    void logError(const std::string& message,
+                  std::map<std::string, std::string>& additionalData);
+
+    /**
+     * Analyzes the device for errors when the device is
+     * known to be in an error state.  A log will be created.
+     * @param[in] timeout if the failure state was determined by timing out
+     * @param[in] powerSupplyError The power supply error to log. A default
+     * std:string, i.e. empty string ("") is passed when there is no power
+     * supply error to log.
+     */
+    virtual void onFailure(bool timeout, const std::string& powerSupplyError);
+
+  protected:
+    /**
+     * The D-Bus bus object
+     */
+    sdbusplus::bus::bus& bus;
 };
 
 } // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.cpp b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
index af04ff0..d7ef7bb 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.cpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
@@ -42,13 +42,13 @@
 
 UCD90320Monitor::UCD90320Monitor(sdbusplus::bus::bus& bus, std::uint8_t i2cBus,
                                  std::uint16_t i2cAddress) :
-    PowerSequencerMonitor(),
-    bus{bus}, match{bus,
-                    sdbusplus::bus::match::rules::interfacesAdded() +
-                        sdbusplus::bus::match::rules::sender(
-                            "xyz.openbmc_project.EntityManager"),
-                    std::bind(&UCD90320Monitor::interfacesAddedHandler, this,
-                              std::placeholders::_1)},
+    PowerSequencerMonitor(bus),
+    match{bus,
+          sdbusplus::bus::match::rules::interfacesAdded() +
+              sdbusplus::bus::match::rules::sender(
+                  "xyz.openbmc_project.EntityManager"),
+          std::bind(&UCD90320Monitor::interfacesAddedHandler, this,
+                    std::placeholders::_1)},
     pmbusInterface{
         fmt::format("/sys/bus/i2c/devices/{}-{:04x}", i2cBus, i2cAddress)
             .c_str(),
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.hpp b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
index 2453251..97958ce 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.hpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
@@ -56,11 +56,6 @@
     gpiod::line_bulk lines;
 
     /**
-     * The D-Bus bus object
-     */
-    sdbusplus::bus::bus& bus;
-
-    /**
      * The match to Entity Manager interfaces added.
      */
     sdbusplus::bus::match_t match;