Fan floor by median sensor value within a range
Add action to set the floor speed based on sensors from within a defined
valid range and using their median value. The floor speed is selected
from the first map key entry that the median value is less than where 3
or more sensor values are valid. In the case where less than 3 sensor
values are valid, use the highest valid value and default the floor
speed when 0 sensor values are valid.
Tested:
Configured wspoon with this action & correct floor resulted:
Sensor value invalidated outside of range
Single valid ambient sensor
Default floor with no valid sensors(kill ambient service)
Highest value used w/ 2 valid sensors
Middle value used w/ odd number of valid sensors(median)
Average value of middle two valid sensors w/ even number(median)
Change-Id: Ia1599ff13e25dbd7caa7b02c9340cc3e1e9947c6
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/actions.cpp b/control/actions.cpp
index 5bd9e3a..0388b5c 100644
--- a/control/actions.cpp
+++ b/control/actions.cpp
@@ -1,4 +1,5 @@
#include "actions.hpp"
+#include "utility.hpp"
namespace phosphor
{
@@ -286,6 +287,75 @@
};
}
+Action set_floor_from_median_sensor_value(
+ int64_t lowerBound,
+ int64_t upperBound,
+ std::map<int64_t, uint64_t>&& valueToSpeed)
+{
+ return [lowerBound,
+ upperBound,
+ valueToSpeed = std::move(valueToSpeed)](control::Zone& zone,
+ const Group& group)
+ {
+ auto speed = zone.getDefFloor();
+ if (group.size() != 0)
+ {
+ std::vector<int64_t> validValues;
+ for (auto const& member : group)
+ {
+ try
+ {
+ auto value = zone.template getPropertyValue<int64_t>(
+ std::get<pathPos>(member),
+ std::get<intfPos>(member),
+ std::get<propPos>(member));
+ if (value == std::clamp(value, lowerBound, upperBound))
+ {
+ // Sensor value is valid
+ validValues.emplace_back(value);
+ }
+ }
+ catch (const std::out_of_range& oore)
+ {
+ continue;
+ }
+ }
+
+ if (!validValues.empty())
+ {
+ auto median = validValues.front();
+ // Get the determined median value
+ if (validValues.size() == 2)
+ {
+ // For 2 values, use the highest instead of the average
+ // for a thermally safe floor
+ median = *std::max_element(validValues.begin(),
+ validValues.end());
+ }
+ else if (validValues.size() > 2)
+ {
+ median = utility::getMedian(validValues);
+ }
+
+ // Use determined median sensor value to find floor speed
+ auto it = std::find_if(
+ valueToSpeed.begin(),
+ valueToSpeed.end(),
+ [&median](auto const& entry)
+ {
+ return median < entry.first;
+ }
+ );
+ if (it != std::end(valueToSpeed))
+ {
+ speed = (*it).second;
+ }
+ }
+ }
+ zone.setFloor(speed);
+ };
+}
+
} // namespace action
} // namespace control
} // namespace fan