monitor: Read fan state from dbus inventory upon starting

tach sensors previously defaulted to functional. Now they default to the
inventory state. For counter-based sensors, nonfunctional state forces
the count to exceed the threshold.

Fan's functional state now depends on the number of functional sensors.

test plan:
1) power-off chassis, stop phosphor-fan-monitor service
2) use busctl to make a fan's tach sensor nonfunctional
3) start fan-monitor
4) verify the fan's state == nonfunctional
5) poweron chassis, verify that fan-monitor updates _sensor_ to
   functional
6) fan should remain nonfunctional
7) set Fan to non-present, then to present again (simulate
   replacement)
8) observe the fans functional state is now true in inventory

Signed-off-by: Mike Capps <mikepcapps@gmail.com>
Change-Id: I0a109a1d85390c0201f8a54942efcd2fb21a2b65
diff --git a/monitor/fan.cpp b/monitor/fan.cpp
index 342b07b..b3cc2a0 100644
--- a/monitor/fan.cpp
+++ b/monitor/fan.cpp
@@ -64,10 +64,6 @@
     _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def)),
     _setFuncOnPresent(std::get<funcOnPresentField>(def))
 {
-    // Start from a known state of functional (even if
-    // _numSensorFailsForNonFunc is 0)
-    updateInventory(true);
-
     // Setup tach sensors for monitoring
     auto& sensors = std::get<sensorListField>(def);
     for (auto& s : sensors)
@@ -84,6 +80,12 @@
         _trustManager->registerSensor(_sensors.back());
     }
 
+    bool functionalState =
+        (_numSensorFailsForNonFunc == 0) ||
+        (countNonFunctionalSensors() < _numSensorFailsForNonFunc);
+
+    updateInventory(functionalState);
+
 #ifndef MONITOR_USE_JSON
     // Check current tach state when entering monitor mode
     if (mode != Mode::init)
@@ -337,7 +339,7 @@
     return target;
 }
 
-size_t Fan::countNonFunctionalSensors()
+size_t Fan::countNonFunctionalSensors() const
 {
     return std::count_if(_sensors.begin(), _sensors.end(),
                          [](const auto& s) { return !s->functional(); });
diff --git a/monitor/fan.hpp b/monitor/fan.hpp
index a932db2..713a1a1 100644
--- a/monitor/fan.hpp
+++ b/monitor/fan.hpp
@@ -196,7 +196,7 @@
     /**
      * @brief Returns the number sensors that are nonfunctional
      */
-    size_t countNonFunctionalSensors();
+    size_t countNonFunctionalSensors() const;
 
     /**
      * @brief Updates the Functional property in the inventory
diff --git a/monitor/tach_sensor.cpp b/monitor/tach_sensor.cpp
index f110722..cc35c97 100644
--- a/monitor/tach_sensor.cpp
+++ b/monitor/tach_sensor.cpp
@@ -82,8 +82,39 @@
     _timer(event, std::bind(&Fan::updateState, &fan, std::ref(*this))),
     _errorDelay(errorDelay), _countInterval(countInterval)
 {
-    // Start from a known state of functional
-    setFunctional(true);
+    // Query functional state from inventory
+    // TODO - phosphor-fan-presence/issues/25
+    auto service = util::SDBusPlus::getService(
+        _bus, util::INVENTORY_PATH + _invName, util::OPERATIONAL_STATUS_INTF);
+
+    try
+    {
+        if (!service.empty())
+        {
+            _functional = util::SDBusPlus::getProperty<bool>(
+                service, util::INVENTORY_PATH + _invName,
+                util::OPERATIONAL_STATUS_INTF, util::FUNCTIONAL_PROPERTY);
+        }
+        else
+        {
+            // default to functional when service not up. Error handling done
+            // later
+            _functional = true;
+            updateInventory(_functional);
+        }
+    }
+    catch (util::DBusError& e)
+    {
+        log<level::DEBUG>(e.what());
+        _functional = true;
+        updateInventory(_functional);
+    }
+
+    if (!_functional && MethodMode::count == _method)
+    {
+        // force continual nonfunctional state
+        _counter = _threshold;
+    }
 
     // Load in current Target and Input values when entering monitor mode
 #ifndef MONITOR_USE_JSON
diff --git a/sdbusplus.hpp b/sdbusplus.hpp
index 1f2839f..73af8b0 100644
--- a/sdbusplus.hpp
+++ b/sdbusplus.hpp
@@ -253,20 +253,27 @@
         return mapperResp;
     }
 
-    /** @brief Get service from the mapper. */
-    static auto getService(sdbusplus::bus::bus& bus, const std::string& path,
-                           const std::string& interface)
+    /** @brief Get service from the mapper without checking response. */
+    static auto getServiceRaw(sdbusplus::bus::bus& bus, const std::string& path,
+                              const std::string& interface)
     {
         using namespace std::literals::string_literals;
         using GetObject = std::map<std::string, std::vector<std::string>>;
 
+        return callMethodAndRead<GetObject>(
+            bus, "xyz.openbmc_project.ObjectMapper"s,
+            "/xyz/openbmc_project/object_mapper"s,
+            "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path,
+            GetObject::mapped_type{interface});
+    }
+
+    /** @brief Get service from the mapper. */
+    static auto getService(sdbusplus::bus::bus& bus, const std::string& path,
+                           const std::string& interface)
+    {
         try
         {
-            auto mapperResp = callMethodAndRead<GetObject>(
-                bus, "xyz.openbmc_project.ObjectMapper"s,
-                "/xyz/openbmc_project/object_mapper"s,
-                "xyz.openbmc_project.ObjectMapper"s, "GetObject"s, path,
-                GetObject::mapped_type{interface});
+            auto mapperResp = getServiceRaw(bus, path, interface);
 
             if (mapperResp.empty())
             {