regulators: Enable sensor monitoring

Enable sensor monitoring in the phosphor-regulators application.

Previous commits implemented the majority of the sensor monitoring
support.  This commit contains the last set of changes needed to enable
monitoring for the entire application.

Modify the top level Manager class to enable or disable monitoring based
on the D-Bus 'monitor' method.  This method is called by the regsctl
command line tool.  Service files use regsctl to enable monitoring
during power on and disable monitoring during power off.

When monitoring is enabled, set a repeating, one-second timer.  When the
timer expires, execute one sensor monitoring cycle for all voltage
regulator sensors.

When monitoring is disabled, turn off the timer.  Put all the sensors in
an inactive state, indicating they are no longer being updated.  They
will remain in this state until the system is powered on again.

Tested:
* Verified sensor monitoring is enabled when chassis is powered on.
* Verified sensor monitoring is disabled when chassis is powered off.
* Verified voltage regulator sensors are read once per second while
  monitoring is enabled.
* Verified sensors are published on D-Bus when monitoring is enabled.
* Verified sensors remain on D-Bus but are put in an inactive state when
  monitoring is disabled.
* Verified sensors are not read when monitoring is disabled.
* Tested where D-Bus queries to obtain current power state fail with a
  D-Bus exception.
* Verified sensor monitoring is enabled if BMC is reset while chassis is
  powered on.
* Verified sensor monitoring is enabled if regulators application is
  stopped and re-started while chassis is powered on.
* For complete test plan, see
  https://gist.github.com/smccarney/0afa4a50afcf9f0d47e1a21ebe33dbfc

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Ic85d4baa0a53e57bd2a494611d70a76ae7b135f7
diff --git a/phosphor-regulators/src/manager.cpp b/phosphor-regulators/src/manager.cpp
index 8385b28..e39273f 100644
--- a/phosphor-regulators/src/manager.cpp
+++ b/phosphor-regulators/src/manager.cpp
@@ -23,6 +23,7 @@
 #include "utility.hpp"
 
 #include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/State/Chassis/server.hpp>
 
 #include <algorithm>
 #include <chrono>
@@ -44,8 +45,14 @@
 constexpr auto compatibleIntf =
     "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
 constexpr auto compatibleNamesProp = "Names";
+constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0";
+constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis";
+constexpr auto chassisStateProp = "CurrentPowerState";
 constexpr std::chrono::minutes maxTimeToWaitForCompatTypes{5};
 
+using PowerState =
+    sdbusplus::xyz::openbmc_project::State::server::Chassis::PowerState;
+
 /**
  * Default configuration file name.  This is used when the system does not
  * implement the D-Bus compatible interface.
@@ -66,7 +73,7 @@
 
 Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) :
     ManagerObject{bus, managerObjPath, true}, bus{bus}, eventLoop{event},
-    services{bus}
+    services{bus}, timer{event, std::bind(&Manager::timerExpired, this)}
 {
     // Subscribe to D-Bus interfacesAdded signal from Entity Manager.  This
     // notifies us if the compatible interface becomes available later.
@@ -88,8 +95,14 @@
     // Try to find and load the JSON configuration file
     loadConfigFile();
 
-    // Obtain dbus service name
+    // Obtain D-Bus service name
     bus.request_name(busName);
+
+    // If system is already powered on, enable monitoring
+    if (isSystemPoweredOn())
+    {
+        monitor(true);
+    }
 }
 
 void Manager::configure()
@@ -173,21 +186,32 @@
 
 void Manager::monitor(bool enable)
 {
-    if (enable)
+    // Check whether already in the requested monitoring state
+    if (enable == isMonitoringEnabled)
     {
-        /* Temporarily comment out until monitoring is supported.
-            Timer timer(eventLoop, std::bind(&Manager::timerExpired, this));
-            // Set timer as a repeating 1sec timer
-            timer.restart(std::chrono::milliseconds(1000));
-            timers.emplace_back(std::move(timer));
-        */
+        return;
+    }
+
+    isMonitoringEnabled = enable;
+    if (isMonitoringEnabled)
+    {
+        services.getJournal().logDebug("Monitoring enabled");
+
+        // Restart timer to have a repeating 1 second interval
+        timer.restart(std::chrono::seconds(1));
+
+        // Enable sensors service; put all sensors in an active state
+        services.getSensors().enable();
     }
     else
     {
-        /* Temporarily comment out until monitoring is supported.
-            // Delete all timers to disable monitoring
-            timers.clear();
-        */
+        services.getJournal().logDebug("Monitoring disabled");
+
+        // Disable timer
+        timer.setEnabled(false);
+
+        // Disable sensors service; put all sensors in an inactive state
+        services.getSensors().disable();
 
         // Verify config file has been loaded and System object is valid
         if (isConfigFileLoaded())
@@ -210,8 +234,18 @@
 
 void Manager::timerExpired()
 {
-    // TODO Analyze, refresh sensor status, and
-    // collect/update telemetry for each regulator
+    // Notify sensors service that a sensor monitoring cycle is starting
+    services.getSensors().startCycle();
+
+    // Verify config file has been loaded and System object is valid
+    if (isConfigFileLoaded())
+    {
+        // Monitor sensors for the voltage rails in the system
+        system->monitorSensors(services);
+    }
+
+    // Notify sensors service that current sensor monitoring cycle has ended
+    services.getSensors().endCycle();
 }
 
 void Manager::clearHardwareData()
@@ -314,6 +348,36 @@
     return fs::path{};
 }
 
