presence: Sensor conflict checking for AnyOf

There can be more than one way to detect the presence of a fan, such as
by a GPIO and by a nonzero tach reading.  The AnyOf redundancy policy
only requires one of these to indicate present when determining the
overall fan presence state.

This commit adds the functionality to check for the case when one of the
method reports not present while another reports present.  In this case,
the one reporting not present will be considered the wrong one, and
depending on the detection type either an information event log or just
a journal trace will be created.

Only one log per method per power cycle will occur.  Since one of the
methods probably looks for nonzero tach readings, there is a 5 second
delay after a power on is detected before a conflict check is done.

If the GPIO method is where the problem is detected, an event log is
created.  If it's instead the tach sensor method, then a trace will just
be put in the journal because there is already code watching for and
creating event logs for stopped tachs - the fan monitor code.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I72a764ecff4076d6dc40335b92d177b6b3cfa2d9
diff --git a/presence/anyof.hpp b/presence/anyof.hpp
index efe3c79..91865b0 100644
--- a/presence/anyof.hpp
+++ b/presence/anyof.hpp
@@ -1,8 +1,11 @@
 #pragma once
 
 #include "fan.hpp"
+#include "power_state.hpp"
 #include "rpolicy.hpp"
 
+#include <sdeventplus/utility/timer.hpp>
+
 #include <functional>
 #include <vector>
 
@@ -62,8 +65,83 @@
     void monitor() override;
 
   private:
-    /** @brief All presence sensors in the redundancy set. */
-    std::vector<std::tuple<std::reference_wrapper<PresenceSensor>, bool>> state;
+    /**
+     * @brief Checks that the sensors contained in this policy all
+     *        agree on the presence value.  If they don't, then call
+     *        logConflict() on the sensors that don't think the fan
+     *        is present as they may be broken.
+     *
+     * This check will only happen when power is on.
+     */
+    void checkSensorConflicts();
+
+    /**
+     * @brief Callback function called after a post-poweron delay.
+     *
+     * The _powerOnDelayTimer is started when _powerState says the
+     * power is on to give fans a bit of time to spin up so tachs
+     * wouldn't be zero.  This is the callback function for that timer.
+     *
+     * It will call checkSensorConflicts().
+     */
+    void delayedAfterPowerOn();
+
+    /**
+     * @brief Says if power is on, though not until the post
+     *        power on delay is complete.
+     *
+     * @return bool - if power is on.
+     */
+    inline bool isPowerOn() const
+    {
+        return _powerOn;
+    }
+
+    /**
+     * @brief Called by the PowerState object when the power
+     *        state changes.
+     *
+     * When power changes to on:
+     *   - Clears the memory of previous sensor conflicts.
+     *   - Starts the post power on delay timer.
+     *
+     * @param[in] powerOn - If power is now on or off.
+     */
+    void powerStateChanged(bool powerOn);
+
+    static constexpr size_t sensorPos = 0;
+    static constexpr size_t presentPos = 1;
+    static constexpr size_t conflictPos = 2;
+
+    /**
+     * @brief All presence sensors in the redundancy set.
+     *
+     * Each entry contains:
+     *  - A reference to a PresenceSensor
+     *  - The current presence state
+     *  - If the sensors have logged conflicts in their answers.
+     */
+    std::vector<std::tuple<std::reference_wrapper<PresenceSensor>, bool, bool>>
+        state;
+
+    /**
+     * @brief Pointer to the PowerState object used to track power changes.
+     */
+    std::shared_ptr<PowerState> _powerState;
+
+    /**
+     * @brief Post power on delay timer, where the conflict checking code
+     *        doesn't consider power on until this timer expires.
+     *
+     * This gives fans a chance to start spinning before checking them.
+     */
+    sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>
+        _powerOnDelayTimer;
+
+    /**
+     * @brief Current power state.
+     */
+    bool _powerOn;
 };
 
 } // namespace presence