Filter ADC threshold crossings

When power goes down we get bad events because the
voltage rail spins down faster than pgood. Add in
a delay to check power 2 seconds after the event to
make sure power isn't going down.

Tested-by: power cycled and extra SEL went away

Change-Id: Ib820ccb50d1a949b8096f08e2711ad7a7c36087b
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/include/ADCSensor.hpp b/include/ADCSensor.hpp
index fd248da..fbb256b 100644
--- a/include/ADCSensor.hpp
+++ b/include/ADCSensor.hpp
@@ -24,6 +24,7 @@
     int errCount;
     double scaleFactor;
     PowerState readState;
+    thresholds::ThresholdTimer thresholdTimer;
     void setupRead(void);
     void handleResponse(const boost::system::error_code& err);
     void checkThresholds(void) override;
diff --git a/include/Thresholds.hpp b/include/Thresholds.hpp
index 22943c9..16b8bb9 100644
--- a/include/Thresholds.hpp
+++ b/include/Thresholds.hpp
@@ -1,5 +1,6 @@
 #pragma once
 #include <Utils.hpp>
+#include <boost/asio/io_service.hpp>
 #include <nlohmann/json.hpp>
 
 struct Sensor;
@@ -27,7 +28,6 @@
     Direction direction;
     double value;
     bool writeable;
-    bool asserted = false;
 
     bool operator==(const Threshold& rhs) const
     {
@@ -36,6 +36,67 @@
     }
 };
 
+void assertThresholds(Sensor* sensor, thresholds::Level level,
+                      thresholds::Direction direction, bool assert);
+
+struct ThresholdTimer
+{
+
+    ThresholdTimer(boost::asio::io_service& io, Sensor* sensor) :
+        criticalTimer(io), warningTimer(io), sensor(sensor)
+    {
+    }
+
+    void startTimer(const Threshold& threshold)
+    {
+        constexpr const size_t waitTime = 2;
+
+        if (threshold.level == WARNING && !warningRunning)
+        {
+            warningRunning = true;
+            warningTimer.expires_from_now(boost::posix_time::seconds(waitTime));
+            warningTimer.async_wait(
+                [this, threshold](boost::system::error_code ec) {
+                    if (ec == boost::asio::error::operation_aborted)
+                    {
+                        return; // we're being canceled
+                    }
+                    if (isPowerOn())
+                    {
+                        assertThresholds(sensor, threshold.level,
+                                         threshold.direction, true);
+                    }
+                    warningRunning = false;
+                });
+        }
+        else if (threshold.level == CRITICAL && !criticalRunning)
+        {
+            criticalRunning = true;
+            criticalTimer.expires_from_now(
+                boost::posix_time::seconds(waitTime));
+            criticalTimer.async_wait(
+                [this, threshold](boost::system::error_code ec) {
+                    if (ec == boost::asio::error::operation_aborted)
+                    {
+                        return; // we're being canceled
+                    }
+                    if (isPowerOn())
+                    {
+                        assertThresholds(sensor, threshold.level,
+                                         threshold.direction, true);
+                    }
+                    criticalRunning = false;
+                });
+        }
+    }
+
+    boost::asio::deadline_timer criticalTimer;
+    boost::asio::deadline_timer warningTimer;
+    bool criticalRunning = false;
+    bool warningRunning = false;
+    Sensor* sensor;
+};
+
 bool parseThresholdsFromConfig(
     const SensorData& sensorData,
     std::vector<thresholds::Threshold>& thresholdVector,
@@ -58,6 +119,5 @@
 void updateThresholds(Sensor* sensor);
 // returns false if a critical threshold has been crossed, true otherwise
 bool checkThresholds(Sensor* sensor);
-void assertThresholds(Sensor* sensor, thresholds::Level level,
-                      thresholds::Direction direction, bool assert);
+void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer);
 } // namespace thresholds
diff --git a/src/ADCSensor.cpp b/src/ADCSensor.cpp
index e06c8dd..01edc13 100644
--- a/src/ADCSensor.cpp
+++ b/src/ADCSensor.cpp
@@ -48,7 +48,7 @@
            "xyz.openbmc_project.Configuration.ADC", maxReading, minReading),
     objServer(objectServer), scaleFactor(scaleFactor),
     readState(std::move(readState)), inputDev(io, open(path.c_str(), O_RDONLY)),
