psu: Introduce the PowerSupply class

The Power Supply Manager (PSUManager) class will need a list of power
supplies to work with. Create these via the PowerSupply class.

Update Power Supply Manager to call the initialization function, and
update the power state.

Update clearFaults() to go through the list of power supplies and call
their individual clearFaults() functions.

Update the power supply manager analyze() function to go through the
list of power supplies and call their analyze() function.

Update the power supply manager updateInventory() function to call the
updateInventory() function in each power supply in the list.

Update the meson.build file to include the header files in the parent
directory, and link with the library containing the utility functions
the binary will need to use.

Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
Change-Id: I743180d47f1b25d34c7e7001b64a6197905b86ff
diff --git a/phosphor-power-supply/main.cpp b/phosphor-power-supply/main.cpp
index d19a254..2622c6a 100644
--- a/phosphor-power-supply/main.cpp
+++ b/phosphor-power-supply/main.cpp
@@ -22,39 +22,55 @@
 
 #include <filesystem>
 
+using namespace phosphor::power::manager;
+
 int main(int argc, char* argv[])
 {
-    using namespace phosphor::logging;
-
-    CLI::App app{"OpenBMC Power Supply Unit Monitor"};
-
-    std::string configfile;
-    app.add_option("-c,--config", configfile, "JSON configuration file path")
-        ->check(CLI::ExistingFile);
-
-    // Read the arguments.
-    CLI11_PARSE(app, argc, argv);
-    if (configfile.empty())
+    try
     {
-        configfile = "/etc/phosphor-psu-monitor/psu_config.json";
-    }
+        using namespace phosphor::logging;
 
-    if (!std::filesystem::exists(configfile))
+        CLI::App app{"OpenBMC Power Supply Unit Monitor"};
+
+        std::string configfile;
+        app.add_option("-c,--config", configfile,
+                       "JSON configuration file path");
+
+        // Read the arguments.
+        CLI11_PARSE(app, argc, argv);
+        if (configfile.empty())
+        {
+            configfile = "/etc/phosphor-psu-monitor/psu_config.json";
+        }
+
+        if (!std::filesystem::exists(configfile))
+        {
+            log<level::ERR>("Configuration file does not exist",
+                            entry("FILENAME=%s", configfile.c_str()));
+            return -1;
+        }
+
+        auto bus = sdbusplus::bus::new_default();
+        auto event = sdeventplus::Event::get_default();
+
+        // Attach the event object to the bus object so we can
+        // handle both sd_events (for the timers) and dbus signals.
+        bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+
+        // TODO: Should get polling interval from JSON file.
+        auto pollInterval = std::chrono::milliseconds(1000);
+        PSUManager manager(bus, event, pollInterval);
+
+        return manager.run();
+    }
+    catch (const std::exception& e)
     {
-        log<level::ERR>("Configuration file does not exist",
-                        entry("FILENAME=%s", configfile.c_str()));
-        return -1;
+        log<level::ERR>(e.what());
+        return -2;
     }
-
-    auto bus = sdbusplus::bus::new_default();
-    auto event = sdeventplus::Event::get_default();
-
-    // Attach the event object to the bus object so we can
-    // handle both sd_events (for the timers) and dbus signals.
-    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
-
-    // TODO: Should get polling interval from JSON file.
-    auto pollInterval = std::chrono::milliseconds(1000);
-
-    return phosphor::power::manager::PSUManager(bus, event, pollInterval).run();
+    catch (...)
+    {
+        log<level::ERR>("Caught unexpected exception type");
+        return -3;
+    }
 }