+bool Manager::isSystemPoweredOn()
+{
+    bool isOn{false};
+
+    try
+    {
+        // Get D-Bus property that contains the current power state for
+        // chassis0, which represents the entire system (all chassis)
+        using namespace phosphor::power::util;
+        auto service = getService(chassisStatePath, chassisStateIntf, bus);
+        if (!service.empty())
+        {
+            PowerState currentPowerState;
+            getProperty(chassisStateIntf, chassisStateProp, chassisStatePath,
+                        service, bus, currentPowerState);
+            if (currentPowerState == PowerState::On)
+            {
+                isOn = true;
+            }
+        }
+    }
+    catch (const std::exception& e)
+    {
+        // Current power state might not be available yet.  The regulators
+        // application can start before the power state is published on D-Bus.
+    }
+
+    return isOn;
+}
+
 void Manager::loadConfigFile()
 {
     try
diff --git a/phosphor-regulators/src/manager.hpp b/phosphor-regulators/src/manager.hpp
index e2ec5a7..d9b62a0 100644
--- a/phosphor-regulators/src/manager.hpp
+++ b/phosphor-regulators/src/manager.hpp
@@ -59,7 +59,12 @@
     Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event);
 
     /**
-     * Overridden manager object's configure method
+     * Implements the D-Bus "configure" method.
+     *
+     * Configures all the voltage regulators in the system.
+     *
+     * This method should be called when the system is being powered on.  It
+     * needs to occur before the regulators have been enabled/turned on.
      */
     void configure() override;
 
@@ -71,9 +76,30 @@
     void interfacesAddedHandler(sdbusplus::message::message& msg);
 
     /**
-     * Overridden manager object's monitor method
+     * Implements the D-Bus "monitor" method.
      *
-     * @param enable Enable or disable regulator monitoring
+     * Sets whether regulator monitoring is enabled.
+     *
+     * When monitoring is enabled:
+     *   - regulator sensors will be read and published on D-Bus
+     *   - phase fault detection will be performed
+     *
+     * Regulator monitoring should be enabled when the system is being powered
+     * on.  It needs to occur after the regulators have been configured and
+     * enabled/turned on.
+     *
+     * Regulator monitoring should be disabled when the system is being powered
+     * off.  It needs to occur before the regulators have been disabled/turned
+     * off.
+     *
+     * Regulator monitoring can also be temporarily disabled and then re-enabled
+     * while the system is powered on.  This allows other applications or tools
+     * to temporarily communicate with the regulators for testing or debug.
+     * Monitoring should be disabled for only short periods of time; other
+     * applications, such as fan control, may be dependent on regulator sensors.
+     *
+     * @param enable true if monitoring should be enabled, false if it should be
+     *               disabled
      */
     void monitor(bool enable) override;
 
@@ -118,13 +144,13 @@
     /**
      * Finds the JSON configuration file.
      *
-     * Looks for a configuration file based on the list of compatable system
+     * Looks for a configuration file based on the list of compatible system
      * types.  If no file is found, looks for a file with the default name.
      *
      * Looks for the file in the test directory and standard directory.
      *
      * Throws an exception if an operating system error occurs while checking
-     * for the existance of a file.
+     * for the existence of a file.
      *
      * @return absolute path to config file, or an empty path if none found
      */
@@ -142,6 +168,13 @@
     }
 
     /**
+     * Returns whether the system is currently powered on.
+     *
+     * @return true if system is powered on, false otherwise
+     */
+    bool isSystemPoweredOn();
+
+    /**
      * Loads the JSON configuration file.
      *
      * Looks for the config file using findConfigFile().
@@ -170,7 +203,7 @@
     /**
      * Event to loop on
      */
-    sdeventplus::Event eventLoop;
+    const sdeventplus::Event& eventLoop;
 
     /**
      * System services like error logging and the journal.
@@ -178,16 +211,21 @@
     BMCServices services;
 
     /**
-     * List of event timers
+     * Event timer used to initiate regulator monitoring.
      */
-    std::vector<Timer> timers{};
+    Timer timer;
 
     /**
-     * List of dbus signal matches
+     * List of D-Bus signal matches
      */
     std::vector<std::unique_ptr<sdbusplus::bus::match::match>> signals{};
 
     /**
+     * Indicates whether regulator monitoring is enabled.
+     */
+    bool isMonitoringEnabled{false};
+
+    /**
      * List of compatible system types for the current system.
      *
      * Used to find the JSON configuration file.