-    waitTimer(io), errCount(0)
+    waitTimer(io), errCount(0), thresholdTimer(io, this)
 {
     sensorInterface = objectServer.add_interface(
         "/xyz/openbmc_project/sensors/voltage/" + name,
@@ -168,5 +168,6 @@
     {
         return;
     }
-    thresholds::checkThresholds(this);
+
+    thresholds::checkThresholdsPowerDelay(this, thresholdTimer);
 }
diff --git a/src/Thresholds.cpp b/src/Thresholds.cpp
index 4932bce..653b4e6 100644
--- a/src/Thresholds.cpp
+++ b/src/Thresholds.cpp
@@ -208,59 +208,80 @@
     }
 }
 
-bool checkThresholds(Sensor* sensor)
+static std::vector<std::pair<Threshold, bool>> checkThresholds(Sensor* sensor,
+                                                               double value)
 {
-    bool status = true;
-
+    std::vector<std::pair<Threshold, bool>> thresholdChanges;
     if (sensor->thresholds.empty())
     {
-        return true;
+        return thresholdChanges;
     }
+
     for (auto& threshold : sensor->thresholds)
     {
-        if (std::isnan(sensor->value))
+        if (threshold.direction == thresholds::Direction::HIGH)
         {
-            threshold.asserted = false;
-        }
-        else if (threshold.direction == thresholds::Direction::HIGH)
-        {
-            if (sensor->value > threshold.value && !threshold.asserted)
+            if (value > threshold.value)
             {
-                assertThresholds(sensor, threshold.level, threshold.direction,
-                                 true);
-                threshold.asserted = true;
+                thresholdChanges.push_back(std::make_pair(threshold, true));
             }
-            else if (sensor->value <= threshold.value && threshold.asserted)
+            else if (value <= threshold.value)
             {
-                assertThresholds(sensor, threshold.level, threshold.direction,
-                                 false);
-                threshold.asserted = false;
+                thresholdChanges.push_back(std::make_pair(threshold, false));
             }
         }
         else
         {
-            if (sensor->value < threshold.value && !threshold.asserted)
+            if (value < threshold.value)
             {
-                assertThresholds(sensor, threshold.level, threshold.direction,
-                                 true);
-                threshold.asserted = true;
+                thresholdChanges.push_back(std::make_pair(threshold, true));
             }
-            else if (sensor->value >= threshold.value && threshold.asserted)
+            else if (value >= threshold.value)
             {
-                assertThresholds(sensor, threshold.level, threshold.direction,
-                                 false);
-                threshold.asserted = false;
+                thresholdChanges.push_back(std::make_pair(threshold, false));
             }
         }
-        if (threshold.level == thresholds::Level::CRITICAL &&
-            threshold.asserted)
-        {
-            status = false;
-        }
     }
+    return thresholdChanges;
+}
+
+bool checkThresholds(Sensor* sensor)
+{
+    bool status = false;
+    std::vector<std::pair<Threshold, bool>> changes =
+        checkThresholds(sensor, sensor->value);
+    for (const auto& [threshold, asserted] : changes)
+    {
+        assertThresholds(sensor, threshold.level, threshold.direction,
+                         asserted);
+        if (threshold.level == thresholds::Level::CRITICAL && asserted)
+        {
+            status = true;
+        }
+    }
+
     return status;
 }
 
+void checkThresholdsPowerDelay(Sensor* sensor, ThresholdTimer& thresholdTimer)
+{
+
+    std::vector<std::pair<Threshold, bool>> changes =
+        checkThresholds(sensor, sensor->value);
+    for (const auto& [threshold, asserted] : changes)
+    {
+        if (asserted)
+        {
+            thresholdTimer.startTimer(threshold);
+        }
+        else
+        {
+            assertThresholds(sensor, threshold.level, threshold.direction,
+                             false);
+        }
+    }
+}
+
 void assertThresholds(Sensor* sensor, thresholds::Level level,
                       thresholds::Direction direction, bool assert)
 {