Add logging for Fan Presence / Redundancy

Add logging and fix bug with shared_ptr usage. We
now use a std::optional * to the shared redundancy
object, so that it can 1. point to object / null and 2.
exist in config or not.

Tested: cat /var/log/redfish to see logs made by removing
and adding fans, and using sensor override to change
redundancy.

Change-Id: Ic4b319cf7484cbbd7ce7dbdf7556f48bebe11cb0
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/include/TachSensor.hpp b/include/TachSensor.hpp
index c6f255f..b160a60 100644
--- a/include/TachSensor.hpp
+++ b/include/TachSensor.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <systemd/sd-journal.h>
+
 #include <Thresholds.hpp>
 #include <boost/container/flat_map.hpp>
 #include <boost/container/flat_set.hpp>
@@ -12,7 +14,7 @@
 
   public:
     PresenceSensor(const size_t index, bool inverted,
-                   boost::asio::io_service& io);
+                   boost::asio::io_service& io, const std::string& name);
     ~PresenceSensor();
 
     void monitorPresence(void);
@@ -24,8 +26,16 @@
     bool inverted;
     boost::asio::ip::tcp::socket inputDev;
     int fd;
+    std::string name;
 };
 
+namespace redundancy
+{
+constexpr const char* full = "Full";
+constexpr const char* degraded = "Degraded";
+constexpr const char* failed = "Failed";
+} // namespace redundancy
+
 class RedundancySensor
 {
   public:
@@ -38,6 +48,7 @@
 
   private:
     size_t count;
+    std::string state = redundancy::full;
     std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
     std::shared_ptr<sdbusplus::asio::dbus_interface> association;
     sdbusplus::asio::object_server& objectServer;
@@ -51,7 +62,7 @@
                sdbusplus::asio::object_server& objectServer,
                std::shared_ptr<sdbusplus::asio::connection>& conn,
                std::unique_ptr<PresenceSensor>&& presence,
-               const std::shared_ptr<RedundancySensor>& redundancy,
+               std::optional<RedundancySensor>* redundancy,
                boost::asio::io_service& io, const std::string& fanName,
                std::vector<thresholds::Threshold>&& thresholds,
                const std::string& sensorConfiguration,
@@ -60,7 +71,7 @@
 
   private:
     sdbusplus::asio::object_server& objServer;
-    std::shared_ptr<RedundancySensor> redundancy;
+    std::optional<RedundancySensor>* redundancy;
     std::unique_ptr<PresenceSensor> presence;
     std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
     boost::asio::posix::stream_descriptor inputDev;
@@ -71,3 +82,35 @@
     void handleResponse(const boost::system::error_code& err);
     void checkThresholds(void) override;
 };
+
+inline void logFanInserted(const std::string& device)
+{
+
+    sd_journal_send("MESSAGE=%s", "Fan Inserted", "PRIORITY=%i", LOG_ERR,
+                    "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.FanInserted",
+                    "REDFISH_MESSAGE_ARGS=%s", device.c_str(), NULL);
+}
+
+inline void logFanRemoved(const std::string& device)
+{
+
+    sd_journal_send("MESSAGE=%s", "Fan Removed", "PRIORITY=%i", LOG_ERR,
+                    "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.FanRemoved",
+                    "REDFISH_MESSAGE_ARGS=%s", device.c_str(), NULL);
+}
+
+inline void logFanRedundancyLost(void)
+{
+
+    sd_journal_send("MESSAGE=%s", "Fan Inserted", "PRIORITY=%i", LOG_ERR,
+                    "REDFISH_MESSAGE_ID=%s", "OpenBMC.0.1.FanRedundancyLost",
+                    NULL);
+}
+
+inline void logFanRedundancyRestored(void)
+{
+
+    sd_journal_send("MESSAGE=%s", "Fan Removed", "PRIORITY=%i", LOG_ERR,
+                    "REDFISH_MESSAGE_ID=%s",
+                    "OpenBMC.0.1.FanRedundancyRegained", NULL);
+}
diff --git a/src/FanMain.cpp b/src/FanMain.cpp
index 962a115..eeface8 100644
--- a/src/FanMain.cpp
+++ b/src/FanMain.cpp
@@ -46,7 +46,7 @@
 };
 
 // todo: power supply fan redundancy
-std::shared_ptr<RedundancySensor> systemRedundancy = nullptr;
+std::optional<RedundancySensor> systemRedundancy;
 
 FanTypes getFanType(const fs::path& parentPath)
 {
@@ -251,14 +251,14 @@
                 size_t index = std::get<uint64_t>(findIndex->second);
                 bool inverted =
                     std::get<std::string>(findPolarity->second) == "Low";
-                presenceSensor =
-                    std::make_unique<PresenceSensor>(index, inverted, io);
+                presenceSensor = std::make_unique<PresenceSensor>(
+                    index, inverted, io, sensorName);
             }
         }
