monitor: Support set_func_on_present

The set_func_on_present JSON field determines if the fan FRU and rotors
should be set to functional immediately when a fan plug is detected.  It
is optional in the JSON file, and defaults to false if not present.

When this is false, a fan will have to spin back up again before it is
set back to functional, meaning that if the new fan is faulty and never
spins up there may not be another error created.  If a faulty fan is
plugged when this setting is true, then an error will be created as soon
as the configuration allows.

In some system configurations, the functional status on the fan FRU may
also control LED status, so setting the FRU to functional on the plug
would also turn off a fault LED, which is a desired behavior in the
systems that do this.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: If1a8e62a7b390e8137353a7ecc423a60df138315
diff --git a/docs/monitor/README.md b/docs/monitor/README.md
index 1bbc61f..a36d0e4 100644
--- a/docs/monitor/README.md
+++ b/docs/monitor/README.md
@@ -147,6 +147,7 @@
 * [monitor_start_delay](monitor_start_delay.md) - Optional, default = 0
 * [fan_missing_error_delay](fan_missing_error_delay.md) - Optional
 * [nonfunc_rotor_error_delay](nonfunc_rotor_error_delay.md) - Optional
+* [set_func_on_present](set_func_on_present.md) - Optional, default = false
 * [sensors](sensors.md)
 
 Trust group attributes: **(Optional)**
diff --git a/docs/monitor/set_func_on_present.md b/docs/monitor/set_func_on_present.md
new file mode 100644
index 0000000..0837c4f
--- /dev/null
+++ b/docs/monitor/set_func_on_present.md
@@ -0,0 +1,42 @@
+# set_func_on_present
+
+## Description
+If the fan FRU and contained rotors should be set to functional immediately on
+presence being detected.  Any faults will be re-detected.  This attribute is
+optional and defaults to false, meaning a newly inserted fan will need to spin
+up before being set back to functional, and if it never spins up, there won't
+be additional errors.
+
+## Attribute Value(s)
+bool (default = false)
+
+## Example
+<pre><code>
+{
+  "fans": [
+    {
+      "inventory": "/system/chassis/motherboard/fan0",
+      "allowed_out_of_range_time": 30,
+      "functional_delay": 5,
+      "deviation": 15,
+      "num_sensors_nonfunc_for_fan_nonfunc": 1,
+      "monitor_start_delay": 30,
+      "fan_missing_error_delay": 20,
+      "nonfunc_rotor_error_delay": 0,
+      <b><i>"set_func_on_present": true</i></b>,
+      "sensors": [
+        {
+          "name": "fan0_0",
+          "has_target": true
+        },
+        {
+          "name": "fan0_1",
+          "has_target": false,
+          "factor": 1.45,
+          "offset": -909
+        }
+      ]
+    }
+  ]
+}
+</code></pre>
diff --git a/monitor/fan.cpp b/monitor/fan.cpp
index 52b4031..09af6cd 100644
--- a/monitor/fan.cpp
+++ b/monitor/fan.cpp
@@ -62,7 +62,8 @@
         std::bind(std::mem_fn(&Fan::presenceIfaceAdded), this,
                   std::placeholders::_1)),
     _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def)),
-    _countInterval(std::get<countIntervalField>(def))
+    _countInterval(std::get<countIntervalField>(def)),
+    _setFuncOnPresent(std::get<funcOnPresentField>(def))
 {
     bool enableCountTimer = false;
 
@@ -456,6 +457,15 @@
 
         _system.fanStatusChange(*this);
 
+        if (_present && _setFuncOnPresent)
+        {
+            updateInventory(true);
+            std::for_each(_sensors.begin(), _sensors.end(), [](auto& sensor) {
+                sensor->setFunctional(true);
+                sensor->resetMethod();
+            });
+        }
+
         if (_fanMissingErrorDelay)
         {
             if (!_present && _system.isPowerOn())
diff --git a/monitor/fan.hpp b/monitor/fan.hpp
index 92016b1..3687a85 100644
--- a/monitor/fan.hpp
+++ b/monitor/fan.hpp
@@ -340,6 +340,12 @@
     std::unique_ptr<
         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
         _countTimer;
+
+    /**
+     * @brief If the fan and sensors should be set to functional when
+     *        a fan plug is detected.
+     */
+    bool _setFuncOnPresent;
 };
 
 } // namespace monitor
diff --git a/monitor/gen-fan-monitor-defs.py b/monitor/gen-fan-monitor-defs.py
index b77ea59..1d46f80 100755
--- a/monitor/gen-fan-monitor-defs.py
+++ b/monitor/gen-fan-monitor-defs.py
@@ -85,7 +85,8 @@
                       ${indent(getCondParams(cond=fan_data['condition']), 5)}\
                   ))
                   %else:
-                  {}
+                  {},
+                  false // set_func_on_present. Hardcoded to false.
                   %endif
     },
 %endfor
diff --git a/monitor/json_parser.cpp b/monitor/json_parser.cpp
index 888a57d..ff86642 100644
--- a/monitor/json_parser.cpp
+++ b/monitor/json_parser.cpp
@@ -331,10 +331,18 @@
             }
         }
 
+        // if the fan should be set to functional when plugged in
+        bool setFuncOnPresent = false;
+        if (fan.contains("set_func_on_present"))
+        {
+            setFuncOnPresent = fan["set_func_on_present"].get<bool>();
+        }
+
         fanDefs.emplace_back(std::tuple(
             fan["inventory"].get<std::string>(), method, funcDelay, timeout,
             deviation, nonfuncSensorsCount, monitorDelay, countInterval,
-            nonfuncRotorErrorDelay, fanMissingErrorDelay, sensorDefs, cond));
+            nonfuncRotorErrorDelay, fanMissingErrorDelay, sensorDefs, cond,
+            setFuncOnPresent));
     }
 
     return fanDefs;
diff --git a/monitor/types.hpp b/monitor/types.hpp
index 15b1f1a..faf3b66 100644
--- a/monitor/types.hpp
+++ b/monitor/types.hpp
@@ -124,11 +124,12 @@
 constexpr auto fanMissingErrDelayField = 9;
 constexpr auto sensorListField = 10;
 constexpr auto conditionField = 11;
+constexpr auto funcOnPresentField = 12;
 
 using FanDefinition =
     std::tuple<std::string, size_t, size_t, size_t, size_t, size_t, size_t,
                size_t, std::optional<size_t>, std::optional<size_t>,
-               std::vector<SensorDefinition>, std::optional<Condition>>;
+               std::vector<SensorDefinition>, std::optional<Condition>, bool>;
 
 constexpr auto presentHealthPos = 0;
 constexpr auto sensorFuncHealthPos = 1;