psusensor: Add support for the 'PowerState' parameter

When CPU is powered off, some sensor reading values are expected
to go below low thresholds. To prevent unnecessary sensor readings
and redundant threshold event trigger in cases like that add support
for the 'PowerState' JSON configuration parameter similar to other
'dbus-sensors' apps.
Use 'checkThresholdsPowerDelay' function in a threshold check like
it is done in ADCSensor app. This is necessary as PSU data can drop
faster than a change in a power state is noticed.

Tested on the AMD EthanolX CRB with ISL68137:

When the PowerState is set to "On" and the platform is powered off,
no transactions are observed on the corresponding I2C bus.

When the PowerState is set to "Always" monitoring is always enabled
and I2C transactions are always observed regardless the platform
power state.

These commands were used to monitor transactions
on the I2C bus:
$ echo 1 > /sys/kernel/debug/tracing/tracing_on
$ echo 1 > /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
$ cat /sys/kernel/debug/tracing/trace_pipe

Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
Change-Id: Ic7b36e48828adf4eb2f7714965a4a1df4eb5ac3e
diff --git a/src/PSUEvent.cpp b/src/PSUEvent.cpp
index 8c12c5b..23021d9 100644
--- a/src/PSUEvent.cpp
+++ b/src/PSUEvent.cpp
@@ -36,6 +36,7 @@
     sdbusplus::asio::object_server& objectServer,
     std::shared_ptr<sdbusplus::asio::connection>& conn,
     boost::asio::io_service& io, const std::string& psuName,
+    const PowerState& powerState,
     boost::container::flat_map<std::string, std::vector<std::string>>&
         eventPathList,
     boost::container::flat_map<
@@ -70,8 +71,8 @@
         for (const auto& path : pathList.second)
         {
             auto p = std::make_shared<PSUSubEvent>(
-                eventInterface, path, conn, io, eventName, eventName, assert,
-                combineEvent, state, psuName, pollRate);
+                eventInterface, path, conn, io, powerState, eventName,
+                eventName, assert, combineEvent, state, psuName, pollRate);
             p->setupRead();
 
             events[eventPSUName].emplace_back(p);
@@ -93,7 +94,7 @@
             for (const auto& path : pathList.second)
             {
                 auto p = std::make_shared<PSUSubEvent>(
-                    eventInterface, path, conn, io, groupEventName,
+                    eventInterface, path, conn, io, powerState, groupEventName,
                     groupPathList.first, assert, combineEvent, state, psuName,
                     pollRate);
                 p->setupRead();
@@ -141,16 +142,17 @@
 PSUSubEvent::PSUSubEvent(
     std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
     const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
-    boost::asio::io_service& io, const std::string& groupEventName,
-    const std::string& eventName,
+    boost::asio::io_service& io, const PowerState& powerState,
+    const std::string& groupEventName, const std::string& eventName,
     std::shared_ptr<std::set<std::string>> asserts,
     std::shared_ptr<std::set<std::string>> combineEvent,
     std::shared_ptr<bool> state, const std::string& psuName, double pollRate) :
     std::enable_shared_from_this<PSUSubEvent>(),
     eventInterface(std::move(eventInterface)), asserts(std::move(asserts)),
     combineEvent(std::move(combineEvent)), assertState(std::move(state)),
-    errCount(0), path(path), eventName(eventName), waitTimer(io), inputDev(io),
-    psuName(psuName), groupEventName(groupEventName), systemBus(conn)
+    errCount(0), path(path), eventName(eventName), readState(powerState),
+    waitTimer(io), inputDev(io), psuName(psuName),
+    groupEventName(groupEventName), systemBus(conn)
 {
     if (pollRate > 0.0)
     {
@@ -188,12 +190,25 @@
     }
 }
 
+PSUSubEvent::~PSUSubEvent()
+{
+    waitTimer.cancel();
+    inputDev.close();
+}
+
 void PSUSubEvent::setupRead(void)
 {
+    if (!readingStateGood(readState))
+    {
+        // Deassert the event
+        updateValue(0);
+        restartRead();
+        return;
+    }
+
     std::shared_ptr<boost::asio::streambuf> buffer =
         std::make_shared<boost::asio::streambuf>();
     std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
-
     boost::asio::async_read_until(
         inputDev, *buffer, '\n',
         [weakRef, buffer](const boost::system::error_code& ec,
@@ -207,10 +222,21 @@
         });
 }
 
-PSUSubEvent::~PSUSubEvent()
+void PSUSubEvent::restartRead()
 {
-    waitTimer.cancel();
-    inputDev.close();
+    std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
+    waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
+    waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
+        if (ec == boost::asio::error::operation_aborted)
+        {
+            return;
+        }
+        std::shared_ptr<PSUSubEvent> self = weakRef.lock();
+        if (self)
+        {
+            self->setupRead();
+        }
+    });
 }
 
 void PSUSubEvent::handleResponse(const boost::system::error_code& err)