-        std::shared_ptr<RedundancySensor> redundancy;
+        std::optional<RedundancySensor>* redundancy = nullptr;
         if (fanType == FanTypes::aspeed)
         {
-            redundancy = systemRedundancy;
+            redundancy = &systemRedundancy;
         }
 
         constexpr double defaultMaxReading = 25000;
@@ -360,10 +360,10 @@
                                 "/xyz/openbmc_project/sensors/fan_tach/" +
                                 sensor.second->name);
                         }
-                        systemRedundancy = nullptr;
-                        systemRedundancy = std::make_shared<RedundancySensor>(
+                        systemRedundancy.reset();
+                        systemRedundancy.emplace(RedundancySensor(
                             std::get<uint64_t>(findCount->second), sensorList,
-                            objectServer, pathPair.first);
+                            objectServer, pathPair.first));
 
                         return;
                     }
diff --git a/src/TachSensor.cpp b/src/TachSensor.cpp
index 03ff4a5..295c7e6 100644
--- a/src/TachSensor.cpp
+++ b/src/TachSensor.cpp
@@ -35,7 +35,7 @@
                        sdbusplus::asio::object_server& objectServer,
                        std::shared_ptr<sdbusplus::asio::connection>& conn,
                        std::unique_ptr<PresenceSensor>&& presenceSensor,
-                       const std::shared_ptr<RedundancySensor>& redundancy,
+                       std::optional<RedundancySensor>* redundancy,
                        boost::asio::io_service& io, const std::string& fanName,
                        std::vector<thresholds::Threshold>&& _thresholds,
                        const std::string& sensorConfiguration,
@@ -187,17 +187,18 @@
 void TachSensor::checkThresholds(void)
 {
     bool status = thresholds::checkThresholds(this);
-    if (redundancy)
+    if (redundancy && *redundancy)
     {
-        redundancy->update("/xyz/openbmc_project/sensors/fan_tach/" + name,
-                           !status);
+        (*redundancy)
+            ->update("/xyz/openbmc_project/sensors/fan_tach/" + name, !status);
     }
 }
 
 PresenceSensor::PresenceSensor(const size_t index, bool inverted,
-                               boost::asio::io_service& io) :
+                               boost::asio::io_service& io,
+                               const std::string& name) :
     inverted(inverted),
-    inputDev(io)
+    inputDev(io), name(name)
 {
     // todo: use gpiodaemon
     std::string device = gpioPath + std::string("gpio") + std::to_string(index);
@@ -267,7 +268,18 @@
         {
             value = !value;
         }
-        status = value;
+        if (value != status)
+        {
+            status = value;
+            if (status)
+            {
+                logFanInserted(name);
+            }
+            else
+            {
+                logFanRemoved(name);
+            }
+        }
     }
 }
 
@@ -305,7 +317,7 @@
     statuses[name] = failed;
     size_t failedCount = 0;
 
-    std::string state = "Full";
+    std::string newState = redundancy::full;
     for (const auto& status : statuses)
     {
         if (status.second)
@@ -314,13 +326,25 @@
         }
         if (failedCount > count)
         {
-            state = "Failed";
+            newState = redundancy::failed;
             break;
         }
         else if (failedCount)
         {
-            state = "Degraded";
+            newState = redundancy::degraded;
         }
     }
-    iface->set_property("Status", state);
+    if (state != newState)
+    {
+        if (state == redundancy::full)
+        {
+            logFanRedundancyLost();
+        }
+        else if (newState == redundancy::full)
+        {
+            logFanRedundancyRestored();
+        }
+        state = newState;
+        iface->set_property("Status", state);
+    }
 }
diff --git a/src/Thresholds.cpp b/src/Thresholds.cpp
index 36556cf..a15da17 100644
--- a/src/Thresholds.cpp
+++ b/src/Thresholds.cpp
@@ -247,7 +247,7 @@
 
 bool checkThresholds(Sensor* sensor)
 {
-    bool status = false;
+    bool status = true;
     std::vector<std::pair<Threshold, bool>> changes =
         checkThresholds(sensor, sensor->value);
     for (const auto& [threshold, asserted] : changes)
@@ -256,7 +256,7 @@
                          asserted);
         if (threshold.level == thresholds::Level::CRITICAL && asserted)
         {
-            status = true;
+            status = false;
         }
     }