Allow floor speed changes based on sensor values

Given a group of sensor values, the average of those sensor values will
be used in selecting the floor speed for the zone. Each time the speed
is set/updated, any speed requested lower than the current floor will be
overwritten with the floor speed to not allow speeds below the floor.

Change-Id: I4c8e8a2cd66892b9fdc2bc5643e907adddff51f8
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/actions.hpp b/control/actions.hpp
index 8a2675b..a99fa97 100644
--- a/control/actions.hpp
+++ b/control/actions.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <algorithm>
+#include <numeric>
 
 namespace phosphor
 {
@@ -50,6 +51,55 @@
     };
 }
 
+/**
+ * @brief An action to set the floor speed on a zone
+ * @details Based on the average of the defined sensor group values, the floor
+ * speed is selected from the first map key entry that the average sensor value
+ * is less than.
+ *
+ * @param[in] val_to_speed - Ordered map of sensor value-to-speed
+ *
+ * @return Lambda function
+ *     A lambda function to set the zone's floor speed when the average of
+ *     property values within the group is below the lowest sensor value given
+ */
+auto set_floor_from_average_sensor_value(
+        std::map<int64_t, uint64_t>&& val_to_speed)
+{
+    return [val_to_speed = std::move(val_to_speed)](auto& zone, auto& group)
+    {
+        auto speed = zone.getDefFloor();
+        if (group.size() != 0)
+        {
+            auto sumValue = std::accumulate(
+                    group.begin(),
+                    group.end(),
+                    0,
+                    [&zone](int64_t sum, auto const& entry)
+                    {
+                        return sum + zone.template getPropertyValue<int64_t>(
+                                entry.first,
+                                std::get<intfPos>(entry.second),
+                                std::get<propPos>(entry.second));
+                    });
+            auto avgValue= sumValue / group.size();
+            auto it = std::find_if(
+                val_to_speed.begin(),
+                val_to_speed.end(),
+                [&avgValue](auto const& entry)
+                {
+                    return avgValue < entry.first;
+                }
+            );
+            if (it != std::end(val_to_speed))
+            {
+                speed = (*it).second;
+            }
+        }
+        zone.setFloor(speed);
+    };
+}
+
 } // namespace action
 } // namespace control
 } // namespace fan