monitor: Create PowerOffRules class

This class contains a PowerOffCause and a PowerOffAction.  It provides a
check() method that takes the FanHealth map which it then checks against
the cause.  If the cause is satisfied, it then starts the power off
action.  It provides a cancel method that will force cancel a running
action in the case that the object owner detects a system power off and
so doesn't need to run this power off anymore.

The class's configuration data is read from the JSON config file.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I5c0c168591d6d62c894c4d036ec762797fd759af
diff --git a/monitor/power_off_rule.hpp b/monitor/power_off_rule.hpp
new file mode 100644
index 0000000..f28dce6
--- /dev/null
+++ b/monitor/power_off_rule.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include "logging.hpp"
+#include "power_off_action.hpp"
+#include "power_off_cause.hpp"
+
+#include <memory>
+
+namespace phosphor::fan::monitor
+{
+
+/**
+ * @brief Describes when the rule should be checked.
+ *        either right at PGOOD, or anytime at runtime.
+ */
+enum class PowerRuleState
+{
+    atPgood, // Only at the moment when PGOOD switches on.
+    runtime  // Anytime that power is on.
+};
+
+/**
+ * @class PowerOffRule
+ *
+ * This class implements a power off rule, which has a cause
+ * that is based on fan status, and an action which is the type of
+ * power off that will occur when the cause is satisfied.
+ *
+ * The user of this class calls the 'check()' method when fan
+ * status may have changed, and then the power off action may
+ * be started.
+ */
+class PowerOffRule
+{
+  public:
+    PowerOffRule() = delete;
+    ~PowerOffRule() = default;
+    PowerOffRule(const PowerOffRule&) = delete;
+    PowerOffRule& operator=(const PowerOffRule&) = delete;
+    PowerOffRule(PowerOffRule&&) = delete;
+    PowerOffRule& operator=(PowerOffRule&&) = delete;
+
+    /**
+     * @brief Constructor
+     *
+     * @param[in] validState - What state the rule is valid for
+     * @param[in] cause - The power off cause to use
+     * @param[in] action - The power off action to use
+     */
+    PowerOffRule(PowerRuleState validState,
+                 std::unique_ptr<PowerOffCause> cause,
+                 std::unique_ptr<PowerOffAction> action) :
+        _validState(validState),
+        _cause(std::move(cause)), _action(std::move(action))
+    {}
+
+    /**
+     * @brief Used to cancel a delay based power off when
+     *        there is still time left.
+     */
+    void cancel()
+    {
+        _active = false;
+
+        // force the cancel
+        _action->cancel(true);
+    }
+
+    /**
+     * @brief Checks the cause against the passed in fan health
+     *        and starts the power off action if the cause
+     *        is satisfied.
+     *
+     * @param[in] state - The state to check the rule at
+     * @param[in] fanHealth - The fan health map
+     */
+    void check(PowerRuleState state, const FanHealth& fanHealth)
+    {
+        if (state == _validState)
+        {
+            auto satisfied = _cause->satisfied(fanHealth);
+
+            if (!_active && satisfied)
+            {
+                // Start the action
+                _active = true;
+                _action->start();
+                getLogger().log(fmt::format(
+                    "Starting shutdown action '{}' due to cause '{}'",
+                    _action->name(), _cause->name()));
+            }
+            else if (_active && !satisfied)
+            {
+                // Attempt to cancel the action, but don't force it
+                if (_action->cancel(false))
+                {
+                    getLogger().log(fmt::format("Stopped shutdown action '{}'",
+                                                _action->name()));
+                    _active = false;
+                }
+                else
+                {
+                    getLogger().log(
+                        fmt::format("Could not stop shutdown action '{}'",
+                                    _action->name()));
+                }
+            }
+        }
+    }
+
+    /**
+     * @brief Says if there is an active power off in progress due to
+     *        this rule.
+     *
+     * @return bool - If the rule is active or not
+     */
+    bool active() const
+    {
+        return _active;
+    }
+
+  private:
+    /**
+     * @brief The state the rule is valid for.
+     */
+    PowerRuleState _validState;
+
+    /**
+     * @brief If there is an active power off in progress.
+     */
+    bool _active{false};
+
+    /**
+     * @brief Base class pointer to the power off cause class
+     */
+    std::unique_ptr<PowerOffCause> _cause;
+
+    /**
+     * @brief Base class pointer to the power off action class
+     */
+    std::unique_ptr<PowerOffAction> _action;
+};
+
+} // namespace phosphor::fan::monitor