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.cpp b/presence/anyof.cpp
index b77d6d7..4dc2ec3 100644
--- a/presence/anyof.cpp
+++ b/presence/anyof.cpp
@@ -16,6 +16,7 @@
#include "anyof.hpp"
#include "fan.hpp"
+#include "get_power_state.hpp"
#include "psensor.hpp"
#include <phosphor-logging/log.hpp>
@@ -28,14 +29,32 @@
{
namespace presence
{
+
+using namespace std::chrono_literals;
+static const auto powerOnDelayTime = 5s;
+
AnyOf::AnyOf(const Fan& fan,
const std::vector<std::reference_wrapper<PresenceSensor>>& s) :
RedundancyPolicy(fan),
- state()
+ state(), _powerState(getPowerStateObject()),
+ _powerOnDelayTimer(sdeventplus::Event::get_default(),
+ std::bind(&AnyOf::delayedAfterPowerOn, this)),
+ _powerOn(false)
{
for (auto& sensor : s)
{
- state.emplace_back(sensor, false);
+ state.emplace_back(sensor, false, false);
+ }
+
+ _powerState->addCallback(
+ std::get<1>(fan) + "-anyOf",
+ std::bind(&AnyOf::powerStateChanged, this, std::placeholders::_1));
+
+ // If power is already on, give the fans some time to spin up
+ // before considering power to actually be on.
+ if (_powerState->isPowerOn())
+ {
+ _powerOnDelayTimer.restartOnce(powerOnDelayTime);
}
}
@@ -44,16 +63,34 @@
// Find the sensor that changed state.
auto sit =
std::find_if(state.begin(), state.end(), [&sensor](const auto& s) {
- return std::get<0>(s).get() == sensor;
+ return std::get<sensorPos>(s).get() == sensor;
});
if (sit != state.end())
{
+ auto origState =
+ std::any_of(state.begin(), state.end(),
+ [](const auto& s) { return std::get<presentPos>(s); });
+
// Update our cache of the sensors state and re-evaluate.
- std::get<bool>(*sit) = present;
+ std::get<presentPos>(*sit) = present;
auto newState =
std::any_of(state.begin(), state.end(),
- [](const auto& s) { return std::get<bool>(s); });
+ [](const auto& s) { return std::get<presentPos>(s); });
setPresence(fan, newState);
+
+ // At least 1 sensor said a fan was present, check if any disagree.
+ if (newState)
+ {
+ if (!origState)
+ {
+ // Fan plug detected, re-enable conflict logging
+ std::for_each(state.begin(), state.end(), [](auto& s) {
+ std::get<conflictPos>(s) = false;
+ });
+ }
+
+ checkSensorConflicts();
+ }
}
}
@@ -63,13 +100,77 @@
for (auto& s : state)
{
- auto& sensor = std::get<0>(s);
- std::get<bool>(s) = sensor.get().start();
+ auto& sensor = std::get<sensorPos>(s);
+ std::get<presentPos>(s) = sensor.get().start();
}
- auto present = std::any_of(state.begin(), state.end(),
- [](const auto& s) { return std::get<bool>(s); });
+ auto present = std::any_of(state.begin(), state.end(), [](const auto& s) {
+ return std::get<presentPos>(s);
+ });
setPresence(fan, present);
+
+ // At least one of the contained methods indicated present,
+ // so check that they all agree.
+ if (present)
+ {
+ checkSensorConflicts();
+ }
+}
+
+void AnyOf::checkSensorConflicts()
+{
+ if (!isPowerOn())
+ {
+ return;
+ }
+
+ // If at least one, but not all, sensors indicate present, then
+ // tell the not present ones to log a conflict if not already done.
+ if (std::any_of(
+ state.begin(), state.end(),
+ [this](const auto& s) { return std::get<presentPos>(s); }) &&
+ !std::all_of(state.begin(), state.end(),
+ [this](const auto& s) { return std::get<presentPos>(s); }))
+ {
+ for (auto& [sensor, present, conflict] : state)
+ {
+ if (!present && !conflict)
+ {
+ sensor.get().logConflict(invNamespace + std::get<1>(fan));
+ conflict = true;
+ }
+ }
+ }
+}
+
+void AnyOf::powerStateChanged(bool powerOn)
+{
+ if (powerOn)
+ {
+ // Clear the conflict state from last time
+ std::for_each(state.begin(), state.end(), [](auto& state) {
+ std::get<conflictPos>(state) = false;
+ });
+
+ // Wait to give the fans time to start spinning before
+ // considering power actually on.
+ _powerOnDelayTimer.restartOnce(powerOnDelayTime);
+ }
+ else
+ {
+ _powerOn = false;
+
+ if (_powerOnDelayTimer.isEnabled())
+ {
+ _powerOnDelayTimer.setEnabled(false);
+ }
+ }
+}
+
+void AnyOf::delayedAfterPowerOn()
+{
+ _powerOn = true;
+ checkSensorConflicts();
}
} // namespace presence