@@ -252,20 +278,7 @@
         errCount++;
     }
     lseek(fd, 0, SEEK_SET);
-    waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
-
-    std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
-    waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
-        std::shared_ptr<PSUSubEvent> self = weakRef.lock();
-        if (ec == boost::asio::error::operation_aborted)
-        {
-            return;
-        }
-        if (self)
-        {
-            self->setupRead();
-        }
-    });
+    restartRead();
 }
 
 // Any of the sub events of one event is asserted, then the event will be
diff --git a/src/PSUSensor.cpp b/src/PSUSensor.cpp
index 81c7b9e..dec02ab 100644
--- a/src/PSUSensor.cpp
+++ b/src/PSUSensor.cpp
@@ -42,15 +42,17 @@
                      boost::asio::io_service& io, const std::string& sensorName,
                      std::vector<thresholds::Threshold>&& thresholdsIn,
                      const std::string& sensorConfiguration,
+                     const PowerState& powerState,
                      const std::string& sensorUnits, unsigned int factor,
                      double max, double min, double offset,
                      const std::string& label, size_t tSize, double pollRate) :
     Sensor(boost::replace_all_copy(sensorName, " ", "_"),
            std::move(thresholdsIn), sensorConfiguration, objectType, false, max,
-           min, conn),
+           min, conn, powerState),
     std::enable_shared_from_this<PSUSensor>(), objServer(objectServer),
     inputDev(io), waitTimer(io), path(path), pathRatedMax(""), pathRatedMin(""),
-    sensorFactor(factor), minMaxReadCounter(0), sensorOffset(offset)
+    sensorFactor(factor), minMaxReadCounter(0), sensorOffset(offset),
+    thresholdTimer(io)
 {
     std::string unitPath = sensor_paths::getPathForUnits(sensorUnits);
     if constexpr (debug)
@@ -136,6 +138,14 @@
 
 void PSUSensor::setupRead(void)
 {
+    if (!readingStateGood())
+    {
+        markAvailable(false);
+        updateValue(std::numeric_limits<double>::quiet_NaN());
+        restartRead();
+        return;
+    }
+
     std::weak_ptr<PSUSensor> weakRef = weak_from_this();
     inputDev.async_wait(boost::asio::posix::descriptor_base::wait_read,
                         [weakRef](const boost::system::error_code& ec) {
@@ -147,6 +157,24 @@
                         });
 }
 
+void PSUSensor::restartRead(void)
+{
+    std::weak_ptr<PSUSensor> weakRef = weak_from_this();
+    waitTimer.expires_from_now(boost::posix_time::milliseconds(sensorPollMs));
+    waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
+        if (ec == boost::asio::error::operation_aborted)
+        {
+            std::cerr << "Failed to reschedule\n";
+            return;
+        }
+        std::shared_ptr<PSUSensor> self = weakRef.lock();
+        if (self)
+        {
+            self->setupRead();
+        }
+    });
+}
+
 void PSUSensor::updateMinMaxValues(void)
 {
     if (auto newVal = readFile(pathRatedMin, sensorFactor))
@@ -204,24 +232,15 @@
     }
 
     lseek(fd, 0, SEEK_SET);
