monitor: Track fan health in the System object

To prepare for being able to power off the system based on missing fans
or nonfunctional fan sensors, put a global view of this health for all
fans in the System object.  This requires now keeping track of fan
presence.

This information is stored in a map based on the fan name.  It is done
this way, as opposed to just always calling present/functional APIs on
the Fan objects, so that the code that will be using this information
can be tested in isolation without the System or Fan objects.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Ieb1d4003bd13cebc806fd06f0064c63ea8ac6180
diff --git a/monitor/fan.cpp b/monitor/fan.cpp
index 525a22b..6abd2da 100644
--- a/monitor/fan.cpp
+++ b/monitor/fan.cpp
@@ -48,7 +48,12 @@
     _monitorDelay(std::get<monitorStartDelayField>(def)),
     _monitorTimer(event, std::bind(std::mem_fn(&Fan::startMonitor), this)),
 #endif
-    _system(system)
+    _system(system),
+    _presenceMatch(bus,
+                   rules::propertiesChanged(util::INVENTORY_PATH + _name,
+                                            util::INV_ITEM_IFACE),
+                   std::bind(std::mem_fn(&Fan::presenceChanged), this,
+                             std::placeholders::_1))
 {
     // Start from a known state of functional
     updateInventory(true);
@@ -92,8 +97,11 @@
     // If it used the JSON config, then it also will do all the work
     // out of fan-monitor-init, after _monitorDelay.
     _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
-
 #endif
+
+    // Get the initial presence state
+    _present = util::SDBusPlus::getProperty<bool>(
+        util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
 }
 
 void Fan::startMonitor()
@@ -229,6 +237,8 @@
 
         updateInventory(false);
     }
+
+    _system.fanStatusChange(*this);
 }
 
 void Fan::updateInventory(bool functional)
@@ -248,6 +258,21 @@
     _functional = functional;
 }
 
+void Fan::presenceChanged(sdbusplus::message::message& msg)
+{
+    std::string interface;
+    std::map<std::string, std::variant<bool>> properties;
+
+    msg.read(interface, properties);
+
+    auto presentProp = properties.find("Present");
+    if (presentProp != properties.end())
+    {
+        _present = std::get<bool>(presentProp->second);
+
+        _system.fanStatusChange(*this);
+    }
+}
 } // namespace monitor
 } // namespace fan
 } // namespace phosphor