diff --git a/phosphor-power-supply/meson.build b/phosphor-power-supply/meson.build
index c1fcb1f..b6fb80f 100644
--- a/phosphor-power-supply/meson.build
+++ b/phosphor-power-supply/meson.build
@@ -2,8 +2,14 @@
 executable(
     'phosphor-psu-monitor',
     'main.cpp',
+    'psu_manager.cpp',
     dependencies: [
         sdbusplus,
         sdeventplus,
     ],
-    install: true)
+    include_directories: '..',
+    install: true,
+    link_with: [
+        libpower,
+    ]
+)
diff --git a/phosphor-power-supply/power_supply.hpp b/phosphor-power-supply/power_supply.hpp
new file mode 100644
index 0000000..3bfa77a
--- /dev/null
+++ b/phosphor-power-supply/power_supply.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+namespace phosphor::power::psu
+{
+/**
+ * @class PowerSupply
+ * Represents a PMBus power supply device.
+ */
+class PowerSupply
+{
+  public:
+    PowerSupply();
+    PowerSupply(const PowerSupply&) = delete;
+    PowerSupply(PowerSupply&&) = delete;
+    PowerSupply& operator=(const PowerSupply&) = delete;
+    PowerSupply& operator=(PowerSupply&&) = delete;
+    ~PowerSupply() = default;
+
+    /**
+     * Power supply specific function to analyze for faults/errors.
+     *
+     * Various PMBus status bits will be checked for fault conditions.
+     * If a certain fault bits are on, the appropriate error will be
+     * committed.
+     */
+    void analyze()
+    {
+    }
+
+    /**
+     * Write PMBus CLEAR_FAULTS
+     *
+     * This function will be called in various situations in order to clear
+     * any fault status bits that may have been set, in order to start over
+     * with a clean state. Presence changes and power state changes will
+     * want to clear any faults logged.
+     */
+    void clearFaults()
+    {
+    }
+
+    /**
+     * @brief Adds properties to the inventory.
+     *
+     * Reads the values from the device and writes them to the
+     * associated power supply D-Bus inventory object.
+     *
+     * This needs to be done on startup, and each time the presence
+     * state changes.
+     *
+     * Properties added:
+     * - Serial Number
+     * - Part Number
+     * - CCIN (Customer Card Identification Number) - added as the Model
+     * - Firmware version
+     */
+    void updateInventory()
+    {
+    }
+
+  private:
+    /** @brief True if a fault has already been found and not cleared */
+    bool faultFound = false;
+};
+
+} // namespace phosphor::power::psu
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
new file mode 100644
index 0000000..54b20d8
--- /dev/null
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -0,0 +1,40 @@
+#include "psu_manager.hpp"
+
+#include "utility.hpp"
+
+namespace phosphor
+{
+namespace power
+{
+namespace manager
+{
+
+void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
+{
+    int32_t state = 0;
+    std::string msgSensor;
+    std::map<std::string, sdbusplus::message::variant<int32_t>> msgData;
+    msg.read(msgSensor, msgData);
+
+    // Check if it was the Present property that changed.
+    auto valPropMap = msgData.find("state");
+    if (valPropMap != msgData.end())
+    {
+        state = std::get<int32_t>(valPropMap->second);
+
+        // Power is on when state=1. Clear faults.
+        if (state)
+        {
+            powerOn = true;
+            clearFaults();
+        }
+        else
+        {
+            powerOn = false;
+        }
+    }
+}
+
+} // namespace manager
+} // namespace power
+} // namespace phosphor
diff --git a/phosphor-power-supply/psu_manager.hpp b/phosphor-power-supply/psu_manager.hpp
index 7e9daf6..1052705 100644
--- a/phosphor-power-supply/psu_manager.hpp
+++ b/phosphor-power-supply/psu_manager.hpp
@@ -1,9 +1,17 @@
 #pragma once
 
+#include "power_supply.hpp"
+#include "types.hpp"
+#include "utility.hpp"
+
+#include <phosphor-logging/log.hpp>
 #include <sdbusplus/bus/match.hpp>
 #include <sdeventplus/event.hpp>
 #include <sdeventplus/utility/timer.hpp>
 
+using namespace phosphor::power::psu;
+using namespace phosphor::logging;
+
 namespace phosphor
 {
 namespace power
@@ -39,6 +47,15 @@
         bus(bus),
         timer(e, std::bind(&PSUManager::analyze, this), i)
     {
+        // Subscribe to power state changes
+        powerService = util::getService(POWER_OBJ_PATH, POWER_IFACE, bus);
+        powerOnMatch = std::make_unique<sdbusplus::bus::match_t>(
+            bus,
+            sdbusplus::bus::match::rules::propertiesChanged(POWER_OBJ_PATH,
+                                                            POWER_IFACE),
+            [this](auto& msg) { this->powerStateChanged(msg); });
+
+        initialize();
     }
 
     /**
@@ -48,6 +65,32 @@
      */
     void initialize()
     {
+        // When state = 1, system is powered on
+        int32_t state = 0;
+
+        try
+        {
+            // Use getProperty utility function to get power state.
+            util::getProperty<int32_t>(POWER_IFACE, "state", POWER_OBJ_PATH,
+                                       powerService, bus, state);
+
+            if (state)
+            {
+                powerOn = true;
+            }
+            else
+            {
+                powerOn = false;
+            }
+        }
+        catch (std::exception& e)
+        {
+            log<level::INFO>("Failed to get power state. Assuming it is off.");
+            powerOn = false;
+        }
+
+        clearFaults();
+        updateInventory();
     }
 
     /**
@@ -66,6 +109,10 @@
      */
     void clearFaults()
     {
+        for (auto& psu : psus)
+        {
+            psu.clearFaults();
+        }
     }
 
   private:
@@ -84,23 +131,22 @@
      */
     void analyze()
     {
+        for (auto& psu : psus)
+        {
+            psu.analyze();
+        }
     }
 
     /** @brief True if the power is on. */
     bool powerOn = false;
 
+    /** @brief Used as part of subscribing to power on state changes*/
+    std::string powerService;
+
     /** @brief Used to subscribe to D-Bus power on state changes */
     std::unique_ptr<sdbusplus::bus::match_t> powerOnMatch;
 
     /**
-     * @brief Updates the poweredOn status by querying D-Bus
-     *
-     * The D-Bus property for the system power state will be read to determine
-     * if the system is powered on or not.
-     */
-    void updatePowerState();
-
-    /**
      * @brief Callback for power state property changes
      *
      * Process changes to the powered on state property for the system.
@@ -118,7 +164,18 @@
      * This needs to be done on startup, and each time the presence state
      * changes.
      */
-    void updateInventory();
+    void updateInventory()
+    {
+        for (auto& psu : psus)
+        {
+            psu.updateInventory();
+        }
+    }
+
+    /**
+     * @brief The vector for power supplies.
+     */
+    std::vector<PowerSupply> psus;
 };
 
 } // namespace manager