Add support for tracking system power state

Change-Id: Ie0f27f696082ff3ee60c955992f3c2e55f4b5e57
Signed-off-by: Brandon Wyman <bjwyman@gmail.com>
diff --git a/power-supply/main.cpp b/power-supply/main.cpp
index af6bb9a..33729d8 100644
--- a/power-supply/main.cpp
+++ b/power-supply/main.cpp
@@ -56,16 +56,6 @@
         return -4;
     }
 
-    auto bus = sdbusplus::bus::new_default();
-
-    auto objname = "power_supply" + instnum;
-    auto instance = std::stoul(instnum);
-    auto psuDevice = std::make_unique<psu::PowerSupply>(objname,
-                                                        std::move(instance),
-                                                        std::move(objpath),
-                                                        std::move(invpath),
-                                                        bus);
-
     sd_event* events = nullptr;
 
     auto r = sd_event_default(&events);
@@ -76,17 +66,24 @@
         return -5;
     }
 
+    auto bus = sdbusplus::bus::new_default();
     witherspoon::power::event::Event eventPtr{events};
 
     //Attach the event object to the bus object so we can
     //handle both sd_events (for the timers) and dbus signals.
     bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL);
 
-    //Attach the event object to the bus object so we can
-    //handle both sd_events (for the timers) and dbus signals.
-    bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL);
-
-    // TODO: Get power state on startup.
+    auto objname = "power_supply" + instnum;
+    auto instance = std::stoul(instnum);
+    // The Witherspoon power supply can delay DC_GOOD active for 1 second.
+    std::chrono::seconds powerOnDelay(1);
+    auto psuDevice = std::make_unique<psu::PowerSupply>(objname,
+                                                        std::move(instance),
+                                                        std::move(objpath),
+                                                        std::move(invpath),
+                                                        bus,
+                                                        eventPtr,
+                                                        powerOnDelay);
 
     auto pollInterval = std::chrono::milliseconds(1000);
     DeviceMonitor mainloop(std::move(psuDevice), eventPtr, pollInterval);
diff --git a/power-supply/power_supply.cpp b/power-supply/power_supply.cpp
index 38eddf6..1510753 100644
--- a/power-supply/power_supply.cpp
+++ b/power-supply/power_supply.cpp
@@ -39,12 +39,21 @@
 constexpr auto INVENTORY_OBJ_PATH = "/xyz/openbmc_project/inventory";
 constexpr auto INVENTORY_INTERFACE = "xyz.openbmc_project.Inventory.Item";
 constexpr auto PRESENT_PROP = "Present";
+constexpr auto POWER_OBJ_PATH = "/org/openbmc/control/power0";
+constexpr auto POWER_INTERFACE = "org.openbmc.control.Power";
 
 PowerSupply::PowerSupply(const std::string& name, size_t inst,
-                         const std::string& objpath, const std::string& invpath,
-                         sdbusplus::bus::bus& bus)
-    : Device(name, inst), monitorPath(objpath), inventoryPath(invpath),
-      bus(bus), pmbusIntf(objpath)
+                         const std::string& objpath,
+                         const std::string& invpath,
+                         sdbusplus::bus::bus& bus,
+                         event::Event& e,
+                         std::chrono::seconds& t)
+    : Device(name, inst), monitorPath(objpath), pmbusIntf(objpath),
+      inventoryPath(invpath), bus(bus), event(e), powerOnInterval(t),
+      powerOnTimer(e, [this]()
+                   {
+                       this->powerOn = true;
+                   })
 {
     using namespace sdbusplus::bus;
     auto present_obj_path = INVENTORY_OBJ_PATH + inventoryPath;
@@ -53,13 +62,26 @@
                                                      present_obj_path,
                                                      INVENTORY_INTERFACE),
                                              [this](auto& msg)
-    {
-        this->inventoryChanged(msg);
-    });
-
+                                             {
+                                                 this->inventoryChanged(msg);
+                                             });
+    // Get initial presence state.
     updatePresence();
+
+    // Subscribe to power state changes
+    powerOnMatch = std::make_unique<match_t>(bus,
+                                             match::rules::propertiesChanged(
+                                                     POWER_OBJ_PATH,
+                                                     POWER_INTERFACE),
+                                             [this](auto& msg)
+                                             {
+                                                 this->powerStateChanged(msg);
+                                             });
+    // Get initial power state.
+    updatePowerState();
 }
 
+
 void PowerSupply::analyze()
 {
     using namespace witherspoon::pmbus;
@@ -196,6 +218,74 @@
 
 }
 
