Add support for hard and soft shutdown thresholds

Add support for the new SoftShutdown and HardShutdown sensor threshold
D-Bus interfaces.  These are optional, so if they aren't in the JSON
config file then the code won't put them on D-Bus.

At least one of the low or high values needs to be in the JSON for the
interface to be created.  If there is only one, then NaN will be used
for the missing threshold value to indicate 'not applicable' as the
phosphor-dbus-interfaces yaml definition states.

Since these new interfaces are optional, they cannot be base classes of
the VirtualSensor class as the other threshold interface classes were,
so they are members of the class instead.  To keep all of the threshold
classes consistent, the warning and critical threshold objects were also
changed to be members.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I099f83ca085e7febd4416c581a19716a83c9d133
diff --git a/virtualSensor.cpp b/virtualSensor.cpp
index e567467..e7363cc 100644
--- a/virtualSensor.cpp
+++ b/virtualSensor.cpp
@@ -74,7 +74,8 @@
     }
 }
 
-void VirtualSensor::initVirtualSensor(const Json& sensorConfig)
+void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
+                                      const std::string& objPath)
 {
 
     static const Json empty{};
@@ -83,18 +84,43 @@
     auto threshold = sensorConfig.value("Threshold", empty);
     if (!threshold.empty())
     {
-        Threshold sensorThreshold;
-        sensorThreshold.criticalHigh =
-            threshold.value("CriticalHigh", defaultHighThreshold);
-        sensorThreshold.criticalLow =
-            threshold.value("CriticalLow", defaultLowThreshold);
-        sensorThreshold.warningHigh =
-            threshold.value("WarningHigh", defaultHighThreshold);
-        sensorThreshold.warningLow =
-            threshold.value("WarningLow", defaultLowThreshold);
+        criticalIface = std::make_unique<CriticalObject>(bus, objPath.c_str());
+        criticalIface->criticalHigh(
+            threshold.value("CriticalHigh", defaultHighThreshold));
+        criticalIface->criticalLow(
+            threshold.value("CriticalLow", defaultLowThreshold));
 
-        /* Set threshold value to dbus */
-        setSensorThreshold(sensorThreshold);
+        warningIface = std::make_unique<WarningObject>(bus, objPath.c_str());
+        warningIface->warningHigh(
+            threshold.value("WarningHigh", defaultHighThreshold));
+        warningIface->warningLow(
+            threshold.value("WarningLow", defaultLowThreshold));
+
+        // Only create the high and low shutdown interfaces if
+        // at least one of their values is present.
+        if (threshold.contains("HardShutdownHigh") ||
+            threshold.contains("HardShutdownLow"))
+        {
+            hardShutdownIface =
+                std::make_unique<HardShutdownObject>(bus, objPath.c_str());
+
+            hardShutdownIface->hardShutdownHigh(threshold.value(
+                "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
+            hardShutdownIface->hardShutdownLow(threshold.value(
+                "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
+        }
+
+        if (threshold.contains("SoftShutdownHigh") ||
+            threshold.contains("SoftShutdownLow"))
+        {
+            softShutdownIface =
+                std::make_unique<SoftShutdownObject>(bus, objPath.c_str());
+
+            softShutdownIface->softShutdownHigh(threshold.value(
+                "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
+            softShutdownIface->softShutdownLow(threshold.value(
+                "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
+        }
     }
 
     /* Get expression string */
@@ -186,91 +212,162 @@
     ValueIface::value(value);
 }
 
-void VirtualSensor::setSensorThreshold(Threshold& sensorThreshold)
-{
-    CriticalInterface::criticalHigh(sensorThreshold.criticalHigh);
-    CriticalInterface::criticalLow(sensorThreshold.criticalLow);
-    WarningInterface::warningHigh(sensorThreshold.warningHigh);
-    WarningInterface::warningLow(sensorThreshold.warningLow);
-}
-
 void VirtualSensor::checkSensorThreshold(const double value)
 {
-    auto criticalHigh = CriticalInterface::criticalHigh();
-    auto criticalLow = CriticalInterface::criticalLow();
-    auto warningHigh = WarningInterface::warningHigh();
-    auto warningLow = WarningInterface::warningLow();
-
-    if (value >= warningHigh)
+    if (warningIface)
     {
-        if (!WarningInterface::warningAlarmHigh())
+        if (value >= warningIface->warningHigh())
         {
-            WarningInterface::warningAlarmHigh(true);
-            log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
-                            "warning high threshold",
-                            entry("NAME = %s", name.c_str()));
+            if (!warningIface->warningAlarmHigh())
+            {
+                warningIface->warningAlarmHigh(true);
+                log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
+                                "warning high threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
+        }
+        else if (warningIface->warningAlarmHigh())
+        {
+            warningIface->warningAlarmHigh(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is under "
+                             "warning high threshold",
+                             entry("NAME = %s", name.c_str()));
+        }
+
+        if (value <= warningIface->warningLow())
+        {
+            if (!warningIface->warningAlarmLow())
+            {
+                warningIface->warningAlarmLow(true);
+                log<level::ERR>("ASSERT: Virtual Sensor is under "
+                                "warning low threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
+        }
+        else if (warningIface->warningAlarmLow())
+        {
+            warningIface->warningAlarmLow(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is above "
+                             "warning low threshold",
+                             entry("NAME = %s", name.c_str()));
         }
     }
-    else if (WarningInterface::warningAlarmHigh())
-    {
-        WarningInterface::warningAlarmHigh(false);
-        log<level::INFO>("DEASSERT: Virtual Sensor is under "
-                         "warning high threshold",
-                         entry("NAME = %s", name.c_str()));
-    }
 
-    if (value >= criticalHigh)
+    if (criticalIface)
     {
-        if (!CriticalInterface::criticalAlarmHigh())
+        if (value >= criticalIface->criticalHigh())
         {
-            CriticalInterface::criticalAlarmHigh(true);
-            log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
-                            "critical high threshold",
-                            entry("NAME = %s", name.c_str()));
+            if (!criticalIface->criticalAlarmHigh())
+            {
+                criticalIface->criticalAlarmHigh(true);
+                log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
+                                "critical high threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
+        }
+        else if (criticalIface->criticalAlarmHigh())
+        {
+            criticalIface->criticalAlarmHigh(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is under "
+                             "critical high threshold",
+                             entry("NAME = %s", name.c_str()));
+        }
+
+        if (value <= criticalIface->criticalLow())
+        {
+            if (!criticalIface->criticalAlarmLow())
+            {
+                criticalIface->criticalAlarmLow(true);
+                log<level::ERR>("ASSERT: Virtual Sensor is under "
+                                "critical low threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
+        }
+        else if (criticalIface->criticalAlarmLow())
+        {
+            criticalIface->criticalAlarmLow(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is above "
+                             "critical low threshold",
+                             entry("NAME = %s", name.c_str()));
         }
     }
-    else if (CriticalInterface::criticalAlarmHigh())
-    {
-        CriticalInterface::criticalAlarmHigh(false);
-        log<level::INFO>("DEASSERT: Virtual Sensor is under "
-                         "critical high threshold",
-                         entry("NAME = %s", name.c_str()));
-    }
 
-    if (value <= warningLow)
+    if (softShutdownIface)
     {
-        if (!WarningInterface::warningAlarmLow())
+        if (value >= softShutdownIface->softShutdownHigh())
         {
-            WarningInterface::warningAlarmLow(true);
-            log<level::ERR>("ASSERT: Virtual Sensor is under "
-                            "warning low threshold",
-                            entry("NAME = %s", name.c_str()));
+            if (!softShutdownIface->softShutdownAlarmHigh())
+            {
+                softShutdownIface->softShutdownAlarmHigh(true);
+                log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
+                                "softShutdown high threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
+        }
+        else if (softShutdownIface->softShutdownAlarmHigh())
+        {
+            softShutdownIface->softShutdownAlarmHigh(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is under "
+                             "softShutdown high threshold",
+                             entry("NAME = %s", name.c_str()));
+        }
+
+        if (value <= softShutdownIface->softShutdownLow())
+        {
+            if (!softShutdownIface->softShutdownAlarmLow())
+            {
+                softShutdownIface->softShutdownAlarmLow(true);
+                log<level::ERR>("ASSERT: Virtual Sensor is under "
+                                "softShutdown low threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
+        }
+        else if (softShutdownIface->softShutdownAlarmLow())
+        {
+            softShutdownIface->softShutdownAlarmLow(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is above "
+                             "softShutdown low threshold",
+                             entry("NAME = %s", name.c_str()));
         }
     }
-    else if (WarningInterface::warningAlarmLow())
-    {
-        WarningInterface::warningAlarmLow(false);
-        log<level::INFO>("DEASSERT: Virtual Sensor is above "
-                         "warning low threshold",
-                         entry("NAME = %s", name.c_str()));
-    }
 
-    if (value <= criticalLow)
+    if (hardShutdownIface)
     {
-        if (!CriticalInterface::criticalAlarmLow())
+        if (value >= hardShutdownIface->hardShutdownHigh())
         {
-            CriticalInterface::criticalAlarmLow(true);
-            log<level::ERR>("ASSERT: Virtual Sensor is under "
-                            "critical low threshold",
-                            entry("NAME = %s", name.c_str()));
+            if (!hardShutdownIface->hardShutdownAlarmHigh())
+            {
+                hardShutdownIface->hardShutdownAlarmHigh(true);
+                log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
+                                "hardShutdown high threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
         }
-    }
-    else if (CriticalInterface::criticalAlarmLow())
-    {
-        CriticalInterface::criticalAlarmLow(false);
-        log<level::INFO>("DEASSERT: Virtual Sensor is above "
-                         "critical low threshold",
-                         entry("NAME = %s", name.c_str()));
+        else if (hardShutdownIface->hardShutdownAlarmHigh())
+        {
+            hardShutdownIface->hardShutdownAlarmHigh(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is under "
+                             "hardShutdown high threshold",
+                             entry("NAME = %s", name.c_str()));
+        }
+
+        if (value <= hardShutdownIface->hardShutdownLow())
+        {
+            if (!hardShutdownIface->hardShutdownAlarmLow())
+            {
+                hardShutdownIface->hardShutdownAlarmLow(true);
+                log<level::ERR>("ASSERT: Virtual Sensor is under "
+                                "hardShutdown low threshold",
+                                entry("NAME = %s", name.c_str()));
+            }
+        }
+        else if (hardShutdownIface->hardShutdownAlarmLow())
+        {
+            hardShutdownIface->hardShutdownAlarmLow(false);
+            log<level::INFO>("DEASSERT: Virtual Sensor is above "
+                             "hardShutdown low threshold",
+                             entry("NAME = %s", name.c_str()));
+        }
     }
 }
 
diff --git a/virtualSensor.hpp b/virtualSensor.hpp
index a04a743..e60bb1a 100644
--- a/virtualSensor.hpp
+++ b/virtualSensor.hpp
@@ -4,6 +4,8 @@
 #include <nlohmann/json.hpp>
 #include <sdbusplus/bus.hpp>
 #include <xyz/openbmc_project/Sensor/Threshold/Critical/server.hpp>
+#include <xyz/openbmc_project/Sensor/Threshold/HardShutdown/server.hpp>
+#include <xyz/openbmc_project/Sensor/Threshold/SoftShutdown/server.hpp>
 #include <xyz/openbmc_project/Sensor/Threshold/Warning/server.hpp>
 #include <xyz/openbmc_project/Sensor/Value/server.hpp>
 
@@ -16,17 +18,28 @@
 {
 
 using Json = nlohmann::json;
+
+template <typename... T>
+using ServerObject = typename sdbusplus::server::object::object<T...>;
+
 using ValueIface = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
+using ValueObject = ServerObject<ValueIface>;
 
-using CriticalInterface =
+using CriticalIface =
     sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::Critical;
+using CriticalObject = ServerObject<CriticalIface>;
 
-using WarningInterface =
+using WarningIface =
     sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::Warning;
+using WarningObject = ServerObject<WarningIface>;
 
-using sensorIfaces =
-    sdbusplus::server::object::object<ValueIface, CriticalInterface,
-                                      WarningInterface>;
+using SoftShutdownIface =
+    sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::SoftShutdown;
+using SoftShutdownObject = ServerObject<SoftShutdownIface>;
+
+using HardShutdownIface =
+    sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::HardShutdown;
+using HardShutdownObject = ServerObject<HardShutdownIface>;
 
 class SensorParam
 {
@@ -67,7 +80,7 @@
     ParamType paramType;
 };
 
-class VirtualSensor : public sensorIfaces
+class VirtualSensor : public ValueObject
 {
   public:
     VirtualSensor() = delete;
@@ -81,20 +94,12 @@
      */
     VirtualSensor(sdbusplus::bus::bus& bus, const char* objPath,
                   const Json& sensorConfig, const std::string& name) :
-        sensorIfaces(bus, objPath),
+        ValueObject(bus, objPath),
         bus(bus), name(name)
     {
-        initVirtualSensor(sensorConfig);
+        initVirtualSensor(sensorConfig, objPath);
     }
 
-    struct Threshold
-    {
-        double criticalHigh;
-        double criticalLow;
-        double warningHigh;
-        double warningLow;
-    };
-
     /** @brief Set sensor value */
     void setSensorValue(double value);
     /** @brief Update sensor at regular intrval */
@@ -119,12 +124,21 @@
     /** @brief The vecops package so the expression can use vectors */
     exprtk::rtl::vecops::package<double> vecopsPackage;
 
+    /** @brief The critical threshold interface object */
+    std::unique_ptr<CriticalObject> criticalIface;
+    /** @brief The warning threshold interface object */
+    std::unique_ptr<WarningObject> warningIface;
+    /** @brief The soft shutdown threshold interface object */
+    std::unique_ptr<SoftShutdownObject> softShutdownIface;
+    /** @brief The hard shutdown threshold interface object */
+    std::unique_ptr<HardShutdownObject> hardShutdownIface;
+
     /** @brief Read config from json object and initialize sensor data
      * for each virtual sensor
      */
-    void initVirtualSensor(const Json& sensorConfig);
-    /** @brief Set Sensor Threshold to D-bus at beginning */
-    void setSensorThreshold(Threshold& sensorThreshold);
+    void initVirtualSensor(const Json& sensorConfig,
+                           const std::string& objPath);
+
     /** @brief Check Sensor threshold and update alarm and log */
     void checkSensorThreshold(const double value);
 };