-    waitTimer.expires_from_now(boost::posix_time::milliseconds(sensorPollMs));
-
-    std::weak_ptr<PSUSensor> weakRef = weak_from_this();
-    waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
-        if (ec == boost::asio::error::operation_aborted)
-        {
-            std::cerr << "Failed to reschedule\n";
-            return;
-        }
-        std::shared_ptr<PSUSensor> self = weakRef.lock();
-        if (self)
-        {
-            self->setupRead();
-        }
-    });
+    restartRead();
 }
 
 void PSUSensor::checkThresholds(void)
 {
-    thresholds::checkThresholds(this);
+    if (!readingStateGood())
+    {
+        return;
+    }
+
+    thresholds::checkThresholdsPowerDelay(weak_from_this(), thresholdTimer);
 }
diff --git a/src/PSUSensorMain.cpp b/src/PSUSensorMain.cpp
index 2cb4df8..3b92ae7 100644
--- a/src/PSUSensorMain.cpp
+++ b/src/PSUSensorMain.cpp
@@ -441,6 +441,15 @@
         checkGroupEvent(directory.string(), groupEventMatch,
                         groupEventPathList);
 
+        PowerState readState = PowerState::always;
+        auto findPowerOn = baseConfig->second.find("PowerState");
+        if (findPowerOn != baseConfig->second.end())
+        {
+            std::string powerState =
+                std::visit(VariantToStringVisitor(), findPowerOn->second);
+            setReadState(powerState, readState);
+        }
+
         /* Check if there are more sensors in the same interface */
         int i = 1;
         std::vector<std::string> psuNames;
@@ -853,9 +862,10 @@
             sensors[sensorName] = std::make_shared<PSUSensor>(
                 sensorPathStr, sensorType, objectServer, dbusConnection, io,
                 sensorName, std::move(sensorThresholds), *interfacePath,
-                findSensorUnit->second, factor, psuProperty->maxReading,
-                psuProperty->minReading, psuProperty->sensorOffset, labelHead,
-                thresholdConfSize, pollRate);
+                readState, findSensorUnit->second, factor,
+                psuProperty->maxReading, psuProperty->minReading,
+                psuProperty->sensorOffset, labelHead, thresholdConfSize,
+                pollRate);
             sensors[sensorName]->setupRead();
             ++numCreated;
             if constexpr (debug)
@@ -867,9 +877,10 @@
         // OperationalStatus event
         combineEvents[*psuName + "OperationalStatus"] = nullptr;
         combineEvents[*psuName + "OperationalStatus"] =
-            std::make_unique<PSUCombineEvent>(
-                objectServer, dbusConnection, io, *psuName, eventPathList,
-                groupEventPathList, "OperationalStatus", pollRate);
+            std::make_unique<PSUCombineEvent>(objectServer, dbusConnection, io,
+                                              *psuName, readState,
+                                              eventPathList, groupEventPathList,
+                                              "OperationalStatus", pollRate);
     }
 
     if constexpr (debug)
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 5a5064f..1a14911 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -240,6 +240,20 @@
     return biosHasPost;
 }
 
+bool readingStateGood(const PowerState& powerState)
+{
+    if (powerState == PowerState::on && !isPowerOn())
+    {
+        return false;
+    }
+    if (powerState == PowerState::biosPost && (!hasBiosPost() || !isPowerOn()))
+    {
+        return false;
+    }
+
+    return true;
+}
+
 static void
     getPowerStatus(const std::shared_ptr<sdbusplus::asio::connection>& conn,
                    size_t retries = 2)
@@ -557,4 +571,4 @@
 bool getManufacturingMode()
 {
     return manufacturingMode;
-}
\ No newline at end of file
+}