+void PowerSupply::powerStateChanged(sdbusplus::message::message& msg)
+{
+    int32_t state = 0;
+    std::string msgSensor;
+    std::map<std::string, sdbusplus::message::variant<int32_t, 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 = sdbusplus::message::variant_ns::get<int32_t>(valPropMap->second);
+
+        // Power is on when state=1. Set the fault logged variables to false
+        // and start the power on timer when the state changes to 1.
+        if (state)
+        {
+            readFailLogged = false;
+            vinUVFault = false;
+            inputFault = false;
+            powerOnTimer.start(powerOnInterval, Timer::TimerType::oneshot);
+        }
+        else
+        {
+            powerOnTimer.stop();
+            powerOn = false;
+        }
+    }
+
+}
+
+void PowerSupply::updatePowerState()
+{
+    // When state = 1, system is powered on
+    int32_t state = 0;
+
+    try
+    {
+        auto service = util::getService(POWER_OBJ_PATH,
+                                        POWER_INTERFACE,
+                                        bus);
+
+        // Use getProperty utility function to get power state.
+        util::getProperty<int32_t>(POWER_INTERFACE,
+                                   "state",
+                                   POWER_OBJ_PATH,
+                                   service,
+                                   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;
+    }
+
+}
+
 void PowerSupply::clearFaults()
 {
     //TODO - Clear faults at pre-poweron. openbmc/openbmc#1736
diff --git a/power-supply/power_supply.hpp b/power-supply/power_supply.hpp
index 496218f..317c20b 100644
--- a/power-supply/power_supply.hpp
+++ b/power-supply/power_supply.hpp
@@ -2,6 +2,7 @@
 #include <sdbusplus/bus/match.hpp>
 #include "device.hpp"
 #include "pmbus.hpp"
+#include "timer.hpp"
 
 namespace witherspoon
 {
@@ -34,11 +35,15 @@
          * @param[in] objpath - the path to monitor
          * @param[in] invpath - the inventory path to use
          * @param[in] bus - D-Bus bus object
+         * @param[in] e - event object
+         * @param[in] t - time to allow power supply to assert PG#
          */
         PowerSupply(const std::string& name, size_t inst,
                     const std::string& objpath,
                     const std::string& invpath,
-                    sdbusplus::bus::bus& bus);
+                    sdbusplus::bus::bus& bus,
+                    event::Event& e,
+                    std::chrono::seconds& t);
 
         /**
          * Power supply specific function to analyze for faults/errors.
@@ -66,14 +71,6 @@
         std::string monitorPath;
 
         /**
-         * The D-Bus path to use for this power supply's inventory status.
-         */
-        std::string inventoryPath;
-
-        /** @brief Connection for sdbusplus bus */
-        sdbusplus::bus::bus& bus;
-
-        /**
          * @brief Pointer to the PMBus interface
          *
          * Used to read out of or write to the /sysfs tree(s) containing files
@@ -83,13 +80,45 @@
         witherspoon::pmbus::PMBus pmbusIntf;
 
         /**
-         * @brief True if the power supply is present.
+         * @brief D-Bus path to use for this power supply's inventory status.
          */
+        std::string inventoryPath;
+
+        /** @brief Connection for sdbusplus bus */
+        sdbusplus::bus::bus& bus;
+
+        /** @brief True if the power supply is present. */
         bool present = false;
 
-        /** @brief Used to subscribe to dbus pcap propety changes **/
+        /** @brief Used to subscribe to D-Bus property changes to Present **/
         std::unique_ptr<sdbusplus::bus::match_t> presentMatch;
 
+        /** @brief True if the power is on. */
+        bool powerOn = false;
+
+        /** @brief The sd_event structure used by the power on timer. */
+        event::Event& event;
+
+        /**
+         * @brief Interval to setting powerOn to true.
+         *
+         * The amount of time to wait from power state on to setting the
+         * internal powerOn state to true. The amount of time the power supply
+         * is allowed to delay setting DGood/PG#.
+         */
+        std::chrono::seconds powerOnInterval;
+
+        /**
+         * @brief Timer used to delay setting the internal powerOn state.
+         *
+         * The timer used to do the callback after the power state has been on
+         * long enough.
+         */
+        Timer powerOnTimer;
+
+        /** @brief Used to subscribe to D-Bus power on state changes **/
+        std::unique_ptr<sdbusplus::bus::match_t> powerOnMatch;
+
         /**
          * @brief Has a PMBus read failure already been logged?
          */
@@ -128,6 +157,22 @@
          * objects present member variable to reflect current status.
          */
         void updatePresence();
+
+        /**
+         * @brief Updates the poweredOn status by querying D-Bus
+         *
+         * The D-Bus property for the sytem 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 stat property for the system.
+         *
+         * @param[in] msg - Data associated with the power state signal
+         */
+        void powerStateChanged(sdbusplus::message::message& msg);
+
 };
 
 }