discover_system_state: apply power restore policy as early as BMC ready

Add a waitBmcReady() to monitor BMC state every second, this function
returns true if BMC enters ready state early or returns flase if
timeout reached.

This allows phosphor-diecover-system-state service can perform power
restore policy as early as BMC enters ready state.

Signed-off-by: Potin Lai <potin.lai@quantatw.com>
Change-Id: I3f1ef2957c9c8218d094b0e7643dc2eec03535b4
diff --git a/README.md b/README.md
index 041843c..7779d3f 100644
--- a/README.md
+++ b/README.md
@@ -96,8 +96,10 @@
 reset. If the chassis or host is on/running then this service will not run. If
 they are off then the `RestorePolicy` will be read and executed by PSM code.
 
-The `PowerRestoreDelay` property within the interface defines how long the
-service will wait before issuing the power on request.
+The `PowerRestoreDelay` property within the interface defines a maximum time the
+service will wait for the BMC to enter the `Ready` state before issuing the
+power on request, this allows host to be powered on as early as the BMC is
+ready.
 
 ## Only Allow System Boot When BMC Ready
 
diff --git a/discover_system_state.cpp b/discover_system_state.cpp
index 789e876..0a9c728 100644
--- a/discover_system_state.cpp
+++ b/discover_system_state.cpp
@@ -166,7 +166,7 @@
             info(
                 "power_policy=ALWAYS_POWER_ON, powering host on ({DELAY}s delay)",
                 "DELAY", powerRestoreDelaySec.count());
-            std::this_thread::sleep_for(powerRestoreDelayUsec);
+            utils::waitBmcReady(bus, powerRestoreDelaySec);
             phosphor::state::manager::utils::setProperty(
                 bus, hostPath, HOST_BUSNAME, "RestartCause",
                 convertForMessage(
@@ -192,7 +192,7 @@
             info(
                 "power_policy=ALWAYS_POWER_OFF, set requested state to off ({DELAY}s delay)",
                 "DELAY", powerRestoreDelaySec.count());
-            std::this_thread::sleep_for(powerRestoreDelayUsec);
+            utils::waitBmcReady(bus, powerRestoreDelaySec);
             // Read last requested state and re-request it to execute it
             auto hostReqState = phosphor::state::manager::utils::getProperty(
                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
@@ -209,7 +209,7 @@
         {
             info("power_policy=RESTORE, restoring last state ({DELAY}s delay)",
                  "DELAY", powerRestoreDelaySec.count());
-            std::this_thread::sleep_for(powerRestoreDelayUsec);
+            utils::waitBmcReady(bus, powerRestoreDelaySec);
             // Read last requested state and re-request it to execute it
             auto hostReqState = phosphor::state::manager::utils::getProperty(
                 bus, hostPath, HOST_BUSNAME, "RequestedHostTransition");
diff --git a/utils.cpp b/utils.cpp
index cad8ffd..95760cb 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -244,6 +244,20 @@
     return true;
 }
 
+bool waitBmcReady(sdbusplus::bus_t& bus, std::chrono::seconds timeout)
+{
+    while (timeout.count() != 0)
+    {
+        timeout--;
+        if (isBmcReady(bus))
+        {
+            return true;
+        }
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+    }
+    return false;
+}
+
 } // namespace utils
 } // namespace manager
 } // namespace state
diff --git a/utils.hpp b/utils.hpp
index fa65c31..49f62bf 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -95,6 +95,13 @@
  */
 bool isBmcReady(sdbusplus::bus_t& bus);
 
+/** @brief Wait BMC to enter ready state or timeout reached.
+ *
+ * @param[in] bus          - The Dbus bus object
+ * @param[in] timeout      - Timeout in second
+ */
+bool waitBmcReady(sdbusplus::bus_t& bus, std::chrono::seconds timeout);
+
 } // namespace utils
 } // namespace manager
 } // namespace state